This commit is contained in:
Tomas Mirchev 2025-04-06 02:58:55 +00:00
parent 54becc8fb3
commit 702faf2fae
12 changed files with 1231 additions and 872 deletions

85
reader/dist/assets/index-9ksktKl0.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
reader/dist/assets/index-_uXYzsY_.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -8,8 +8,8 @@
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Vite + React</title> <title>Vite + React</title>
<script type="module" crossorigin src="/assets/index-DqzzgF7m.js"></script> <script type="module" crossorigin src="/assets/index-9ksktKl0.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CzJs6pbA.css"> <link rel="stylesheet" crossorigin href="/assets/index-_uXYzsY_.css">
</head> </head>
<body> <body>

View File

@ -14,7 +14,9 @@
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-pdf": "^9.2.1", "react-pdf": "^9.2.1",
"tailwindcss": "^4.1.2" "react-router": "^7.5.0",
"tailwindcss": "^4.1.2",
"zustand": "^5.0.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.21.0", "@eslint/js": "^9.21.0",

View File

@ -20,9 +20,15 @@ importers:
react-pdf: react-pdf:
specifier: ^9.2.1 specifier: ^9.2.1
version: 9.2.1(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) version: 9.2.1(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-router:
specifier: ^7.5.0
version: 7.5.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
tailwindcss: tailwindcss:
specifier: ^4.1.2 specifier: ^4.1.2
version: 4.1.2 version: 4.1.2
zustand:
specifier: ^5.0.3
version: 5.0.3(@types/react@19.1.0)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0))
devDependencies: devDependencies:
'@eslint/js': '@eslint/js':
specifier: ^9.21.0 specifier: ^9.21.0
@ -560,6 +566,9 @@ packages:
'@types/babel__traverse@7.20.7': '@types/babel__traverse@7.20.7':
resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==}
'@types/cookie@0.6.0':
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
'@types/estree@1.0.7': '@types/estree@1.0.7':
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
@ -655,6 +664,10 @@ packages:
convert-source-map@2.0.0: convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
cookie@1.0.2:
resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
engines: {node: '>=18'}
cross-spawn@7.0.6: cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -1116,6 +1129,16 @@ packages:
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
react-router@7.5.0:
resolution: {integrity: sha512-estOHrRlDMKdlQa6Mj32gIks4J+AxNsYoE0DbTTxiMy2mPzZuWSDU+N85/r1IlNR7kGfznF3VCUlvc5IUO+B9g==}
engines: {node: '>=20.0.0'}
peerDependencies:
react: '>=18'
react-dom: '>=18'
peerDependenciesMeta:
react-dom:
optional: true
react@19.1.0: react@19.1.0:
resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -1148,6 +1171,9 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
set-cookie-parser@2.7.1:
resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
shebang-command@2.0.0: shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -1201,6 +1227,9 @@ packages:
tunnel-agent@0.6.0: tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
turbo-stream@2.4.0:
resolution: {integrity: sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==}
type-check@0.4.0: type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
@ -1214,6 +1243,11 @@ packages:
uri-js@4.4.1: uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
use-sync-external-store@1.5.0:
resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
util-deprecate@1.0.2: util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
@ -1279,6 +1313,24 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'} engines: {node: '>=10'}
zustand@5.0.3:
resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==}
engines: {node: '>=12.20.0'}
peerDependencies:
'@types/react': '>=18.0.0'
immer: '>=9.0.6'
react: '>=18.0.0'
use-sync-external-store: '>=1.2.0'
peerDependenciesMeta:
'@types/react':
optional: true
immer:
optional: true
react:
optional: true
use-sync-external-store:
optional: true
snapshots: snapshots:
'@ampproject/remapping@2.3.0': '@ampproject/remapping@2.3.0':
@ -1691,6 +1743,8 @@ snapshots:
dependencies: dependencies:
'@babel/types': 7.27.0 '@babel/types': 7.27.0
'@types/cookie@0.6.0': {}
'@types/estree@1.0.7': {} '@types/estree@1.0.7': {}
'@types/json-schema@7.0.15': {} '@types/json-schema@7.0.15': {}
@ -1793,6 +1847,8 @@ snapshots:
convert-source-map@2.0.0: {} convert-source-map@2.0.0: {}
cookie@1.0.2: {}
cross-spawn@7.0.6: cross-spawn@7.0.6:
dependencies: dependencies:
path-key: 3.1.1 path-key: 3.1.1
@ -2246,6 +2302,16 @@ snapshots:
react-refresh@0.14.2: {} react-refresh@0.14.2: {}
react-router@7.5.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@types/cookie': 0.6.0
cookie: 1.0.2
react: 19.1.0
set-cookie-parser: 2.7.1
turbo-stream: 2.4.0
optionalDependencies:
react-dom: 19.1.0(react@19.1.0)
react@19.1.0: {} react@19.1.0: {}
readable-stream@3.6.2: readable-stream@3.6.2:
@ -2293,6 +2359,8 @@ snapshots:
semver@7.7.1: semver@7.7.1:
optional: true optional: true
set-cookie-parser@2.7.1: {}
shebang-command@2.0.0: shebang-command@2.0.0:
dependencies: dependencies:
shebang-regex: 3.0.0 shebang-regex: 3.0.0
@ -2353,6 +2421,8 @@ snapshots:
safe-buffer: 5.2.1 safe-buffer: 5.2.1
optional: true optional: true
turbo-stream@2.4.0: {}
type-check@0.4.0: type-check@0.4.0:
dependencies: dependencies:
prelude-ls: 1.2.1 prelude-ls: 1.2.1
@ -2367,6 +2437,11 @@ snapshots:
dependencies: dependencies:
punycode: 2.3.1 punycode: 2.3.1
use-sync-external-store@1.5.0(react@19.1.0):
dependencies:
react: 19.1.0
optional: true
util-deprecate@1.0.2: util-deprecate@1.0.2:
optional: true optional: true
@ -2396,3 +2471,9 @@ snapshots:
yallist@3.1.1: {} yallist@3.1.1: {}
yocto-queue@0.1.0: {} yocto-queue@0.1.0: {}
zustand@5.0.3(@types/react@19.1.0)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)):
optionalDependencies:
'@types/react': 19.1.0
react: 19.1.0
use-sync-external-store: 1.5.0(react@19.1.0)

