diff --git a/README.md b/README.md index 8b58ea3..3cfa22d 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Reskin of https://lk.ks.psuti.ru/ since it lacks mobile support and is generally - node-html-parser for scraping, rehydration strategy for cache - TypeScript with types for each package -Built in 1 day. Tools used: pnpm, eslint. +Built in 1 day. Tools used: pnpm, eslint, react-icons. ## Hire me! diff --git a/package.json b/package.json index 5169660..9035da1 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-select": "^2.0.0", @@ -26,6 +27,7 @@ "node-html-parser": "^6.1.10", "react": "latest", "react-dom": "latest", + "react-icons": "^4.11.0", "sharp": "^0.32.6", "tailwind-merge": "^1.14.0", "tailwindcss-animate": "^1.0.7" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7f22e91..4270e20 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ dependencies: '@radix-ui/react-avatar': specifier: ^1.0.4 version: 1.0.4(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dialog': + specifier: ^1.0.5 + version: 1.0.5(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dropdown-menu': specifier: ^2.0.6 version: 2.0.6(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0) @@ -56,6 +59,9 @@ dependencies: react-dom: specifier: latest version: 18.2.0(react@18.2.0) + react-icons: + specifier: ^4.11.0 + version: 4.11.0(react@18.2.0) sharp: specifier: ^0.32.6 version: 0.32.6 @@ -446,6 +452,40 @@ packages: react: 18.2.0 dev: false + /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.1 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.24)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.24)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.24)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.24)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.24)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.24)(react@18.2.0) + '@types/react': 18.2.24 + '@types/react-dom': 18.2.8 + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.24)(react@18.2.0) + dev: false + /@radix-ui/react-direction@1.0.1(@types/react@18.2.24)(react@18.2.0): resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} peerDependencies: @@ -3241,6 +3281,14 @@ packages: scheduler: 0.23.0 dev: false + /react-icons@4.11.0(react@18.2.0): + resolution: {integrity: sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==} + peerDependencies: + react: '*' + dependencies: + react: 18.2.0 + dev: false + /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true diff --git a/src/app/agregator/schedule.ts b/src/app/agregator/schedule.ts index 8ec2ccb..7ba4523 100644 --- a/src/app/agregator/schedule.ts +++ b/src/app/agregator/schedule.ts @@ -6,15 +6,15 @@ import { JSDOM } from 'jsdom' import { content as mockContent } from './mock' // ПС-7: 146 -export async function getSchedule(groupID: number): Promise { - // const page = await fetch(`${process.env.PROXY_URL ?? 'https://lk.ks.psuti.ru'}/?mn=2&obj=${groupID}`) - const page = { text: async () => mockContent, status: 200, headers: { get: (s: string) => s && 'text/html' } } +export async function getSchedule(groupID: number, groupName: string): Promise { + const page = await fetch(`${process.env.PROXY_URL ?? 'https://lk.ks.psuti.ru'}/?mn=2&obj=${groupID}`) + // const page = { text: async () => mockContent, status: 200, headers: { get: (s: string) => s && 'text/html' } } const content = await page.text() const contentType = page.headers.get('content-type') if (page.status === 200 && contentType && contentTypeParser.parse(contentType).type === 'text/html') { try { const root = new JSDOM(content).window.document - return parsePage(root) + return parsePage(root, groupName) } catch(e) { console.error('Error while parsing lk.ks.psuti.ru') throw e diff --git a/src/app/parser/schedule.ts b/src/app/parser/schedule.ts index 4d110f3..56c45d1 100644 --- a/src/app/parser/schedule.ts +++ b/src/app/parser/schedule.ts @@ -10,67 +10,69 @@ const dayTitleParser = (text: string) => { } const parseLesson = (row: Element): Lesson | null => { - const cells = Array.from(row.querySelectorAll(':scope > td')) - if (cells[3].textContent!.trim() === 'Свободное время') return null + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + const lesson: LessonObject = {} - const isChange = cells.every(td => td.getAttribute('bgcolor') === 'ffffbb') + try { + const cells = Array.from(row.querySelectorAll(':scope > td')) + if (cells[3].textContent!.trim() === 'Свободное время') return null - const timeCell = cells[1].childNodes - const [startTime, endTime] = timeCell[0].textContent!.trim().split(' – ') - const time: Lesson['time'] = { - start: startTime ?? '', - end: endTime ?? '' - } - if (timeCell[2]) { - time.hint = timeCell[2].textContent!.trim() - } + lesson.isChange = cells.every(td => td.getAttribute('bgcolor') === 'ffffbb') - const subject = cells[3].childNodes[0].textContent!.trim() - - let teacher: Lesson['teacher'] - const teacherCell = cells[3].childNodes[2] - if (teacherCell) { - teacher = teacherCell.textContent!.trim() - } - - const placeCell = cells[3].childNodes[3] - - let place: Lesson['place'] - if (placeCell) { - place = { - address: placeCell.childNodes[1].textContent!.trim(), - classroom: Number(placeCell.childNodes[3].textContent!.trim().match(/^Кабинет: (\d+)(-2)?$/)![1]) + const timeCell = cells[1].childNodes + const [startTime, endTime] = timeCell[0].textContent!.trim().split(' – ') + lesson.time = { + start: startTime ?? '', + end: endTime ?? '' + } + if (timeCell[2]) { + lesson.time.hint = timeCell[2].textContent!.trim() } - } - const topic: Lesson['topic'] = cells[4].textContent!.trim() + try { + lesson.subject = cells[3].childNodes[0].textContent!.trim() - const resources: Lesson['resources'] = [] - Array.from(cells[5].querySelectorAll('a')) - .forEach(a => { - resources.push({ - type: 'link', - title: a.textContent!.trim(), - url: a.getAttribute('href')! + const teacherCell = cells[3].childNodes[2] + if (teacherCell) { + lesson.teacher = teacherCell.textContent!.trim() + } + + const placeCell = cells[3].childNodes[3] + + if (placeCell) { + lesson.place = { + address: placeCell.childNodes[1].textContent!.trim(), + classroom: placeCell.childNodes[3].textContent!.trim().match(/^Кабинет: ([^ ]+)(-2)?$/)![1] + } + } + } catch(e) { + console.error('Error while parsing discipline', e, cells[3].textContent?.trim()) + lesson.fallbackDiscipline = cells[3].textContent?.trim() + } + + lesson.topic = cells[4].textContent!.trim() + + lesson.resources = [] + Array.from(cells[5].querySelectorAll('a')) + .forEach(a => { + lesson.resources.push({ + type: 'link', + title: a.textContent!.trim(), + url: a.getAttribute('href')! + }) }) - }) - return { - isChange, - time, - type: cells[2].textContent!.trim(), - subject, - ...(teacher && { teacher }), - ...(place && { place }), - ...(topic && { topic }), - resources, - homework: cells[6].textContent!.trim() + return lesson + } catch(e) { + console.error('Error while parsing lesson in table', e, row.textContent?.trim()) + return null } } -export function parsePage(document: Document): Day[] { +export function parsePage(document: Document, groupName: string): Day[] { const tables = Array.from(document.querySelectorAll('body > table')) - const table = tables.find(table => table.querySelector(':scope > tbody > tr:first-child')?.textContent?.trim() === 'ПС-7') + const table = tables.find(table => table.querySelector(':scope > tbody > tr:first-child')?.textContent?.trim() === groupName) const rows = Array.from(table!.children[0].children).filter(el => el.tagName === 'TR').slice(2) const days = [] diff --git a/src/pages/[group].tsx b/src/pages/[group].tsx index ba93979..6929b18 100644 --- a/src/pages/[group].tsx +++ b/src/pages/[group].tsx @@ -1,6 +1,6 @@ import { Schedule } from '@/widgets/schedule' import { Day } from '@/shared/model/day' -import { GetServerSidePropsResult } from 'next' +import { GetServerSidePropsContext, GetServerSidePropsResult } from 'next' import { getSchedule } from '@/app/agregator/schedule' import { NextSerialized, nextDeserializer, nextSerialized } from '@/app/utils/date-serializer' import { NavBar } from '@/widgets/navbar' @@ -20,12 +20,22 @@ export default function HomePage(props: PageProps) { ) } -export async function getServerSideProps(): Promise> { - const schedule = await getSchedule(146) - - return { - props: { - schedule: nextSerialized(schedule) - } +export async function getServerSideProps(context: GetServerSidePropsContext<{ group: string }>): Promise> { + const groups: { [group: string]: [number, string] } = { + ps7: [146, 'ПС-7'], + pks35k: [78, 'ПКС-35к'] } + const group = context.params?.group + if (group && Object.hasOwn(groups, group) && group in groups) { + const schedule = await getSchedule(...groups[group]) + return { + props: { + schedule: nextSerialized(schedule) + } + } + } else { + return { + notFound: true + } + } } \ No newline at end of file diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index 54e8bf3..d8f078a 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -2,7 +2,7 @@ import { Html, Head, Main, NextScript } from 'next/document' export default function Document() { return ( - +
diff --git a/src/shadcn/ui/badge.tsx b/src/shadcn/ui/badge.tsx new file mode 100644 index 0000000..5b0ba5b --- /dev/null +++ b/src/shadcn/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/shared/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/src/shadcn/ui/dialog.tsx b/src/shadcn/ui/dialog.tsx new file mode 100644 index 0000000..ee82a04 --- /dev/null +++ b/src/shadcn/ui/dialog.tsx @@ -0,0 +1,121 @@ +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { X } from "lucide-react" + +import { cn } from "@/shared/utils" + +const Dialog = DialogPrimitive.Root + +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal = ({ + className, + ...props +}: DialogPrimitive.DialogPortalProps) => ( + +) +DialogPortal.displayName = DialogPrimitive.Portal.displayName + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +DialogContent.displayName = DialogPrimitive.Content.displayName + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogHeader.displayName = "DialogHeader" + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogFooter.displayName = "DialogFooter" + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogTitle.displayName = DialogPrimitive.Title.displayName + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogDescription.displayName = DialogPrimitive.Description.displayName + +export { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +} diff --git a/src/shared/data/teachers.ts b/src/shared/data/teachers.ts index 852821d..df99e36 100644 --- a/src/shared/data/teachers.ts +++ b/src/shared/data/teachers.ts @@ -1,247 +1,258 @@ // https://gist.github.com/VityaSchel/28f1a360ee7798511765910b39c6086c export const teachers = [ { - 'name': 'Абалымова Людмила Павловна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/abalimova-l-.jpg' + name: 'Абалымова Людмила Павловна', + picture: 'https://ks.psuti.ru/images/stories/emp/abalimova-l-.jpg' }, { - 'name': 'Абрамова Светлана Геннадьевна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/qwefdsfsd.jpg' + name: 'Абрамова Светлана Геннадьевна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/qwefdsfsd.jpg' }, { - 'name': 'Алехин Иван Николаевич', - 'picture': 'https://ks.psuti.ru/images/stories/emp/alehin-i-n.jpg' + name: 'Алехин Иван Николаевич', + picture: 'https://ks.psuti.ru/images/stories/emp/alehin-i-n.jpg' }, { - 'name': 'Амукова Светлана Николаевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/' + name: 'Амукова Светлана Николаевна', + picture: 'https://ks.psuti.ru/images/stories/emp/', + pronouns: 'she' }, { - 'name': 'Андреева Елена Сергеевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/-i-n.jpg' + name: 'Тарасова Таисия Евгеньевна', + picture: '', + pronouns: 'she' }, { - 'name': 'Андреевская Наталья Владимировна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2021/IMG_5419.jpg' + name: 'Андреева Елена Сергеевна', + picture: 'https://ks.psuti.ru/images/stories/emp/-i-n.jpg' }, { - 'name': 'Андрющенко Анна Вячеславовна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/andriushenko.jpg' + name: 'Андреевская Наталья Владимировна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2021/IMG_5419.jpg' }, { - 'name': 'Арефьев Андрей Андреевич', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/%20class=' + name: 'Андрющенко Анна Вячеславовна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/andriushenko.jpg' }, { - 'name': 'Бондаренко Анастасия Вячеславовна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/9GLhbTgCmhk.jpg' + name: 'Арефьев Андрей Андреевич', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/%20class=', + pronouns: 'he' }, { - 'name': 'Горшенина Ольга Николаевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/gorwenina-o-n.jpg' + name: 'Бондаренко Анастасия Вячеславовна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/9GLhbTgCmhk.jpg' }, { - 'name': 'Дмитриева Наталья Владимировна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/dmitrieva-n-v.jpg' + name: 'Горшенина Ольга Николаевна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/gorwenina-o-n.jpg' }, { - 'name': 'Елисеева Эмиля Владиславовна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/9GLhbTgC.jpg' + name: 'Дмитриева Наталья Владимировна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/dmitrieva-n-v.jpg' }, { - 'name': 'Ермолаева Галина Владимировна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/ermolaeva-g-v.jpg' + name: 'Елисеева Эмиля Владиславовна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/9GLhbTgC.jpg' }, { - 'name': 'Жабборова Светлана Сергеевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/tryaskina-s-s.jpg' + name: 'Ермолаева Галина Владимировна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/ermolaeva-g-v.jpg' }, { - 'name': 'Жилина Елена Николаевна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/jilina.jpg' + name: 'Жабборова Светлана Сергеевна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/tryaskina-s-s.jpg' }, { - 'name': 'Иванова Мария Сергеевна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/konovalova.jpg' + name: 'Жилина Елена Николаевна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/jilina.jpg' }, { - 'name': 'Карпеева Александра Сергеевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/2014-karpeeva-a-s.jpg' + name: 'Иванова Мария Сергеевна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/konovalova.jpg' }, { - 'name': 'Карпова Ирина Васильевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/karpova-i-v.jpg' + name: 'Карпеева Александра Сергеевна', + picture: 'https://ks.psuti.ru/images/stories/emp/2014-karpeeva-a-s.jpg', + pronouns: 'she' }, { - 'name': 'Козько Диана Игоревна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/kozko-d-i.jpg' + name: 'Карпова Ирина Васильевна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/karpova-i-v.jpg' }, { - 'name': 'Корнилова Светлана Александровна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/kornilova-s-a.jpg' + name: 'Козько Диана Игоревна', + picture: 'https://ks.psuti.ru/images/stories/emp/kozko-d-i.jpg' }, { - 'name': 'Краюшкина Ольга Борисовна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/fsdadsd.jpg' + name: 'Корнилова Светлана Александровна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/kornilova-s-a.jpg' }, { - 'name': 'Крынкина Анна Андреевна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/%D0%A7%D0%B0%D0%B4%D0%B5%D0%BD%D0%BA%D0%BE%D0%B2%D0%B0%20DSC06721.JPG' + name: 'Краюшкина Ольга Борисовна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/fsdadsd.jpg' }, { - 'name': 'Кукарская Людмила Петровна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/kukarskaya-l-p.jpg' + name: 'Крынкина Анна Андреевна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/%D0%A7%D0%B0%D0%B4%D0%B5%D0%BD%D0%BA%D0%BE%D0%B2%D0%B0%20DSC06721.JPG' }, { - 'name': 'Кусаева Зарина Владимировна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2022/%D0%B0%D0%B2%D0%BF%D0%BA%D1%83%D0%BF%D1%8B%D0%BF.jpg' + name: 'Кукарская Людмила Петровна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/kukarskaya-l-p.jpg' }, { - 'name': 'Ларионова Софья Николаевна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/.jpg' + name: 'Кусаева Зарина Владимировна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2022/%D0%B0%D0%B2%D0%BF%D0%BA%D1%83%D0%BF%D1%8B%D0%BF.jpg', + pronouns: 'she' }, { - 'name': 'Лизунова Елена Владимировна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/lizunova.jpg' + name: 'Ларионова Софья Николаевна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/.jpg', + pronouns: 'she' }, { - 'name': 'Лобачева Милана Евгеньевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/lobacheva-m-e.jpg' + name: 'Лизунова Елена Владимировна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/lizunova.jpg' }, { - 'name': 'Логвинов Александр Владимирович', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/logvinov_a_v.jpg' + name: 'Лобачева Милана Евгеньевна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/lobacheva-m-e.jpg' }, { - 'name': 'Малбасарова Галия Худанбаевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/2014-kuntaeva.jpg' + name: 'Логвинов Александр Владимирович', + picture: 'https://ks.psuti.ru/images/stories/emp/new/logvinov_a_v.jpg' }, { - 'name': 'Матулина Татьяна Сергеевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/matulina.jpg' + name: 'Малбасарова Галия Худанбаевна', + picture: 'https://ks.psuti.ru/images/stories/emp/2014-kuntaeva.jpg' }, { - 'name': 'Михалькова Ирина Евгеньевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/2019-mihalkova.jpg' + name: 'Матулина Татьяна Сергеевна', + picture: 'https://ks.psuti.ru/images/stories/emp/matulina.jpg' }, { - 'name': 'Назарова Елена Федоровна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/nazarova.jpg' + name: 'Михалькова Ирина Евгеньевна', + picture: 'https://ks.psuti.ru/images/stories/emp/2019-mihalkova.jpg' }, { - 'name': 'Негина Айгуль Зинуловна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/2014-aitasova-a-z.jpg' + name: 'Назарова Елена Федоровна', + picture: 'https://ks.psuti.ru/images/stories/emp/nazarova.jpg', + pronouns: 'bitch' }, { - 'name': 'Некрылова Татьяна Борисовна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/nekrylova-t-b.jpg' + name: 'Негина Айгуль Зинуловна', + picture: 'https://ks.psuti.ru/images/stories/emp/2014-aitasova-a-z.jpg' }, { - 'name': 'Никифоров Михаил Михайлович', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/nikiforov-m-m.jpg' + name: 'Некрылова Татьяна Борисовна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/nekrylova-t-b.jpg' }, { - 'name': 'Першина Елена Викторовна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/pershina-e-v.jpg' + name: 'Никифоров Михаил Михайлович', + picture: 'https://ks.psuti.ru/images/stories/emp/new/nikiforov-m-m.jpg' }, { - 'name': 'Потяйкин Роман Владимирович', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/.jpg' + name: 'Першина Елена Викторовна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/pershina-e-v.jpg' }, { - 'name': 'Рзаева Алина Игоревна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/DSC06789.JPG' + name: 'Потяйкин Роман Владимирович', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/.jpg' }, { - 'name': 'Савич Мария Владимировна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/asdasad.jpg' + name: 'Рзаева Алина Игоревна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/DSC06789.JPG' }, { - 'name': 'Самойлова Наталья Николаевна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2022/samoylova.jpg' + name: 'Савич Мария Владимировна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/asdasad.jpg' }, { - 'name': 'Семенов Антон Сергеевич', - 'picture': 'https://ks.psuti.ru/images/stories/emp/antonov-a-s.jpg' + name: 'Самойлова Наталья Николаевна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2022/samoylova.jpg' }, { - 'name': 'Сергеев Роман Алексеевич', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/.jpg' + name: 'Семенов Антон Сергеевич', + picture: 'https://ks.psuti.ru/images/stories/emp/antonov-a-s.jpg' }, { - 'name': 'Сиднина Юлия Валерьевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/%D1%81%D0%B8%D0%B4%D0%BD%D0%B8%D0%BD%D0%B0.jpg' + name: 'Сергеев Роман Алексеевич', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/.jpg' }, { - 'name': 'Синекопова Лариса Владимировна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/sinekopova-l-v.jpg' + name: 'Сиднина Юлия Валерьевна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/%D1%81%D0%B8%D0%B4%D0%BD%D0%B8%D0%BD%D0%B0.jpg' }, { - 'name': 'Сироткина Ольга Владимировна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/sirotkina-o-v.jpg' + name: 'Синекопова Лариса Владимировна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/sinekopova-l-v.jpg' }, { - 'name': 'Ситникова Людмила Геннадьевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/sitnikova-l-.jpg' + name: 'Сироткина Ольга Владимировна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/sirotkina-o-v.jpg' }, { - 'name': 'Славкина Татьяна Анатольевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/slavkina-t-a.jpg' + name: 'Ситникова Людмила Геннадьевна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/sitnikova-l-.jpg' }, { - 'name': 'Сорокина Надежда Леонидовна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/sorokina-n.jpg' + name: 'Славкина Татьяна Анатольевна', + picture: 'https://ks.psuti.ru/images/stories/emp/slavkina-t-a.jpg' }, { - 'name': 'Странник Дмитрий Христианович', - 'picture': 'https://ks.psuti.ru/images/stories/emp/stranik.jpg' + name: 'Сорокина Надежда Леонидовна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/sorokina-n.jpg' }, { - 'name': 'Тананыхина Надежда Воалимировна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/IMG-f8eeb02.jpg' + name: 'Странник Дмитрий Христианович', + picture: 'https://ks.psuti.ru/images/stories/emp/stranik.jpg' }, { - 'name': 'Терёхин Дмитрий Вячеславович', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/terexin.jpg' + name: 'Тананыхина Надежда Воалимировна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/IMG-f8eeb02.jpg' }, { - 'name': 'Упанова Анастасия Владимировна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/.jpg' + name: 'Терёхин Дмитрий Вячеславович', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/terexin.jpg' }, { - 'name': 'Утыбаева Светлана Михайловна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/2019-utibaeva-s-m.jpg' + name: 'Упанова Анастасия Владимировна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/.jpg' }, { - 'name': 'Федотова Елена Дмитриевна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/fedotova-e-.jpg' + name: 'Утыбаева Светлана Михайловна', + picture: 'https://ks.psuti.ru/images/stories/emp/2019-utibaeva-s-m.jpg' }, { - 'name': 'Фомин Александр Васильевич', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/fomin-a-v.jpg' + name: 'Федотова Елена Дмитриевна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/fedotova-e-.jpg' }, { - 'name': 'Ходотова Евгения Андреевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/hodotova.jpg' + name: 'Фомин Александр Васильевич', + picture: 'https://ks.psuti.ru/images/stories/emp/new/fomin-a-v.jpg' }, { - 'name': 'Черненкова Наталья Владимировна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/chernenkova-n-v.jpg' + name: 'Ходотова Евгения Андреевна', + picture: 'https://ks.psuti.ru/images/stories/emp/hodotova.jpg' }, { - 'name': 'Шамбер Лола Низамовна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/shamber-l-n.jpg' + name: 'Черненкова Наталья Владимировна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/chernenkova-n-v.jpg' }, { - 'name': 'Шомас Елена Александровна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/2014-shomas.jpg' + name: 'Шамбер Лола Низамовна', + picture: 'https://ks.psuti.ru/images/stories/emp/shamber-l-n.jpg' }, { - 'name': 'Шукова Марина Геннадьевна', - 'picture': 'https://ks.psuti.ru/images/stories/emp/new/shykova.jpg' + name: 'Шомас Елена Александровна', + picture: 'https://ks.psuti.ru/images/stories/emp/2014-shomas.jpg' }, { - 'name': 'Щербакова Надежда Юрьевна', - 'picture': 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/andreeva.jpg' + name: 'Шукова Марина Геннадьевна', + picture: 'https://ks.psuti.ru/images/stories/emp/new/shykova.jpg' + }, + { + name: 'Щербакова Надежда Юрьевна', + picture: 'https://ks.psuti.ru/images/stories/ks-news/2016/prepodavateli-foto/andreeva.jpg' } ] \ No newline at end of file diff --git a/src/shared/model/lesson.ts b/src/shared/model/lesson.ts index 3e6d06c..ce17e0f 100644 --- a/src/shared/model/lesson.ts +++ b/src/shared/model/lesson.ts @@ -6,12 +6,6 @@ export type Lesson = { hint?: string } type: string - subject: string - teacher?: string - place?: { - address: string - classroom: number - } topic?: string resources: { type: 'link' @@ -19,4 +13,13 @@ export type Lesson = { url: string }[] homework: string -} \ No newline at end of file +} & ( + { + subject: string + teacher?: string + place?: { + address: string + classroom: string + } + } | { fallbackDiscipline?: string } +) \ No newline at end of file diff --git a/src/widgets/navbar/index.tsx b/src/widgets/navbar/index.tsx index 29cd531..87948be 100644 --- a/src/widgets/navbar/index.tsx +++ b/src/widgets/navbar/index.tsx @@ -6,11 +6,11 @@ import { useRouter } from 'next/router' import cx from 'classnames' export function NavBar() { - const { theme } = useTheme() + const { resolvedTheme } = useTheme() return (
-