Compare commits
4 Commits
e9ea35a064
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
4fccc793bb
|
|||
|
7c75a8a485
|
|||
|
df9fa7c605
|
|||
|
793cce4ae7
|
10
README.md
10
README.md
@@ -2,12 +2,6 @@
|
|||||||
|
|
||||||
Jednoduchý parser pro SPŠE Ječná tabulku suplování.
|
Jednoduchý parser pro SPŠE Ječná tabulku suplování.
|
||||||
|
|
||||||
## Environmental variables
|
## Self-hosting
|
||||||
|
|
||||||
- `SHAREPOINT_URL` - URL adresa sharepointu dané tabulky (volitelné, využije hard-coded URL)
|
[Dokumentace zde](https://jecnarozvrh.jzitnik.dev/posts/self-hosting/)
|
||||||
- `EMAIL` - SPŠE Ječná account email (povinné, username@spsejecna.cz)
|
|
||||||
- `PASSWORD` - Heslo k SPŠE Ječná účtu
|
|
||||||
|
|
||||||
## Spouštění serveru
|
|
||||||
|
|
||||||
Just `npm start` 💀
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -13,7 +13,8 @@
|
|||||||
"build-noweb": "tsc",
|
"build-noweb": "tsc",
|
||||||
"serve": "concurrently \"node dist/server.js\" \"node dist/cron-runner.js\"",
|
"serve": "concurrently \"node dist/server.js\" \"node dist/cron-runner.js\"",
|
||||||
"dev-web": "cd web && hugo serve",
|
"dev-web": "cd web && hugo serve",
|
||||||
"parse-timetable": "node scripts/load_static_schedule.js"
|
"parse-timetable": "node scripts/load_static_schedule.js",
|
||||||
|
"dev-preview": "tsx server.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.13.5",
|
"axios": "^1.13.5",
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ if (SERVE_WEB) {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const nextApp = next({
|
const nextApp = next({
|
||||||
dev,
|
dev,
|
||||||
dir: path.join(__dirname, '../viewer')
|
dir: dev ? path.join(__dirname, 'viewer') : path.join(__dirname, '../viewer')
|
||||||
})
|
})
|
||||||
|
|
||||||
const handle = nextApp.getRequestHandler()
|
const handle = nextApp.getRequestHandler()
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
First, run the development server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
# or
|
|
||||||
yarn dev
|
|
||||||
# or
|
|
||||||
pnpm dev
|
|
||||||
# or
|
|
||||||
bun dev
|
|
||||||
```
|
|
||||||
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
||||||
|
|
||||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
|
||||||
|
|
||||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
|
||||||
|
|
||||||
## Learn More
|
|
||||||
|
|
||||||
To learn more about Next.js, take a look at the following resources:
|
|
||||||
|
|
||||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
||||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
||||||
|
|
||||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
|
||||||
|
|
||||||
## Deploy on Vercel
|
|
||||||
|
|
||||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
|
||||||
|
|
||||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
|
||||||
@@ -49,11 +49,11 @@ export default function RootLayout({
|
|||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
</div>
|
</div>
|
||||||
<footer className="text-center text-xs text-white/70 pb-4">
|
<footer className="text-center text-xs text-foreground/70 pb-4">
|
||||||
© 2026{" "}
|
© 2026{" "}
|
||||||
<a href="https://jzitnik.dev" target="_blank" className="underline hover:text-white/90">Jakub Žitník</a>{" "}
|
<a href="https://jzitnik.dev" target="_blank" className="underline hover:text-foreground/90">Jakub Žitník</a>{" "}
|
||||||
•{" "}
|
•{" "}
|
||||||
<a href="https://www.gnu.org/licenses/gpl-3.0.html" target="_blank" className="underline hover:text-white/90">Licencováno pod GNU GPL v3.0</a>
|
<a href="https://www.gnu.org/licenses/gpl-3.0.html" target="_blank" className="underline hover:text-foreground/90">Licencováno pod GNU GPL v3.0</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<Toaster />
|
<Toaster />
|
||||||
|
|||||||
@@ -7,6 +7,15 @@ import { LocalData, SubstitutionData, ChangeEntry, Hour } from '@/lib/types';
|
|||||||
import { Card } from '@/components/ui/card';
|
import { Card } from '@/components/ui/card';
|
||||||
import { capitalizeFirstLetter } from '@/lib/utils';
|
import { capitalizeFirstLetter } from '@/lib/utils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
|
||||||
interface ScheduleViewerProps {
|
interface ScheduleViewerProps {
|
||||||
localData: LocalData;
|
localData: LocalData;
|
||||||
substitutionData: SubstitutionData | null;
|
substitutionData: SubstitutionData | null;
|
||||||
@@ -133,11 +142,58 @@ export default function ScheduleViewer({ localData, substitutionData, hideSubsti
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function LessonDialog({ lesson, children }: { lesson: Hour, children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild className="cursor-pointer hover:bg-accent hover:text-accent-foreground transition-colors">
|
||||||
|
{children}
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>{lesson.title || lesson.subject}</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Detailní informace o hodině
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="grid gap-4 py-4">
|
||||||
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
|
<span className="font-bold text-right col-span-1">Předmět:</span>
|
||||||
|
<span className="col-span-3">{lesson.title} ({lesson.subject})</span>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
|
<span className="font-bold text-right col-span-1">Učitel:</span>
|
||||||
|
<a
|
||||||
|
href={`https://spsejecna.cz/ucitel/${lesson.teacher.code}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="col-span-3 text-primary underline-offset-4 hover:underline"
|
||||||
|
>
|
||||||
|
{lesson.teacher.name}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
|
<span className="font-bold text-right col-span-1">Místnost:</span>
|
||||||
|
<a
|
||||||
|
href={`https://spsejecna.cz/ucebna/${lesson.room}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="col-span-3 text-primary underline-offset-4 hover:underline"
|
||||||
|
>
|
||||||
|
{lesson.room}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function CellContent({ staticLessons, change }: { staticLessons: Hour[], change: ChangeEntry | null | undefined }) {
|
function CellContent({ staticLessons, change }: { staticLessons: Hour[], change: ChangeEntry | null | undefined }) {
|
||||||
if (change) {
|
if (change) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="w-full h-full p-2 text-xs flex items-center justify-center text-center font-medium shadow-sm"
|
className="w-full h-full p-2 text-xs flex items-center justify-center text-center font-medium"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: change.backgroundColor || '#f0f0f0',
|
backgroundColor: change.backgroundColor || '#f0f0f0',
|
||||||
color: change.foregroundColor ? "#" + change.foregroundColor.substring(3, 6) : '#000',
|
color: change.foregroundColor ? "#" + change.foregroundColor.substring(3, 6) : '#000',
|
||||||
@@ -156,7 +212,8 @@ function CellContent({ staticLessons, change }: { staticLessons: Hour[], change:
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col min-h-[80px] h-full">
|
<div className="flex flex-col min-h-[80px] h-full">
|
||||||
{staticLessons.map((lesson, idx) => (
|
{staticLessons.map((lesson, idx) => (
|
||||||
<div key={idx} className="flex-1 flex flex-col justify-between p-1 text-[10px] border-b min-h-[40px]">
|
<LessonDialog key={idx} lesson={lesson}>
|
||||||
|
<div role="button" tabIndex={0} className="flex-1 flex flex-col justify-between p-1 text-[10px] border-b min-h-[40px] text-left">
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-between'>
|
||||||
<div className="font-bold truncate">{lesson.subject}</div>
|
<div className="font-bold truncate">{lesson.subject}</div>
|
||||||
<span className="truncate opacity-70">{capitalizeFirstLetter(lesson.teacher.code)}</span>
|
<span className="truncate opacity-70">{capitalizeFirstLetter(lesson.teacher.code)}</span>
|
||||||
@@ -166,6 +223,7 @@ function CellContent({ staticLessons, change }: { staticLessons: Hour[], change:
|
|||||||
<span className="truncate opacity-70">{lesson.group}</span>
|
<span className="truncate opacity-70">{lesson.group}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</LessonDialog>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -173,12 +231,14 @@ function CellContent({ staticLessons, change }: { staticLessons: Hour[], change:
|
|||||||
|
|
||||||
const lesson = staticLessons[0];
|
const lesson = staticLessons[0];
|
||||||
return (
|
return (
|
||||||
<div className="w-full min-h-[80px] h-full p-2 border-b flex flex-col justify-between text-xs hover:shadow-md transition-shadow">
|
<LessonDialog lesson={lesson}>
|
||||||
|
<div role="button" tabIndex={0} className="w-full min-h-[80px] h-full p-2 border-b flex flex-col justify-between text-xs text-left">
|
||||||
<div className="font-bold text-lg text-primary">{lesson.subject}</div>
|
<div className="font-bold text-lg text-primary">{lesson.subject}</div>
|
||||||
<div className="flex justify-between items-end mt-1">
|
<div className="flex justify-between items-end mt-1">
|
||||||
<div className="font-mono font-medium">{lesson.room}</div>
|
<div className="font-mono font-medium">{lesson.room}</div>
|
||||||
<div className="text-[10px] opacity-80" title={lesson.teacher.name}>{capitalizeFirstLetter(lesson.teacher.code)}</div>
|
<div className="text-[10px] opacity-80" title={lesson.teacher.name}>{capitalizeFirstLetter(lesson.teacher.code)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</LessonDialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
reactCompiler: true,
|
reactCompiler: true,
|
||||||
basePath: '/viewer',
|
basePath: '/viewer',
|
||||||
turbopack: {
|
turbopack: {
|
||||||
root: __dirname,
|
root: process.env.NODE_ENV == "development" ? path.resolve(__dirname) : path.resolve(__dirname, '..'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ Použití Dockeru je nejjednodušší způsob, jak projekt spustit, protože aut
|
|||||||
|
|
||||||
1. **Klonování repozitáře**:
|
1. **Klonování repozitáře**:
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/zitnik/jecnarozvrh.git
|
git clone https://gitea.local.jzitnik.dev/jzitnik/jecnarozvrh.git
|
||||||
cd jecnarozvrh
|
cd jecnarozvrh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -22,15 +22,18 @@ pagination:
|
|||||||
|
|
||||||
menu:
|
menu:
|
||||||
main:
|
main:
|
||||||
|
- name: Online client
|
||||||
|
url: /viewer
|
||||||
|
weight: 10
|
||||||
- name: Status
|
- name: Status
|
||||||
url: https://status.jzitnik.dev
|
url: https://status.jzitnik.dev
|
||||||
weight: 10
|
weight: 20
|
||||||
- name: Autor
|
- name: Autor
|
||||||
url: https://jzitnik.dev
|
url: https://jzitnik.dev
|
||||||
weight: 20
|
weight: 30
|
||||||
- name: Zdrojový kód
|
- name: Zdrojový kód
|
||||||
url: https://gitea.jzitnik.dev/jzitnik/jecnarozvrh
|
url: https://gitea.jzitnik.dev/jzitnik/jecnarozvrh
|
||||||
weight: 30
|
weight: 40
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
home:
|
home:
|
||||||
|
|||||||
Reference in New Issue
Block a user