Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 48 additions & 9 deletions packages/alea-frontend/components/instructor-panel/CourseInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,47 @@ import { useRouter } from 'next/router';
import { getLocaleObject } from '../../lang/utils';
import { useEffect, useState } from 'react';

function parseInstructors(val: unknown): Array<{ id: string; name: string }> {
if (val == null) return [];
if (Array.isArray(val)) {
return val.map((v: any) => {
if (typeof v === 'string') {
return { id: v, name: v };
}
return {
id: v.id || v.name || '',
name: v.name || v.id || '',
};
});
}
if (typeof val === 'string') {
const s = val.trim();
if (!s) return [];

try {
const parsed = JSON.parse(s);

if (Array.isArray(parsed)) {
return parsed.map((v: any) => {
if (typeof v === 'string') {
return { id: v, name: v };
}
return {
id: v.id || v.name || '',
name: v.name || v.id || '',
};
});
}

return [];
} catch {
return [{ id: s, name: s }];
}
}

return [];
}

interface CourseInfoTabProps {
courseId: string;
instanceId: string;
Expand Down Expand Up @@ -114,10 +155,8 @@ export default function CourseInfoTab({ courseId, instanceId }: CourseInfoTabPro
}

try {
const savedInstructors = resolvedInfo!.instructors ?? [];
const savedMap = new Map<string, InstructorInfo>(
resolvedInfo.instructors.map((s) => [s.id, s])
);
const savedInstructors = parseInstructors(resolvedInfo!.instructors);
const savedMap = new Map<string, InstructorInfo>(savedInstructors.map((s) => [s.id, s]));

const aclIds = await getCourseAcls(courseId, instanceId);
const instructorAclIds = (aclIds || []).filter((id) => id.endsWith('-instructors'));
Expand Down Expand Up @@ -323,7 +362,6 @@ export default function CourseInfoTab({ courseId, instanceId }: CourseInfoTabPro
checked={courseInfo.hasHomework || false}
onChange={async (e) => {
const next = e.target.checked;
if (!confirm(t.confirmUpdateHomework)) return;

try {
await updateHasHomework({ courseId, instanceId, hasHomework: next });
Expand All @@ -344,7 +382,6 @@ export default function CourseInfoTab({ courseId, instanceId }: CourseInfoTabPro
checked={courseInfo.hasQuiz || false}
onChange={async (e) => {
const next = e.target.checked;
if (!confirm(t.confirmUpdateQuiz)) return;

try {
await updateHasQuiz({ courseId, instanceId, hasQuiz: next });
Expand All @@ -365,7 +402,6 @@ export default function CourseInfoTab({ courseId, instanceId }: CourseInfoTabPro
checked={courseInfo.hasCheatsheet || false}
onChange={async (e) => {
const next = e.target.checked;
if (!confirm(t.confirmUpdateCheatsheet)) return;

try {
await updateHasCheatsheet({ courseId, instanceId, hasCheatsheet: next });
Expand All @@ -385,10 +421,13 @@ export default function CourseInfoTab({ courseId, instanceId }: CourseInfoTabPro
checked={courseInfo.canStudentUploadCheatsheet || false}
onChange={async (e) => {
const next = e.target.checked;
if (!confirm(t.confirmUpdateCanStudentUploadCheatsheet)) return;

try {
await updateCanStudentUploadCheatsheet({ courseId, instanceId, canStudentUploadCheatsheet: next });
await updateCanStudentUploadCheatsheet({
courseId,
instanceId,
canStudentUploadCheatsheet: next,
});
setField('canStudentUploadCheatsheet', next);
} catch (err) {
console.error('Failed to update student cheatsheet upload', err);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,33 +36,35 @@ export const DashboardHeader: React.FC<DashboardHeaderProps> = ({
University Admin Dashboard
</Typography>
<Stack direction="row" spacing={2} mb={3}>
<FormControl size="small" sx={{ minWidth: 160 }}>
<InputLabel id="semester-select-label">Semester</InputLabel>
<Select
labelId="semester-select-label"
value={semester}
label="Semester"
onChange={(e) => onSemesterChange(e.target.value)}
disabled={loadingOptions}
>
{loadingOptions ? (
<MenuItem disabled>Loading...</MenuItem>
) : (
semesterOptions.map((opt) => (
<MenuItem key={opt} value={opt}>
{opt}
</MenuItem>
))
)}
</Select>
</FormControl>
{!showSemForm && (
<FormControl size="small" sx={{ minWidth: 160 }}>
<InputLabel id="semester-select-label">Semester</InputLabel>
<Select
labelId="semester-select-label"
value={semester}
label="Semester"
onChange={(e) => onSemesterChange(e.target.value)}
disabled={loadingOptions}
>
{loadingOptions ? (
<MenuItem disabled>Loading...</MenuItem>
) : (
semesterOptions.map((opt) => (
<MenuItem key={opt} value={opt}>
{opt}
</MenuItem>
))
)}
</Select>
</FormControl>
)}
<Button
variant="outlined"
color="primary"
sx={{ fontWeight: 600 }}
onClick={onToggleSemForm}
>
{showSemForm ? 'Hide' : 'Add'} Sem Detail
{showSemForm ? 'Exit' : 'Create New Semester'}
</Button>
</Stack>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (directMembers.length === 0) {
return res.status(200).send([]);
}
const userIdPlaceholders = directMembers.map(() => '?').join(', ');
const userInfoResults: { firstname: string; lastname: string; userId: string }[] =
const placeholders = directMembers.map(() => '?').join(',');
const userInfoResults: { firstName: string; lastName: string; userId: string }[] =
await executeDontEndSet500OnError(
`SELECT firstName as firstname, lastName as lastname, userId FROM userInfo WHERE userId IN (${userIdPlaceholders})`,
`select firstName, lastName, userId from userInfo where userId IN (${placeholders})`,
directMembers,
res
);
const result = directMembers.map((userId) => {
const userInfo = userInfoResults.find((record) => record.userId === userId);
const fullName = userInfo ? `${userInfo.firstname} ${userInfo.lastname}` : '';
const fullName = userInfo ? `${userInfo.firstName} ${userInfo.lastName}` : '';
return { fullName, userId };
});
res.status(200).send(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (!members || members.length === 0) {
return res.status(200).send([]);
}

const result: { firstname: string; lastname: string; userId: string }[] =

const placeholders = members.map(() => '?').join(',');
const result: { firstName: string; lastName: string; userId: string }[] =
await executeDontEndSet500OnError(
`select firstname, lastname, userId from userInfo where userId IN (?)`,
[members],
`select firstName, lastName, userId from userInfo where userId IN (${placeholders})`,
members,
res
);
res
.status(200)
.send(result.map((c) => ({ fullName: `${c.firstname} ${c.lastname}`, userId: c.userId })));
.send(result.map((c) => ({ fullName: `${c.firstName} ${c.lastName}`, userId: c.userId })));
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}

const alreadyExistCourseIdRow = await executeAndEndSet500OnError(
`SELECT courseName, notes, landing, slides, teaser
FROM courseMetadata
`SELECT courseName, notes, landing, slides, teaser, instructors FROM courseMetadata
WHERE courseId = ? AND universityId = ?
LIMIT 1`,
[courseId, universityId],
Expand All @@ -48,6 +47,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
let landing = '';
let slides = '';
let teaser: string | null = null;
let instructors: any = [];

if (Array.isArray(alreadyExistCourseIdRow) && alreadyExistCourseIdRow.length > 0) {
const template = alreadyExistCourseIdRow[0] as any;
Expand All @@ -56,9 +56,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
landing = template?.landing || landing;
slides = template?.slides || slides;
teaser = template?.teaser ?? teaser;
instructors = template?.instructors || instructors;
}


const insertResult = await executeAndEndSet500OnError(
`INSERT INTO courseMetadata (
courseId,
Expand Down Expand Up @@ -92,7 +92,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
landing,
slides,
teaser,
JSON.stringify([]),
typeof instructors === 'string' ? instructors : JSON.stringify(instructors),
],
res
);
Expand Down
63 changes: 39 additions & 24 deletions packages/alea-frontend/pages/u/[institution]/university-admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ import { useEffect, useState } from 'react';
import { Box, Paper, Typography } from '@mui/material';
import { useRouter } from 'next/router';
import MainLayout from '../../../layouts/MainLayout';
import {
getSemesterInfo,
getInstances,
SemesterData,
} from '@alea/spec';
import { getSemesterInfo, getInstances, SemesterData } from '@alea/spec';
import { DashboardHeader } from '../../../components/university-admin/DashboardHeader';
import { SemesterForm } from '../../../components/university-admin/SemesterForm';
import { CourseManagement } from '../../../components/university-admin/CourseManagement';
Expand Down Expand Up @@ -121,9 +117,7 @@ export default function UniversityAdminDashboard() {
<Typography variant="h5" fontWeight={600} gutterBottom>
Checking Authorization...
</Typography>
<Typography variant="body1">
Please wait while we verify your access.
</Typography>
<Typography variant="body1">Please wait while we verify your access.</Typography>
</Box>
</Paper>
</Box>
Expand Down Expand Up @@ -186,13 +180,23 @@ export default function UniversityAdminDashboard() {
/>

{semesterOptions.length === 0 && !loadingOptions && (
<Paper elevation={2} sx={{ p: 3, mb: 3, borderRadius: 3, background: '#fff3cd', border: '1px solid #ffeaa7' }}>
<Paper
elevation={2}
sx={{
p: 3,
mb: 3,
borderRadius: 3,
background: '#fff3cd',
border: '1px solid #ffeaa7',
}}
>
<Box textAlign="center">
<Typography variant="h6" sx={{ color: '#856404', fontWeight: 600, mb: 1 }}>
No Semester Available
</Typography>
<Typography variant="body1" sx={{ color: '#856404' }}>
No semester is available to create semester info. Please add a new semester to create semester info.
No semester is available to create semester info. Please add a new semester to
create semester info.
</Typography>
</Box>
</Paper>
Expand All @@ -201,26 +205,37 @@ export default function UniversityAdminDashboard() {
{showSemForm && (
<SemesterForm
onSemesterCreated={handleSemesterCreated}
currentSemester={semester}
currentSemester=""
universityId={universityId}
/>
)}

<CourseManagement semester={semester} universityId={universityId} disabled={semesterOptions.length === 0} />
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
<Box flex={1}>
<SemesterDetail
semesters={semesters}
{!showSemForm && (
<>
<CourseManagement
semester={semester}
universityId={universityId}
instanceId={semester}
onSemesterUpdated={handleSemesterCreated}
disabled={semesterOptions.length === 0}
/>
</Box>
<Box flex={1} mb={{ xs: 3, md: 0 }}>
<HolidayManagement universityId={universityId} instanceId={semester} disabled={semesterOptions.length === 0} />
</Box>
</Box>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
<Box flex={1}>
<SemesterDetail
semesters={semesters}
universityId={universityId}
instanceId={semester}
onSemesterUpdated={handleSemesterCreated}
disabled={semesterOptions.length === 0}
/>
</Box>
<Box flex={1} mb={{ xs: 3, md: 0 }}>
<HolidayManagement
universityId={universityId}
instanceId={semester}
disabled={semesterOptions.length === 0}
/>
</Box>
</Box>
</>
)}
</Paper>
</Box>
</Box>
Expand Down
Loading