fix auth redirects and API env naming
This commit is contained in:
parent
0db53bfd9c
commit
4b6600e01f
@ -18,7 +18,6 @@ VITE_APP_HMR_URL=http://192.168.0.94:5173
|
|||||||
VITE_SUPABASE_URL=http://192.168.0.155:8000
|
VITE_SUPABASE_URL=http://192.168.0.155:8000
|
||||||
|
|
||||||
# API should use localhost for local development
|
# API should use localhost for local development
|
||||||
VITE_API_URL=http://192.168.0.94:8000
|
|
||||||
VITE_API_BASE=http://192.168.0.94:8000
|
VITE_API_BASE=http://192.168.0.94:8000
|
||||||
|
|
||||||
# Neo4j
|
# Neo4j
|
||||||
|
|||||||
@ -14,7 +14,6 @@ VITE_APP_HMR_URL=http://localhost:24678
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
# API Configuration
|
# API Configuration
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
VITE_API_URL=https://api.classroomcopilot.ai
|
|
||||||
VITE_API_BASE=https://api.classroomcopilot.ai
|
VITE_API_BASE=https://api.classroomcopilot.ai
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
@ -121,3 +121,24 @@ describe('/admin route authorization', () => {
|
|||||||
expect(screen.getByText('Platform Admin Page')).toBeInTheDocument();
|
expect(screen.getByText('Platform Admin Page')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('authenticated route redirects', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mockUseAuth.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['/calendar'],
|
||||||
|
['/settings'],
|
||||||
|
['/single-player'],
|
||||||
|
['/lesson-plans'],
|
||||||
|
])('redirects anonymous users from %s to login', (path) => {
|
||||||
|
mockUseAuth.mockReturnValue(authState({ user: null, user_role: null }));
|
||||||
|
|
||||||
|
renderAt(path);
|
||||||
|
|
||||||
|
expect(screen.getByText('Login Page')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('Public Not Found')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import { CCExamMarker } from './pages/tldraw/CCExamMarker/CCExamMarker';
|
|||||||
import CalendarPage from './pages/user/calendarPage';
|
import CalendarPage from './pages/user/calendarPage';
|
||||||
import SettingsPage from './pages/user/settingsPage';
|
import SettingsPage from './pages/user/settingsPage';
|
||||||
import TLDrawCanvas from './pages/tldraw/TLDrawCanvas';
|
import TLDrawCanvas from './pages/tldraw/TLDrawCanvas';
|
||||||
import AdminDashboard from './pages/auth/adminPage';
|
|
||||||
import PlatformAdminPage from './pages/auth/PlatformAdminPage';
|
import PlatformAdminPage from './pages/auth/PlatformAdminPage';
|
||||||
import TLDrawDevPage from './pages/tldraw/devPlayerPage';
|
import TLDrawDevPage from './pages/tldraw/devPlayerPage';
|
||||||
import DevPage from './pages/tldraw/devPage';
|
import DevPage from './pages/tldraw/devPage';
|
||||||
@ -44,6 +43,17 @@ import {
|
|||||||
LessonPlanDetailPage,
|
LessonPlanDetailPage,
|
||||||
} from './pages/timetable';
|
} from './pages/timetable';
|
||||||
|
|
||||||
|
const ProtectedRoutes: React.FC = () => {
|
||||||
|
const { user } = useAuth();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return <Navigate to="/login" replace state={{ from: location }} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Outlet />;
|
||||||
|
};
|
||||||
|
|
||||||
const FullContextRoutes: React.FC = () => {
|
const FullContextRoutes: React.FC = () => {
|
||||||
// Only block on Supabase profile being ready — Neo4j contexts initialize in the background.
|
// Only block on Supabase profile being ready — Neo4j contexts initialize in the background.
|
||||||
// Individual pages handle their own Neo4j loading states.
|
// Individual pages handle their own Neo4j loading states.
|
||||||
@ -118,7 +128,7 @@ const AppRoutes: React.FC = () => {
|
|||||||
{/* Lightweight authenticated routes */}
|
{/* Lightweight authenticated routes */}
|
||||||
<Route
|
<Route
|
||||||
path="/dashboard"
|
path="/dashboard"
|
||||||
element={user ? <DashboardPage /> : <Navigate to="/login" replace />}
|
element={user ? <DashboardPage /> : <Navigate to="/login" replace state={{ from: location }} />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Super Admin only routes */}
|
{/* Super Admin only routes */}
|
||||||
@ -135,8 +145,8 @@ const AppRoutes: React.FC = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Authentication only routes - only render if all contexts are initialized */}
|
{/* Authentication only routes */}
|
||||||
{user && (
|
<Route element={<ProtectedRoutes />}>
|
||||||
<Route element={<FullContextRoutes />}>
|
<Route element={<FullContextRoutes />}>
|
||||||
{/* Timetable Module Routes */}
|
{/* Timetable Module Routes */}
|
||||||
<Route path="/timetable" element={<TimetablePage />} />
|
<Route path="/timetable" element={<TimetablePage />} />
|
||||||
@ -167,7 +177,7 @@ const AppRoutes: React.FC = () => {
|
|||||||
<Route path="/calendar" element={<CalendarPage />} />
|
<Route path="/calendar" element={<CalendarPage />} />
|
||||||
<Route path="/settings" element={<SettingsPage />} />
|
<Route path="/settings" element={<SettingsPage />} />
|
||||||
</Route>
|
</Route>
|
||||||
)}
|
</Route>
|
||||||
|
|
||||||
{/* Fallback route - use different NotFound pages based on auth state */}
|
{/* Fallback route - use different NotFound pages based on auth state */}
|
||||||
<Route path="*" element={user ? <NotFound /> : <NotFoundPublic />} />
|
<Route path="*" element={user ? <NotFound /> : <NotFoundPublic />} />
|
||||||
|
|||||||
@ -1,12 +1,8 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { logger } from './debugConfig';
|
import { logger } from './debugConfig';
|
||||||
|
import { API_BASE } from './config/api';
|
||||||
|
|
||||||
// Use development backend URL if no custom URL is provided
|
const baseURL = API_BASE;
|
||||||
const baseURL = import.meta.env.VITE_API_BASE || 'http://localhost:8080';
|
|
||||||
|
|
||||||
if (!import.meta.env.VITE_API_BASE) {
|
|
||||||
logger.warn('axios', '⚠️ VITE_API_BASE not set, defaulting to http://localhost:8080');
|
|
||||||
}
|
|
||||||
|
|
||||||
const instance = axios.create({
|
const instance = axios.create({
|
||||||
baseURL,
|
baseURL,
|
||||||
|
|||||||
14
src/config/api.ts
Normal file
14
src/config/api.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { logger } from '../debugConfig';
|
||||||
|
|
||||||
|
const DEFAULT_API_BASE = 'http://localhost:8080';
|
||||||
|
|
||||||
|
const normalizeApiBase = (value: string | undefined): string => {
|
||||||
|
const trimmed = value?.trim();
|
||||||
|
return trimmed ? trimmed.replace(/\/$/, '') : DEFAULT_API_BASE;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const API_BASE = normalizeApiBase(import.meta.env.VITE_API_BASE);
|
||||||
|
|
||||||
|
if (!import.meta.env.VITE_API_BASE) {
|
||||||
|
logger.warn('api-config', `⚠️ VITE_API_BASE not set, defaulting to ${DEFAULT_API_BASE}`);
|
||||||
|
}
|
||||||
@ -34,11 +34,12 @@ import Lessons from '@mui/icons-material/EventNote';
|
|||||||
import LessonPlans from '@mui/icons-material/LibraryBooks';
|
import LessonPlans from '@mui/icons-material/LibraryBooks';
|
||||||
import People from '@mui/icons-material/People';
|
import People from '@mui/icons-material/People';
|
||||||
import SchoolSettings from '@mui/icons-material/Tune';
|
import SchoolSettings from '@mui/icons-material/Tune';
|
||||||
|
import AddBusiness from '@mui/icons-material/AddBusiness';
|
||||||
import { HEADER_HEIGHT } from './Layout';
|
import { HEADER_HEIGHT } from './Layout';
|
||||||
import { logger } from '../debugConfig';
|
import { logger } from '../debugConfig';
|
||||||
import { GraphNavigator } from '../components/navigation/GraphNavigator';
|
import { GraphNavigator } from '../components/navigation/GraphNavigator';
|
||||||
|
import { SchoolOnboardingWizard } from '../utils/tldraw/ui-overrides/components/shared/navigation/SchoolOnboardingWizard';
|
||||||
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
import { API_BASE } from '../config/api';
|
||||||
|
|
||||||
const Header: React.FC = () => {
|
const Header: React.FC = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -48,6 +49,8 @@ const Header: React.FC = () => {
|
|||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
const [isPlatformAdmin, setIsPlatformAdmin] = useState(false);
|
const [isPlatformAdmin, setIsPlatformAdmin] = useState(false);
|
||||||
const [schoolRole, setSchoolRole] = useState<string | null>(null);
|
const [schoolRole, setSchoolRole] = useState<string | null>(null);
|
||||||
|
const [schoolStatus, setSchoolStatus] = useState<string | null>(null);
|
||||||
|
const [onboardingOpen, setOnboardingOpen] = useState(false);
|
||||||
const [isAuthenticated, setIsAuthenticated] = useState(!!user);
|
const [isAuthenticated, setIsAuthenticated] = useState(!!user);
|
||||||
const showGraphNavigation = location.pathname === '/single-player';
|
const showGraphNavigation = location.pathname === '/single-player';
|
||||||
const isSchoolAdmin = schoolRole === 'school_admin' || schoolRole === 'department_head';
|
const isSchoolAdmin = schoolRole === 'school_admin' || schoolRole === 'department_head';
|
||||||
@ -76,15 +79,17 @@ const Header: React.FC = () => {
|
|||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
setSchoolRole(data.user_role || null);
|
setSchoolRole(data.user_role || null);
|
||||||
|
setSchoolStatus(data.status || null);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
setSchoolRole(null);
|
setSchoolRole(null);
|
||||||
|
setSchoolStatus(null);
|
||||||
}
|
}
|
||||||
}, [accessToken]);
|
}, [accessToken]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (accessToken) checkAdminStatus();
|
if (accessToken) checkAdminStatus();
|
||||||
else { setIsPlatformAdmin(false); setSchoolRole(null); }
|
else { setIsPlatformAdmin(false); setSchoolRole(null); setSchoolStatus(null); }
|
||||||
}, [accessToken, checkAdminStatus]);
|
}, [accessToken, checkAdminStatus]);
|
||||||
|
|
||||||
const handleMenuOpen = (event: React.MouseEvent<HTMLElement>) => {
|
const handleMenuOpen = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
@ -100,6 +105,16 @@ const Header: React.FC = () => {
|
|||||||
handleMenuClose();
|
handleMenuClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOnboardingOpen = () => {
|
||||||
|
setOnboardingOpen(true);
|
||||||
|
handleMenuClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnboardingComplete = () => {
|
||||||
|
setOnboardingOpen(false);
|
||||||
|
void checkAdminStatus();
|
||||||
|
};
|
||||||
|
|
||||||
const handleSignupNavigation = (role: 'teacher' | 'student') => {
|
const handleSignupNavigation = (role: 'teacher' | 'student') => {
|
||||||
navigate('/signup', { state: { role } });
|
navigate('/signup', { state: { role } });
|
||||||
handleMenuClose();
|
handleMenuClose();
|
||||||
@ -119,6 +134,7 @@ const Header: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<AppBar
|
<AppBar
|
||||||
position="fixed"
|
position="fixed"
|
||||||
sx={{
|
sx={{
|
||||||
@ -262,6 +278,15 @@ const Header: React.FC = () => {
|
|||||||
secondary="Plan and manage lessons"
|
secondary="Plan and manage lessons"
|
||||||
/>
|
/>
|
||||||
</MenuItem>,
|
</MenuItem>,
|
||||||
|
<MenuItem key="school-onboarding" onClick={handleOnboardingOpen}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<AddBusiness />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText
|
||||||
|
primary={schoolStatus === 'no_school' ? 'Set up your school' : 'School setup'}
|
||||||
|
secondary="Find your school and create a calendar"
|
||||||
|
/>
|
||||||
|
</MenuItem>,
|
||||||
<Divider key="work-divider" />,
|
<Divider key="work-divider" />,
|
||||||
|
|
||||||
// School Admin Section
|
// School Admin Section
|
||||||
@ -357,6 +382,13 @@ const Header: React.FC = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
<SchoolOnboardingWizard
|
||||||
|
open={onboardingOpen}
|
||||||
|
onClose={() => setOnboardingOpen(false)}
|
||||||
|
onComplete={handleOnboardingComplete}
|
||||||
|
apiBase={API_BASE}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { Refresh, School, People, EventNote, HourglassEmpty } from '@mui/icons-m
|
|||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
import { useAuth } from '../../contexts/AuthContext';
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
|
|
||||||
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
import { API_BASE } from '../../config/api';
|
||||||
|
|
||||||
interface SchoolEntry {
|
interface SchoolEntry {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { useAuth } from '../../contexts/AuthContext';
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
|
|
||||||
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
import { API_BASE } from '../../config/api';
|
||||||
|
|
||||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import {
|
|||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { useAuth } from '../../contexts/AuthContext';
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
|
|
||||||
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
import { API_BASE } from '../../config/api';
|
||||||
|
|
||||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
import { Add, Search, LibraryBooks, Edit, Delete, FilterList } from '@mui/icons-material';
|
import { Add, Search, LibraryBooks, Edit, Delete, FilterList } from '@mui/icons-material';
|
||||||
import { useAuth } from '../../contexts/AuthContext';
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
|
|
||||||
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
import { API_BASE } from '../../config/api';
|
||||||
|
|
||||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import {
|
|||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
import { useAuth } from '../../contexts/AuthContext';
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
|
|
||||||
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
import { API_BASE } from '../../config/api';
|
||||||
|
|
||||||
const DAY_TYPE_OPTIONS = ['Academic', 'Holiday', 'Staff', 'OffTimetable'];
|
const DAY_TYPE_OPTIONS = ['Academic', 'Holiday', 'Staff', 'OffTimetable'];
|
||||||
const DAY_TYPE_COLORS: Record<string, 'default' | 'primary' | 'success' | 'warning' | 'error'> = {
|
const DAY_TYPE_COLORS: Record<string, 'default' | 'primary' | 'success' | 'warning' | 'error'> = {
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { useAuth } from '../../contexts/AuthContext';
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
|
|
||||||
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
import { API_BASE } from '../../config/api';
|
||||||
|
|
||||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
import { ChevronLeft, ChevronRight, Today } from '@mui/icons-material';
|
import { ChevronLeft, ChevronRight, Today } from '@mui/icons-material';
|
||||||
import { useAuth } from '../../contexts/AuthContext';
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
|
|
||||||
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
import { API_BASE } from '../../config/api';
|
||||||
|
|
||||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { useAuth } from '../../contexts/AuthContext';
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
|
|
||||||
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
import { API_BASE } from '../../config/api';
|
||||||
|
|
||||||
interface Student {
|
interface Student {
|
||||||
profile_id: string;
|
profile_id: string;
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import {
|
|||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { useAuth } from '../../contexts/AuthContext';
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
|
|
||||||
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
import { API_BASE } from '../../config/api';
|
||||||
|
|
||||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@ -6,16 +6,21 @@ import {
|
|||||||
Container,
|
Container,
|
||||||
Grid,
|
Grid,
|
||||||
Paper,
|
Paper,
|
||||||
|
Alert,
|
||||||
Stack,
|
Stack,
|
||||||
Typography
|
Typography
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useAuth } from '../../contexts/AuthContext';
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
import { useUser } from '../../contexts/UserContext';
|
import { useUser } from '../../contexts/UserContext';
|
||||||
|
import { API_BASE } from '../../config/api';
|
||||||
|
import { SchoolOnboardingWizard } from '../../utils/tldraw/ui-overrides/components/shared/navigation/SchoolOnboardingWizard';
|
||||||
|
|
||||||
const DashboardPage: React.FC = () => {
|
const DashboardPage: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { user: authUser } = useAuth();
|
const { user: authUser } = useAuth();
|
||||||
const { profile, loading } = useUser();
|
const { profile, loading } = useUser();
|
||||||
|
const [schoolOnboardingOpen, setSchoolOnboardingOpen] = useState(false);
|
||||||
|
const [schoolSetupComplete, setSchoolSetupComplete] = useState(false);
|
||||||
|
|
||||||
const displayName = profile?.display_name || authUser?.display_name || authUser?.username || 'Member';
|
const displayName = profile?.display_name || authUser?.display_name || authUser?.username || 'Member';
|
||||||
const emailAddress = profile?.email || authUser?.email || '';
|
const emailAddress = profile?.email || authUser?.email || '';
|
||||||
@ -87,8 +92,39 @@ const DashboardPage: React.FC = () => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Paper elevation={2} sx={{ p: 3 }}>
|
||||||
|
<Stack spacing={2} direction={{ xs: 'column', md: 'row' }} alignItems={{ md: 'center' }} justifyContent="space-between">
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h6" gutterBottom>
|
||||||
|
School onboarding
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary" sx={{ maxWidth: 680 }}>
|
||||||
|
Link your account to a school, confirm school details, and set up an academic calendar without opening the canvas navigation panel first.
|
||||||
|
</Typography>
|
||||||
|
{schoolSetupComplete && (
|
||||||
|
<Alert severity="success" sx={{ mt: 2 }}>
|
||||||
|
School setup completed. Refreshing navigation will pick up the new school context.
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Button variant="contained" color="secondary" onClick={() => setSchoolOnboardingOpen(true)}>
|
||||||
|
Set up school
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<SchoolOnboardingWizard
|
||||||
|
open={schoolOnboardingOpen}
|
||||||
|
onClose={() => setSchoolOnboardingOpen(false)}
|
||||||
|
onComplete={() => {
|
||||||
|
setSchoolOnboardingOpen(false);
|
||||||
|
setSchoolSetupComplete(true);
|
||||||
|
}}
|
||||||
|
apiBase={API_BASE}
|
||||||
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import {
|
|||||||
} from '../types/timetable.types';
|
} from '../types/timetable.types';
|
||||||
|
|
||||||
// API base URL
|
// API base URL
|
||||||
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
import { API_BASE } from '../config/api';
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Helper: Get auth headers
|
// Helper: Get auth headers
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { RegularToolbar } from './regular/toolbar';
|
|||||||
import { RegularHelperButtons } from './regular/helperButton';
|
import { RegularHelperButtons } from './regular/helperButton';
|
||||||
// import { RegularHelpMenu } from './regular/helpMenu';
|
// import { RegularHelpMenu } from './regular/helpMenu';
|
||||||
// import { RegularMainMenu } from './regular/mainMenu';
|
// import { RegularMainMenu } from './regular/mainMenu';
|
||||||
import { RegularNavigationPanel } from './regular/navigationPanel';
|
|
||||||
// import { RegularPageMenu } from './regular/pageMenu';
|
// import { RegularPageMenu } from './regular/pageMenu';
|
||||||
// import { RegularQuickActions } from './regular/quickActions';
|
// import { RegularQuickActions } from './regular/quickActions';
|
||||||
import { RegularStylePanel } from './regular/stylePanel';
|
import { RegularStylePanel } from './regular/stylePanel';
|
||||||
@ -18,7 +17,6 @@ import { PresentationToolbar } from './presentation/toolbar';
|
|||||||
import { PresentationHelperButtons } from './presentation/helperButton';
|
import { PresentationHelperButtons } from './presentation/helperButton';
|
||||||
// import { PresentationHelpMenu } from './presentation/helpMenu';
|
// import { PresentationHelpMenu } from './presentation/helpMenu';
|
||||||
// import { PresentationMainMenu } from './presentation/mainMenu';
|
// import { PresentationMainMenu } from './presentation/mainMenu';
|
||||||
import { PresentationNavigationPanel } from './presentation/navigationPanel';
|
|
||||||
// import { PresentationPageMenu } from './presentation/pageMenu';
|
// import { PresentationPageMenu } from './presentation/pageMenu';
|
||||||
import { PresentationQuickActions } from './presentation/quickActions';
|
import { PresentationQuickActions } from './presentation/quickActions';
|
||||||
import { PresentationStylePanel } from './presentation/stylePanel';
|
import { PresentationStylePanel } from './presentation/stylePanel';
|
||||||
@ -35,7 +33,7 @@ export const regularComponentsIndex: TLComponents = {
|
|||||||
HelperButtons: RegularHelperButtons,
|
HelperButtons: RegularHelperButtons,
|
||||||
// HelpMenu: RegularHelpMenu,
|
// HelpMenu: RegularHelpMenu,
|
||||||
// MainMenu: RegularMainMenu,
|
// MainMenu: RegularMainMenu,
|
||||||
NavigationPanel: RegularNavigationPanel,
|
// NavigationPanel intentionally omitted; CCPanel/CCGraphNavPanel is the supported navigation surface.
|
||||||
// PageMenu: RegularPageMenu,
|
// PageMenu: RegularPageMenu,
|
||||||
// QuickActions: RegularQuickActions,
|
// QuickActions: RegularQuickActions,
|
||||||
StylePanel: RegularStylePanel,
|
StylePanel: RegularStylePanel,
|
||||||
@ -52,7 +50,7 @@ export const presentationComponentsIndex: TLComponents = {
|
|||||||
HelperButtons: PresentationHelperButtons,
|
HelperButtons: PresentationHelperButtons,
|
||||||
// HelpMenu: PresentationHelpMenu,
|
// HelpMenu: PresentationHelpMenu,
|
||||||
// MainMenu: PresentationMainMenu,
|
// MainMenu: PresentationMainMenu,
|
||||||
NavigationPanel: PresentationNavigationPanel,
|
// NavigationPanel intentionally omitted; CCPanel/CCGraphNavPanel is the supported navigation surface.
|
||||||
// PageMenu: PresentationPageMenu,
|
// PageMenu: PresentationPageMenu,
|
||||||
QuickActions: PresentationQuickActions,
|
QuickActions: PresentationQuickActions,
|
||||||
StylePanel: PresentationStylePanel,
|
StylePanel: PresentationStylePanel,
|
||||||
|
|||||||
@ -119,7 +119,7 @@ export const CCTranscriptionPanel: React.FC = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const detectTimetable = async () => {
|
const detectTimetable = async () => {
|
||||||
try {
|
try {
|
||||||
const apiBase = import.meta.env.VITE_API_URL || 'https://api.classroomcopilot.ai';
|
const apiBase = API_BASE;
|
||||||
const response = await fetch(`${apiBase}/database/timetables/current-period`);
|
const response = await fetch(`${apiBase}/database/timetables/current-period`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (data.period_id) {
|
if (data.period_id) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user