Add dates, discipline fallbacks

This commit is contained in:
VityaSchel
2023-10-02 01:04:55 +04:00
parent de4208337e
commit f6daee6201
15 changed files with 592 additions and 219 deletions

View File

@@ -6,11 +6,11 @@ import { useRouter } from 'next/router'
import cx from 'classnames'
export function NavBar() {
const { theme } = useTheme()
const { resolvedTheme } = useTheme()
return (
<header className="w-full p-2">
<nav className={cx('rounded-lg p-2 w-full flex justify-between', { 'bg-slate-200': theme === 'light', 'bg-slate-900': theme === 'dark' })}>
<nav className={cx('rounded-lg p-2 w-full flex justify-between', { 'bg-slate-200': resolvedTheme === 'light', 'bg-slate-900': resolvedTheme === 'dark' })}>
<ul className="flex gap-2">
<NavBarItem url="/ps7">ПС-7</NavBarItem>
<NavBarItem url="/pks35k">ПКС-35к</NavBarItem>

View File

@@ -14,15 +14,30 @@ export function Day({ day }: {
'Воскресенье'
][day.date.getDay()-1]
const longNames = day.lessons
.some(lesson => 'subject' in lesson && lesson.subject.length > 20)
return (
<div className="flex flex-col gap-5">
<h1 className="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl">
{dayOfWeek}
{dayOfWeek} <span className='text-muted ml-3'>{Intl.DateTimeFormat('ru-RU', {
day: 'numeric',
month: 'long',
// year: 'numeric'
}).format(day.date)}</span>
</h1>
<div className="flex flex-row gap-4">
{day.lessons.map((lesson, i) => (
<Lesson lesson={lesson} key={i} />
))}
<div className='overflow-hidden'>
<div className='overflow-auto'>
<div className="flex flex-row gap-4 w-max">
{day.lessons.map((lesson, i) => (
<Lesson
width={longNames ? 450 : 350}
lesson={lesson}
key={i}
/>
))}
</div>
</div>
</div>
</div>
)

View File

@@ -12,13 +12,28 @@ import {
AvatarFallback,
AvatarImage,
} from '@/shadcn/ui/avatar'
import { Badge } from '@/shadcn/ui/badge'
import { teachers } from '@/shared/data/teachers'
import { Lesson as LessonType } from '@/shared/model/lesson'
import React from 'react'
import { MdSchool } from 'react-icons/md'
import { AiOutlineFolderView } from 'react-icons/ai'
import { BsFillGeoAltFill } from 'react-icons/bs'
import { RiGroup2Fill } from 'react-icons/ri'
import { ResourcesDialog } from '@/widgets/schedule/resources-dialog'
export function Lesson({ lesson }: {
export function Lesson({ lesson, width = 350 }: {
lesson: LessonType
width: number
}) {
const teacherObj = lesson.teacher ? teachers.find(t => t.name === lesson.teacher) : null
const [resourcesDialogOpened, setResourcesDialogOpened] = React.useState(false)
const hasTeacher = 'teacher' in lesson && lesson.teacher
const teacherObj = hasTeacher ? teachers.find(t => t.name === lesson.teacher) : null
const hasPlace = 'place' in lesson && lesson.place
const isFallbackDiscipline = 'fallbackDiscipline' in lesson && lesson.fallbackDiscipline
const getTeacherPhoto = (url?: string) => {
if(url) {
@@ -34,27 +49,76 @@ export function Lesson({ lesson }: {
}
}
const fallbackTeacherName = () => {
if (!hasTeacher || !lesson.teacher) return ''
const [, firstName, middleName] = lesson.teacher.split(' ')
return firstName.at(0)! + middleName.at(0)!
}
const handleOpenResources = () => {
setResourcesDialogOpened(true)
}
return (
<Card className="w-[350px]">
<div>
<Avatar>
<AvatarImage src={getTeacherPhoto(teacherObj?.picture)} alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar></Avatar>
<CardHeader>
<CardTitle>{lesson.subject}</CardTitle>
<CardDescription>{lesson.teacher}</CardDescription>
<CardDescription>{lesson.place?.classroom}</CardDescription>
</CardHeader>
</div>
<Card className={`w-[${width}px] min-w-[${width}px] max-w-[${width}px] flex flex-col relative overflow-hidden`} style={{ minWidth: width, maxWidth: width }}>
{lesson.isChange && <div className='absolute top-0 left-0 w-full h-full bg-gradient-to-br from-[#ffc60026] to-[#95620026]'></div>}
<CardHeader>
<div className='flex gap-4'>
{hasTeacher ? (
<Avatar>
<AvatarImage
src={getTeacherPhoto(teacherObj?.picture)!}
alt={lesson.teacher}
title={lesson.teacher}
/>
<AvatarFallback title={lesson.teacher}>
{fallbackTeacherName()}
</AvatarFallback>
</Avatar>
) : (
<Avatar>
<AvatarFallback><MdSchool /></AvatarFallback>
</Avatar>
)}
<div className='flex flex-col gap-1'>
{'subject' in lesson && <CardTitle className='hyphens-auto'>{lesson.subject}</CardTitle>}
<CardDescription>
{lesson.time.start} - {lesson.time.end}{
}{lesson.time.hint && <span className='font-bold'>&nbsp;({lesson.time.hint})</span>}
</CardDescription>
</div>
</div>
</CardHeader>
<CardContent>
{lesson.type && <><Badge>{lesson.type}</Badge>{' '}&nbsp;</>}
{isFallbackDiscipline && (
<span className='leading-relaxed hyphens-auto block'>{lesson.fallbackDiscipline}</span>
)}
{lesson.topic ? (
<span className='leading-relaxed hyphens-auto'>{lesson.topic}</span>
) : (
!isFallbackDiscipline && <span className='text-muted font-semibold'>Нет описания пары</span>
)}
</CardContent>
<CardFooter className="flex justify-between">
<Button variant="outline">Cancel</Button>
<Button>Deploy</Button>
</CardFooter>
{(Boolean(lesson.resources.length) || hasPlace) && (
<CardFooter className="flex justify-between mt-auto">
{('place' in lesson && lesson.place) ? (
<div className='flex flex-col text-muted-foreground text-xs'>
<span className='flex items-center gap-2'><BsFillGeoAltFill /> {lesson.place.address}</span>
<span className='font-bold flex items-center gap-2'><RiGroup2Fill /> {lesson.place.classroom}</span>
</div>
) : <span />}
{Boolean(lesson.resources.length) && (
<Button onClick={handleOpenResources}><AiOutlineFolderView />&nbsp;Материалы</Button>
)}
</CardFooter>
)}
<ResourcesDialog
open={resourcesDialogOpened}
onClose={() => setResourcesDialogOpened(false)}
teacherName={('teacher' in lesson && lesson.teacher) ? lesson.teacher : undefined}
resources={lesson.resources}
/>
</Card>
)
}

View File

@@ -0,0 +1,61 @@
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '@/shadcn/ui/dialog'
import { teachers } from '@/shared/data/teachers'
import { Lesson } from '@/shared/model/lesson'
import Link from 'next/link'
import { BiLink } from 'react-icons/bi'
export function ResourcesDialog({ open, onClose, teacherName, resources }: {
open: boolean
onClose: () => any
teacherName?: string
resources: Lesson['resources']
}) {
const teacherPronouns = teachers.find(t => t.name === teacherName)?.pronouns
return (
<Dialog open={open} onOpenChange={isOpen => !isOpen && onClose()}>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Материалы к уроку</DialogTitle>
{teacherName && (
<DialogDescription>
{teacherName} {
teacherPronouns === 'she'
? 'поделилась'
: teacherPronouns === 'he'
? 'поделился'
: teacherPronouns === 'bitch'
? '(тварь) поделилась'
: 'поделилась(-ся)'
} материалами к этому уроку.
</DialogDescription>
)}
</DialogHeader>
<div className="grid gap-4 py-4">
{resources.map((resource, i) => <Resource resource={resource} key={i} />)}
</div>
</DialogContent>
</Dialog>
)
}
function Resource({ resource }: {
resource: Lesson['resources'][number]
}) {
if(resource.type === 'link') {
return (
<div className="flex items-center gap-4">
<BiLink />
<Link href={resource.url} className='whitespace-pre-wrap' target='_blank' rel='nofollower noreferrer'>
{resource.title}
</Link>
</div>
)
}
}