Initial commit

This commit is contained in:
2025-07-11 13:21:49 +00:00
commit 8a7ab3ac24
262 changed files with 28219 additions and 0 deletions
@@ -0,0 +1,55 @@
import { useState } from 'react';
import { Button, Box, Typography, Alert } from '@mui/material';
import { logger } from '../../../debugConfig';
import { SchoolNeoDBService } from '../../../services/graph/schoolNeoDBService';
export const SchoolUploadSection = () => {
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState<string | null>(null);
const [isCreating, setIsCreating] = useState(false);
const handleSchoolUpload = async () => {
try {
setIsCreating(true);
setError(null);
setSuccess(null);
const result = await SchoolNeoDBService.createSchools();
setSuccess(result.message);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Failed to upload school';
logger.error('admin-page', '❌ School upload failed:', error);
setError(errorMessage);
} finally {
setIsCreating(false);
}
};
return (
<Box sx={{ mt: 4 }}>
<Typography variant="h6" gutterBottom>
Create Schools
</Typography>
{error && (
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
)}
{success && (
<Alert severity="success" sx={{ mb: 2 }}>
{success}
</Alert>
)}
<Button
variant="contained"
onClick={handleSchoolUpload}
disabled={isCreating}
>
{isCreating ? 'Creating...' : 'Create Schools'}
</Button>
</Box>
);
};
@@ -0,0 +1,78 @@
import React, { useState } from 'react';
import { Button, Box, Typography, Alert } from '@mui/material';
import { useNeoUser } from '../../../contexts/NeoUserContext';
import { TimetableNeoDBService } from '../../../services/graph/timetableNeoDBService';
import { CCTeacherNodeProps } from '../../../utils/tldraw/cc-base/cc-graph/cc-graph-types';
export const TimetableUploadSection = () => {
const { userNode, workerNode } = useNeoUser();
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState<string | null>(null);
const [isUploading, setIsUploading] = useState(false);
const handleTimetableUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
try {
setIsUploading(true);
setError(null);
setSuccess(null);
const result = await TimetableNeoDBService.handleTimetableUpload(
event.target.files?.[0],
userNode || undefined,
workerNode?.nodeData as CCTeacherNodeProps | undefined
);
if (result.success) {
setSuccess(result.message);
} else {
setError(result.message);
}
} finally {
setIsUploading(false);
if (event.target) {
event.target.value = '';
}
}
};
return (
<Box sx={{ mt: 4 }}>
<Typography variant="h6" gutterBottom>
Upload Teacher Timetable
</Typography>
{error && (
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
)}
{success && (
<Alert severity="success" sx={{ mb: 2 }}>
{success}
</Alert>
)}
<Button
variant="contained"
component="label"
disabled={isUploading || !workerNode}
>
{isUploading ? 'Uploading...' : 'Upload Timetable'}
<input
type="file"
hidden
accept=".xlsx"
onChange={handleTimetableUpload}
disabled={isUploading}
/>
</Button>
{!workerNode && (
<Typography color="error" sx={{ mt: 1 }}>
No teacher node found. Please ensure you have the correct permissions.
</Typography>
)}
</Box>
);
};
+45
View File
@@ -0,0 +1,45 @@
import React, { useState } from 'react';
import { Button, TextField } from '@mui/material';
import { EmailCredentials } from '../../../services/auth/authService';
import { logger } from '../../../debugConfig';
interface LoginFormProps {
role: 'email_teacher' | 'email_student';
onSubmit: (credentials: EmailCredentials) => Promise<void>;
}
export const LoginForm: React.FC<LoginFormProps> = ({ role, onSubmit }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
logger.debug('login-form', '🔄 Submitting login form', { role });
await onSubmit({ email, password, role });
};
return (
<form onSubmit={handleSubmit} className="login-form">
<TextField
label="Email"
variant="outlined"
value={email}
onChange={(e) => setEmail(e.target.value)}
fullWidth
autoComplete="new-username"
/>
<TextField
label="Password"
type="password"
variant="outlined"
value={password}
onChange={(e) => setPassword(e.target.value)}
fullWidth
autoComplete="new-password"
/>
<Button type="submit" variant="contained" fullWidth>
Login
</Button>
</form>
);
};
@@ -0,0 +1,23 @@
import { Button } from '@mui/material';
import { useNavigate } from 'react-router-dom';
import { logger } from '../../../debugConfig';
export const SuperAdminSection = () => {
const navigate = useNavigate();
const handleAdminClick = () => {
logger.info('super-admin-section', '🔑 Navigating to admin page');
navigate('/admin');
};
return (
<Button
onClick={handleAdminClick}
variant="contained"
color="warning"
sx={{ mb: 2 }}
>
Admin Dashboard
</Button>
);
};
@@ -0,0 +1,12 @@
import { CircularProgress, Box } from '@mui/material';
export const LoadingSpinner = () => (
<Box
display="flex"
justifyContent="center"
alignItems="center"
minHeight="100vh"
>
<CircularProgress />
</Box>
);