View File

@ -1,314 +0,0 @@
import { useLayoutEffect, useState, useRef, useEffect } from "react";
import {
ArrowBackIcon,
ArrowForwardIcon,
MenuBookIcon,
MyLocationIcon,
TitleIcon,
WidthIcon,
} from "./icons/Icons";
import structureOriginal from "./structure.json";
const structure = structureOriginal.map((topic) => ({ ...topic, version: 0 }));
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, displayTitle }) {
return (
<div className="max-w-7xl mx-auto h-full relative flex flex-col">
{displayTitle && (
<div className="w-full px-4 py-2 font-medium text-large text-white bg-blue-600">
<span>{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));
const [displayTitle, setDisplayTitle] = useState(true);
const [isWideMode, setIsWideMode] = useState(false);
function handleChange(i) {
setSelectedIndex(i);
setIsMenuOpen(false);
}
if (isMenuOpen) {
return (
<Layout title="Конспект за Държавен Изпит" displayTitle>
<TopicListView selectedIndex={selectedIndex} onChange={handleChange} />
</Layout>
);
}
return (
<Layout
title={`${selectedIndex + 1}: ${structure[selectedIndex].title}`}
displayTitle={displayTitle}
>
<div className="flex-1">
<Reader
file={structure[selectedIndex].files[versions[selectedIndex]]}
compact={!isWideMode}
/>
<div className="absolute bottom-10 flex justify-between px-4 py-2 w-full z-999">
<div className="flex space-x-2">
<button
className="cursor-pointer p-2 rounded-full bg-blue-600 text-white"
onClick={() => {
setIsMenuOpen(true);
}}
>
<MenuBookIcon className="fill-gray-100" />
</button>
<button
className={`cursor-pointer p-2 rounded-full text-white border ${displayTitle ? "bg-blue-100 border-blue-400" : "bg-gray-100 border-gray-400"}`}
onClick={() => {
setDisplayTitle((prev) => !prev);
}}
>
<TitleIcon className="fill-gray-600" />
</button>
{window.innerWidth > 576 && (
<button
className={`cursor-pointer p-2 rounded-full text-white border ${!isWideMode ? "bg-blue-100 border-blue-400" : "bg-gray-100 border-gray-400"}`}
onClick={() => {
setIsWideMode((prev) => !prev);
}}
>
<WidthIcon className="fill-gray-600" />
</button>
)}
</div>
{structure[selectedIndex].files.length > 1 && (
<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" />
) : (
<button
onClick={() => setSelectedIndex((prev) => prev - 1)}
className="border-r border-blue-200 w-1/2 flex-1 px-4 py-2 hover:bg-blue-200 cursor-pointer flex align-center justify-start"
>
<ArrowBackIcon />
<span className="ml-2 truncate w-full ">
{selectedIndex}: {structure[selectedIndex - 1].title}
</span>
</button>
)}
{selectedIndex === structure.length - 1 ? (
<div className="flex-1" />
) : (
<button
onClick={() => setSelectedIndex((prev) => prev + 1)}
className="flex-1 px-4 py-2 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 />
</button>
)}
</div>
</div>
</Layout>
);
}
export function Reader({ file, compact }) {
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;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding-bottom: 40px;
}
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 <DelayedLoader />;
}
return (
<div className={`w-full h-full overflow-hidden ${compact ? "max-w-xl mx-auto" : ""}`}>
<iframe
srcDoc={content}
title={`File: ${file}`}
className="w-full h-full border-0"
key={file}
allow="fullscreen"
/>
</div>
);
}
const DelayedLoader = ({
delayMs = 2000,
className = "p-4 flex justify-center items-center h-40",
text = "Loading...",
}) => {
const [showLoader, setShowLoader] = useState(false);
useEffect(() => {
// Set a timeout to show the loader after the specified delay
const timer = setTimeout(() => {
setShowLoader(true);
}, delayMs);
// Clear the timeout on unmount
return () => clearTimeout(timer);
}, []); // Empty dependency array - only run on mount
// Only render if the delay has passed
if (showLoader) {
return (
<div className={className}>
<div className="animate-pulse">{text}</div>
</div>
);
}
// Return null before delay threshold
return null;
};

View File

