This commit is contained in:
2025-04-05 21:22:17 +00:00
parent a8fc262398
commit 695e6cce01
112 changed files with 3464 additions and 0 deletions

251
reader/src/App.jsx Normal file
View File

@@ -0,0 +1,251 @@
import { useLayoutEffect, useState, useRef, useEffect } from "react";
import { pdfjs, Document, Page } from "react-pdf";
import { ArrowBackIcon, ArrowForwardIcon, MenuBookIcon, MyLocationIcon } from "./icons/Icons";
import structureOriginal from "./structure.json";
const structure = structureOriginal.map((topic) => ({ ...topic, version: 0 }));
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
export function TopicListView({ selectedIndex, onChange }) {
const itemRefs = useRef([]);
useLayoutEffect(() => {
if (selectedIndex !== null) {
itemRefs.current?.[Math.max(selectedIndex - 3, 0)].scrollIntoView();
}
}, [selectedIndex]);
return (
<>
<div className="flex-1 relative overflow-y-scroll">
{structure.map((topic, i) => (
<div
key={topic.id}
ref={(node) => {
itemRefs.current[i] = node;
}}
onClick={() => onChange(i)}
className={`flex px-2 py-1 rounded-md cursor-pointer border-l-4 ${selectedIndex === i ? "bg-blue-100 border-blue-500" : "border-transparent hover:bg-gray-100"}`}
>
<div
className={`w-6 flex-shrink-0 flex font-medium justify-end ${selectedIndex === i ? "text-blue-600" : "text-blue-800"}`}
>
{i + 1}
</div>
<span className="ml-2">
<span className={`leading-5 ${selectedIndex === i ? "font-medium" : "font-normal"}`}>
{topic.title}
</span>
</span>
</div>
))}
{selectedIndex !== null && (
<div className="sticky bottom-0 p-2 w-full flex flex-col">
<div className="w-full flex justify-between items-center gap-2">
<button
className="w-full p-2 bg-blue-600 hover:bg-blue-700 cursor-pointer truncate rounded-md text-sm text-white text-center shadow-md transition-colors"
onClick={() => onChange(selectedIndex)}
>
<span>Продължи четенето:</span>
<br />
<span className="font-medium">{structure[selectedIndex].title}</span>
</button>
<button
className="px-3 py-3 bg-teal-500 hover:bg-teal-300 cursor-pointer rounded-full flex items-center justify-center shadow-md transition-colors"
onClick={() => {
itemRefs.current?.[Math.max(selectedIndex - 3, 0)].scrollIntoView({
behavior: "smooth",
});
}}
>
<MyLocationIcon className="h-5 w-5" />
</button>
</div>
</div>
)}
</div>
</>
);
}
function Layout({ children, title }) {
return (
<div className="max-w-xl mx-auto h-full relative flex flex-col">
<div className="w-full p-4 font-medium text-large text-white bg-blue-600">
<span className="line-clamp-2">{title}</span>
</div>
{children}
</div>
);
}
export default function App() {
const [isMenuOpen, setIsMenuOpen] = useState(true);
const [selectedIndex, setSelectedIndex] = useState(null);
const [versions, setVersions] = useState(Array.from({ length: structure.length }, () => 0));
function handleChange(i) {
setSelectedIndex(i);
setIsMenuOpen(false);
}
if (isMenuOpen) {
return (
<Layout title="Конспект за Държавен Изпит">
<TopicListView selectedIndex={selectedIndex} onChange={handleChange} />
</Layout>
);
}
return (
<Layout title={`${selectedIndex + 1}: ${structure[selectedIndex].title}`}>
<div className="flex-1 overflow-y-scroll">
<Reader
key={structure[selectedIndex].files[versions[selectedIndex]]}
file={structure[selectedIndex].files[versions[selectedIndex]]}
/>
<div className="absolute bottom-14 flex justify-between p-2 w-full z-999">
<button
className="cursor-pointer p-2 rounded-full bg-blue-600 text-white"
onClick={() => {
setIsMenuOpen(true);
}}
>
<MenuBookIcon className="fill-gray-100" />
</button>
<div className="flex space-x-1">
{structure[selectedIndex].files.map((file, vIndex) => (
<button
key={file}
className={`flex-1 cursor-pointer px-2 py-1 rounded-md text-xs whitespace-nowrap ${
versions[selectedIndex] === vIndex
? "bg-blue-100 text-blue-800 font-medium"
: "bg-gray-100 hover:bg-gray-200"
}`}
onClick={() => {
setVersions((prev) => {
const newVersions = [...prev];
newVersions[selectedIndex] = vIndex;
return newVersions;
});
}}
>
Version {vIndex + 1}
</button>
))}
</div>
</div>
</div>
<div className="w-full flex flex-col space-y-2">
<div className="flex bg-gray-100 border-t border-blue-200 text-center">
{selectedIndex === 0 ? (
<div className="flex-1 border-r border-blue-200" />
) : (
<div className="border-r border-blue-200 w-1/2 flex-1 p-4 hover:bg-blue-200 cursor-pointer flex align-center">
<ArrowBackIcon />
<span className="ml-2 truncate w-full">
{selectedIndex}: {structure[selectedIndex - 1].title}
</span>
</div>
)}
{selectedIndex === structure.length - 1 ? (
<div className="flex-1" />
) : (
<div className="flex-1 p-4 hover:bg-blue-200 w-1/2 cursor-pointer flex align-center justify-end">
<span className="mr-2 w-full truncate">
{selectedIndex + 2}: {structure[selectedIndex + 1].title}
</span>
<ArrowForwardIcon />
</div>
)}
</div>
</div>
</Layout>
);
}
export function Reader({ file }) {
const [content, setContent] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchFile = async () => {
try {
setIsLoading(true);
const response = await fetch(`/files_html/${file}`);
if (!response.ok) {
throw new Error(`Failed to load file: ${response.status}`);
}
let fileContent = await response.text();
// If no head tag, add a basic HTML structure with fonts
fileContent = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
<style>
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
line-height: 1.5;
color: #333;
padding: 20px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding-bottom: 56px;
}
pre, code {
font-family: 'SF Mono', Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
}
</style>
</head>
<body>
${fileContent}
</body>
</html>
`;
setContent(fileContent);
setError(null);
} catch (err) {
console.error("Error loading file:", err);
setError(err.message);
} finally {
setIsLoading(false);
}
};
fetchFile();
}, [file]);
if (error) {
return <div className="text-red-500 p-4 border border-red-300 rounded">Error: {error}</div>;
}
if (isLoading) {
return (
<div className="p-4 flex justify-center items-center h-40">
<div className="animate-pulse">Loading...</div>
</div>
);
}
return (
<div className="w-full h-full border border-gray-300 rounded overflow-hidden">
<iframe
srcDoc={content}
title={`File: ${file}`}
className="w-full h-full border-0"
key={file}
allow="fullscreen"
/>
</div>
);
}

View File

@@ -0,0 +1,76 @@
export function MyLocationIcon({ className }) {
return (
<svg
className={className}
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 0 24 24"
width="24px"
fill="inherit"
>
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z" />
</svg>
);
}
export function MenuBookIcon({ className }) {
return (
<svg
className={className}
xmlns="http://www.w3.org/2000/svg"
enableBackground="new 0 0 24 24"
height="24px"
viewBox="0 0 24 24"
width="24px"
fill="inherit"
>
<g>
<rect fill="none" height="24" width="24" />
</g>
<g>
<g />
<g>
<path d="M21,5c-1.11-0.35-2.33-0.5-3.5-0.5c-1.95,0-4.05,0.4-5.5,1.5c-1.45-1.1-3.55-1.5-5.5-1.5S2.45,4.9,1,6v14.65 c0,0.25,0.25,0.5,0.5,0.5c0.1,0,0.15-0.05,0.25-0.05C3.1,20.45,5.05,20,6.5,20c1.95,0,4.05,0.4,5.5,1.5c1.35-0.85,3.8-1.5,5.5-1.5 c1.65,0,3.35,0.3,4.75,1.05c0.1,0.05,0.15,0.05,0.25,0.05c0.25,0,0.5-0.25,0.5-0.5V6C22.4,5.55,21.75,5.25,21,5z M21,18.5 c-1.1-0.35-2.3-0.5-3.5-0.5c-1.7,0-4.15,0.65-5.5,1.5V8c1.35-0.85,3.8-1.5,5.5-1.5c1.2,0,2.4,0.15,3.5,0.5V18.5z" />
<g>
<path d="M17.5,10.5c0.88,0,1.73,0.09,2.5,0.26V9.24C19.21,9.09,18.36,9,17.5,9c-1.7,0-3.24,0.29-4.5,0.83v1.66 C14.13,10.85,15.7,10.5,17.5,10.5z" />
<path d="M13,12.49v1.66c1.13-0.64,2.7-0.99,4.5-0.99c0.88,0,1.73,0.09,2.5,0.26V11.9c-0.79-0.15-1.64-0.24-2.5-0.24 C15.8,11.66,14.26,11.96,13,12.49z" />
<path d="M17.5,14.33c-1.7,0-3.24,0.29-4.5,0.83v1.66c1.13-0.64,2.7-0.99,4.5-0.99c0.88,0,1.73,0.09,2.5,0.26v-1.52 C19.21,14.41,18.36,14.33,17.5,14.33z" />
</g>
</g>
</g>
</svg>
);
}
export function ArrowBackIcon({ className }) {
return (
<svg
className={className}
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 0 24 24"
width="24px"
fill="inherit"
>
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" />
</svg>
);
}
export function ArrowForwardIcon({ className }) {
return (
<svg
className={className}
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 0 24 24"
width="24px"
fill="inherit"
>
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8-8-8z" />
</svg>
);
}

14
reader/src/index.css Normal file
View File

@@ -0,0 +1,14 @@
@import "tailwindcss";
#root {
height: 100dvh;
}
/*.pdf-container {*/
/* isolation: isolate;*/
/*}*/
.pdf-container {
-webkit-overflow-scrolling: touch;
}

23
reader/src/main.jsx Normal file
View File

@@ -0,0 +1,23 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.jsx";
createRoot(document.getElementById("root")).render(
<StrictMode>
<App />
</StrictMode>,
);
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker
.register("/service-worker.js")
.then((registration) => {
console.log("SW registered:", registration);
})
.catch((error) => {
console.log("SW registration failed:", error);
});
});
}

466
reader/src/structure.json Normal file
View File

@@ -0,0 +1,466 @@
[
{
"id": "T001",
"title": "Предмет и задачи на вътрешната медицина. Раздели на вътрешните болести",
"files": [
"f001.html"
]
},
{
"id": "T002",
"title": "Тумори на белия дроб",
"files": [
"f002.html"
]
},
{
"id": "T003",
"title": "Белодробен тромбоемболизъм",
"files": [
"f003.html",
"f096.html"
]
},
{
"id": "T004",
"title": "Плеврити",
"files": [
"f004.html",
"f097.html"
]
},
{
"id": "T005",
"title": "Белодробна туберколоза-етиология, патогенеза, и клинична картина. Първична и вторична ТБК. Лечение и профилактика на туберкулоза",
"files": [
"f005.html",
"f098.html"
]
},
{
"id": "T006",
"title": "Дихателна недостатъчност",
"files": [
"f006.html"
]
},
{
"id": "T007",
"title": "Основни симптоми и синдроми при заболявания на ССС Физикални и специални методи на изследване на ССС",
"files": [
"f007.html",
"f031.html"
]
},
{
"id": "T008",
"title": "Болест и здраве. Етилогия и патогенеза на болестите. Периоди на болестта",
"files": [
"f008.html"
]
},
{
"id": "T009",
"title": "Болести на надбъбречните жлези: Хиперкортицизъм. Хипокортицизъм",
"files": [
"f009.html",
"f047.html"
]
},
{
"id": "T010",
"title": "Агония. Клинична смърт. Биологична смърт",
"files": [
"f010.html"
]
},
{
"id": "T011",
"title": "Физикални методи на изследване на стомашно-чревния тракт",
"files": [
"f011.html"
]
},
{
"id": "T012",
"title": "Основни симптоми и синдроми при заболявания на отделителната система. Функционално изследване на ОС",
"files": [
"f012.html"
]
},
{
"id": "T013",
"title": "Основни класически методи на изследване във вътрешната медицина-анамнеза. Физикални методи на изследване на болните –оглед, палпация, перкусия, аускултация. Специални методи на изследване на пациентите",
"files": [
"f013.html"
]
},
{
"id": "T014",
"title": "Деформираща артроза ОСТЕОАРТРОЗА",
"files": [
"f014.html"
]
},
{
"id": "T015",
"title": "Сегашно състояние-обективен статус на болния. Клинична диагноза и прогноза. Проследяване на болния-декурзус",
"files": [
"f015.html"
]
},
{
"id": "T016",
"title": "Изследване на ДС. Основни симптоми и синдроми на заболявания на ДС. Физиклани и спциални методи на изследване",
"files": [
"f016.html"
]
},
{
"id": "T017",
"title": "Остър и хроничен бронхит. Белодробен емфизем. ХОББ",
"files": [
"f017.html",
"f057.html"
]
},
{
"id": "T018",
"title": "Пневмонии: класификация, клиника, лечение",
"files": [
"f018.html",
"f075.html"
]
},
{
"id": "T019",
"title": "Бронхиектазии. Белодробен абцес",
"files": [
"f019.html"
]
},
{
"id": "T020",
"title": "Хипертонична болест- рискови фактори;патогенеза;клиника, лечение",
"files": [
"f020.html",
"f035.html"
]
},
{
"id": "T021",
"title": "Изследване на стомашно-чревния тракт. Анемнеза. Основни симптоми и синдроми при заболявания на стомашно-чревния тракт",
"files": [
"f021.html",
"f054.html"
]
},
{
"id": "T022",
"title": "Пиелонефрити",
"files": [
"f022.html",
"f074.html"
]
},
{
"id": "T023",
"title": "Хемолитични анемии вследствие на вътре и извънеритроцитни фактори, вродени и придобити",
"files": [
"f023.html",
"f084.html"
]
},
{
"id": "T024",
"title": "Хеморагични диатези хемофилия, есенциална тромбоцитопения, капиляротоксикоза",
"files": [
"f024.html",
"f091.html"
]
},
{
"id": "T025",
"title": "Бластна левкоза. Хронична миелолевкоза",
"files": [
"f025.html",
"f087.html"
]
},
{
"id": "T026",
"title": "Нехочкинови и хочкинови лимфоми",
"files": [
"f026.html",
"f089.html"
]
},
{
"id": "T027",
"title": "Остри екзогенни интоксикации. Общи принципи и правила в лечението на острите екзогенни отравяния. Поведение на медицинската сестра и грижи за болния с остро отравяне",
"files": [
"f027.html",
"f093.html"
]
},
{
"id": "T028",
"title": "Алергия. Алергични заболявания. Анафилактичен шок. Поведение на медицинската сестра при спешни алергични състояния",
"files": [
"f028.html",
"f094.html"
]
},
{
"id": "T029",
"title": "Основни класически методи на изследване във вътрешната медицина-анамнеза. Физикални методи за изледване на болните-оглед, палпация, перкусия, аускултация. Сегашно състояние-обективен статус на болния. Клинична диагноза и прогноза. Проследяване на болния-Декурзус",
"files": [
"f029.html"
]
},
{
"id": "T030",
"title": "Дихателна недостатъчност- остра и хронична. Етиология, степени, клиника",
"files": [
"f030.html"
]
},
{
"id": "T031",
"title": "Ревматизъм",
"files": [
"f032.html",
"f034.html"
]
},
{
"id": "T032",
"title": "Ендокардити, перикардити",
"files": [
"f033.html",
"f039.html"
]
},
{
"id": "T033",
"title": "Остра периферна сърдечно-съдова недостатъчност. Кардиологичен шок",
"files": [
"f036.html"
]
},
{
"id": "T034",
"title": "Лечение на СН и поведение на м. с",
"files": [
"f037.html"
]
},
{
"id": "T035",
"title": "Ритъмни нарушения на сърдечната дейност. Проводни нарушения на сърдечната дейност",
"files": [
"f038.html",
"f044.html"
]
},
{
"id": "T036",
"title": "ИБС: етиология, рискови фактори, патофизиология. Стенокардия",
"files": [
"f040.html",
"f045.html"
]
},
{
"id": "T037",
"title": "Изследване на ДС. Основни симптоми и синдроми при заболявания на ДС. Физикални и специаални методи на изследване на ДС",
"files": [
"f041.html"
]
},
{
"id": "T038",
"title": "ИБС: етиология, рискови фактори, патофизиология. Инфаркт на миокарда",
"files": [
"f042.html",
"f046.html"
]
},
{
"id": "T039",
"title": "Болести на хипофизата- Акромегалия ;Безвкусен диабет",
"files": [
"f043.html",
"f048.html"
]
},
{
"id": "T040",
"title": "Захарен диабет-етиология, патогенеза, класификация, клиника. Диабетна кетоацидоза и хипокликемична кома. Поведение на МС при диабетно болен в кома",
"files": [
"f049.html"
]
},
{
"id": "T041",
"title": "Болести на щитовидната жлеза: Тиреотоксикоза. Микседем. Ендемична гуша",
"files": [
"f050.html"
]
},
{
"id": "T042",
"title": "Захарен диабет късни усложнения. Захарен диабет- диета и медикаментозно лечение",
"files": [
"f051.html",
"f056.html"
]
},
{
"id": "T043",
"title": "Затлъстяване. Подагра",
"files": [
"f052.html",
"f053.html"
]
},
{
"id": "T044",
"title": "Физикални и специални методи на изследване на стомашно-чревния тракт",
"files": [
"f055.html"
]
},
{
"id": "T045",
"title": "Гастрити. ГЕРБ",
"files": [
"f058.html",
"f061.html"
]
},
{
"id": "T046",
"title": "Язвена болест. Рак на стомаха",
"files": [
"f059.html",
"f063.html"
]
},
{
"id": "T047",
"title": "Ентерити и колити. Рак на дебелото черво",
"files": [
"f060.html",
"f065.html"
]
},
{
"id": "T048",
"title": "Основни симптоми и синдроми при заболяване на черния дроб и жлъчните пътища. Жълтеница, портална хипертония, асцит. Анамнеза, физикални и специални методи за изследване на черния дроб и жлъчните пътища",
"files": [
"f062.html"
]
},
{
"id": "T049",
"title": "Хронични хепатити. Чернодробна цироза",
"files": [
"f064.html",
"f071.html"
]
},
{
"id": "T050",
"title": "Холелитиаза. Холецистити",
"files": [
"f066.html",
"f073.html"
]
},
{
"id": "T051",
"title": "Основни симптоми и синдроми при заболяване на черния дроб и жлъчните пътища. Анамнеза, физикални и специални методи за изледване на черния дроб и жлъчните пътища",
"files": [
"f067.html",
"f068.html"
]
},
{
"id": "T052",
"title": "Остър и хроничен гломерулонефрит",
"files": [
"f069.html",
"f070.html"
]
},
{
"id": "T053",
"title": "Нефролитиаза",
"files": [
"f072.html",
"f077.html"
]
},
{
"id": "T054",
"title": "Остра бъбречна и хронична бъбречна недостатъчност",
"files": [
"f076.html"
]
},
{
"id": "T055",
"title": "Балканска ендемична нефропатия. Бъбречна поликистозна болест. Бъбречна туберкулоза",
"files": [
"f078.html",
"f082.html"
]
},
{
"id": "T056",
"title": "Ревмтоиден артрит",
"files": [
"f079.html",
"f085.html"
]
},
{
"id": "T057",
"title": "Деформираща артроза",
"files": [
"f080.html",
"f081.html"
]
},
{
"id": "T058",
"title": "Желязодефицитни анемии",
"files": [
"f083.html",
"f088.html"
]
},
{
"id": "T059",
"title": "Витамин В 12 дефицитни анемии",
"files": [
"f086.html",
"f090.html"
]
},
{
"id": "T060",
"title": "Бронхоектазии. Белодробен абцес",
"files": [
"f092.html"
]
},
{
"id": "T061",
"title": "Tумори на белия дроб",
"files": [
"f095.html"
]
}
]