diff --git a/apps/web/src/components/practice/StudentFilterBar.tsx b/apps/web/src/components/practice/StudentFilterBar.tsx index 7526ee74..8941dbc8 100644 --- a/apps/web/src/components/practice/StudentFilterBar.tsx +++ b/apps/web/src/components/practice/StudentFilterBar.tsx @@ -666,13 +666,11 @@ export interface TeacherClassroomCardProps { } /** - * Unified classroom control card for teachers + * Unified classroom control chip for teachers - matches other filter pill styling * - * ┌──────────────────────────────────────────────────────────────────┐ - * │ 📚 Mrs. Smith's Class [ABC-123] [+Student] [⚙️] │ - * ├──────────────────────────────────────────────────────────────────┤ - * │ [📋 Enrolled (10)] [🏫 Present (5)] [🎯 Active (2)] │ - * └──────────────────────────────────────────────────────────────────┘ + * ┌───────────────────────────────────────────────────────────────────────────┐ + * │ [+] [📋 Class Name ⚙️ (10)] [🏫 Present (5)] [🎯 Active (2)] │ + * └───────────────────────────────────────────────────────────────────────────┘ */ export function TeacherClassroomCard({ classroom, @@ -687,7 +685,6 @@ export function TeacherClassroomCard({ const updateClassroom = useUpdateClassroom() const [isSettingsOpen, setIsSettingsOpen] = useState(false) const [nameValue, setNameValue] = useState(classroom.name) - const nameInputRef = useRef(null) // Reset name value when classroom changes or settings popover opens useEffect(() => { @@ -717,251 +714,186 @@ export function TeacherClassroomCard({ const currentExpiry = classroom.entryPromptExpiryMinutes return ( -
- {/* Header row: Action buttons only - classroom name is now in first segment */} -
- {/* Action buttons - minimal icons */} -
- {/* Add student button - opens unified modal with share code, create, family code */} - {onAddStudentToClassroom && ( - - )} - - {/* Settings button with popover - icon only */} - - - - - - - -

- Classroom Settings -

- - {/* Classroom name setting */} -
- - setNameValue(e.target.value)} - onKeyDown={(e) => { - if (e.key === 'Enter') { - handleNameSave() - } else if (e.key === 'Escape') { - setNameValue(classroom.name) - } - }} - onBlur={handleNameSave} - disabled={updateClassroom.isPending} - className={css({ - width: '100%', - padding: '6px 8px', - fontSize: '13px', - borderRadius: '6px', - border: '1px solid', - borderColor: isDark ? 'gray.600' : 'gray.300', - backgroundColor: isDark ? 'gray.700' : 'white', - color: isDark ? 'gray.100' : 'gray.800', - cursor: updateClassroom.isPending ? 'wait' : 'text', - opacity: updateClassroom.isPending ? 0.7 : 1, - _focus: { - outline: '2px solid', - outlineColor: 'blue.500', - outlineOffset: '1px', - }, - })} - /> -
- - {/* Entry prompt expiry setting */} -
- - -

- How long parents have to respond before the entry prompt expires -

-
- - -
-
-
-
-
- - {/* Filter row: Embedded compound chip - flush with card edges */} + + {/* No wrapper div - TeacherCompoundChip handles its own border/radius like other pills */} + + + } /> -
+ + + +

+ Classroom Settings +

+ + {/* Classroom name setting */} +
+ + setNameValue(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + handleNameSave() + } else if (e.key === 'Escape') { + setNameValue(classroom.name) + } + }} + onBlur={handleNameSave} + disabled={updateClassroom.isPending} + className={css({ + width: '100%', + padding: '6px 8px', + fontSize: '13px', + borderRadius: '6px', + border: '1px solid', + borderColor: isDark ? 'gray.600' : 'gray.300', + backgroundColor: isDark ? 'gray.700' : 'white', + color: isDark ? 'gray.100' : 'gray.800', + cursor: updateClassroom.isPending ? 'wait' : 'text', + opacity: updateClassroom.isPending ? 0.7 : 1, + _focus: { + outline: '2px solid', + outlineColor: 'blue.500', + outlineOffset: '1px', + }, + })} + /> +
+ + {/* Entry prompt expiry setting */} +
+ + +

+ How long parents have to respond before the entry prompt expires +

+
+ + +
+
+ ) } diff --git a/apps/web/src/components/practice/ViewSelector.tsx b/apps/web/src/components/practice/ViewSelector.tsx index ce30b61b..83d9aa17 100644 --- a/apps/web/src/components/practice/ViewSelector.tsx +++ b/apps/web/src/components/practice/ViewSelector.tsx @@ -535,6 +535,10 @@ export interface TeacherCompoundChipProps { embedded?: boolean /** Optional classroom name to display instead of "Enrolled" in first segment */ classroomName?: string + /** Optional callback for add student action - renders a "+" prefix when provided */ + onAddStudent?: () => void + /** Optional settings trigger element to render inside the first segment */ + settingsTrigger?: ReactNode } /** @@ -553,6 +557,8 @@ export function TeacherCompoundChip({ isDark: isDarkProp, embedded = false, classroomName, + onAddStudent, + settingsTrigger, }: TeacherCompoundChipProps) { const { resolvedTheme } = useTheme() const isDark = isDarkProp ?? resolvedTheme === 'dark' @@ -593,6 +599,39 @@ export function TeacherCompoundChip({ : 'gray.300', })} > + {/* Add student prefix button */} + {onAddStudent && ( + + )} + {/* Enrolled segment - shows classroom name if provided */} {/* In Classroom segment */} @@ -651,6 +691,8 @@ interface ChipSegmentProps { embedded?: boolean /** Optional label override (e.g., classroom name instead of "Enrolled") */ labelOverride?: string + /** Optional settings trigger element to render after the label */ + settingsTrigger?: ReactNode } function ChipSegment({ @@ -664,6 +706,7 @@ function ChipSegment({ colorScheme, embedded = false, labelOverride, + settingsTrigger, }: ChipSegmentProps) { const isLast = position === 'last' @@ -728,6 +771,7 @@ function ChipSegment({ > {config.icon} {labelOverride ?? config.shortLabel ?? config.label} + {settingsTrigger} {count !== undefined && (