@local-civics/mgmt-ui 0.1.185 → 0.1.186

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 CHANGED
@@ -294,9 +294,6 @@ type ClassProps = {
294
294
  */
295
295
  declare const Class: (props: ClassProps) => JSX.Element;
296
296
 
297
- /**
298
- * Item
299
- */
300
297
  type Item$d = {
301
298
  href: string;
302
299
  classId: string;
package/dist/index.js CHANGED
@@ -62,7 +62,7 @@ var __objRest$2 = (source, exclude) => {
62
62
  }
63
63
  return target;
64
64
  };
65
- const useStyles$t = core.createStyles((theme) => ({
65
+ const useStyles$u = core.createStyles((theme) => ({
66
66
  user: {
67
67
  display: "block",
68
68
  width: "100%",
@@ -73,7 +73,7 @@ const useStyles$t = core.createStyles((theme) => ({
73
73
  }));
74
74
  function UserButton(_a) {
75
75
  var _b = _a, { image, name, email, icon } = _b, others = __objRest$2(_b, ["image", "name", "email", "icon"]);
76
- const { classes } = useStyles$t();
76
+ const { classes } = useStyles$u();
77
77
  return /* @__PURE__ */ React__namespace.createElement(core.Group, __spreadValues$9({ className: classes.user }, others), /* @__PURE__ */ React__namespace.createElement(core.Avatar, { src: image, radius: "xl" }), /* @__PURE__ */ React__namespace.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React__namespace.createElement(core.Text, { size: "sm", weight: 500 }, name), /* @__PURE__ */ React__namespace.createElement(core.Text, { color: "dimmed", size: "xs" }, email)));
78
78
  }
79
79
 
@@ -84,7 +84,7 @@ const compact = (num) => {
84
84
  }).format(num || 0);
85
85
  };
86
86
 
87
- const useStyles$s = core.createStyles((theme, _params, getRef) => {
87
+ const useStyles$t = core.createStyles((theme, _params, getRef) => {
88
88
  const icon = getRef("icon");
89
89
  return {
90
90
  control: {
@@ -137,7 +137,7 @@ const useStyles$s = core.createStyles((theme, _params, getRef) => {
137
137
  };
138
138
  });
139
139
  function LinksGroup({ icon: Icon, href, label, initiallyOpened, links, active, notifications }) {
140
- const { classes, theme, cx } = useStyles$s();
140
+ const { classes, theme, cx } = useStyles$t();
141
141
  const hasLinks = Array.isArray(links) && links.length > 0;
142
142
  const hasActiveLinks = Array.isArray(links) && links.map((l) => !!active && active === `${label}/${l.label}`).reduce((a, b) => a || b, false);
143
143
  const [opened, setOpened] = React.useState(initiallyOpened || hasActiveLinks || false);
@@ -201,7 +201,7 @@ var __spreadValues$8 = (a, b) => {
201
201
  return a;
202
202
  };
203
203
  var __spreadProps$5 = (a, b) => __defProps$5(a, __getOwnPropDescs$5(b));
204
- const useStyles$r = core.createStyles((theme, _params, getRef) => {
204
+ const useStyles$s = core.createStyles((theme, _params, getRef) => {
205
205
  const icon = getRef("icon");
206
206
  return {
207
207
  navbar: {
@@ -301,7 +301,7 @@ const TRIAL_PAGES = [
301
301
  "Badges"
302
302
  ];
303
303
  function Navbar(props) {
304
- const { classes, cx } = useStyles$r();
304
+ const { classes, cx } = useStyles$s();
305
305
  const [burgerOpen, setBurgerOpen] = React__namespace.useState(false);
306
306
  const toggle = () => setBurgerOpen(!burgerOpen);
307
307
  const links = data.map((item) => {
@@ -344,7 +344,7 @@ function Navbar(props) {
344
344
  } }, /* @__PURE__ */ React__namespace.createElement(icons.IconLogout, { className: classes.linkIcon, stroke: 1.5 }), /* @__PURE__ */ React__namespace.createElement("span", null, "Logout"))))));
345
345
  }
346
346
 
347
- const useStyles$q = core.createStyles((theme) => ({
347
+ const useStyles$r = core.createStyles((theme) => ({
348
348
  inner: {
349
349
  paddingTop: theme.spacing.xl,
350
350
  paddingBottom: theme.spacing.xl * 4
@@ -380,7 +380,7 @@ const useStyles$q = core.createStyles((theme) => ({
380
380
  }
381
381
  }));
382
382
  const GettingStarted = (props) => {
383
- const { classes } = useStyles$q();
383
+ const { classes } = useStyles$r();
384
384
  return /* @__PURE__ */ React__namespace.createElement(
385
385
  core.Modal,
386
386
  {
@@ -394,7 +394,7 @@ const GettingStarted = (props) => {
394
394
  );
395
395
  };
396
396
 
397
- const useStyles$p = core.createStyles((theme) => ({
397
+ const useStyles$q = core.createStyles((theme) => ({
398
398
  title: {
399
399
  fontSize: 34,
400
400
  fontWeight: 900,
@@ -435,7 +435,7 @@ const useStyles$p = core.createStyles((theme) => ({
435
435
  }
436
436
  }));
437
437
  const SwitchAccount = (props) => {
438
- const { classes, theme } = useStyles$p();
438
+ const { classes, theme } = useStyles$q();
439
439
  const options = props.accounts.map((a) => {
440
440
  return /* @__PURE__ */ React__namespace.createElement(core.UnstyledButton, { onClick: () => props.onClick && props.onClick(a.accountId), key: a.accountId, p: theme.spacing.md }, /* @__PURE__ */ React__namespace.createElement(core.Card, { withBorder: true, shadow: "md", radius: "md", className: classes.card, p: "xl" }, a.isAdmin && /* @__PURE__ */ React__namespace.createElement(React__namespace.Fragment, null, /* @__PURE__ */ React__namespace.createElement(icons.IconBatteryEco, { size: 50, stroke: 2, color: theme.fn.primaryColor() })), a.isGroupAdmin && !a.isAdmin && /* @__PURE__ */ React__namespace.createElement(React__namespace.Fragment, null, /* @__PURE__ */ React__namespace.createElement(icons.IconBooks, { size: 50, stroke: 2, color: theme.fn.primaryColor() })), !a.isAdmin && !a.isGroupAdmin && /* @__PURE__ */ React__namespace.createElement(React__namespace.Fragment, null, /* @__PURE__ */ React__namespace.createElement(icons.IconBackpack, { size: 50, stroke: 2, color: theme.fn.primaryColor() })), /* @__PURE__ */ React__namespace.createElement(core.Text, { size: "lg", weight: 500, className: classes.cardTitle, mt: "md" }, a.name), /* @__PURE__ */ React__namespace.createElement(core.Text, { size: "sm", color: "dimmed", mt: "sm" }, a.isAdmin ? "Admin" : a.isGroupAdmin ? "Educator" : "Student")));
441
441
  });
@@ -452,7 +452,7 @@ const SwitchAccount = (props) => {
452
452
  );
453
453
  };
454
454
 
455
- const useStyles$o = core.createStyles((theme) => ({
455
+ const useStyles$p = core.createStyles((theme) => ({
456
456
  root: {
457
457
  display: "flex",
458
458
  backgroundImage: `linear-gradient(-60deg, ${theme.colors[theme.primaryColor][4]} 0%, ${theme.colors[theme.primaryColor][7]} 100%)`,
@@ -496,7 +496,7 @@ const useStyles$o = core.createStyles((theme) => ({
496
496
  }
497
497
  }));
498
498
  const StatsGroup = ({ data, footer }) => {
499
- const { classes } = useStyles$o();
499
+ const { classes } = useStyles$p();
500
500
  const stats = data.map((stat) => {
501
501
  const value = (() => {
502
502
  if (stat.unit === "%") {
@@ -516,7 +516,7 @@ const Tabs = (props) => {
516
516
  return /* @__PURE__ */ React__namespace.createElement(core.Tabs, { value: props.value, onTabChange: props.onChange }, /* @__PURE__ */ React__namespace.createElement(core.Tabs.List, null, tabs));
517
517
  };
518
518
 
519
- const useStyles$n = core.createStyles((theme) => ({
519
+ const useStyles$o = core.createStyles((theme) => ({
520
520
  button: {
521
521
  borderTopRightRadius: 0,
522
522
  borderBottomRightRadius: 0,
@@ -531,7 +531,7 @@ const useStyles$n = core.createStyles((theme) => ({
531
531
  }
532
532
  }));
533
533
  const SplitButton$4 = (props) => {
534
- const { classes, theme } = useStyles$n();
534
+ const { classes, theme } = useStyles$o();
535
535
  theme.colors[theme.primaryColor][theme.colorScheme === "dark" ? 5 : 6];
536
536
  return /* @__PURE__ */ React__namespace.createElement(core.Stack, { spacing: "sm" }, /* @__PURE__ */ React__namespace.createElement(
537
537
  core.Button,
@@ -559,7 +559,7 @@ const SplitButton$4 = (props) => {
559
559
  ));
560
560
  };
561
561
 
562
- const useStyles$m = core.createStyles((theme) => ({
562
+ const useStyles$n = core.createStyles((theme) => ({
563
563
  wrapper: {
564
564
  display: "flex",
565
565
  alignItems: "center",
@@ -610,7 +610,7 @@ const useStyles$m = core.createStyles((theme) => ({
610
610
  }
611
611
  }));
612
612
  const PlaceholderBanner = (props) => {
613
- const { classes } = useStyles$m();
613
+ const { classes } = useStyles$n();
614
614
  const title = props.title || "Nothing to display";
615
615
  const description = props.description || "We don't have anything to show you here just yet. Add data, check back later, or adjust your search.";
616
616
  return /* @__PURE__ */ React__namespace.createElement("div", { className: classes.wrapper }, /* @__PURE__ */ React__namespace.createElement("div", { className: classes.body }, /* @__PURE__ */ React__namespace.createElement(core.Title, { className: classes.title }, props.loading ? "Loading..." : title), /* @__PURE__ */ React__namespace.createElement(core.Text, { size: "sm", color: "dimmed" }, props.loading ? "Hold on, we're loading your data." : description)), /* @__PURE__ */ React__namespace.createElement(core.Image, { src: `https://cdn.localcivics.io/illustrations/${props.icon}.svg`, className: classes.image }));
@@ -682,7 +682,7 @@ function Table$i(props) {
682
682
  return /* @__PURE__ */ React__namespace.createElement(core.ScrollArea.Autosize, { maxHeight: 600 }, /* @__PURE__ */ React__namespace.createElement(core.Table, { verticalSpacing: "sm", sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React__namespace.createElement("thead", null, /* @__PURE__ */ React__namespace.createElement("tr", null, /* @__PURE__ */ React__namespace.createElement("th", null, "Lesson Name"), /* @__PURE__ */ React__namespace.createElement("th", null, "Lesson Completion"))), /* @__PURE__ */ React__namespace.createElement("tbody", null, rows)));
683
683
  }
684
684
 
685
- const useStyles$l = core.createStyles((theme) => ({
685
+ const useStyles$m = core.createStyles((theme) => ({
686
686
  title: {
687
687
  fontSize: 34,
688
688
  fontWeight: 900,
@@ -695,7 +695,7 @@ const useStyles$l = core.createStyles((theme) => ({
695
695
  }
696
696
  }));
697
697
  const Badge = (props) => {
698
- const { classes } = useStyles$l();
698
+ const { classes } = useStyles$m();
699
699
  const [tab, setTab] = React.useState("lessons");
700
700
  const numberOfStudents = props.students.length;
701
701
  const numberOfBadges = numberOfStudents > 0 ? props.students.filter((u) => u.isComplete).length : 0;
@@ -793,7 +793,7 @@ function Table$h(props) {
793
793
  return /* @__PURE__ */ React__namespace.createElement(core.ScrollArea.Autosize, { maxHeight: 600 }, /* @__PURE__ */ React__namespace.createElement(core.Table, { horizontalSpacing: 0, verticalSpacing: 0, sx: { minWidth: 700 } }, /* @__PURE__ */ React__namespace.createElement("tbody", null, rows)));
794
794
  }
795
795
 
796
- const useStyles$k = core.createStyles((theme) => ({
796
+ const useStyles$l = core.createStyles((theme) => ({
797
797
  title: {
798
798
  fontSize: 34,
799
799
  fontWeight: 900,
@@ -806,7 +806,7 @@ const useStyles$k = core.createStyles((theme) => ({
806
806
  }
807
807
  }));
808
808
  const Badges = (props) => {
809
- const { classes } = useStyles$k();
809
+ const { classes } = useStyles$l();
810
810
  return /* @__PURE__ */ React__namespace.createElement(core.Container, { size: "lg", py: "xl" }, /* @__PURE__ */ React__namespace.createElement(core.Stack, { spacing: "md" }, /* @__PURE__ */ React__namespace.createElement(core.Grid, null, /* @__PURE__ */ React__namespace.createElement(core.Grid.Col, { sm: "auto" }, /* @__PURE__ */ React__namespace.createElement(core.Badge, { variant: "filled", size: "lg" }, "Badges"), /* @__PURE__ */ React__namespace.createElement(core.Title, { order: 2, className: classes.title, mt: "md" }, "Badges and micro-credentials"), /* @__PURE__ */ React__namespace.createElement(core.Text, { color: "dimmed", className: classes.description, mt: "sm" }, "Key milestones that reflect skill development, micro-credentials, or academic progress"))), /* @__PURE__ */ React__namespace.createElement(
811
811
  core.Autocomplete,
812
812
  {
@@ -1087,7 +1087,7 @@ var __spreadValues$7 = (a, b) => {
1087
1087
  return a;
1088
1088
  };
1089
1089
  var __spreadProps$4 = (a, b) => __defProps$4(a, __getOwnPropDescs$4(b));
1090
- const useStyles$j = core.createStyles((theme) => ({
1090
+ const useStyles$k = core.createStyles((theme) => ({
1091
1091
  title: {
1092
1092
  fontSize: 34,
1093
1093
  fontWeight: 900,
@@ -1120,7 +1120,7 @@ const useStyles$j = core.createStyles((theme) => ({
1120
1120
  }
1121
1121
  }));
1122
1122
  const Class = (props) => {
1123
- const { classes } = useStyles$j();
1123
+ const { classes } = useStyles$k();
1124
1124
  const form$1 = form.useForm({
1125
1125
  initialValues: {
1126
1126
  classId: "",
@@ -1221,7 +1221,7 @@ const Class = (props) => {
1221
1221
  ))))));
1222
1222
  };
1223
1223
  const DropzoneButton$1 = (props) => {
1224
- const { classes, theme } = useStyles$j();
1224
+ const { classes, theme } = useStyles$k();
1225
1225
  const openRef = React__namespace.useRef(null);
1226
1226
  const [loading, setLoading] = React__namespace.useState(false);
1227
1227
  const onDrop = React__namespace.useCallback((acceptedFiles) => {
@@ -1267,7 +1267,63 @@ const DropzoneButton$1 = (props) => {
1267
1267
  } }, "Select file"));
1268
1268
  };
1269
1269
 
1270
+ function useSortableData(items, config = { key: "", direction: null }) {
1271
+ const [sortConfig, setSortConfig] = React.useState(config);
1272
+ const sortedItems = React.useMemo(() => {
1273
+ let sortableItems = [...items];
1274
+ if (sortConfig.direction !== null) {
1275
+ sortableItems.sort((a, b) => {
1276
+ const aValue = a[sortConfig.key];
1277
+ const bValue = b[sortConfig.key];
1278
+ if (aValue === bValue)
1279
+ return 0;
1280
+ if (aValue === null || aValue === void 0)
1281
+ return 1;
1282
+ if (bValue === null || bValue === void 0)
1283
+ return -1;
1284
+ if (typeof aValue === "number" && typeof bValue === "number") {
1285
+ return sortConfig.direction === "asc" ? aValue - bValue : bValue - aValue;
1286
+ }
1287
+ if (typeof aValue === "boolean" && typeof bValue === "boolean") {
1288
+ return sortConfig.direction === "asc" ? aValue === bValue ? 0 : aValue ? 1 : -1 : aValue === bValue ? 0 : aValue ? -1 : 1;
1289
+ }
1290
+ if (aValue instanceof Date && bValue instanceof Date) {
1291
+ return sortConfig.direction === "asc" ? aValue.getTime() - bValue.getTime() : bValue.getTime() - aValue.getTime();
1292
+ }
1293
+ return sortConfig.direction === "asc" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
1294
+ });
1295
+ }
1296
+ return sortableItems;
1297
+ }, [items, sortConfig]);
1298
+ const requestSort = (key) => {
1299
+ let direction = "asc";
1300
+ if (sortConfig.key === key && sortConfig.direction === "asc") {
1301
+ direction = "desc";
1302
+ } else if (sortConfig.key === key && sortConfig.direction === "desc") {
1303
+ direction = null;
1304
+ }
1305
+ setSortConfig({ key, direction });
1306
+ };
1307
+ return { items: sortedItems, requestSort, sortConfig };
1308
+ }
1309
+
1310
+ const useStyles$j = core.createStyles((theme) => ({
1311
+ th: { padding: "0 !important" },
1312
+ control: {
1313
+ width: "100%",
1314
+ padding: `${theme.spacing.xs}px ${theme.spacing.md}px`,
1315
+ "&:hover": {
1316
+ backgroundColor: theme.colorScheme === "dark" ? theme.colors.dark[6] : theme.colors.gray[0]
1317
+ }
1318
+ }
1319
+ }));
1320
+ function Th({ children, reversed, sorted, onSort }) {
1321
+ const { classes } = useStyles$j();
1322
+ const Icon = sorted ? reversed ? icons.IconChevronUp : icons.IconChevronDown : icons.IconSelector;
1323
+ return /* @__PURE__ */ React__namespace.createElement("th", { className: classes.th }, /* @__PURE__ */ React__namespace.createElement(core.UnstyledButton, { onClick: onSort, className: classes.control }, /* @__PURE__ */ React__namespace.createElement(core.Group, { position: "apart" }, /* @__PURE__ */ React__namespace.createElement(core.Text, { weight: 500, size: "sm" }, children), /* @__PURE__ */ React__namespace.createElement(core.Center, null, /* @__PURE__ */ React__namespace.createElement(Icon, { size: 14, stroke: 1.5 })))));
1324
+ }
1270
1325
  function Table$a(props) {
1326
+ const { items: sortedItems, requestSort, sortConfig } = useSortableData(props.items);
1271
1327
  if (props.items.length === 0) {
1272
1328
  return /* @__PURE__ */ React__namespace.createElement(
1273
1329
  PlaceholderBanner,
@@ -1287,8 +1343,32 @@ function Table$a(props) {
1287
1343
  confirmProps: { color: "red" },
1288
1344
  onConfirm: () => props.onDeleteClass(group)
1289
1345
  });
1290
- const rows = props.items.map((row) => /* @__PURE__ */ React__namespace.createElement("tr", { key: row.classId }, /* @__PURE__ */ React__namespace.createElement("td", null, /* @__PURE__ */ React__namespace.createElement(core.UnstyledButton, { component: reactRouterDom.Link, to: row.href }, /* @__PURE__ */ React__namespace.createElement(core.Text, { size: 14 }, row.name))), /* @__PURE__ */ React__namespace.createElement("td", null, /* @__PURE__ */ React__namespace.createElement(core.Text, { size: 14 }, row.description)), /* @__PURE__ */ React__namespace.createElement("td", null, /* @__PURE__ */ React__namespace.createElement(core.Text, { size: 14 }, row.numberOfStudents || 0)), /* @__PURE__ */ React__namespace.createElement("td", null, /* @__PURE__ */ React__namespace.createElement(core.Group, { noWrap: true, spacing: 0, position: "right" }, /* @__PURE__ */ React__namespace.createElement(core.ActionIcon, { color: "red" }, /* @__PURE__ */ React__namespace.createElement(icons.IconTrash, { onClick: () => openDeleteModal(row), size: 16, stroke: 1.5 }))))));
1291
- return /* @__PURE__ */ React__namespace.createElement(core.ScrollArea.Autosize, { maxHeight: 600 }, /* @__PURE__ */ React__namespace.createElement(core.Table, { verticalSpacing: 20, sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React__namespace.createElement("thead", null, /* @__PURE__ */ React__namespace.createElement("tr", null, /* @__PURE__ */ React__namespace.createElement("th", null, "Class Name"), /* @__PURE__ */ React__namespace.createElement("th", null, "Description"), /* @__PURE__ */ React__namespace.createElement("th", null, "# of Students"), /* @__PURE__ */ React__namespace.createElement("th", null))), /* @__PURE__ */ React__namespace.createElement("tbody", null, rows)));
1346
+ const rows = sortedItems.map((row) => /* @__PURE__ */ React__namespace.createElement("tr", { key: row.classId }, /* @__PURE__ */ React__namespace.createElement("td", null, /* @__PURE__ */ React__namespace.createElement(core.UnstyledButton, { component: reactRouterDom.Link, to: row.href }, /* @__PURE__ */ React__namespace.createElement(core.Text, { size: 14 }, row.name))), /* @__PURE__ */ React__namespace.createElement("td", null, /* @__PURE__ */ React__namespace.createElement(core.Text, { size: 14 }, row.description)), /* @__PURE__ */ React__namespace.createElement("td", null, /* @__PURE__ */ React__namespace.createElement(core.Text, { size: 14 }, row.numberOfStudents || 0)), /* @__PURE__ */ React__namespace.createElement("td", null, /* @__PURE__ */ React__namespace.createElement(core.Group, { noWrap: true, spacing: 0, position: "right" }, /* @__PURE__ */ React__namespace.createElement(core.ActionIcon, { color: "red" }, /* @__PURE__ */ React__namespace.createElement(icons.IconTrash, { onClick: () => openDeleteModal(row), size: 16, stroke: 1.5 }))))));
1347
+ return /* @__PURE__ */ React__namespace.createElement(core.ScrollArea.Autosize, { maxHeight: 600 }, /* @__PURE__ */ React__namespace.createElement(core.Table, { verticalSpacing: 20, sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React__namespace.createElement("thead", null, /* @__PURE__ */ React__namespace.createElement("tr", null, /* @__PURE__ */ React__namespace.createElement(
1348
+ Th,
1349
+ {
1350
+ sorted: sortConfig.key === "name",
1351
+ reversed: sortConfig.direction === "desc",
1352
+ onSort: () => requestSort("name")
1353
+ },
1354
+ "Class Name"
1355
+ ), /* @__PURE__ */ React__namespace.createElement(
1356
+ Th,
1357
+ {
1358
+ sorted: sortConfig.key === "description",
1359
+ reversed: sortConfig.direction === "desc",
1360
+ onSort: () => requestSort("description")
1361
+ },
1362
+ "Description"
1363
+ ), /* @__PURE__ */ React__namespace.createElement(
1364
+ Th,
1365
+ {
1366
+ sorted: sortConfig.key === "numberOfStudents",
1367
+ reversed: sortConfig.direction === "desc",
1368
+ onSort: () => requestSort("numberOfStudents")
1369
+ },
1370
+ "# of Students"
1371
+ ), /* @__PURE__ */ React__namespace.createElement("th", null))), /* @__PURE__ */ React__namespace.createElement("tbody", null, rows)));
1292
1372
  }
1293
1373
 
1294
1374
  var __defProp$6 = Object.defineProperty;