@local-civics/mgmt-ui 0.1.196 → 0.1.197

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.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { useState, useMemo } from 'react';
3
3
  import { createStyles, Group, Avatar, Text, Box, Badge as Badge$1, ThemeIcon, Collapse, Navbar as Navbar$1, Center, Image, Code, Burger, ScrollArea, Modal, Loader, Container, Button, Title, UnstyledButton, Card, SimpleGrid, Tabs as Tabs$1, Stack as Stack$5, Grid, ActionIcon, LoadingOverlay, Select, Table as Table$l, Autocomplete, Drawer, Divider, TextInput, Tooltip, Paper, Overlay, Anchor, Menu, Checkbox, createEmotionCache, MantineProvider, AppShell } from '@mantine/core';
4
- import { IconChevronRight, IconChevronLeft, IconVideo, IconSwitchHorizontal, IconLogout, IconHome2, IconGauge, IconCategory2, IconRoute, IconAlbum, IconLambda, IconClipboard, IconBuilding, IconBatteryEco, IconBooks, IconBackpack, IconClipboardCopy, IconTableExport, IconArrowLeft, IconPlaylistAdd, IconCheck, IconTrash, IconDownload, IconX, IconCloudUpload, IconChevronUp, IconChevronDown, IconSelector, IconInfoCircle, IconColorSwatch, IconPointer, IconScribble, IconSchool, IconPodium, IconBriefcase, IconPresentation, IconNews, IconTools, IconBrandInstagram, IconBrandLinkedin, IconBrandFacebook } from '@tabler/icons';
4
+ import { IconChevronRight, IconChevronLeft, IconVideo, IconSwitchHorizontal, IconLogout, IconHome2, IconGauge, IconCategory2, IconRoute, IconAlbum, IconLambda, IconClipboard, IconBuilding, IconBatteryEco, IconBooks, IconBackpack, IconClipboardCopy, IconTableExport, IconArrowLeft, IconPlaylistAdd, IconCheck, IconTrash, IconDownload, IconX, IconCloudUpload, IconInfoCircle, IconColorSwatch, IconPointer, IconChevronDown, IconScribble, IconSchool, IconPodium, IconBriefcase, IconPresentation, IconNews, IconTools, IconBrandInstagram, IconBrandLinkedin, IconBrandFacebook } from '@tabler/icons';
5
5
  import { Link } from 'react-router-dom';
6
6
  import { showNotification, NotificationsProvider } from '@mantine/notifications';
7
7
  export { showNotification, updateNotification } from '@mantine/notifications';
@@ -42,7 +42,7 @@ var __objRest$2 = (source, exclude) => {
42
42
  }
43
43
  return target;
44
44
  };
