feat(phase-b): school onboarding wizard + GAIS school search UI
- SchoolOnboardingWizard.tsx: new 3-step wizard (GAIS search → confirm → calendar setup) triggered from the nav panel school section when no school is linked. Calls GET /school/search for live school lookup and POST /school/register to create the institute record. - CCGraphNavPanel.tsx: add showOnboard button for no_school state, onOnboardSchool context action, wire up SchoolOnboardingWizard state. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
@@ -26,6 +26,7 @@ import { NeoGraphNode } from '../../../../../../types/navigation';
|
||||
import { logger } from '../../../../../../debugConfig';
|
||||
import { SchoolCalendarWizard, SchoolInfo } from './SchoolCalendarWizard';
|
||||
import { TeacherTimetableWizard, PeriodTemplate } from './TeacherTimetableWizard';
|
||||
import { SchoolOnboardingWizard } from './SchoolOnboardingWizard';
|
||||
|
||||
type NodeStatus = 'populated' | 'empty' | 'no_school' | 'not_initialized';
|
||||
type CalendarMode = 'generic' | 'academic';
|
||||
@@ -97,6 +98,7 @@ interface NavPanelContextValue {
|
||||
schoolStatus: SchoolStatus | null;
|
||||
onSetupSchoolCalendar: () => void;
|
||||
onSetupTimetable: () => void;
|
||||
onOnboardSchool: () => void;
|
||||
activeNodeId?: string;
|
||||
}
|
||||
|
||||
@@ -108,6 +110,7 @@ const NavPanelContext = createContext<NavPanelContextValue>({
|
||||
schoolStatus: null,
|
||||
onSetupSchoolCalendar: () => {},
|
||||
onSetupTimetable: () => {},
|
||||
onOnboardSchool: () => {},
|
||||
});
|
||||
|
||||
// ─── TreeItem ─────────────────────────────────────────────────────────────────
|
||||
@@ -181,6 +184,8 @@ function TreeItem({ node, depth, onSelect, onExpand }: TreeItemProps) {
|
||||
const showCalendarPending = isSchoolSection
|
||||
&& ss && ss.status !== 'no_school'
|
||||
&& !ss.school_has_calendar && ss.user_role !== 'school_admin';
|
||||
// School section: onboarding prompt when no school linked
|
||||
const showOnboard = isSchoolSection && (!ss || ss.status === 'no_school');
|
||||
// Timetable section: teacher timetable setup (requires school calendar first)
|
||||
const showTimetableSetup = isTimetableSection && node.status === 'empty'
|
||||
&& ss && ss.status !== 'no_school'
|
||||
@@ -249,7 +254,19 @@ function TreeItem({ node, depth, onSelect, onExpand }: TreeItemProps) {
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{/* Timetable section — role-aware action */}
|
||||
{/* School section — onboarding when no school */}
|
||||
{showOnboard && (
|
||||
<Tooltip title="Find and join your school" placement="right">
|
||||
<IconButton
|
||||
size="small"
|
||||
sx={{ p: 0.25, ml: 0.5, color: 'primary.main' }}
|
||||
onClick={e => { e.stopPropagation(); ctx.onOnboardSchool(); }}
|
||||
>
|
||||
<SetupIcon sx={{ fontSize: 13 }} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
{/* Calendar section — role-aware action */}
|
||||
{showCalendarSetup && (
|
||||
<Tooltip title="Set up school calendar" placement="right">
|
||||
<IconButton
|
||||
@@ -407,6 +424,7 @@ export function CCGraphNavPanel() {
|
||||
|
||||
const [calendarWizardOpen, setCalendarWizardOpen] = useState(false);
|
||||
const [timetableWizardOpen, setTimetableWizardOpen] = useState(false);
|
||||
const [onboardingWizardOpen, setOnboardingWizardOpen] = useState(false);
|
||||
|
||||
const apiBase = import.meta.env.VITE_API_BASE as string;
|
||||
const activeNodeId = context.node?.type !== 'workspace' ? context.node?.id : undefined;
|
||||
@@ -544,6 +562,7 @@ export function CCGraphNavPanel() {
|
||||
schoolStatus,
|
||||
onSetupSchoolCalendar: () => setCalendarWizardOpen(true),
|
||||
onSetupTimetable: () => setTimetableWizardOpen(true),
|
||||
onOnboardSchool: () => setOnboardingWizardOpen(true),
|
||||
activeNodeId,
|
||||
};
|
||||
|
||||
@@ -581,6 +600,18 @@ export function CCGraphNavPanel() {
|
||||
periodsTemplate={schoolStatus?.periods_template || []}
|
||||
timetableId={schoolStatus?.timetable_id || null}
|
||||
/>
|
||||
|
||||
<SchoolOnboardingWizard
|
||||
open={onboardingWizardOpen}
|
||||
onClose={() => setOnboardingWizardOpen(false)}
|
||||
onComplete={() => {
|
||||
setOnboardingWizardOpen(false);
|
||||
// Reload tree + school status after successful onboarding
|
||||
setTree(null);
|
||||
setSchoolStatus(null);
|
||||
}}
|
||||
apiBase={apiBase}
|
||||
/>
|
||||
</NavPanelContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user