Add lessons module, rate cycles, EC2 deploy scripts, and help content
- Lessons module: lesson types, instructors, schedule slots, enrollments, sessions (list + week grid view), lesson plans, grading scales, templates - Rate cycles: replace monthly_rate with billing_interval + billing_unit on enrollments; add weekly/monthly/quarterly rate presets to lesson types and schedule slots with auto-fill on enrollment form - Member detail page: tabbed layout for details, identity documents, enrollments - Sessions week view: custom 7-column grid replacing react-big-calendar - Music store seed: instructors, lesson types, slots, enrollments, sessions, grading scale, lesson plan template - Scrollbar styling: themed to match sidebar/app palette - deploy/: EC2 setup and redeploy scripts, nginx config, systemd service - Help: add Lessons category (overview, types, instructors, slots, enrollments, sessions, plans/grading); collapsible sidebar with independent scroll; remove POS/accounting references from docs
This commit is contained in:
87
packages/admin/src/components/lessons/weekly-slot-grid.tsx
Normal file
87
packages/admin/src/components/lessons/weekly-slot-grid.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Pencil, Trash2 } from 'lucide-react'
|
||||
import type { ScheduleSlot, LessonType } from '@/types/lesson'
|
||||
|
||||
const DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
||||
|
||||
interface Props {
|
||||
slots: ScheduleSlot[]
|
||||
lessonTypes: LessonType[]
|
||||
onEdit: (slot: ScheduleSlot) => void
|
||||
onDelete: (slot: ScheduleSlot) => void
|
||||
}
|
||||
|
||||
function formatTime(t: string) {
|
||||
const [h, m] = t.split(':').map(Number)
|
||||
const ampm = h >= 12 ? 'PM' : 'AM'
|
||||
const hour = h % 12 || 12
|
||||
return `${hour}:${String(m).padStart(2, '0')} ${ampm}`
|
||||
}
|
||||
|
||||
export function WeeklySlotGrid({ slots, lessonTypes, onEdit, onDelete }: Props) {
|
||||
const ltMap = new Map(lessonTypes.map((lt) => [lt.id, lt]))
|
||||
|
||||
const slotsByDay = DAYS.map((_, day) =>
|
||||
slots.filter((s) => s.dayOfWeek === day).sort((a, b) => a.startTime.localeCompare(b.startTime)),
|
||||
)
|
||||
|
||||
const hasAny = slots.length > 0
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-7 gap-2">
|
||||
{DAYS.map((day, idx) => (
|
||||
<div key={day} className="min-h-[120px]">
|
||||
<div className="text-xs font-semibold text-muted-foreground uppercase tracking-wide text-center mb-2 py-1 border-b">
|
||||
{day}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{slotsByDay[idx].map((slot) => {
|
||||
const lt = ltMap.get(slot.lessonTypeId)
|
||||
return (
|
||||
<div
|
||||
key={slot.id}
|
||||
className="bg-sidebar-accent rounded-md p-2 text-xs group relative"
|
||||
>
|
||||
<div className="font-medium">{formatTime(slot.startTime)}</div>
|
||||
<div className="text-muted-foreground truncate">{lt?.name ?? 'Unknown'}</div>
|
||||
{slot.room && <div className="text-muted-foreground">{slot.room}</div>}
|
||||
{lt && (
|
||||
<Badge variant="outline" className="mt-1 text-[10px] py-0">
|
||||
{lt.lessonFormat}
|
||||
</Badge>
|
||||
)}
|
||||
<div className="absolute top-1 right-1 hidden group-hover:flex gap-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-5 w-5"
|
||||
onClick={() => onEdit(slot)}
|
||||
title="Edit"
|
||||
>
|
||||
<Pencil className="h-3 w-3" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-5 w-5"
|
||||
onClick={() => onDelete(slot)}
|
||||
title="Delete"
|
||||
>
|
||||
<Trash2 className="h-3 w-3 text-destructive" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{!hasAny && (
|
||||
<div className="col-span-7 text-center text-sm text-muted-foreground py-8">
|
||||
No schedule slots yet — add one to get started.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user