45
- const useStyles$w = createStyles((theme) => ({
45
+ const useStyles$v = createStyles((theme) => ({
46
46
  user: {
47
47
  display: "block",
48
48
  width: "100%",
@@ -53,7 +53,7 @@ const useStyles$w = createStyles((theme) => ({
53
53
  }));
54
54
  function UserButton(_a) {
55
55
  var _b = _a, { image, name, email, icon } = _b, others = __objRest$2(_b, ["image", "name", "email", "icon"]);
56
- const { classes } = useStyles$w();
56
+ const { classes } = useStyles$v();
57
57
  return /* @__PURE__ */ React.createElement(Group, __spreadValues$e({ className: classes.user }, others), /* @__PURE__ */ React.createElement(Avatar, { src: image, radius: "xl" }), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement(Text, { size: "sm", weight: 500 }, name), /* @__PURE__ */ React.createElement(Text, { color: "dimmed", size: "xs" }, email)));
58
58
  }
59
59
 
@@ -64,7 +64,7 @@ const compact = (num) => {
64
64
  }).format(num || 0);
65
65
  };
66
66
 
67
- const useStyles$v = createStyles((theme, _params, getRef) => {
67
+ const useStyles$u = createStyles((theme, _params, getRef) => {
68
68
  const icon = getRef("icon");
69
69
  return {
70
70
  control: {
@@ -117,7 +117,7 @@ const useStyles$v = createStyles((theme, _params, getRef) => {
117
117
  };
118
118
  });
119
119
  function LinksGroup({ icon: Icon, href, label, initiallyOpened, links, active, notifications }) {
120
- const { classes, theme, cx } = useStyles$v();
120
+ const { classes, theme, cx } = useStyles$u();
121
121
  const hasLinks = Array.isArray(links) && links.length > 0;
122
122
  const hasActiveLinks = Array.isArray(links) && links.map((l) => !!active && active === `${label}/${l.label}`).reduce((a, b) => a || b, false);
123
123
  const [opened, setOpened] = useState(initiallyOpened || hasActiveLinks || false);
@@ -181,7 +181,7 @@ var __spreadValues$d = (a, b) => {
181
181
  return a;
182
182
  };
183
183
  var __spreadProps$9 = (a, b) => __defProps$9(a, __getOwnPropDescs$9(b));
184
- const useStyles$u = createStyles((theme, _params, getRef) => {
184
+ const useStyles$t = createStyles((theme, _params, getRef) => {
185
185
  const icon = getRef("icon");
186
186
  return {
187
187
  navbar: {
@@ -282,7 +282,7 @@ const TRIAL_PAGES = [
282
282
  "Badges"
283
283
  ];
284
284
  function Navbar(props) {
285
- const { classes, cx } = useStyles$u();
285
+ const { classes, cx } = useStyles$t();
286
286
  const [burgerOpen, setBurgerOpen] = React.useState(false);
287
287
  const toggle = () => setBurgerOpen(!burgerOpen);
288
288
  const links = data.map((item) => {
@@ -325,7 +325,7 @@ function Navbar(props) {
325
325
  } }, /* @__PURE__ */ React.createElement(IconLogout, { className: classes.linkIcon, stroke: 1.5 }), /* @__PURE__ */ React.createElement("span", null, "Logout"))))));
326
326
  }
327
327
 
328
- const useStyles$t = createStyles((theme) => ({
328
+ const useStyles$s = createStyles((theme) => ({
329
329
  inner: {
330
330
  paddingTop: theme.spacing.xl,
331
331
  paddingBottom: theme.spacing.xl * 4
@@ -361,7 +361,7 @@ const useStyles$t = createStyles((theme) => ({
361
361
  }
362
362
  }));
363
363
  const GettingStarted = (props) => {
364
- const { classes } = useStyles$t();
364
+ const { classes } = useStyles$s();
365
365
  return /* @__PURE__ */ React.createElement(
366
366
  Modal,
367
367
  {
@@ -375,7 +375,7 @@ const GettingStarted = (props) => {
375
375
  );
376
376
  };
377
377
 
378
- const useStyles$s = createStyles((theme) => ({
378
+ const useStyles$r = createStyles((theme) => ({
379
379
  title: {
380
380
  fontSize: 34,
381
381
  fontWeight: 900,
@@ -416,7 +416,7 @@ const useStyles$s = createStyles((theme) => ({
416
416
  }
417
417
  }));
418
418
  const SwitchAccount = (props) => {
419
- const { classes, theme } = useStyles$s();
419
+ const { classes, theme } = useStyles$r();
420
420
  const options = props.accounts.map((a) => {
421
421
  return /* @__PURE__ */ React.createElement(UnstyledButton, { onClick: () => props.onClick && props.onClick(a.accountId), key: a.accountId, p: theme.spacing.md }, /* @__PURE__ */ React.createElement(Card, { withBorder: true, shadow: "md", radius: "md", className: classes.card, p: "xl" }, a.isAdmin && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(IconBatteryEco, { size: 50, stroke: 2, color: theme.fn.primaryColor() })), a.isGroupAdmin && !a.isAdmin && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(IconBooks, { size: 50, stroke: 2, color: theme.fn.primaryColor() })), !a.isAdmin && !a.isGroupAdmin && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(IconBackpack, { size: 50, stroke: 2, color: theme.fn.primaryColor() })), /* @__PURE__ */ React.createElement(Text, { size: "lg", weight: 500, className: classes.cardTitle, mt: "md" }, a.name), /* @__PURE__ */ React.createElement(Text, { size: "sm", color: "dimmed", mt: "sm" }, a.isAdmin ? "Admin" : a.isGroupAdmin ? "Educator" : "Student")));
422
422
  });
@@ -433,7 +433,7 @@ const SwitchAccount = (props) => {
433
433
  );
434
434
  };
435
435
 
436
- const useStyles$r = createStyles((theme) => ({
436
+ const useStyles$q = createStyles((theme) => ({
437
437
  root: {
438
438
  display: "flex",
439
439
  backgroundImage: `linear-gradient(-60deg, ${theme.colors[theme.primaryColor][4]} 0%, ${theme.colors[theme.primaryColor][7]} 100%)`,
@@ -477,7 +477,7 @@ const useStyles$r = createStyles((theme) => ({
477
477
  }
478
478
  }));
479
479
  const StatsGroup = ({ data, footer }) => {
480
- const { classes } = useStyles$r();
480
+ const { classes } = useStyles$q();
481
481
  const stats = data.map((stat) => {
482
482
  const value = (() => {
483
483
  if (stat.unit === "%") {
@@ -497,7 +497,7 @@ const Tabs = (props) => {
497
497
  return /* @__PURE__ */ React.createElement(Tabs$1, { value: props.value, onTabChange: props.onChange }, /* @__PURE__ */ React.createElement(Tabs$1.List, null, tabs));
498
498
  };
499
499
 
500
- const useStyles$q = createStyles((theme) => ({
500
+ const useStyles$p = createStyles((theme) => ({
501
501
  button: {
502
502
  borderTopRightRadius: 0,
503
503
  borderBottomRightRadius: 0,
@@ -512,7 +512,7 @@ const useStyles$q = createStyles((theme) => ({
512
512
  }
513
513
  }));
514
514
  const SplitButton$5 = (props) => {
515
- const { classes, theme } = useStyles$q();
515
+ const { classes, theme } = useStyles$p();
516
516
  theme.colors[theme.primaryColor][theme.colorScheme === "dark" ? 5 : 6];
517
517
  return /* @__PURE__ */ React.createElement(Stack$5, { spacing: "sm" }, /* @__PURE__ */ React.createElement(
518
518
  Button,
@@ -540,7 +540,7 @@ const SplitButton$5 = (props) => {
540
540
  ));
541
541
  };
542
542
 
543
- const useStyles$p = createStyles((theme) => ({
543
+ const useStyles$o = createStyles((theme) => ({
544
544
  wrapper: {
545
545
  display: "flex",
546
546
  alignItems: "center",
@@ -591,7 +591,7 @@ const useStyles$p = createStyles((theme) => ({
591
591
  }
592
592
  }));
593
593
  const PlaceholderBanner = (props) => {
594
- const { classes } = useStyles$p();
594
+ const { classes } = useStyles$o();
595
595
  const title = props.title || "Nothing to display";
596
596
  const description = props.description || "We don't have anything to show you here just yet. Add data, check back later, or adjust your search.";
597
597
  return /* @__PURE__ */ React.createElement("div", { className: classes.wrapper }, /* @__PURE__ */ React.createElement("div", { className: classes.body }, /* @__PURE__ */ React.createElement(Title, { className: classes.title }, props.loading ? "Loading..." : title), /* @__PURE__ */ React.createElement(Text, { size: "sm", color: "dimmed" }, props.loading ? "Hold on, we're loading your data." : description)), /* @__PURE__ */ React.createElement(Image, { src: `https://cdn.localcivics.io/illustrations/${props.icon}.svg`, className: classes.image }));
@@ -761,7 +761,7 @@ function Table$j(props) {
761
761
  ));
762
762
  }
763
763
 
764
- const useStyles$o = createStyles((theme) => ({
764
+ const useStyles$n = createStyles((theme) => ({
765
765
  title: {
766
766
  fontSize: 34,
767
767
  fontWeight: 900,
@@ -774,7 +774,7 @@ const useStyles$o = createStyles((theme) => ({
774
774
  }
775
775
  }));
776
776
  const Badge = (props) => {
777
- const { classes } = useStyles$o();
777
+ const { classes } = useStyles$n();
778
778
  const [tab, setTab] = useState("lessons");
779
779
  const numberOfStudents = props.students.length;
780
780
  const numberOfBadges = numberOfStudents > 0 ? props.students.filter((u) => u.isComplete).length : 0;
@@ -872,7 +872,7 @@ function Table$i(props) {
872
872
  return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 600 }, /* @__PURE__ */ React.createElement(Table$l, { horizontalSpacing: 0, verticalSpacing: 0, sx: { minWidth: 700 } }, /* @__PURE__ */ React.createElement("tbody", null, rows)));
873
873
  }
874
874
 
875
- const useStyles$n = createStyles((theme) => ({
875
+ const useStyles$m = createStyles((theme) => ({
876
876
  title: {
877
877
  fontSize: 34,
878
878
  fontWeight: 900,
@@ -885,7 +885,7 @@ const useStyles$n = createStyles((theme) => ({
885
885
  }
886
886
  }));
887
887
  const Badges = (props) => {
888
- const { classes } = useStyles$n();
888
+ const { classes } = useStyles$m();
889
889
  return /* @__PURE__ */ React.createElement(Container, { size: "lg", py: "xl" }, /* @__PURE__ */ React.createElement(Stack$5, { spacing: "md" }, /* @__PURE__ */ React.createElement(Grid, null, /* @__PURE__ */ React.createElement(Grid.Col, { sm: "auto" }, /* @__PURE__ */ React.createElement(Badge$1, { variant: "filled", size: "lg" }, "Badges"), /* @__PURE__ */ React.createElement(Title, { order: 2, className: classes.title, mt: "md" }, "Badges and micro-credentials"), /* @__PURE__ */ React.createElement(Text, { color: "dimmed", className: classes.description, mt: "sm" }, "Key milestones that reflect skill development, micro-credentials, or academic progress"))), /* @__PURE__ */ React.createElement(
890
890
  Autocomplete,
891
891
  {
@@ -1252,7 +1252,7 @@ var __spreadValues$a = (a, b) => {
1252
1252
  return a;
1253
1253
  };
1254
1254
  var __spreadProps$6 = (a, b) => __defProps$6(a, __getOwnPropDescs$6(b));
1255
- const useStyles$m = createStyles((theme) => ({
1255
+ const useStyles$l = createStyles((theme) => ({
1256
1256
  title: {
1257
1257
  fontSize: 34,
1258
1258
  fontWeight: 900,
@@ -1285,7 +1285,7 @@ const useStyles$m = createStyles((theme) => ({
1285
1285
  }
1286
1286
  }));
1287
1287
  const Class = (props) => {
1288
- const { classes } = useStyles$m();
1288
+ const { classes } = useStyles$l();
1289
1289
  const form = useForm({
1290
1290
  initialValues: {
1291
1291
  classId: "",
@@ -1386,7 +1386,7 @@ const Class = (props) => {
1386
1386
  ))))));
1387
1387
  };
1388
1388
  const DropzoneButton$1 = (props) => {
1389
- const { classes, theme } = useStyles$m();
1389
+ const { classes, theme } = useStyles$l();
1390
1390
  const openRef = React.useRef(null);
1391
1391
  const [loading, setLoading] = React.useState(false);
1392
1392
  const onDrop = React.useCallback((acceptedFiles) => {
@@ -1432,21 +1432,6 @@ const DropzoneButton$1 = (props) => {
1432
1432
  } }, "Select file"));
1433
1433
  };
1434
1434
 
1435
- const useStyles$l = createStyles((theme) => ({
1436
- th: { padding: "0 !important" },
1437
- control: {
1438
- width: "100%",
1439
- padding: `${theme.spacing.xs}px ${theme.spacing.md}px`,
1440
- "&:hover": {
1441
- backgroundColor: theme.colorScheme === "dark" ? theme.colors.dark[6] : theme.colors.gray[0]
1442
- }
1443
- }
1444
- }));
1445
- function Th({ children, reversed, sorted, onSort }) {
1446
- const { classes } = useStyles$l();
1447
- const Icon = sorted ? reversed ? IconChevronUp : IconChevronDown : IconSelector;
1448
- return /* @__PURE__ */ React.createElement("th", { className: classes.th }, /* @__PURE__ */ React.createElement(UnstyledButton, { onClick: onSort, className: classes.control }, /* @__PURE__ */ React.createElement(Group, { position: "apart", noWrap: true, spacing: "xs" }, /* @__PURE__ */ React.createElement(Text, { weight: 500, size: "sm", sx: { whiteSpace: "nowrap" } }, children), /* @__PURE__ */ React.createElement(Center, null, /* @__PURE__ */ React.createElement(Icon, { size: 14, stroke: 1.5 })))));
1449
- }
1450
1435
  function Table$b(props) {
1451
1436
  const { items: sortedItems, requestSort, sortConfig } = useSortableData(props.items);
1452
1437
  if (props.items.length === 0) {
@@ -1468,24 +1453,49 @@ function Table$b(props) {
1468
1453
  confirmProps: { color: "red" },
1469
1454
  onConfirm: () => props.onDeleteClass(group)
1470
1455
  });
1471
- const rows = sortedItems.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.classId }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(UnstyledButton, { component: Link, to: row.href }, /* @__PURE__ */ React.createElement(Text, { size: 14 }, row.name))), /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(Text, { size: 14 }, row.description)), /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(Text, { size: 14 }, row.numberOfStudents || 0)), /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(Group, { noWrap: true, spacing: 0, position: "right" }, /* @__PURE__ */ React.createElement(ActionIcon, { color: "red" }, /* @__PURE__ */ React.createElement(IconTrash, { onClick: () => openDeleteModal(row), size: 16, stroke: 1.5 }))))));
1472
- return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 600 }, /* @__PURE__ */ React.createElement(Table$l, { verticalSpacing: 20, sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement(
1473
- Th,
1474
- {
1475
- sorted: sortConfig.key === "name",
1476
- reversed: sortConfig.direction === "desc",
1477
- onSort: () => requestSort("name")
1478
- },
1479
- "Class Name"
1480
- ), /* @__PURE__ */ React.createElement("th", { style: { padding: "7px 16px" } }, /* @__PURE__ */ React.createElement(Text, { weight: 500, size: "sm" }, "Description")), /* @__PURE__ */ React.createElement(
1481
- Th,
1456
+ const sortStatus = {
1457
+ columnAccessor: sortConfig.key,
1458
+ direction: sortConfig.direction === "desc" ? "desc" : "asc"
1459
+ };
1460
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 600 }, /* @__PURE__ */ React.createElement(
1461
+ DataTable,
1482
1462
  {
1483
- sorted: sortConfig.key === "numberOfStudents",
1484
- reversed: sortConfig.direction === "desc",
1485
- onSort: () => requestSort("numberOfStudents")
1486
- },
1487
- "# of Students"
1488
- ), /* @__PURE__ */ React.createElement("th", null))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
1463
+ withBorder: false,
1464
+ borderRadius: "sm",
1465
+ verticalSpacing: "sm",
1466
+ sx: { minWidth: 700 },
1467
+ highlightOnHover: true,
1468
+ striped: true,
1469
+ records: sortedItems,
1470
+ idAccessor: "classId",
1471
+ sortStatus,
1472
+ onSortStatusChange: (status) => requestSort(status.columnAccessor),
1473
+ columns: [
1474
+ {
1475
+ accessor: "name",
1476
+ title: "Class Name",
1477
+ sortable: true,
1478
+ render: (row) => /* @__PURE__ */ React.createElement(UnstyledButton, { component: Link, to: row.href }, /* @__PURE__ */ React.createElement(Text, { size: 14, weight: 500 }, row.name))
1479
+ },
1480
+ {
1481
+ accessor: "description",
1482
+ title: "Description",
1483
+ render: (row) => /* @__PURE__ */ React.createElement(Text, { size: 14 }, row.description)
1484
+ },
1485
+ {
1486
+ accessor: "numberOfStudents",
1487
+ title: "# of Students",
1488
+ sortable: true,
1489
+ render: (row) => /* @__PURE__ */ React.createElement(Text, { size: 14 }, row.numberOfStudents || 0)
1490
+ },
1491
+ {
1492
+ accessor: "actions",
1493
+ title: "",
1494
+ render: (row) => /* @__PURE__ */ React.createElement(Group, { noWrap: true, spacing: 0, position: "right" }, /* @__PURE__ */ React.createElement(ActionIcon, { color: "red", onClick: () => openDeleteModal(row) }, /* @__PURE__ */ React.createElement(IconTrash, { size: 16, stroke: 1.5 })))
1495
+ }
1496
+ ]
1497
+ }
1498
+ ));
1489
1499
  }
1490
1500
 
1491
1501
  var __defProp$9 = Object.defineProperty;
@@ -2074,6 +2084,11 @@ function Table$6(props) {
2074
2084
  status: item.isComplete ? 2 : item.isStarted ? 1 : 0
2075
2085
  }));
2076
2086
  }, [props.items]);
2087
+ const { items: sortedItems, requestSort, sortConfig } = useSortableData(preparedItems);
2088
+ const sortStatus = {
2089
+ columnAccessor: sortConfig.key,
2090
+ direction: sortConfig.direction === "desc" ? "desc" : "asc"
2091
+ };
2077
2092
  if (props.items.length === 0) {
2078
2093
  return /* @__PURE__ */ React.createElement(
2079
2094
  PlaceholderBanner,
@@ -2085,11 +2100,6 @@ function Table$6(props) {
2085
2100
  }
2086
2101
  );
2087
2102
  }
2088
- const { items: sortedItems, requestSort, sortConfig } = useSortableData(preparedItems);
2089
- const sortStatus = {
2090
- columnAccessor: sortConfig.key,
2091
- direction: sortConfig.direction === "desc" ? "desc" : "asc"
2092
- };
2093
2103
  return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 600 }, /* @__PURE__ */ React.createElement(
2094
2104
  DataTable,
2095
2105
  {