@ -1,23 +1,326 @@
import { StrictMode } from "react"; import { StrictMode, useLayoutEffect, useRef, useState, useEffect } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { useParams, Navigate, BrowserRouter, Link, Outlet, Route, Routes } from "react-router";
import "./index.css"; import "./index.css";
import App from "./App.jsx"; import { useShallow } from "zustand/shallow";
import { useStore } from "./store.js";
import {
ArrowBackIcon,
ArrowForwardIcon,
MenuBookIcon,
MyLocationIcon,
TitleIcon,
WidthIcon,
} from "./icons/Icons";
createRoot(document.getElementById("root")).render( createRoot(document.getElementById("root")).render(
<StrictMode> <StrictMode>
<App /> <BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<TopicListView />} />
<Route path=":topicId" element={<FileReader />} />
</Route>
</Routes>
</BrowserRouter>
</StrictMode>, </StrictMode>,
); );
if ("serviceWorker" in navigator) { function Layout() {
window.addEventListener("load", () => { const params = useParams();
navigator.serviceWorker const topic = useStore((state) => params.topicId && state.topics.byId[params.topicId]);
.register("/service-worker.js") const config = useStore((state) => state.config);
.then((registration) => {
console.log("SW registered:", registration); return (
}) <div className="max-w-7xl mx-auto h-full relative flex flex-col">
.catch((error) => { {config.displayTitle && (
console.log("SW registration failed:", error); <div className="w-full px-4 py-2 font-medium text-large text-white bg-blue-600">
}); <span>{topic ? `${topic.index + 1}: ${topic.title}` : "Конспект за Държавен Изпит"}</span>
}); </div>
)}
<Outlet />
</div>
);
} }
export function TopicListView() {
const itemRefs = useRef({});
const topics = useStore(
useShallow((state) => state.topics.allIds.map((id) => state.topics.byId[id])),
);
const selectedTopic = useStore((state) => state.selectedTopic);
const selectTopic = useStore((state) => state.selectTopic);
useLayoutEffect(() => {
if (selectedTopic && selectedTopic.id !== null) {
itemRefs.current?.[Math.max(selectedTopic.index - 3, 0)].scrollIntoView();
}
}, [selectedTopic]);
return (
<>
<div className="flex-1 overflow-y-scroll">
{topics.map((topic, i) => (
<Link
key={topic.id}
ref={(node) => {
itemRefs.current[i] = node;
}}
to={`/${topic.id}`}
onClick={() => selectTopic(topic.id)}
className={`flex px-2 py-1 rounded-md cursor-pointer border-l-4 ${topic.id === selectedTopic?.id ? "bg-blue-100 border-blue-500" : "border-transparent hover:bg-gray-100"}`}
>
<div
className={`w-6 flex-shrink-0 flex font-medium justify-end ${topic.id === selectedTopic?.id ? "text-blue-600" : "text-blue-800"}`}
>
{i + 1}
</div>
<span className="ml-2">
<span
className={`leading-5 ${topic.id === selectedTopic?.id ? "font-medium" : "font-normal"}`}
>
{topic.title}
</span>
</span>
</Link>
))}
{selectedTopic !== null && (
<div className="sticky bottom-0 p-4 w-full flex flex-col">
<div className="w-full flex justify-between items-center gap-2">
<Link
to={`/${selectedTopic.id}`}
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"
>
<span>Продължи четенето:</span>
<br />
<span className="font-medium">{selectedTopic.title}</span>
</Link>
<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(selectedTopic.index - 3, 0)].scrollIntoView({
behavior: "smooth",
});
}}
>
<MyLocationIcon className="h-5 w-5" />
</button>
</div>
</div>
)}
</div>
</>
);
}
export function FileReader() {
const params = useParams();
const topicsById = useStore((state) => state.topics.byId);
const selectTopic = useStore((state) => state.selectTopic);
const topic = topicsById[params.topicId];
if (!topic) {
return <Navigate to="/" replace />;
}
selectTopic(topic.id);
return <Reader topic={topic} />;
}
export function Reader({ topic }) {
console.log("render!");
const config = useStore((state) => state.config);
const changeConfig = useStore((state) => state.changeConfig);
const selectedVersion = useStore((state) => state.topicVersions[topic.id] ?? 0);
const selectVersion = useStore((state) => state.selectTopicVersion);
const getTopicAtIndex = useStore((state) => state.getTopicAtIndex);
const prevTopic = getTopicAtIndex(topic.index - 1);
const nextTopic = getTopicAtIndex(topic.index + 1);
return (
<>
<div className="flex-1">
<PDFViewer file={topic.files[selectedVersion]} compact={config.narrowMode} />
<div className="absolute bottom-10 flex justify-between px-4 py-2 w-full z-999">
<div className="flex space-x-2">
<Link to="/" className="cursor-pointer p-2 rounded-full bg-blue-600 text-white">
<MenuBookIcon className="fill-gray-100" />
</Link>
<button
className={`cursor-pointer p-2 rounded-full text-white border ${config.displayTitle ? "bg-blue-100 border-blue-400" : "bg-gray-100 border-gray-400"}`}
onClick={() => changeConfig({ displayTitle: !config.displayTitle })}
>
<TitleIcon className="fill-gray-600" />
</button>
{window.innerWidth > 576 && (
<button
className={`cursor-pointer p-2 rounded-full text-white border ${config.narrowMode ? "bg-blue-100 border-blue-400" : "bg-gray-100 border-gray-400"}`}
onClick={() => changeConfig({ narrowMode: !config.narrowMode })}
>
<WidthIcon className="fill-gray-600" />
</button>
)}
</div>
{topic.files.length > 1 && (
<div className="flex space-x-1">
{topic.files.map((file, vIndex) => (
<button
key={file}
className={`flex-1 cursor-pointer px-2 py-1 rounded-md text-xs whitespace-nowrap ${
selectedVersion === vIndex
? "bg-blue-100 text-blue-800 font-medium"
: "bg-gray-100 hover:bg-gray-200"
}`}
onClick={() => selectVersion(topic.id, vIndex)}
>
Версия {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">
{topic.isFirst ? (
<div className="flex-1 border-r border-blue-200" />
) : (
<Link
to={`/${prevTopic.id}`}
className="border-r border-blue-200 w-1/2 flex-1 px-4 py-2 hover:bg-blue-200 cursor-pointer flex align-center justify-start"
>
<ArrowBackIcon />
<span className="ml-2 truncate w-full ">
{prevTopic.index + 1}: {prevTopic.title}
</span>
</Link>
)}
{topic.isLast ? (
<div className="flex-1" />
) : (
<Link
to={`/${nextTopic.id}`}
className="flex-1 px-4 py-2 hover:bg-blue-200 w-1/2 cursor-pointer flex align-center justify-end"
>
<span className="mr-2 w-full truncate">
{nextTopic.index + 1}: {nextTopic.title}
</span>
<ArrowForwardIcon />
</Link>
)}
</div>
</div>
</>
);
}
export function PDFViewer({ file, compact }) {
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;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding-bottom: 40px;
}
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 <DelayedLoader />;
}
return (
<div className={`w-full h-full overflow-hidden ${compact ? "max-w-xl mx-auto" : ""}`}>
<iframe
srcDoc={content}
title={`File: ${file}`}
className="w-full h-full border-0"
key={file}
allow="fullscreen"
/>
</div>
);
}
const DelayedLoader = ({
delayMs = 2000,
className = "p-4 flex justify-center items-center h-40",
text = "Loading...",
}) => {
const [showLoader, setShowLoader] = useState(false);
useEffect(() => {
// Set a timeout to show the loader after the specified delay
const timer = setTimeout(() => {
setShowLoader(true);
}, delayMs);
// Clear the timeout on unmount
return () => clearTimeout(timer);
}, []); // Empty dependency array - only run on mount
// Only render if the delay has passed
if (showLoader) {
return (
<div className={className}>
<div className="animate-pulse">{text}</div>
</div>
);
}
// Return null before delay threshold
return null;
};

28
reader/src/store.js Normal file
View File

@ -0,0 +1,28 @@
import { create } from "zustand";
import topics from "./topics.json";
export const useStore = create((set, get) => ({
topics,
topicVersions: {},
getTopicAtIndex: (index) => {
const topicId = get().topics.allIds[index];
if (!topicId) {
return null;
}
return get().topics.byId[topicId];
},
selectTopicVersion: (id, version) =>
set({ topicVersions: { ...get().topicVersions, [id]: version } }),
selectedTopic: null,
selectTopic: (id) => {
const topic = get().topics.byId[id];
if (topic) {
set({ selectedTopic: topic });
}
},
config: {
displayTitle: true,
narrowMode: true,
},
changeConfig: (config) => set({ config: { ...get().config, ...config } }),
}));

View File

@ -1,466 +0,0 @@
[
{
"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"
]
}
]

714
reader/src/topics.json Normal file
View File

@ -0,0 +1,714 @@
{
"byId": {
"a4d9fa36-8ed7-4d80-a108-eac209ca01a8": {
"id": "a4d9fa36-8ed7-4d80-a108-eac209ca01a8",
"title": "Предмет и задачи на вътрешната медицина. Раздели на вътрешните болести",
"files": [
"f001.html"
],
"index": 0,
"isFirst": true,
"isLast": false
},
"f7fed0c0-15d9-439c-a3ef-24cec673dd44": {
"id": "f7fed0c0-15d9-439c-a3ef-24cec673dd44",
"title": "Тумори на белия дроб",
"files": [
"f002.html"
],
"index": 1,
"isFirst": false,
"isLast": false
},
"b20a76c0-59a1-4f0a-bb33-39c4bf437eac": {
"id": "b20a76c0-59a1-4f0a-bb33-39c4bf437eac",
"title": "Белодробен тромбоемболизъм",
"files": [
"f003.html",
"f096.html"
],
"index": 2,
"isFirst": false,
"isLast": false
},
"a2a3a830-9f1d-4dd0-ab52-c42ae66c17b3": {
"id": "a2a3a830-9f1d-4dd0-ab52-c42ae66c17b3",
"title": "Плеврити",
"files": [
"f004.html",
"f097.html"
],
"index": 3,
"isFirst": false,
"isLast": false
},
"38899f8f-2291-4923-ae59-928181c88bbd": {
"id": "38899f8f-2291-4923-ae59-928181c88bbd",
"title": "Белодробна туберколоза-етиология, патогенеза, и клинична картина. Първична и вторична ТБК. Лечение и профилактика на туберкулоза",
"files": [
"f005.html",
"f098.html"
],
"index": 4,
"isFirst": false,
"isLast": false
},
"b0460271-a4e2-4e82-a7ea-0f98689fcbb6": {
"id": "b0460271-a4e2-4e82-a7ea-0f98689fcbb6",
"title": "Дихателна недостатъчност",
"files": [
"f006.html"
],
"index": 5,
"isFirst": false,
"isLast": false
},
"9db34720-4756-4469-bbf0-1666d4b6c65f": {
"id": "9db34720-4756-4469-bbf0-1666d4b6c65f",
"title": "Основни симптоми и синдроми при заболявания на ССС Физикални и специални методи на изследване на ССС",
"files": [
"f007.html",
"f031.html"
],
"index": 6,
"isFirst": false,
"isLast": false
},
"c12799f8-63d2-4d76-88b9-e0fa68def5a4": {
"id": "c12799f8-63d2-4d76-88b9-e0fa68def5a4",
"title": "Болест и здраве. Етилогия и патогенеза на болестите. Периоди на болестта",
"files": [
"f008.html"
],
"index": 7,
"isFirst": false,
"isLast": false
},
"c200281a-d2cd-4ca6-b6d3-114db5753fc5": {
"id": "c200281a-d2cd-4ca6-b6d3-114db5753fc5",
"title": "Болести на надбъбречните жлези: Хиперкортицизъм. Хипокортицизъм",
"files": [
"f009.html",
"f047.html"
],
"index": 8,
"isFirst": false,
"isLast": false
},
"f191d184-a26f-41aa-b213-ead39c22f4c1": {
"id": "f191d184-a26f-41aa-b213-ead39c22f4c1",
"title": "Агония. Клинична смърт. Биологична смърт",
"files": [
"f010.html"
],
"index": 9,
"isFirst": false,
"isLast": false
},
"6b01e219-49d9-4ced-ae83-0e21a6cba53c": {
"id": "6b01e219-49d9-4ced-ae83-0e21a6cba53c",
"title": "Физикални методи на изследване на стомашно-чревния тракт",
"files": [
"f011.html"
],
"index": 10,
"isFirst": false,
"isLast": false
},
"bc201e49-52a3-42da-b47a-ed786576b377": {
"id": "bc201e49-52a3-42da-b47a-ed786576b377",
"title": "Основни симптоми и синдроми при заболявания на отделителната система. Функционално изследване на ОС",
"files": [
"f012.html"
],
"index": 11,
"isFirst": false,
"isLast": false
},
"1d71aeff-1637-493f-93ae-c7126367528c": {
"id": "1d71aeff-1637-493f-93ae-c7126367528c",
"title": "Основни класически методи на изследване във вътрешната медицина-анамнеза. Физикални методи на изследване на болните –оглед, палпация, перкусия, аускултация. Специални методи на изследване на пациентите",
"files": [
"f013.html"
],
"index": 12,
"isFirst": false,
"isLast": false
},
"7fcb4e5c-ef95-4fb0-a6a9-174318fb7aef": {
"id": "7fcb4e5c-ef95-4fb0-a6a9-174318fb7aef",
"title": "Деформираща артроза ОСТЕОАРТРОЗА",
"files": [
"f014.html"
],
"index": 13,
"isFirst": false,
"isLast": false
},
"5617f428-8057-42a4-8445-39085b5d5561": {
"id": "5617f428-8057-42a4-8445-39085b5d5561",
"title": "Сегашно състояние-обективен статус на болния. Клинична диагноза и прогноза. Проследяване на болния-декурзус",
"files": [
"f015.html"
],
"index": 14,
"isFirst": false,
"isLast": false
},
"501db50d-1cdf-425d-ac2e-e994b5abeba0": {
"id": "501db50d-1cdf-425d-ac2e-e994b5abeba0",
"title": "Изследване на ДС. Основни симптоми и синдроми на заболявания на ДС. Физиклани и спциални методи на изследване",
"files": [
"f016.html"
],
"index": 15,
"isFirst": false,
"isLast": false
},
"db96af60-ee77-4bdb-b4e3-edf93b274593": {
"id": "db96af60-ee77-4bdb-b4e3-edf93b274593",
"title": "Остър и хроничен бронхит. Белодробен емфизем. ХОББ",
"files": [
"f017.html",
"f057.html"
],
"index": 16,
"isFirst": false,
"isLast": false
},
"7fa4d0eb-0243-4cf7-8667-7cf130d92f2c": {
"id": "7fa4d0eb-0243-4cf7-8667-7cf130d92f2c",
"title": "Пневмонии: класификация, клиника, лечение",
"files": [
"f018.html",
"f075.html"
],
"index": 17,
"isFirst": false,
"isLast": false
},
"2cc4aa8c-978f-4d61-aa3c-9199a955682a": {
"id": "2cc4aa8c-978f-4d61-aa3c-9199a955682a",
"title": "Бронхиектазии. Белодробен абцес",
"files": [
"f019.html"
],
"index": 18,
"isFirst": false,
"isLast": false
},
"20b8b133-cb95-4855-a19b-50a0d2c64f21": {
"id": "20b8b133-cb95-4855-a19b-50a0d2c64f21",
"title": "Хипертонична болест- рискови фактори; патогенеза; клиника, лечение",
"files": [
"f020.html",
"f035.html"
],
"index": 19,
"isFirst": false,
"isLast": false
},
"ae779ecf-2f35-445e-91b1-a418ac7cf9cb": {
"id": "ae779ecf-2f35-445e-91b1-a418ac7cf9cb",
"title": "Изследване на стомашно-чревния тракт. Анемнеза. Основни симптоми и синдроми при заболявания на стомашно-чревния тракт",
"files": [
"f021.html",
"f054.html"
],
"index": 20,
"isFirst": false,
"isLast": false
},
"e4d7fb75-96f3-421d-9c1d-cd05bccc729b": {
"id": "e4d7fb75-96f3-421d-9c1d-cd05bccc729b",
"title": "Пиелонефрити",
"files": [
"f022.html",
"f074.html"
],
"index": 21,
"isFirst": false,
"isLast": false
},
"969617b2-ee8b-4d95-a24a-911adf4a7d9f": {
"id": "969617b2-ee8b-4d95-a24a-911adf4a7d9f",
"title": "Хемолитични анемии вследствие на вътре и извънеритроцитни фактори, вродени и придобити",
"files": [
"f023.html",
"f084.html"
],
"index": 22,
"isFirst": false,
"isLast": false
},
"61f037df-1000-402f-a0f5-ffcbde8bb5c7": {
"id": "61f037df-1000-402f-a0f5-ffcbde8bb5c7",
"title": "Хеморагични диатези хемофилия, есенциална тромбоцитопения, капиляротоксикоза",
"files": [
"f024.html",
"f091.html"
],
"index": 23,
"isFirst": false,
"isLast": false
},
"c5053c28-67fc-45a3-8d0a-c8a90c27867d": {
"id": "c5053c28-67fc-45a3-8d0a-c8a90c27867d",
"title": "Бластна левкоза. Хронична миелолевкоза",
"files": [
"f025.html",
"f087.html"
],
"index": 24,
"isFirst": false,
"isLast": false
},
"a5fb0e0c-ad77-4a1f-ab08-2978179243e3": {
"id": "a5fb0e0c-ad77-4a1f-ab08-2978179243e3",
"title": "Нехочкинови и хочкинови лимфоми",
"files": [
"f026.html",
"f089.html"
],
"index": 25,
"isFirst": false,
"isLast": false
},
"c9bc30ff-a149-43db-b442-a7848b49d4bc": {
"id": "c9bc30ff-a149-43db-b442-a7848b49d4bc",
"title": "Остри екзогенни интоксикации. Общи принципи и правила в лечението на острите екзогенни отравяния. Поведение на медицинската сестра и грижи за болния с остро отравяне",
"files": [
"f027.html",
"f093.html"
],
"index": 26,
"isFirst": false,
"isLast": false
},
"b58d8623-cdcf-4428-85fb-7bfe819341b0": {
"id": "b58d8623-cdcf-4428-85fb-7bfe819341b0",
"title": "Алергия. Алергични заболявания. Анафилактичен шок. Поведение на медицинската сестра при спешни алергични състояния",
"files": [
"f028.html",
"f094.html"
],
"index": 27,
"isFirst": false,
"isLast": false
},
"6e12b33e-dead-4584-be58-cd11f3f6f787": {
"id": "6e12b33e-dead-4584-be58-cd11f3f6f787",
"title": "Основни класически методи на изследване във вътрешната медицина-анамнеза. Физикални методи за изледване на болните-оглед, палпация, перкусия, аускултация. Сегашно състояние-обективен статус на болния. Клинична диагноза и прогноза. Проследяване на болния-Декурзус",
"files": [
"f029.html"
],
"index": 28,
"isFirst": false,
"isLast": false
},
"b1264871-a4f9-4fe5-9ea2-b64cb586025b": {
"id": "b1264871-a4f9-4fe5-9ea2-b64cb586025b",
"title": "Дихателна недостатъчност- остра и хронична. Етиология, степени, клиника",
"files": [
"f030.html"
],
"index": 29,
"isFirst": false,
"isLast": false
},
"bae8e355-9716-42cc-88db-59e3bb85cc30": {
"id": "bae8e355-9716-42cc-88db-59e3bb85cc30",
"title": "Ревматизъм",
"files": [
"f032.html",
"f034.html"
],
"index": 30,
"isFirst": false,
"isLast": false
},
"2ef0265d-2b6a-4683-9ae7-72e2c22e23c6": {
"id": "2ef0265d-2b6a-4683-9ae7-72e2c22e23c6",
"title": "Ендокардити, перикардити",
"files": [
"f033.html",
"f039.html"
],
"index": 31,
"isFirst": false,
"isLast": false
},
"faa3cf05-8c89-4407-bfd2-00145aed9ecd": {
"id": "faa3cf05-8c89-4407-bfd2-00145aed9ecd",
"title": "Остра периферна сърдечно-съдова недостатъчност. Кардиологичен шок",
"files": [
"f036.html"
],
"index": 32,
"isFirst": false,
"isLast": false
},
"f04a41b6-e204-4964-8ccd-736d6cdd783a": {
"id": "f04a41b6-e204-4964-8ccd-736d6cdd783a",
"title": "Лечение на СН и поведение на м. с",
"files": [
"f037.html"
],
"index": 33,
"isFirst": false,
"isLast": false
},
"9c799e65-1b44-4665-8ec8-f3c21c1e9af0": {
"id": "9c799e65-1b44-4665-8ec8-f3c21c1e9af0",
"title": "Ритъмни нарушения на сърдечната дейност. Проводни нарушения на сърдечната дейност",
"files": [
"f038.html",
"f044.html"
],
"index": 34,
"isFirst": false,
"isLast": false
},
"82a3b90c-5f00-43aa-9331-2a2d98570189": {
"id": "82a3b90c-5f00-43aa-9331-2a2d98570189",
"title": "ИБС: етиология, рискови фактори, патофизиология. Стенокардия",
"files": [
"f040.html",
"f045.html"
],
"index": 35,
"isFirst": false,
"isLast": false
},
"30acab20-39b9-4c0e-a4fc-e70c09777f2c": {
"id": "30acab20-39b9-4c0e-a4fc-e70c09777f2c",
"title": "Изследване на ДС. Основни симптоми и синдроми при заболявания на ДС. Физикални и специаални методи на изследване на ДС",
"files": [
"f041.html"
],
"index": 36,
"isFirst": false,
"isLast": false
},
"16f88ceb-5718-4e64-badf-57125972ea03": {
"id": "16f88ceb-5718-4e64-badf-57125972ea03",
"title": "ИБС: етиология, рискови фактори, патофизиология. Инфаркт на миокарда",
"files": [
"f042.html",
"f046.html"
],
"index": 37,
"isFirst": false,
"isLast": false
},
"7f1b60f2-6eef-4974-aa3c-5e9fd9b965c7": {
"id": "7f1b60f2-6eef-4974-aa3c-5e9fd9b965c7",
"title": "Болести на хипофизата- Акромегалия; Безвкусен диабет",
"files": [
"f043.html",
"f048.html"
],
"index": 38,
"isFirst": false,
"isLast": false
},
"af8e5f62-f7ed-4b8d-9c82-adf5d050d401": {
"id": "af8e5f62-f7ed-4b8d-9c82-adf5d050d401",
"title": "Захарен диабет-етиология, патогенеза, класификация, клиника. Диабетна кетоацидоза и хипокликемична кома. Поведение на МС при диабетно болен в кома",
"files": [
"f049.html"
],
"index": 39,
"isFirst": false,
"isLast": false
},
"690b6497-c240-47d1-ba48-1a608f7e9d50": {
"id": "690b6497-c240-47d1-ba48-1a608f7e9d50",
"title": "Болести на щитовидната жлеза: Тиреотоксикоза. Микседем. Ендемична гуша",
"files": [
"f050.html"
],
"index": 40,
"isFirst": false,
"isLast": false
},
"9b6d0e9c-05cb-41ea-9d15-35f1b394817c": {
"id": "9b6d0e9c-05cb-41ea-9d15-35f1b394817c",
"title": "Захарен диабет късни усложнения. Захарен диабет- диета и медикаментозно лечение",
"files": [
"f051.html",
"f056.html"
],
"index": 41,
"isFirst": false,
"isLast": false
},
"b9f5b23e-a110-4dc5-8b5c-e2b8bf2b3b59": {
"id": "b9f5b23e-a110-4dc5-8b5c-e2b8bf2b3b59",
"title": "Затлъстяване. Подагра",
"files": [
"f052.html",
"f053.html"
],
"index": 42,
"isFirst": false,
"isLast": false
},
"6a015a8a-725f-404e-a622-0c1ebba568e7": {
"id": "6a015a8a-725f-404e-a622-0c1ebba568e7",
"title": "Физикални и специални методи на изследване на стомашно-чревния тракт",
"files": [
"f055.html"
],
"index": 43,
"isFirst": false,
"isLast": false
},
"0fc6a512-3b04-4b79-bef7-c658d5bd01cb": {
"id": "0fc6a512-3b04-4b79-bef7-c658d5bd01cb",
"title": "Гастрити. ГЕРБ",
"files": [
"f058.html",
"f061.html"
],
"index": 44,
"isFirst": false,
"isLast": false
},
"f331d024-1846-44ae-860e-9dfc8522fafc": {
"id": "f331d024-1846-44ae-860e-9dfc8522fafc",
"title": "Язвена болест. Рак на стомаха",
"files": [
"f059.html",
"f063.html"
],
"index": 45,
"isFirst": false,
"isLast": false
},
"85f1a01e-3a5d-44da-9021-7460152cd5d1": {
"id": "85f1a01e-3a5d-44da-9021-7460152cd5d1",
"title": "Ентерити и колити. Рак на дебелото черво",
"files": [
"f060.html",
"f065.html"
],
"index": 46,
"isFirst": false,
"isLast": false
},
"c6524b3f-9cd6-4d2e-96ea-63120f2d8f7b": {
"id": "c6524b3f-9cd6-4d2e-96ea-63120f2d8f7b",
"title": "Основни симптоми и синдроми при заболяване на черния дроб и жлъчните пътища. Жълтеница, портална хипертония, асцит. Анамнеза, физикални и специални методи за изследване на черния дроб и жлъчните пътища",
"files": [
"f062.html"
],
"index": 47,
"isFirst": false,
"isLast": false
},
"fafe1f1a-71fc-4756-8aa7-38eeadb156a7": {
"id": "fafe1f1a-71fc-4756-8aa7-38eeadb156a7",
"title": "Хронични хепатити. Чернодробна цироза",
"files": [
"f064.html",
"f071.html"
],
"index": 48,
"isFirst": false,
"isLast": false
},
"be416a10-e96b-4a98-b413-47dce446c9ac": {
"id": "be416a10-e96b-4a98-b413-47dce446c9ac",
"title": "Холелитиаза. Холецистити",
"files": [
"f066.html",
"f073.html"
],
"index": 49,
"isFirst": false,
"isLast": false
},
"4788afb8-c112-42d3-9bae-40362556fa2b": {
"id": "4788afb8-c112-42d3-9bae-40362556fa2b",
"title": "Основни симптоми и синдроми при заболяване на черния дроб и жлъчните пътища. Анамнеза, физикални и специални методи за изледване на черния дроб и жлъчните пътища",
"files": [
"f067.html",
"f068.html"
],
"index": 50,
"isFirst": false,
"isLast": false
},
"8995a8df-963f-4459-bb8e-21912a9d0023": {
"id": "8995a8df-963f-4459-bb8e-21912a9d0023",
"title": "Остър и хроничен гломерулонефрит",
"files": [
"f069.html",
"f070.html"
],
"index": 51,
"isFirst": false,
"isLast": false
},
"9678afb6-4173-40f2-bcea-026a7b8307b9": {
"id": "9678afb6-4173-40f2-bcea-026a7b8307b9",
"title": "Нефролитиаза",
"files": [
"f072.html",
"f077.html"
],
"index": 52,
"isFirst": false,
"isLast": false
},
"c3c7ce4c-c9a0-449f-9ad2-5ce92ce203dd": {
"id": "c3c7ce4c-c9a0-449f-9ad2-5ce92ce203dd",
"title": "Остра бъбречна и хронична бъбречна недостатъчност",
"files": [
"f076.html"
],
"index": 53,
"isFirst": false,
"isLast": false
},
"281dc9d6-8971-42ff-a6b9-8901cc19e845": {
"id": "281dc9d6-8971-42ff-a6b9-8901cc19e845",
"title": "Балканска ендемична нефропатия. Бъбречна поликистозна болест. Бъбречна туберкулоза",
"files": [
"f078.html",
"f082.html"
],
"index": 54,
"isFirst": false,
"isLast": false
},
"d5183ba3-7de8-4676-ac25-81ec2f3915da": {
"id": "d5183ba3-7de8-4676-ac25-81ec2f3915da",
"title": "Ревмтоиден артрит",
"files": [
"f079.html",
"f085.html"
],
"index": 55,
"isFirst": false,
"isLast": false
},
"356b7311-aae7-4303-91d2-e0817bc462dd": {
"id": "356b7311-aae7-4303-91d2-e0817bc462dd",
"title": "Деформираща артроза",
"files": [
"f080.html",
"f081.html"
],
"index": 56,
"isFirst": false,
"isLast": false
},
"fb12f399-7a8d-47ba-8460-8b9291a16e53": {
"id": "fb12f399-7a8d-47ba-8460-8b9291a16e53",
"title": "Желязодефицитни анемии",
"files": [
"f083.html",
"f088.html"
],
"index": 57,
"isFirst": false,
"isLast": false
},
"5a493bc7-e45e-46f8-9fb8-722ea9636750": {
"id": "5a493bc7-e45e-46f8-9fb8-722ea9636750",
"title": "Витамин В 12 дефицитни анемии",
"files": [
"f086.html",
"f090.html"
],
"index": 58,
"isFirst": false,
"isLast": false
},
"bc84aab9-dd28-4166-939e-b288cd84cf3d": {
"id": "bc84aab9-dd28-4166-939e-b288cd84cf3d",
"title": "Бронхоектазии. Белодробен абцес",
"files": [
"f092.html"
],
"index": 59,
"isFirst": false,
"isLast": false
},
"6d88be6a-0f4f-43ec-82cb-71b57fdfa785": {
"id": "6d88be6a-0f4f-43ec-82cb-71b57fdfa785",
"title": "Tумори на белия дроб",
"files": [
"f095.html"
],
"index": 60,
"isFirst": false,
"isLast": true
}
},
"allIds": [
"a4d9fa36-8ed7-4d80-a108-eac209ca01a8",
"f7fed0c0-15d9-439c-a3ef-24cec673dd44",
"b20a76c0-59a1-4f0a-bb33-39c4bf437eac",
"a2a3a830-9f1d-4dd0-ab52-c42ae66c17b3",
"38899f8f-2291-4923-ae59-928181c88bbd",
"b0460271-a4e2-4e82-a7ea-0f98689fcbb6",
"9db34720-4756-4469-bbf0-1666d4b6c65f",
"c12799f8-63d2-4d76-88b9-e0fa68def5a4",
"c200281a-d2cd-4ca6-b6d3-114db5753fc5",
"f191d184-a26f-41aa-b213-ead39c22f4c1",
"6b01e219-49d9-4ced-ae83-0e21a6cba53c",
"bc201e49-52a3-42da-b47a-ed786576b377",
"1d71aeff-1637-493f-93ae-c7126367528c",
"7fcb4e5c-ef95-4fb0-a6a9-174318fb7aef",
"5617f428-8057-42a4-8445-39085b5d5561",
"501db50d-1cdf-425d-ac2e-e994b5abeba0",
"db96af60-ee77-4bdb-b4e3-edf93b274593",
"7fa4d0eb-0243-4cf7-8667-7cf130d92f2c",
"2cc4aa8c-978f-4d61-aa3c-9199a955682a",
"20b8b133-cb95-4855-a19b-50a0d2c64f21",
"ae779ecf-2f35-445e-91b1-a418ac7cf9cb",
"e4d7fb75-96f3-421d-9c1d-cd05bccc729b",
"969617b2-ee8b-4d95-a24a-911adf4a7d9f",
"61f037df-1000-402f-a0f5-ffcbde8bb5c7",
"c5053c28-67fc-45a3-8d0a-c8a90c27867d",
"a5fb0e0c-ad77-4a1f-ab08-2978179243e3",
"c9bc30ff-a149-43db-b442-a7848b49d4bc",
"b58d8623-cdcf-4428-85fb-7bfe819341b0",
"6e12b33e-dead-4584-be58-cd11f3f6f787",
"b1264871-a4f9-4fe5-9ea2-b64cb586025b",
"bae8e355-9716-42cc-88db-59e3bb85cc30",
"2ef0265d-2b6a-4683-9ae7-72e2c22e23c6",
"faa3cf05-8c89-4407-bfd2-00145aed9ecd",
"f04a41b6-e204-4964-8ccd-736d6cdd783a",
"9c799e65-1b44-4665-8ec8-f3c21c1e9af0",
"82a3b90c-5f00-43aa-9331-2a2d98570189",
"30acab20-39b9-4c0e-a4fc-e70c09777f2c",
"16f88ceb-5718-4e64-badf-57125972ea03",
"7f1b60f2-6eef-4974-aa3c-5e9fd9b965c7",
"af8e5f62-f7ed-4b8d-9c82-adf5d050d401",
"690b6497-c240-47d1-ba48-1a608f7e9d50",
"9b6d0e9c-05cb-41ea-9d15-35f1b394817c",
"b9f5b23e-a110-4dc5-8b5c-e2b8bf2b3b59",
"6a015a8a-725f-404e-a622-0c1ebba568e7",
"0fc6a512-3b04-4b79-bef7-c658d5bd01cb",
"f331d024-1846-44ae-860e-9dfc8522fafc",
"85f1a01e-3a5d-44da-9021-7460152cd5d1",
"c6524b3f-9cd6-4d2e-96ea-63120f2d8f7b",
"fafe1f1a-71fc-4756-8aa7-38eeadb156a7",
"be416a10-e96b-4a98-b413-47dce446c9ac",
"4788afb8-c112-42d3-9bae-40362556fa2b",
"8995a8df-963f-4459-bb8e-21912a9d0023",
"9678afb6-4173-40f2-bcea-026a7b8307b9",
"c3c7ce4c-c9a0-449f-9ad2-5ce92ce203dd",
"281dc9d6-8971-42ff-a6b9-8901cc19e845",
"d5183ba3-7de8-4676-ac25-81ec2f3915da",
"356b7311-aae7-4303-91d2-e0817bc462dd",
"fb12f399-7a8d-47ba-8460-8b9291a16e53",
"5a493bc7-e45e-46f8-9fb8-722ea9636750",
"bc84aab9-dd28-4166-939e-b288cd84cf3d",
"6d88be6a-0f4f-43ec-82cb-71b57fdfa785"
]
}