Compare commits

...

1 Commits

Author SHA1 Message Date
34ea1e3f08 G1 surface homework in lesson planner UI 2026-07-24 19:20:43 +01:00
8 changed files with 88 additions and 25 deletions

View File

@ -39,6 +39,7 @@ interface LessonPlan {
year_group: string | null; year_group: string | null;
duration_minutes: number | null; duration_minutes: number | null;
context_notes: string | null; context_notes: string | null;
homework: string | null;
objectives: Objective[]; objectives: Objective[];
activities: Activity[]; activities: Activity[];
is_public: boolean; is_public: boolean;
@ -88,6 +89,7 @@ const LessonPlanDetailPage: React.FC = () => {
const [yearGroup, setYearGroup] = useState(''); const [yearGroup, setYearGroup] = useState('');
const [duration, setDuration] = useState(''); const [duration, setDuration] = useState('');
const [contextNotes, setContextNotes] = useState(''); const [contextNotes, setContextNotes] = useState('');
const [homework, setHomework] = useState('');
const [isPublic, setIsPublic] = useState(false); const [isPublic, setIsPublic] = useState(false);
const [objectives, setObjectives] = useState<Objective[]>([]); const [objectives, setObjectives] = useState<Objective[]>([]);
const [activities, setActivities] = useState<Activity[]>([]); const [activities, setActivities] = useState<Activity[]>([]);
@ -110,6 +112,7 @@ const LessonPlanDetailPage: React.FC = () => {
setYearGroup(data.year_group || ''); setYearGroup(data.year_group || '');
setDuration(data.duration_minutes?.toString() || ''); setDuration(data.duration_minutes?.toString() || '');
setContextNotes(data.context_notes || ''); setContextNotes(data.context_notes || '');
setHomework(data.homework || '');
setIsPublic(data.is_public || false); setIsPublic(data.is_public || false);
setObjectives(data.objectives || []); setObjectives(data.objectives || []);
setActivities(data.activities || []); setActivities(data.activities || []);
@ -128,7 +131,7 @@ const LessonPlanDetailPage: React.FC = () => {
// ── Save ────────────────────────────────────────────────────────────────── // ── Save ──────────────────────────────────────────────────────────────────
const save = useCallback(async ( const save = useCallback(async (
overrides?: Partial<{ title: string; subject: string; year_group: string; duration_minutes: number | null; context_notes: string; is_public: boolean; objectives: Objective[]; activities: Activity[] }> overrides?: Partial<{ title: string; subject: string; year_group: string; duration_minutes: number | null; context_notes: string; homework: string; is_public: boolean; objectives: Objective[]; activities: Activity[] }>
) => { ) => {
if (!accessToken || !planId) return; if (!accessToken || !planId) return;
setSaving(true); setSaving(true);
@ -143,6 +146,7 @@ const LessonPlanDetailPage: React.FC = () => {
year_group: yearGroup || null, year_group: yearGroup || null,
duration_minutes: duration ? parseInt(duration, 10) : null, duration_minutes: duration ? parseInt(duration, 10) : null,
context_notes: contextNotes || null, context_notes: contextNotes || null,
homework: homework || '',
is_public: isPublic, is_public: isPublic,
objectives, objectives,
activities, activities,
@ -156,7 +160,7 @@ const LessonPlanDetailPage: React.FC = () => {
} finally { } finally {
setSaving(false); setSaving(false);
} }
}, [accessToken, planId, title, subject, yearGroup, duration, contextNotes, isPublic, objectives, activities]); }, [accessToken, planId, title, subject, yearGroup, duration, contextNotes, homework, isPublic, objectives, activities]);
const scheduleAutoSave = useCallback(() => { const scheduleAutoSave = useCallback(() => {
if (autoSaveTimer.current) clearTimeout(autoSaveTimer.current); if (autoSaveTimer.current) clearTimeout(autoSaveTimer.current);
@ -359,6 +363,22 @@ const LessonPlanDetailPage: React.FC = () => {
/> />
</Box> </Box>
{/* Homework */}
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle2" fontWeight={600} sx={{ mb: 0.75 }}>
Homework
</Typography>
<TextField
value={homework}
onChange={e => { setHomework(e.target.value); scheduleAutoSave(); }}
multiline
minRows={2}
fullWidth
size="small"
placeholder="Homework to carry with this plan when assigned to a taught lesson…"
/>
</Box>
<Divider sx={{ mb: 3 }} /> <Divider sx={{ mb: 3 }} />
{/* Objectives */} {/* Objectives */}

View File

@ -21,6 +21,7 @@ interface PlanSummary {
duration_minutes: number | null; duration_minutes: number | null;
objectives: { id: string; text: string; bloom_level?: string }[]; objectives: { id: string; text: string; bloom_level?: string }[];
activities: { id: string; section: string; title: string }[]; activities: { id: string; section: string; title: string }[];
homework: string | null;
is_public: boolean; is_public: boolean;
created_at: string; created_at: string;
updated_at: string; updated_at: string;
@ -144,6 +145,7 @@ function PlanCard({ plan, onOpen, onDelete }: {
</Box> </Box>
<Typography variant="caption" color="text.secondary"> <Typography variant="caption" color="text.secondary">
{objCount} objective{objCount !== 1 ? 's' : ''} · {actCount} activit{actCount !== 1 ? 'ies' : 'y'} {objCount} objective{objCount !== 1 ? 's' : ''} · {actCount} activit{actCount !== 1 ? 'ies' : 'y'}
{plan.homework ? ' · homework set' : ''}
</Typography> </Typography>
</CardContent> </CardContent>
<CardActions sx={{ pt: 0, px: 1.5, pb: 1.5, justifyContent: 'flex-end' }} onClick={e => e.stopPropagation()}> <CardActions sx={{ pt: 0, px: 1.5, pb: 1.5, justifyContent: 'flex-end' }} onClick={e => e.stopPropagation()}>

View File

@ -22,6 +22,7 @@ interface Lesson {
week_cycle: string; week_cycle: string;
day_of_week: string; day_of_week: string;
status: string; status: string;
homework: string | null;
teacher_name: string | null; teacher_name: string | null;
} }
@ -95,6 +96,11 @@ function LessonCard({ lesson }: { lesson: Lesson }) {
sx={{ height: 16, fontSize: '0.65rem' }} sx={{ height: 16, fontSize: '0.65rem' }}
/> />
</Box> </Box>
{lesson.homework && (
<Typography variant="caption" sx={{ color: 'primary.main', fontSize: '0.68rem', fontWeight: 600 }}>
Homework: {lesson.homework.length > 80 ? lesson.homework.slice(0, 80) + '…' : lesson.homework}
</Typography>
)}
{lesson.teacher_name && ( {lesson.teacher_name && (
<Typography variant="caption" sx={{ color: 'text.secondary', fontSize: '0.68rem' }}> <Typography variant="caption" sx={{ color: 'text.secondary', fontSize: '0.68rem' }}>
{lesson.teacher_name} {lesson.teacher_name}

View File

@ -30,6 +30,7 @@ interface Lesson {
day_of_week: string; day_of_week: string;
status: string; status: string;
lesson_plan: Record<string, any>; lesson_plan: Record<string, any>;
homework: string | null;
notes: string | null; notes: string | null;
whiteboard_room_id: string | null; whiteboard_room_id: string | null;
} }
@ -76,16 +77,18 @@ const STATUS_COLORS: Record<string, 'default' | 'primary' | 'success' | 'error'
interface EditDialogProps { interface EditDialogProps {
lesson: Lesson | null; lesson: Lesson | null;
onClose: () => void; onClose: () => void;
onSave: (id: string, updates: { notes?: string; status?: string }) => Promise<void>; onSave: (id: string, updates: { homework?: string; notes?: string; status?: string }) => Promise<void>;
} }
function LessonEditDialog({ lesson, onClose, onSave }: EditDialogProps) { function LessonEditDialog({ lesson, onClose, onSave }: EditDialogProps) {
const [homework, setHomework] = useState('');
const [notes, setNotes] = useState(''); const [notes, setNotes] = useState('');
const [status, setStatus] = useState('planned'); const [status, setStatus] = useState('planned');
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
useEffect(() => { useEffect(() => {
if (lesson) { if (lesson) {
setHomework(lesson.homework || '');
setNotes(lesson.notes || ''); setNotes(lesson.notes || '');
setStatus(lesson.status || 'planned'); setStatus(lesson.status || 'planned');
} }
@ -94,7 +97,7 @@ function LessonEditDialog({ lesson, onClose, onSave }: EditDialogProps) {
const handleSave = async () => { const handleSave = async () => {
if (!lesson) return; if (!lesson) return;
setSaving(true); setSaving(true);
await onSave(lesson.id, { notes, status }); await onSave(lesson.id, { homework, notes, status });
setSaving(false); setSaving(false);
onClose(); onClose();
}; };
@ -127,6 +130,16 @@ function LessonEditDialog({ lesson, onClose, onSave }: EditDialogProps) {
<MenuItem value="substituted">Substituted</MenuItem> <MenuItem value="substituted">Substituted</MenuItem>
</Select> </Select>
</FormControl> </FormControl>
<TextField
label="Homework"
value={homework}
onChange={e => setHomework(e.target.value)}
multiline
minRows={2}
fullWidth
size="small"
placeholder="Homework for this lesson…"
/>
<TextField <TextField
label="Notes" label="Notes"
value={notes} value={notes}
@ -209,6 +222,11 @@ function LessonCard({ lesson, onEdit, onAssignPlan }: LessonCardProps) {
sx={{ height: 16, fontSize: '0.65rem' }} sx={{ height: 16, fontSize: '0.65rem' }}
/> />
</Box> </Box>
{lesson.homework && (
<Typography variant="caption" sx={{ color: 'primary.main', fontSize: '0.68rem', fontWeight: 600, mt: 0.25 }}>
Homework: {lesson.homework.length > 80 ? lesson.homework.slice(0, 80) + '…' : lesson.homework}
</Typography>
)}
{lesson.notes && ( {lesson.notes && (
<Typography variant="caption" sx={{ color: 'text.secondary', fontSize: '0.68rem', fontStyle: 'italic', mt: 0.25 }}> <Typography variant="caption" sx={{ color: 'text.secondary', fontSize: '0.68rem', fontStyle: 'italic', mt: 0.25 }}>
{lesson.notes.length > 80 ? lesson.notes.slice(0, 80) + '…' : lesson.notes} {lesson.notes.length > 80 ? lesson.notes.slice(0, 80) + '…' : lesson.notes}
@ -287,7 +305,7 @@ const TaughtLessonsPage: React.FC = () => {
} }
}; };
const handleSaveLesson = async (id: string, updates: { notes?: string; status?: string }) => { const handleSaveLesson = async (id: string, updates: { homework?: string; notes?: string; status?: string }) => {
if (!accessToken) return; if (!accessToken) return;
await fetch(`${API_BASE}/timetable/lessons/${id}`, { await fetch(`${API_BASE}/timetable/lessons/${id}`, {
method: 'PATCH', method: 'PATCH',

View File

@ -86,6 +86,12 @@ export class CCPlannedLessonNodeShapeUtil extends CCBaseShapeUtil<CCPlannedLesso
labelStyle={styles.property.label} labelStyle={styles.property.label}
valueStyle={styles.property.value} valueStyle={styles.property.value}
/> />
<NodeProperty
label="Homework"
value={shape.props.homework}
labelStyle={styles.property.label}
valueStyle={styles.property.value}
/>
<NodeProperty <NodeProperty
label="Topic Code" label="Topic Code"
value={shape.props.topic_code} value={shape.props.topic_code}

View File

@ -5,7 +5,8 @@ import { CCGraphShape, GraphShapeType } from './cc-graph-types'
// Helper function to create version IDs for a shape type // Helper function to create version IDs for a shape type
const createVersions = (shapeType: GraphShapeType) => { const createVersions = (shapeType: GraphShapeType) => {
return createShapePropsMigrationIds(shapeType, { return createShapePropsMigrationIds(shapeType, {
Initial: 1 // All shapes start at version 1 as required by TLDraw Initial: 1, // All shapes start at version 1 as required by TLDraw
AddPlannedLessonHomework: 2,
}) })
} }
@ -21,6 +22,13 @@ const createMigrationSequence = (shapeType: GraphShapeType) => {
return props return props
}, },
}, },
...(shapeType === 'cc-planned-lesson-node' ? [{
id: versions.AddPlannedLessonHomework,
up: (props: CCGraphShape['props']) => ({
homework: '',
...props,
}),
}] : []),
], ],
}) })
} }

View File

@ -199,6 +199,7 @@ export const ccGraphShapeProps = {
subject: T.string, subject: T.string,
teacher_code: T.string, teacher_code: T.string,
planning_status: T.string, planning_status: T.string,
homework: T.string,
topic_code: T.string, topic_code: T.string,
topic_name: T.string, topic_name: T.string,
lesson_code: T.string, lesson_code: T.string,
@ -560,6 +561,7 @@ export const getDefaultCCPlannedLessonNodeProps = () => ({
subject: '', subject: '',
teacher_code: '', teacher_code: '',
planning_status: '', planning_status: '',
homework: '',
topic_code: '', topic_code: '',
topic_name: '', topic_name: '',
lesson_code: '', lesson_code: '',

View File

@ -204,6 +204,7 @@ export type CCPlannedLessonNodeProps = CCGraphShapeProps & {
subject: string subject: string
teacher_code: string teacher_code: string
planning_status: string planning_status: string
homework: string
topic_code: string topic_code: string
topic_name: string topic_name: string
lesson_code: string lesson_code: string