Add dates, discipline fallbacks
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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'> ({lesson.time.hint})</span>}
|
||||
</CardDescription>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
|
||||
{lesson.type && <><Badge>{lesson.type}</Badge>{' '} </>}
|
||||
{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 /> Материалы</Button>
|
||||
)}
|
||||
</CardFooter>
|
||||
)}
|
||||
<ResourcesDialog
|
||||
open={resourcesDialogOpened}
|
||||
onClose={() => setResourcesDialogOpened(false)}
|
||||
teacherName={('teacher' in lesson && lesson.teacher) ? lesson.teacher : undefined}
|
||||
resources={lesson.resources}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
61
src/widgets/schedule/resources-dialog.tsx
Normal file
61
src/widgets/schedule/resources-dialog.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user