@local-civics/mgmt-ui 0.1.229 → 0.1.231
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5 -0
- package/dist/index.js +222 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +223 -33
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useState, useMemo } from 'react';
|
|
3
|
-
import { IconChevronRight, IconVideo, IconSwitchHorizontal, IconLogout, IconHome2, IconGauge, IconCategory2, IconRoute, IconAlbum, IconLambda, IconClipboard, IconBuilding, IconBooks, IconBackpack, IconClipboardCopy, IconTableExport, IconCaretDown, IconCaretUp, IconChevronDown, IconArrowLeft, IconSearch, IconCheck, IconTrash, IconPlus, IconLink, IconDownload, IconCloudUpload, IconInfoCircle, IconPlaylistAdd, IconPointer, IconX, IconScribble, IconSchool, IconPodium, IconBriefcase, IconPresentation, IconNews, IconTools, IconBrandInstagram, IconBrandLinkedin, IconBrandFacebook,
|
|
3
|
+
import { IconChevronRight, IconVideo, IconSwitchHorizontal, IconLogout, IconHome2, IconGauge, IconCategory2, IconRoute, IconAlbum, IconLambda, IconClipboard, IconBuilding, IconBooks, IconBackpack, IconClipboardCopy, IconTableExport, IconCaretDown, IconCaretUp, IconChevronDown, IconArrowLeft, IconSearch, IconCheck, IconTrash, IconPlus, IconLink, IconDownload, IconCloudUpload, IconInfoCircle, IconPlaylistAdd, IconPointer, IconX, IconScribble, IconSchool, IconPodium, IconBriefcase, IconPresentation, IconNews, IconTools, IconBrandInstagram, IconBrandLinkedin, IconBrandFacebook, IconChevronLeft } from '@tabler/icons';
|
|
4
4
|
import { Avatar, createStyles, Modal, Center, Loader, Container, Group, Button, Title, Text, Image, Drawer, Divider, TextInput, Tooltip, ActionIcon, SimpleGrid, Badge as Badge$1, Card, UnstyledButton, Overlay, Stack as Stack$5, ThemeIcon, Checkbox, Autocomplete, createEmotionCache, MantineProvider, AppShell } from '@mantine/core';
|
|
5
5
|
import { Link } from 'react-router-dom';
|
|
6
6
|
import 'external-svg-loader';
|
|
@@ -3335,31 +3335,21 @@ const SplitButton = (props) => {
|
|
|
3335
3335
|
};
|
|
3336
3336
|
|
|
3337
3337
|
function Stack(props) {
|
|
3338
|
-
const { items, hideBadge, hideLesson, hidePathway } = props;
|
|
3338
|
+
const { items, hideBadge, hideLesson, hidePathway, onBadgeClick, onLessonClick, onPathwayClick } = props;
|
|
3339
3339
|
if (!items.length)
|
|
3340
3340
|
return null;
|
|
3341
3341
|
const columns = [
|
|
3342
|
-
"minmax(0,2fr)",
|
|
3343
3342
|
!hidePathway && "minmax(0,1.3fr)",
|
|
3344
|
-
!hideLesson && "minmax(0,1.3fr)",
|
|
3345
3343
|
!hideBadge && "minmax(0,1.3fr)",
|
|
3344
|
+
!hideLesson && "minmax(0,1.3fr)",
|
|
3345
|
+
"minmax(0,2fr)",
|
|
3346
3346
|
"auto"
|
|
3347
3347
|
].filter(Boolean).join(" ");
|
|
3348
3348
|
const headerCell = "border-b border-slate-100 pb-2 text-[10.5px] font-extrabold uppercase tracking-wide text-slate-400";
|
|
3349
3349
|
const dataCell = "py-3";
|
|
3350
|
-
return /* @__PURE__ */ React.createElement("div", { className: "grid items-center gap-x-3", style: { gridTemplateColumns: columns } }, /* @__PURE__ */ React.createElement("div", { className: headerCell }, "
|
|
3350
|
+
return /* @__PURE__ */ React.createElement("div", { className: "grid items-center gap-x-3", style: { gridTemplateColumns: columns } }, !hidePathway && /* @__PURE__ */ React.createElement("div", { className: headerCell }, "Pathway"), !hideBadge && /* @__PURE__ */ React.createElement("div", { className: headerCell }, "Badge"), !hideLesson && /* @__PURE__ */ React.createElement("div", { className: headerCell }, "Lesson"), /* @__PURE__ */ React.createElement("div", { className: headerCell }, "Question"), /* @__PURE__ */ React.createElement("div", { className: headerCell }), items.map((row, i) => {
|
|
3351
3351
|
const rowBorder = i > 0 ? "border-t border-slate-100" : "";
|
|
3352
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, { key: i }, /* @__PURE__ */ React.createElement("
|
|
3353
|
-
"a",
|
|
3354
|
-
{
|
|
3355
|
-
href: row.link,
|
|
3356
|
-
target: "_blank",
|
|
3357
|
-
rel: "noopener noreferrer",
|
|
3358
|
-
className: "flex items-center gap-1 rounded-md border border-slate-200 px-2.5 py-1.5 text-[11px] font-bold text-slate-600 no-underline hover:bg-slate-50"
|
|
3359
|
-
},
|
|
3360
|
-
/* @__PURE__ */ React.createElement(IconExternalLink, { size: 12, stroke: 2 }),
|
|
3361
|
-
"Preview"
|
|
3362
|
-
), /* @__PURE__ */ React.createElement(
|
|
3352
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, { key: i }, !hidePathway && (row.pathwayId && onPathwayClick ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => onPathwayClick(row.pathwayId), className: `${dataCell} ${rowBorder} truncate text-left text-[11px] font-semibold text-sky-blue-400 hover:underline` }, row.pathwayName || "\u2014") : /* @__PURE__ */ React.createElement("div", { className: `${dataCell} ${rowBorder} truncate text-[11px] text-slate-500` }, row.pathwayName || "\u2014")), !hideBadge && (row.badgeId && onBadgeClick ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => onBadgeClick(row.badgeId), className: `${dataCell} ${rowBorder} truncate text-left text-[11px] font-semibold text-sky-blue-400 hover:underline` }, row.badgeName) : /* @__PURE__ */ React.createElement("div", { className: `${dataCell} ${rowBorder} truncate text-[11px] text-slate-500` }, row.badgeName)), !hideLesson && (row.lessonId && onLessonClick ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => onLessonClick(row.lessonId), className: `${dataCell} ${rowBorder} truncate text-left text-[11px] font-semibold text-sky-blue-400 hover:underline` }, row.lessonName) : /* @__PURE__ */ React.createElement("div", { className: `${dataCell} ${rowBorder} truncate text-[11px] text-slate-500` }, row.lessonName)), /* @__PURE__ */ React.createElement("div", { className: `${dataCell} ${rowBorder} text-xs font-semibold text-dark-blue-400` }, row.question), /* @__PURE__ */ React.createElement("div", { className: `${dataCell} ${rowBorder} flex shrink-0 gap-2` }, /* @__PURE__ */ React.createElement(
|
|
3363
3353
|
"a",
|
|
3364
3354
|
{
|
|
3365
3355
|
href: row.link,
|
|
@@ -3438,14 +3428,33 @@ function Table(props) {
|
|
|
3438
3428
|
"Review Submission"
|
|
3439
3429
|
)),
|
|
3440
3430
|
/* @__PURE__ */ React.createElement(IconChevronRight, { size: 16, stroke: 2, className: `w-4 shrink-0 text-slate-300 transition-transform ${isOpen ? "rotate-90" : ""}` })
|
|
3441
|
-
), isOpen && /* @__PURE__ */ React.createElement("div", { className: "border-t border-slate-100 px-4 py-3" }, /* @__PURE__ */ React.createElement(
|
|
3431
|
+
), isOpen && /* @__PURE__ */ React.createElement("div", { className: "border-t border-slate-100 px-4 py-3" }, /* @__PURE__ */ React.createElement(
|
|
3432
|
+
Stack,
|
|
3433
|
+
{
|
|
3434
|
+
items: row.submissions,
|
|
3435
|
+
hideBadge: props.hideBadge,
|
|
3436
|
+
hideLesson: props.hideLesson,
|
|
3437
|
+
hidePathway: props.hidePathway,
|
|
3438
|
+
onBadgeClick: props.onBadgeClick,
|
|
3439
|
+
onLessonClick: props.onLessonClick,
|
|
3440
|
+
onPathwayClick: props.onPathwayClick
|
|
3441
|
+
}
|
|
3442
|
+
)));
|
|
3442
3443
|
}));
|
|
3443
3444
|
}
|
|
3444
3445
|
|
|
3445
3446
|
const initials = (name) => name.split(" ").map((w) => w[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();
|
|
3446
3447
|
function SubmissionDetail(props) {
|
|
3447
3448
|
const { student } = props;
|
|
3448
|
-
return /* @__PURE__ */ React.createElement("div", { className: "flex w-full flex-col gap-4 px-4 py-8" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("button", { onClick: props.onBack, className: "font-bold text-sky-blue-400 hover:underline" }, "File Locker"), /* @__PURE__ */ React.createElement("span", null, "/"), /* @__PURE__ */ React.createElement("span", { className: "font-bold text-dark-blue-400" }, student.name)), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-4 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm" }, student.avatar ? /* @__PURE__ */ React.createElement("img", { src: student.avatar, className: "h-12 w-12 shrink-0 rounded-full object-cover", alt: "" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-mint-400 to-dark-blue-400 text-sm font-bold text-white" }, initials(student.name)), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "text-lg font-extrabold text-dark-blue-400" }, student.name), /* @__PURE__ */ React.createElement("div", { className: "mt-0.5 text-xs text-slate-400" }, student.email, props.context ? ` \xB7 ${props.context}` : "")), /* @__PURE__ */ React.createElement("div", { className: "shrink-0 rounded-full bg-slate-100 px-3 py-1 text-xs font-bold text-slate-500" }, student.submissions.length, " file", student.submissions.length === 1 ? "" : "s")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-slate-200 bg-white p-5 shadow-sm" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-extrabold text-dark-blue-400" }, "Files"), /* @__PURE__ */ React.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React.createElement(
|
|
3449
|
+
return /* @__PURE__ */ React.createElement("div", { className: "flex w-full flex-col gap-4 px-4 py-8" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 text-xs text-slate-400" }, /* @__PURE__ */ React.createElement("button", { onClick: props.onBack, className: "font-bold text-sky-blue-400 hover:underline" }, "File Locker"), /* @__PURE__ */ React.createElement("span", null, "/"), /* @__PURE__ */ React.createElement("span", { className: "font-bold text-dark-blue-400" }, student.name)), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-4 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm" }, student.avatar ? /* @__PURE__ */ React.createElement("img", { src: student.avatar, className: "h-12 w-12 shrink-0 rounded-full object-cover", alt: "" }) : /* @__PURE__ */ React.createElement("div", { className: "flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-mint-400 to-dark-blue-400 text-sm font-bold text-white" }, initials(student.name)), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "text-lg font-extrabold text-dark-blue-400" }, student.name), /* @__PURE__ */ React.createElement("div", { className: "mt-0.5 text-xs text-slate-400" }, student.email, props.context ? ` \xB7 ${props.context}` : "")), /* @__PURE__ */ React.createElement("div", { className: "shrink-0 rounded-full bg-slate-100 px-3 py-1 text-xs font-bold text-slate-500" }, student.submissions.length, " file", student.submissions.length === 1 ? "" : "s")), /* @__PURE__ */ React.createElement("div", { className: "rounded-2xl border border-slate-200 bg-white p-5 shadow-sm" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-extrabold text-dark-blue-400" }, "Files"), /* @__PURE__ */ React.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React.createElement(
|
|
3450
|
+
Stack,
|
|
3451
|
+
{
|
|
3452
|
+
items: student.submissions,
|
|
3453
|
+
onBadgeClick: props.onBadgeClick,
|
|
3454
|
+
onLessonClick: props.onLessonClick,
|
|
3455
|
+
onPathwayClick: props.onPathwayClick
|
|
3456
|
+
}
|
|
3457
|
+
))), /* @__PURE__ */ React.createElement("div", { className: "flex gap-3" }, /* @__PURE__ */ React.createElement(
|
|
3449
3458
|
"button",
|
|
3450
3459
|
{
|
|
3451
3460
|
onClick: () => props.onNav(-1),
|
|
@@ -3536,42 +3545,143 @@ const TABS = [
|
|
|
3536
3545
|
{ value: "badges", label: "By badge" },
|
|
3537
3546
|
{ value: "lessons", label: "By lesson" }
|
|
3538
3547
|
];
|
|
3548
|
+
const SEARCH_PLACEHOLDER = "Search by student, pathway, badge, or lesson";
|
|
3539
3549
|
const countFiles = (students) => students.reduce((acc, s) => {
|
|
3540
3550
|
var _a;
|
|
3541
3551
|
return acc + (((_a = s.submissions) == null ? void 0 : _a.length) || 0);
|
|
3542
3552
|
}, 0);
|
|
3553
|
+
const matchesSearch = (student, term) => {
|
|
3554
|
+
if (!term)
|
|
3555
|
+
return true;
|
|
3556
|
+
if (student.name.toLowerCase().includes(term))
|
|
3557
|
+
return true;
|
|
3558
|
+
return (student.submissions || []).some(
|
|
3559
|
+
(sub) => {
|
|
3560
|
+
var _a, _b, _c;
|
|
3561
|
+
return ((_a = sub.badgeName) == null ? void 0 : _a.toLowerCase().includes(term)) || ((_b = sub.lessonName) == null ? void 0 : _b.toLowerCase().includes(term)) || ((_c = sub.pathwayName) == null ? void 0 : _c.toLowerCase().includes(term));
|
|
3562
|
+
}
|
|
3563
|
+
);
|
|
3564
|
+
};
|
|
3543
3565
|
const GroupCard = (props) => {
|
|
3544
3566
|
const [open, setOpen] = React.useState(false);
|
|
3545
|
-
return /* @__PURE__ */ React.createElement("div", { className: "overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm" }, /* @__PURE__ */ React.createElement("div", { onClick: () => setOpen(!open), className: "flex cursor-pointer items-center gap-3.5 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" },
|
|
3567
|
+
return /* @__PURE__ */ React.createElement("div", { className: "overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm" }, /* @__PURE__ */ React.createElement("div", { onClick: () => setOpen(!open), className: "flex cursor-pointer items-center gap-3.5 p-4" }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, props.onTitleClick ? /* @__PURE__ */ React.createElement(
|
|
3568
|
+
"button",
|
|
3569
|
+
{
|
|
3570
|
+
type: "button",
|
|
3571
|
+
onClick: (e) => {
|
|
3572
|
+
e.stopPropagation();
|
|
3573
|
+
props.onTitleClick();
|
|
3574
|
+
},
|
|
3575
|
+
className: "text-left text-sm font-extrabold text-sky-blue-400 hover:underline"
|
|
3576
|
+
},
|
|
3577
|
+
props.title
|
|
3578
|
+
) : /* @__PURE__ */ React.createElement("div", { className: "text-sm font-extrabold text-dark-blue-400" }, props.title), props.description && /* @__PURE__ */ React.createElement("div", { className: "mt-1 text-xs leading-relaxed text-slate-500" }, props.description)), /* @__PURE__ */ React.createElement("div", { className: "shrink-0 rounded-full bg-mint-100 px-2.5 py-1 text-xs font-bold text-dark-blue-400" }, props.count), /* @__PURE__ */ React.createElement(IconChevronDown, { size: 15, stroke: 2.3, className: `shrink-0 text-slate-400 transition-transform ${open ? "rotate-180" : ""}` })), open && /* @__PURE__ */ React.createElement("div", { className: "border-t border-slate-100 p-4" }, props.children));
|
|
3546
3579
|
};
|
|
3547
3580
|
const PathwayGroups = (props) => {
|
|
3548
3581
|
const { byPathway } = useFilteredStudents(props.students);
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3582
|
+
const term = props.search.trim().toLowerCase();
|
|
3583
|
+
const groups = props.pathways.map((p) => {
|
|
3584
|
+
const base = byPathway(p.pathwayId, props.badges);
|
|
3585
|
+
const ownNameMatches = !term || p.title.toLowerCase().includes(term);
|
|
3586
|
+
const filtered = ownNameMatches ? base : base.filter((s) => matchesSearch(s, term));
|
|
3587
|
+
return { pathway: p, filtered };
|
|
3588
|
+
}).filter((g) => !term || g.filtered.length > 0);
|
|
3589
|
+
if (term && groups.length === 0) {
|
|
3590
|
+
return /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "No matching entries.");
|
|
3591
|
+
}
|
|
3592
|
+
return /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-3" }, groups.map(({ pathway: p, filtered }) => /* @__PURE__ */ React.createElement(
|
|
3593
|
+
GroupCard,
|
|
3594
|
+
{
|
|
3595
|
+
key: p.pathwayId,
|
|
3596
|
+
title: p.title,
|
|
3597
|
+
description: p.description,
|
|
3598
|
+
count: countFiles(filtered),
|
|
3599
|
+
onTitleClick: props.onPathwayClick ? () => props.onPathwayClick(p.pathwayId) : void 0
|
|
3600
|
+
},
|
|
3601
|
+
/* @__PURE__ */ React.createElement(
|
|
3602
|
+
Table,
|
|
3603
|
+
{
|
|
3604
|
+
loading: props.loading,
|
|
3605
|
+
items: filtered,
|
|
3606
|
+
hidePathway: true,
|
|
3607
|
+
onReview: (item) => props.onReview(item, filtered, p.title),
|
|
3608
|
+
onBadgeClick: props.onBadgeClick,
|
|
3609
|
+
onLessonClick: props.onLessonClick
|
|
3610
|
+
}
|
|
3611
|
+
)
|
|
3612
|
+
)));
|
|
3553
3613
|
};
|
|
3554
3614
|
const BadgeGroups = (props) => {
|
|
3555
3615
|
const { byBadge } = useFilteredStudents(props.students);
|
|
3556
|
-
|
|
3557
|
-
|
|
3616
|
+
const term = props.search.trim().toLowerCase();
|
|
3617
|
+
const groups = props.badges.map((b) => {
|
|
3618
|
+
const base = byBadge(b.badgeId);
|
|
3619
|
+
const ownNameMatches = !term || b.displayName.toLowerCase().includes(term);
|
|
3620
|
+
const filtered = ownNameMatches ? base : base.filter((s) => matchesSearch(s, term));
|
|
3621
|
+
return { badge: b, filtered };
|
|
3622
|
+
}).filter((g) => !term || g.filtered.length > 0);
|
|
3623
|
+
if (term && groups.length === 0) {
|
|
3624
|
+
return /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "No matching entries.");
|
|
3625
|
+
}
|
|
3626
|
+
return /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-3" }, groups.map(({ badge: b, filtered }) => {
|
|
3558
3627
|
const pathwayTitle = props.pathwayNameForBadge[b.badgeId];
|
|
3559
|
-
return /* @__PURE__ */ React.createElement(
|
|
3628
|
+
return /* @__PURE__ */ React.createElement(
|
|
3629
|
+
GroupCard,
|
|
3630
|
+
{
|
|
3631
|
+
key: b.badgeId,
|
|
3632
|
+
title: b.displayName,
|
|
3633
|
+
description: pathwayTitle ? `${pathwayTitle} pathway` : void 0,
|
|
3634
|
+
count: countFiles(filtered),
|
|
3635
|
+
onTitleClick: props.onBadgeClick ? () => props.onBadgeClick(b.badgeId) : void 0
|
|
3636
|
+
},
|
|
3637
|
+
/* @__PURE__ */ React.createElement(
|
|
3638
|
+
Table,
|
|
3639
|
+
{
|
|
3640
|
+
loading: props.loading,
|
|
3641
|
+
items: filtered,
|
|
3642
|
+
hideBadge: true,
|
|
3643
|
+
hidePathway: true,
|
|
3644
|
+
onReview: (item) => props.onReview(item, filtered, b.displayName),
|
|
3645
|
+
onLessonClick: props.onLessonClick
|
|
3646
|
+
}
|
|
3647
|
+
)
|
|
3648
|
+
);
|
|
3560
3649
|
}));
|
|
3561
3650
|
};
|
|
3562
3651
|
const LessonGroups = (props) => {
|
|
3563
3652
|
const { byLesson } = useFilteredStudents(props.students);
|
|
3564
|
-
|
|
3565
|
-
|
|
3653
|
+
const term = props.search.trim().toLowerCase();
|
|
3654
|
+
const groups = props.lessons.map((l) => {
|
|
3655
|
+
const base = byLesson(l.lessonName);
|
|
3656
|
+
const ownNameMatches = !term || l.lessonName.toLowerCase().includes(term);
|
|
3657
|
+
const filtered = ownNameMatches ? base : base.filter((s) => matchesSearch(s, term));
|
|
3658
|
+
return { lesson: l, filtered };
|
|
3659
|
+
}).filter((g) => !term || g.filtered.length > 0);
|
|
3660
|
+
if (term && groups.length === 0) {
|
|
3661
|
+
return /* @__PURE__ */ React.createElement("p", { className: "text-sm text-slate-400" }, "No matching entries.");
|
|
3662
|
+
}
|
|
3663
|
+
return /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-3" }, groups.map(({ lesson: l, filtered }) => {
|
|
3566
3664
|
const badge = props.badgeForLesson[l.lessonId];
|
|
3567
3665
|
const pathwayTitle = badge ? props.pathwayNameForBadge[badge.badgeId] : void 0;
|
|
3568
3666
|
const description = badge ? pathwayTitle ? `${badge.displayName} \xB7 ${pathwayTitle} pathway` : badge.displayName : void 0;
|
|
3569
|
-
return /* @__PURE__ */ React.createElement(
|
|
3667
|
+
return /* @__PURE__ */ React.createElement(
|
|
3668
|
+
GroupCard,
|
|
3669
|
+
{
|
|
3670
|
+
key: l.lessonId,
|
|
3671
|
+
title: l.lessonName,
|
|
3672
|
+
description,
|
|
3673
|
+
count: countFiles(filtered),
|
|
3674
|
+
onTitleClick: props.onLessonClick ? () => props.onLessonClick(l.lessonId) : void 0
|
|
3675
|
+
},
|
|
3676
|
+
/* @__PURE__ */ React.createElement(Table, { loading: props.loading, items: filtered, hideBadge: true, hideLesson: true, hidePathway: true, onReview: (item) => props.onReview(item, filtered, l.lessonName) })
|
|
3677
|
+
);
|
|
3570
3678
|
}));
|
|
3571
3679
|
};
|
|
3572
3680
|
const FileLocker = (props) => {
|
|
3573
3681
|
const [tab, setTab] = React.useState("students");
|
|
3574
3682
|
const [reviewing, setReviewing] = React.useState(null);
|
|
3683
|
+
const [search, setSearch] = React.useState("");
|
|
3684
|
+
const searchLower = search.trim().toLowerCase();
|
|
3575
3685
|
const numberOfFiles = countFiles(props.students);
|
|
3576
3686
|
const { thisWeek, thisMonth } = React.useMemo(() => {
|
|
3577
3687
|
const now = new Date();
|
|
@@ -3602,6 +3712,17 @@ const FileLocker = (props) => {
|
|
|
3602
3712
|
});
|
|
3603
3713
|
return map;
|
|
3604
3714
|
}, [props.badges, props.pathways]);
|
|
3715
|
+
const pathwayIdForBadge = React.useMemo(() => {
|
|
3716
|
+
const map = {};
|
|
3717
|
+
(props.badges || []).forEach((b) => {
|
|
3718
|
+
if (!Array.isArray(b.categories))
|
|
3719
|
+
return;
|
|
3720
|
+
const pathway = (props.pathways || []).find((p) => b.categories.some((c) => c.startsWith(p.pathwayId)));
|
|
3721
|
+
if (pathway)
|
|
3722
|
+
map[b.badgeId] = pathway.pathwayId;
|
|
3723
|
+
});
|
|
3724
|
+
return map;
|
|
3725
|
+
}, [props.badges, props.pathways]);
|
|
3605
3726
|
const badgeForLesson = React.useMemo(() => {
|
|
3606
3727
|
const map = {};
|
|
3607
3728
|
(props.badges || []).forEach((b) => {
|
|
@@ -3616,9 +3737,12 @@ const FileLocker = (props) => {
|
|
|
3616
3737
|
}, [props.badges]);
|
|
3617
3738
|
const enrichedStudents = React.useMemo(() => {
|
|
3618
3739
|
return props.students.map((s) => __spreadProps(__spreadValues({}, s), {
|
|
3619
|
-
submissions: (s.submissions || []).map((sub) => __spreadProps(__spreadValues({}, sub), {
|
|
3740
|
+
submissions: (s.submissions || []).map((sub) => __spreadProps(__spreadValues({}, sub), {
|
|
3741
|
+
pathwayName: pathwayNameForBadge[sub.badgeId] || "",
|
|
3742
|
+
pathwayId: pathwayIdForBadge[sub.badgeId]
|
|
3743
|
+
}))
|
|
3620
3744
|
}));
|
|
3621
|
-
}, [props.students, pathwayNameForBadge]);
|
|
3745
|
+
}, [props.students, pathwayNameForBadge, pathwayIdForBadge]);
|
|
3622
3746
|
const { byPathway } = useFilteredStudents(enrichedStudents);
|
|
3623
3747
|
const mostActivePathway = React.useMemo(() => {
|
|
3624
3748
|
return (props.pathways || []).reduce((best, p) => {
|
|
@@ -3626,6 +3750,10 @@ const FileLocker = (props) => {
|
|
|
3626
3750
|
return count > 0 && (!best || count > best.count) ? { title: p.title, count } : best;
|
|
3627
3751
|
}, null);
|
|
3628
3752
|
}, [props.pathways, props.badges, byPathway]);
|
|
3753
|
+
const searchedStudents = React.useMemo(
|
|
3754
|
+
() => searchLower ? enrichedStudents.filter((s) => matchesSearch(s, searchLower)) : enrichedStudents,
|
|
3755
|
+
[enrichedStudents, searchLower]
|
|
3756
|
+
);
|
|
3629
3757
|
const onReview = (item, list, context) => setReviewing({ student: item, list, context });
|
|
3630
3758
|
const onNav = (dir) => {
|
|
3631
3759
|
if (!reviewing)
|
|
@@ -3643,7 +3771,10 @@ const FileLocker = (props) => {
|
|
|
3643
3771
|
student: reviewing.student,
|
|
3644
3772
|
context: reviewing.context,
|
|
3645
3773
|
onBack: () => setReviewing(null),
|
|
3646
|
-
onNav
|
|
3774
|
+
onNav,
|
|
3775
|
+
onBadgeClick: props.onBadgeClick,
|
|
3776
|
+
onLessonClick: props.onLessonClick,
|
|
3777
|
+
onPathwayClick: props.onPathwayClick
|
|
3647
3778
|
}
|
|
3648
3779
|
);
|
|
3649
3780
|
}
|
|
@@ -3670,7 +3801,66 @@ const FileLocker = (props) => {
|
|
|
3670
3801
|
className: `rounded-lg px-4 py-2 text-xs font-bold ${tab === t.value ? "bg-sky-blue-400/20 text-dark-blue-400" : "text-slate-400 hover:text-slate-600"}`
|
|
3671
3802
|
},
|
|
3672
3803
|
t.label
|
|
3673
|
-
))), !props.trial &&
|
|
3804
|
+
))), !props.trial && /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2.5 rounded-xl border border-slate-200 bg-white px-4 py-3 shadow-sm" }, /* @__PURE__ */ React.createElement(IconSearch, { size: 15, stroke: 1.75, className: "text-slate-400" }), /* @__PURE__ */ React.createElement(
|
|
3805
|
+
"input",
|
|
3806
|
+
{
|
|
3807
|
+
type: "text",
|
|
3808
|
+
value: search,
|
|
3809
|
+
onChange: (e) => setSearch(e.target.value),
|
|
3810
|
+
placeholder: SEARCH_PLACEHOLDER,
|
|
3811
|
+
className: "flex-1 border-none bg-transparent text-sm text-dark-blue-400 outline-none placeholder:text-slate-400"
|
|
3812
|
+
}
|
|
3813
|
+
)), !props.trial && tab === "students" && /* @__PURE__ */ React.createElement(
|
|
3814
|
+
Table,
|
|
3815
|
+
{
|
|
3816
|
+
loading: props.loading,
|
|
3817
|
+
items: searchedStudents,
|
|
3818
|
+
onReview: (item) => onReview(item, searchedStudents),
|
|
3819
|
+
onBadgeClick: props.onBadgeClick,
|
|
3820
|
+
onLessonClick: props.onLessonClick,
|
|
3821
|
+
onPathwayClick: props.onPathwayClick
|
|
3822
|
+
}
|
|
3823
|
+
), !props.trial && tab === "pathways" && /* @__PURE__ */ React.createElement(
|
|
3824
|
+
PathwayGroups,
|
|
3825
|
+
{
|
|
3826
|
+
students: enrichedStudents,
|
|
3827
|
+
loading: props.loading,
|
|
3828
|
+
search,
|
|
3829
|
+
pathways: props.pathways || [],
|
|
3830
|
+
badges: props.badges || [],
|
|
3831
|
+
onReview,
|
|
3832
|
+
onBadgeClick: props.onBadgeClick,
|
|
3833
|
+
onLessonClick: props.onLessonClick,
|
|
3834
|
+
onPathwayClick: props.onPathwayClick
|
|
3835
|
+
}
|
|
3836
|
+
), !props.trial && tab === "badges" && /* @__PURE__ */ React.createElement(
|
|
3837
|
+
BadgeGroups,
|
|
3838
|
+
{
|
|
3839
|
+
students: enrichedStudents,
|
|
3840
|
+
loading: props.loading,
|
|
3841
|
+
search,
|
|
3842
|
+
badges: props.badges || [],
|
|
3843
|
+
pathwayNameForBadge,
|
|
3844
|
+
onReview,
|
|
3845
|
+
onBadgeClick: props.onBadgeClick,
|
|
3846
|
+
onLessonClick: props.onLessonClick,
|
|
3847
|
+
onPathwayClick: props.onPathwayClick
|
|
3848
|
+
}
|
|
3849
|
+
), !props.trial && tab === "lessons" && /* @__PURE__ */ React.createElement(
|
|
3850
|
+
LessonGroups,
|
|
3851
|
+
{
|
|
3852
|
+
students: enrichedStudents,
|
|
3853
|
+
loading: props.loading,
|
|
3854
|
+
search,
|
|
3855
|
+
lessons: props.lessons,
|
|
3856
|
+
badgeForLesson,
|
|
3857
|
+
pathwayNameForBadge,
|
|
3858
|
+
onReview,
|
|
3859
|
+
onBadgeClick: props.onBadgeClick,
|
|
3860
|
+
onLessonClick: props.onLessonClick,
|
|
3861
|
+
onPathwayClick: props.onPathwayClick
|
|
3862
|
+
}
|
|
3863
|
+
));
|
|
3674
3864
|
};
|
|
3675
3865
|
|
|
3676
3866
|
export { AdminProvider, App, Badge, Badges, Class, Classes, Dashboard, FileLocker, GettingStarted, Home, Lesson, Lessons, Navbar, Organization, Pathway, Pathways, People, StartAnonymousLesson, Student, SwitchAccount, TrialHome, TrialRegistration };
|