@local-civics/mgmt-ui 0.1.51 → 0.1.53

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.
Files changed (61) hide show
  1. package/dist/index.d.ts +486 -12
  2. package/dist/index.js +1742 -1
  3. package/dist/index.js.map +1 -0
  4. package/dist/index.mjs +1702 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/package.json +5 -3
  7. package/dist/components/banners/PlaceholderBanner/PlaceholderBanner.d.ts +0 -14
  8. package/dist/components/banners/PlaceholderBanner/component.stories.d.ts +0 -14
  9. package/dist/components/banners/TenantBanner/TenantBanner.d.ts +0 -23
  10. package/dist/components/cards/CardGradient.d.ts +0 -10
  11. package/dist/components/cards/component.stories.d.ts +0 -14
  12. package/dist/components/data/LineChart/LineChart.d.ts +0 -18
  13. package/dist/components/data/StatsGrid/StatsGrid.d.ts +0 -14
  14. package/dist/components/data/StatsGroup/StatsGroup.d.ts +0 -14
  15. package/dist/components/navigation/Navbar/Navbar.d.ts +0 -15
  16. package/dist/components/navigation/Navbar/component.stories.d.ts +0 -14
  17. package/dist/components/navigation/Tabs/Tabs.d.ts +0 -18
  18. package/dist/components/users/UserInfo/UserInfo.d.ts +0 -11
  19. package/dist/hooks/notifications.d.ts +0 -1
  20. package/dist/index.es.js +0 -1
  21. package/dist/pages/Badge/Badge.d.ts +0 -37
  22. package/dist/pages/Badge/LessonTable.d.ts +0 -31
  23. package/dist/pages/Badge/Table.d.ts +0 -33
  24. package/dist/pages/Badge/component.stories.d.ts +0 -18
  25. package/dist/pages/Badges/Badges.d.ts +0 -20
  26. package/dist/pages/Badges/Table.d.ts +0 -31
  27. package/dist/pages/Badges/component.stories.d.ts +0 -14
  28. package/dist/pages/Class/Class.d.ts +0 -27
  29. package/dist/pages/Class/Table.d.ts +0 -31
  30. package/dist/pages/Class/component.stories.d.ts +0 -18
  31. package/dist/pages/Classes/Classes.d.ts +0 -21
  32. package/dist/pages/Classes/Table.d.ts +0 -23
  33. package/dist/pages/Classes/component.stories.d.ts +0 -18
  34. package/dist/pages/Dashboard/BadgeTable.d.ts +0 -32
  35. package/dist/pages/Dashboard/Dashboard.d.ts +0 -38
  36. package/dist/pages/Dashboard/ImpactTable.d.ts +0 -28
  37. package/dist/pages/Dashboard/LessonTable.d.ts +0 -32
  38. package/dist/pages/Dashboard/ReflectionTable.d.ts +0 -30
  39. package/dist/pages/Dashboard/StudentTable.d.ts +0 -31
  40. package/dist/pages/Dashboard/component.stories.d.ts +0 -18
  41. package/dist/pages/Home/Home.d.ts +0 -25
  42. package/dist/pages/Home/component.stories.d.ts +0 -14
  43. package/dist/pages/Lesson/Lesson.d.ts +0 -38
  44. package/dist/pages/Lesson/QuestionStack.d.ts +0 -30
  45. package/dist/pages/Lesson/ReflectionTable.d.ts +0 -29
  46. package/dist/pages/Lesson/Table.d.ts +0 -25
  47. package/dist/pages/Lesson/component.stories.d.ts +0 -18
  48. package/dist/pages/Lessons/Lessons.d.ts +0 -20
  49. package/dist/pages/Lessons/Table.d.ts +0 -22
  50. package/dist/pages/Lessons/component.stories.d.ts +0 -14
  51. package/dist/pages/Student/AnswerTable.d.ts +0 -32
  52. package/dist/pages/Student/BadgeTable.d.ts +0 -31
  53. package/dist/pages/Student/ReflectionTable.d.ts +0 -32
  54. package/dist/pages/Student/Student.d.ts +0 -21
  55. package/dist/pages/Student/component.stories.d.ts +0 -18
  56. package/dist/providers/AdminProvider/AdminProvider.d.ts +0 -14
  57. package/dist/shells/App/App.d.ts +0 -21
  58. package/dist/shells/App/SwitchAccount/SwitchAccount.d.ts +0 -23
  59. package/dist/shells/App/SwitchAccount/component.stories.d.ts +0 -14
  60. package/dist/shells/App/component.stories.d.ts +0 -14
  61. package/dist/utils/time.d.ts +0 -11
package/dist/index.mjs ADDED
@@ -0,0 +1,1702 @@
1
+ import { NotificationsProvider } from '@mantine/notifications';
2
+ export { showNotification, updateNotification } from '@mantine/notifications';
3
+ import { IconArrowLeft, IconCategory2, IconCheck, IconTrash, IconPlaylistAdd, IconDownload, IconX, IconCloudUpload, IconInfoCircle, IconColorSwatch, IconSwitchHorizontal, IconLogout, IconHome2, IconGauge, IconAlbum, IconLambda, IconBrandInstagram, IconBrandLinkedin, IconBrandFacebook } from '@tabler/icons';
4
+ import * as React from 'react';
5
+ import { useState } from 'react';
6
+ import { createStyles, Text, Tabs as Tabs$1, Title, Image, UnstyledButton, Group, Avatar, Badge as Badge$1, ScrollArea, Table as Table$g, Container, Stack as Stack$1, Grid, ActionIcon, Button, LoadingOverlay, Select, Autocomplete, Drawer, Divider, TextInput, Tooltip, Paper, ThemeIcon, Card, Overlay, createEmotionCache, MantineProvider, Modal, Navbar as Navbar$1, Center, AppShell, Loader } from '@mantine/core';
7
+ import { Dropzone, MIME_TYPES } from '@mantine/dropzone';
8
+ import { useForm } from '@mantine/form';
9
+ import * as papa from 'papaparse';
10
+ import { openConfirmModal, ModalsProvider } from '@mantine/modals';
11
+ import { Chart } from 'react-charts';
12
+
13
+ const useStyles$c = createStyles((theme) => ({
14
+ root: {
15
+ display: "flex",
16
+ backgroundImage: `linear-gradient(-60deg, ${theme.colors[theme.primaryColor][4]} 0%, ${theme.colors[theme.primaryColor][7]} 100%)`,
17
+ padding: theme.spacing.xl * 1.5,
18
+ borderRadius: theme.radius.md,
19
+ [theme.fn.smallerThan("sm")]: {
20
+ flexDirection: "column"
21
+ }
22
+ },
23
+ title: {
24
+ color: theme.white,
25
+ textTransform: "uppercase",
26
+ fontWeight: 700,
27
+ fontSize: theme.fontSizes.sm
28
+ },
29
+ count: {
30
+ color: theme.white,
31
+ fontSize: 32,
32
+ lineHeight: 1,
33
+ fontWeight: 700,
34
+ marginBottom: theme.spacing.md,
35
+ fontFamily: `Greycliff CF, ${theme.fontFamily}`
36
+ },
37
+ description: {
38
+ color: theme.colors[theme.primaryColor][0],
39
+ fontSize: theme.fontSizes.sm,
40
+ marginTop: 5
41
+ },
42
+ stat: {
43
+ flex: 1,
44
+ "& + &": {
45
+ paddingLeft: theme.spacing.xl,
46
+ marginLeft: theme.spacing.xl,
47
+ borderLeft: `1px solid ${theme.colors[theme.primaryColor][3]}`,
48
+ [theme.fn.smallerThan("sm")]: {
49
+ paddingLeft: 0,
50
+ marginLeft: 0,
51
+ borderLeft: 0,
52
+ paddingTop: theme.spacing.xl,
53
+ marginTop: theme.spacing.xl,
54
+ borderTop: `1px solid ${theme.colors[theme.primaryColor][3]}`
55
+ }
56
+ }
57
+ }
58
+ }));
59
+ const StatsGroup = ({ data }) => {
60
+ const { classes } = useStyles$c();
61
+ const stats = data.map((stat) => {
62
+ const value = (() => {
63
+ if (stat.unit === "%") {
64
+ return Math.round((stat.value + Number.EPSILON) * 100);
65
+ }
66
+ return stat.value;
67
+ })();
68
+ return /* @__PURE__ */ React.createElement("div", { key: stat.title, className: classes.stat }, /* @__PURE__ */ React.createElement(Text, { className: classes.count }, value.toLocaleString(), stat.unit), /* @__PURE__ */ React.createElement(Text, { className: classes.title }, stat.title));
69
+ });
70
+ return /* @__PURE__ */ React.createElement("div", { className: classes.root }, stats);
71
+ };
72
+
73
+ const Tabs = (props) => {
74
+ var _a;
75
+ const tabs = (_a = props.data) == null ? void 0 : _a.map((item) => /* @__PURE__ */ React.createElement(Tabs$1.Tab, { key: item.value, value: item.value }, item.label || item.value));
76
+ return /* @__PURE__ */ React.createElement(Tabs$1, { value: props.value, onTabChange: props.onChange }, /* @__PURE__ */ React.createElement(Tabs$1.List, null, tabs));
77
+ };
78
+
79
+ const useStyles$b = createStyles((theme) => ({
80
+ wrapper: {
81
+ display: "flex",
82
+ alignItems: "center",
83
+ padding: theme.spacing.xl * 2,
84
+ borderRadius: theme.radius.md,
85
+ backgroundColor: theme.colorScheme === "dark" ? theme.colors.dark[8] : theme.white,
86
+ border: `1px solid ${theme.colorScheme === "dark" ? theme.colors.dark[8] : theme.colors.gray[3]}`,
87
+ [`@media (max-width: ${theme.breakpoints.sm}px)`]: {
88
+ flexDirection: "column-reverse",
89
+ padding: theme.spacing.xl
90
+ }
91
+ },
92
+ image: {
93
+ maxWidth: "40%",
94
+ [`@media (max-width: ${theme.breakpoints.sm}px)`]: {
95
+ maxWidth: "100%"
96
+ }
97
+ },
98
+ body: {
99
+ paddingRight: theme.spacing.xl * 4,
100
+ [`@media (max-width: ${theme.breakpoints.sm}px)`]: {
101
+ paddingRight: 0,
102
+ marginTop: theme.spacing.xl
103
+ }
104
+ },
105
+ title: {
106
+ color: theme.colorScheme === "dark" ? theme.white : theme.black,
107
+ fontFamily: `Greycliff CF, ${theme.fontFamily}`,
108
+ lineHeight: 1,
109
+ marginBottom: theme.spacing.md
110
+ },
111
+ controls: {
112
+ display: "flex",
113
+ marginTop: theme.spacing.xl
114
+ },
115
+ inputWrapper: {
116
+ width: "100%",
117
+ flex: "1"
118
+ },
119
+ input: {
120
+ borderTopRightRadius: 0,
121
+ borderBottomRightRadius: 0,
122
+ borderRight: 0
123
+ },
124
+ control: {
125
+ borderTopLeftRadius: 0,
126
+ borderBottomLeftRadius: 0
127
+ }
128
+ }));
129
+ const PlaceholderBanner = (props) => {
130
+ const { classes } = useStyles$b();
131
+ const title = props.title || "Nothing to display";
132
+ const description = props.description || "We don't have anything to show you here just yet. Add data, check back later, or adjust your search.";
133
+ 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 }));
134
+ };
135
+
136
+ function Table$f(props) {
137
+ if (props.items.length === 0) {
138
+ return /* @__PURE__ */ React.createElement(
139
+ PlaceholderBanner,
140
+ {
141
+ title: "No badges to display",
142
+ description: "There has not been any badge progress just yet.",
143
+ loading: props.loading,
144
+ icon: "badges"
145
+ }
146
+ );
147
+ }
148
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.name }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(UnstyledButton, { onClick: () => props.onClick && props.onClick(row) }, /* @__PURE__ */ React.createElement(Group, { spacing: "sm" }, /* @__PURE__ */ React.createElement(Avatar, { size: 40, src: row.avatar, radius: 40 }), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Text, { size: "sm", weight: 500 }, row.name), /* @__PURE__ */ React.createElement(Text, { size: "xs", color: "dimmed" }, row.email))))), /* @__PURE__ */ React.createElement("td", null, !!row.isComplete && /* @__PURE__ */ React.createElement(Badge$1, { variant: "filled" }, "Complete"), !row.isComplete && /* @__PURE__ */ React.createElement(Badge$1, { color: "red", variant: "filled" }, "Incomplete"))));
149
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: "sm", sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Student Name"), /* @__PURE__ */ React.createElement("th", null, "Badge Status"))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
150
+ }
151
+
152
+ function Table$e(props) {
153
+ if (props.items.length === 0) {
154
+ return /* @__PURE__ */ React.createElement(
155
+ PlaceholderBanner,
156
+ {
157
+ title: "No lessons to display",
158
+ description: "There are not lessons in badge.",
159
+ loading: props.loading,
160
+ icon: "badges"
161
+ }
162
+ );
163
+ }
164
+ const rows = props.items.map((row) => {
165
+ const percentageCompletion = Math.round((row.percentageCompletion + Number.EPSILON) * 100);
166
+ return /* @__PURE__ */ React.createElement("tr", { key: row.lessonName }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(UnstyledButton, { onClick: () => props.onClick && props.onClick(row) }, row.lessonName)), /* @__PURE__ */ React.createElement("td", null, percentageCompletion, "%"));
167
+ });
168
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: "sm", sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Lesson Name"), /* @__PURE__ */ React.createElement("th", null, "Lesson Completion"))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
169
+ }
170
+
171
+ const useStyles$a = createStyles((theme) => ({
172
+ title: {
173
+ fontSize: 34,
174
+ fontWeight: 900,
175
+ [theme.fn.smallerThan("sm")]: {
176
+ fontSize: 24
177
+ }
178
+ },
179
+ description: {
180
+ maxWidth: 600
181
+ }
182
+ }));
183
+ const Badge = (props) => {
184
+ const { classes } = useStyles$a();
185
+ const [tab, setTab] = useState("lessons");
186
+ const numberOfStudents = props.students.length;
187
+ const percentageOfBadgesEarned = numberOfStudents > 0 ? props.students.filter((u) => u.isComplete).length / numberOfStudents : 0;
188
+ return /* @__PURE__ */ React.createElement(Container, { size: "lg", py: "xl" }, /* @__PURE__ */ React.createElement(Stack$1, { spacing: "md" }, /* @__PURE__ */ React.createElement(Grid, null, /* @__PURE__ */ React.createElement(Grid.Col, { sm: "auto" }, /* @__PURE__ */ React.createElement(
189
+ Badge$1,
190
+ {
191
+ variant: "filled",
192
+ leftSection: /* @__PURE__ */ React.createElement(ActionIcon, { onClick: props.onBackClick, color: "blue", size: "xs", radius: "xl", variant: "filled" }, /* @__PURE__ */ React.createElement(IconArrowLeft, { size: 14 })),
193
+ size: "lg"
194
+ },
195
+ "Badges"
196
+ ), /* @__PURE__ */ React.createElement(Group, null, /* @__PURE__ */ React.createElement(Stack$1, { spacing: 0 }, /* @__PURE__ */ React.createElement(Title, { order: 2, className: classes.title, mt: "md" }, props.displayName || "Badge"), /* @__PURE__ */ React.createElement(Text, { color: "dimmed", className: classes.description, mt: "sm" }, props.description || "No description")), /* @__PURE__ */ React.createElement(Stack$1, { ml: "auto" }, /* @__PURE__ */ React.createElement(
197
+ Button,
198
+ {
199
+ variant: "gradient",
200
+ onClick: props.onPreviewClick
201
+ },
202
+ "Preview"
203
+ ))))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(LoadingOverlay, { visible: props.loading, overlayBlur: 2 }), /* @__PURE__ */ React.createElement(Stack$1, null, /* @__PURE__ */ React.createElement(StatsGroup, { data: [
204
+ {
205
+ title: "BADGE COMPLETION",
206
+ value: percentageOfBadgesEarned,
207
+ unit: "%"
208
+ }
209
+ ] }), /* @__PURE__ */ React.createElement(
210
+ Select,
211
+ {
212
+ clearable: true,
213
+ clearButtonLabel: "Clear class selection",
214
+ size: "sm",
215
+ placeholder: "Select a class",
216
+ nothingFound: "No options",
217
+ value: props.classId,
218
+ onChange: props.onClassChange,
219
+ icon: /* @__PURE__ */ React.createElement(IconCategory2, null),
220
+ data: props.classes.map((g) => {
221
+ return { value: g.classId, label: g.name };
222
+ })
223
+ }
224
+ ), /* @__PURE__ */ React.createElement(Stack$1, { spacing: 0 }, /* @__PURE__ */ React.createElement(
225
+ Tabs,
226
+ {
227
+ value: tab,
228
+ data: [
229
+ { label: "By lesson", value: "lessons" },
230
+ { label: "By student", value: "students" }
231
+ ],
232
+ onChange: setTab
233
+ }
234
+ ), tab === "lessons" && /* @__PURE__ */ React.createElement(
235
+ Table$e,
236
+ {
237
+ loading: props.loading,
238
+ items: props.lessons,
239
+ onClick: props.onLessonClick
240
+ }
241
+ ), tab === "students" && /* @__PURE__ */ React.createElement(
242
+ Table$f,
243
+ {
244
+ loading: props.loading,
245
+ items: props.students,
246
+ onClick: props.onUserClick
247
+ }
248
+ )))))));
249
+ };
250
+
251
+ function Table$d(props) {
252
+ if (props.items.length === 0) {
253
+ return /* @__PURE__ */ React.createElement(
254
+ PlaceholderBanner,
255
+ {
256
+ title: "No badges to display",
257
+ description: "We don't have any badges to show you just yet.",
258
+ loading: props.loading,
259
+ icon: "badges"
260
+ }
261
+ );
262
+ }
263
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.badgeId }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(
264
+ UnstyledButton,
265
+ {
266
+ sx: (theme) => ({
267
+ display: "block",
268
+ width: "100%",
269
+ padding: theme.spacing.md,
270
+ color: theme.colorScheme === "dark" ? theme.colors.dark[0] : theme.black,
271
+ "&:hover": {
272
+ backgroundColor: theme.colorScheme === "dark" ? theme.colors.dark[8] : theme.colors.gray[1]
273
+ }
274
+ }),
275
+ onClick: () => props.onClick && props.onClick(row)
276
+ },
277
+ /* @__PURE__ */ React.createElement(Group, null, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Text, { size: "sm", weight: 500 }, row.name), /* @__PURE__ */ React.createElement(Text, { size: "xs", color: "dimmed" }, row.description)))
278
+ ))));
279
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { horizontalSpacing: 0, verticalSpacing: 0, sx: { minWidth: 700 } }, /* @__PURE__ */ React.createElement("tbody", null, rows)));
280
+ }
281
+
282
+ const useStyles$9 = createStyles((theme) => ({
283
+ title: {
284
+ fontSize: 34,
285
+ fontWeight: 900,
286
+ [theme.fn.smallerThan("sm")]: {
287
+ fontSize: 24
288
+ }
289
+ },
290
+ description: {
291
+ maxWidth: 600
292
+ }
293
+ }));
294
+ const Badges = (props) => {
295
+ const { classes } = useStyles$9();
296
+ return /* @__PURE__ */ React.createElement(Container, { size: "lg", py: "xl" }, /* @__PURE__ */ React.createElement(Stack$1, { 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" }, "Project-sized skills acquisition and standards alignment."))), /* @__PURE__ */ React.createElement(
297
+ Autocomplete,
298
+ {
299
+ placeholder: "Search for a badge that fits your needs",
300
+ data: props.badges.map((item) => item.name),
301
+ onChange: props.onAutocompleteChange
302
+ }
303
+ ), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(LoadingOverlay, { visible: props.loading, overlayBlur: 2 }), /* @__PURE__ */ React.createElement(
304
+ Table$d,
305
+ {
306
+ loading: props.loading,
307
+ items: props.badges,
308
+ onClick: props.onBadgeClick
309
+ }
310
+ ))));
311
+ };
312
+
313
+ function Table$c(props) {
314
+ if (props.items.length === 0) {
315
+ return /* @__PURE__ */ React.createElement(
316
+ PlaceholderBanner,
317
+ {
318
+ title: "No students to display",
319
+ description: "You don't have any students yet, add them and revisit.",
320
+ loading: props.loading,
321
+ icon: "groups"
322
+ }
323
+ );
324
+ }
325
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.studentName }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(UnstyledButton, { onClick: () => props.onViewProfile(row) }, row.studentName)), /* @__PURE__ */ React.createElement("td", null, row.className)));
326
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: "sm", sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Student Name"), /* @__PURE__ */ React.createElement("th", null, "Class Name"))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
327
+ }
328
+
329
+ const units = [
330
+ { unit: "year", ms: 31536e6 },
331
+ { unit: "month", ms: 2628e6 },
332
+ { unit: "day", ms: 864e5 },
333
+ { unit: "hour", ms: 36e5 },
334
+ { unit: "minute", ms: 6e4 },
335
+ { unit: "second", ms: 1e3 }
336
+ ];
337
+ const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
338
+ function relativeTimeFromDates(relative, pivot = new Date()) {
339
+ if (!relative)
340
+ return "";
341
+ const elapsed = relative.getTime() - pivot.getTime();
342
+ return relativeTimeFromElapsed(elapsed);
343
+ }
344
+ function relativeTimeFromElapsed(elapsed) {
345
+ for (const { unit, ms } of units) {
346
+ if (Math.abs(elapsed) >= ms || unit === "second") {
347
+ return rtf.format(Math.round(elapsed / ms), unit);
348
+ }
349
+ }
350
+ return "";
351
+ }
352
+
353
+ function Table$b(props) {
354
+ if (props.items.length === 0) {
355
+ return /* @__PURE__ */ React.createElement(
356
+ PlaceholderBanner,
357
+ {
358
+ title: "No reflections to display",
359
+ description: "There has not been any lesson progress just yet.",
360
+ loading: props.loading,
361
+ icon: "lessons"
362
+ }
363
+ );
364
+ }
365
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.studentName + row.lessonName }, /* @__PURE__ */ React.createElement("td", null, row.studentName), /* @__PURE__ */ React.createElement("td", null, row.lessonName), /* @__PURE__ */ React.createElement("td", null, row.reflection), /* @__PURE__ */ React.createElement("td", null, relativeTimeFromDates(new Date(row.updatedAt)))));
366
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: "sm", sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Student Name"), /* @__PURE__ */ React.createElement("th", null, "Lesson Name"), /* @__PURE__ */ React.createElement("th", null, "Reflection"), /* @__PURE__ */ React.createElement("th", null, "Updated At"))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
367
+ }
368
+
369
+ function Table$a(props) {
370
+ if (props.items.length === 0) {
371
+ return /* @__PURE__ */ React.createElement(
372
+ PlaceholderBanner,
373
+ {
374
+ title: "No impact statements to display",
375
+ description: "There are no students with impact statements yet.",
376
+ loading: props.loading,
377
+ icon: "kindergarten"
378
+ }
379
+ );
380
+ }
381
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.studentName }, /* @__PURE__ */ React.createElement("td", null, row.studentName), /* @__PURE__ */ React.createElement("td", null, row.impactStatement)));
382
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: "sm", sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Student Name"), /* @__PURE__ */ React.createElement("th", null, "Impact Statement"))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
383
+ }
384
+
385
+ function Table$9(props) {
386
+ if (props.items.length === 0) {
387
+ return /* @__PURE__ */ React.createElement(
388
+ PlaceholderBanner,
389
+ {
390
+ title: "No badges to display",
391
+ description: "We don't have any badges to show you just yet.",
392
+ loading: props.loading,
393
+ icon: "badges"
394
+ }
395
+ );
396
+ }
397
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.badgeId }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(UnstyledButton, { onClick: () => props.onClick && props.onClick(row) }, row.name)), /* @__PURE__ */ React.createElement("td", null, row.description), /* @__PURE__ */ React.createElement("td", null, Math.round((row.percentageCompletion + Number.EPSILON) * 100), "%")));
398
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: "sm", sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Badge Name"), /* @__PURE__ */ React.createElement("th", null, "Description"), /* @__PURE__ */ React.createElement("th", null, "Completion"))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
399
+ }
400
+
401
+ function Table$8(props) {
402
+ if (props.items.length === 0) {
403
+ return /* @__PURE__ */ React.createElement(
404
+ PlaceholderBanner,
405
+ {
406
+ title: "No lessons to display",
407
+ description: "We don't have any lessons to show you just yet.",
408
+ loading: props.loading,
409
+ icon: "lessons"
410
+ }
411
+ );
412
+ }
413
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.lessonId }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(UnstyledButton, { onClick: () => props.onClick && props.onClick(row) }, row.name)), /* @__PURE__ */ React.createElement("td", null, row.description), /* @__PURE__ */ React.createElement("td", null, Math.round((row.percentageCompletion + Number.EPSILON) * 100), "%")));
414
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: "sm", sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Lesson Name"), /* @__PURE__ */ React.createElement("th", null, "Description"), /* @__PURE__ */ React.createElement("th", null, "Completion"))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
415
+ }
416
+
417
+ const Dashboard = (props) => {
418
+ const [tab, setTab] = useState("students");
419
+ return /* @__PURE__ */ React.createElement(Container, { size: "lg", py: "xl" }, /* @__PURE__ */ React.createElement(Stack$1, null, /* @__PURE__ */ React.createElement(Stack$1, { spacing: 0 }, /* @__PURE__ */ React.createElement(Title, { size: "h3" }, "Dashboard"), /* @__PURE__ */ React.createElement(Text, { color: "dimmed", size: "sm", mt: "md" }, "Fast-track learning for your students.")), /* @__PURE__ */ React.createElement(Stack$1, null, /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(LoadingOverlay, { visible: props.loading, overlayBlur: 2 }), /* @__PURE__ */ React.createElement(Stack$1, { spacing: "sm" }, /* @__PURE__ */ React.createElement(StatsGroup, { data: [
420
+ {
421
+ title: "# OF STUDENTS",
422
+ value: props.students.length
423
+ },
424
+ {
425
+ title: "ACCOUNT CREATION",
426
+ value: props.percentageOfAccountsCreated,
427
+ unit: "%"
428
+ },
429
+ {
430
+ title: "BADGE COMPLETION",
431
+ value: props.percentageOfBadgesEarned,
432
+ unit: "%"
433
+ },
434
+ {
435
+ title: "LESSON COMPLETION",
436
+ value: props.percentageOfLessonsCompleted,
437
+ unit: "%"
438
+ }
439
+ ] }), /* @__PURE__ */ React.createElement(
440
+ Select,
441
+ {
442
+ clearable: true,
443
+ clearButtonLabel: "Clear class selection",
444
+ size: "sm",
445
+ placeholder: "Select a class",
446
+ nothingFound: "No options",
447
+ value: props.classId,
448
+ onChange: props.onClassChange,
449
+ icon: /* @__PURE__ */ React.createElement(IconCategory2, null),
450
+ data: props.classes.map((g) => {
451
+ return { value: g.classId, label: g.name };
452
+ })
453
+ }
454
+ ), /* @__PURE__ */ React.createElement(Stack$1, { spacing: 0 }, /* @__PURE__ */ React.createElement(
455
+ Tabs,
456
+ {
457
+ value: tab,
458
+ data: [
459
+ { label: "My students", value: "students" },
460
+ { label: "Impact statements", value: "impact" },
461
+ { label: "Reflections", value: "reflections" },
462
+ { label: "Badges", value: "badges" },
463
+ { label: "Lessons", value: "lessons" }
464
+ ],
465
+ onChange: setTab
466
+ }
467
+ ), tab === "impact" && /* @__PURE__ */ React.createElement(
468
+ Table$a,
469
+ {
470
+ loading: props.loading,
471
+ items: props.impacts
472
+ }
473
+ ), tab === "reflections" && /* @__PURE__ */ React.createElement(
474
+ Table$b,
475
+ {
476
+ loading: props.loading,
477
+ items: props.reflections
478
+ }
479
+ ), tab === "badges" && /* @__PURE__ */ React.createElement(
480
+ Table$9,
481
+ {
482
+ loading: props.loading,
483
+ items: props.badges,
484
+ onClick: props.onBadgeClick
485
+ }
486
+ ), tab === "lessons" && /* @__PURE__ */ React.createElement(
487
+ Table$8,
488
+ {
489
+ loading: props.loading,
490
+ items: props.lessons,
491
+ onClick: props.onLessonClick
492
+ }
493
+ ), tab === "students" && /* @__PURE__ */ React.createElement(
494
+ Table$c,
495
+ {
496
+ loading: props.loading,
497
+ items: props.students,
498
+ onViewProfile: props.onViewStudentProfile
499
+ }
500
+ )))))));
501
+ };
502
+
503
+ function Table$7(props) {
504
+ if (props.items.length === 0) {
505
+ return /* @__PURE__ */ React.createElement(
506
+ PlaceholderBanner,
507
+ {
508
+ title: "No students to display",
509
+ description: "You have not rostered any students yet.",
510
+ loading: props.loading,
511
+ icon: "groups"
512
+ }
513
+ );
514
+ }
515
+ const openDeleteModal = (student) => openConfirmModal({
516
+ title: `Delete "${student.givenName && student.familyName ? `${student.givenName} ${student.familyName}` : student.email}"?`,
517
+ centered: true,
518
+ children: /* @__PURE__ */ React.createElement(Text, { size: "sm" }, "Are you sure you want to delete this person? This action is destructive and you will have to contact support to restore your data."),
519
+ labels: { confirm: "Delete", cancel: "No don't delete them" },
520
+ confirmProps: { color: "red" },
521
+ onConfirm: () => props.onDelete && props.onDelete(student)
522
+ });
523
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.email }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(UnstyledButton, { onClick: () => props.onViewProfile && props.onViewProfile(row) }, /* @__PURE__ */ React.createElement(Group, { spacing: "sm" }, /* @__PURE__ */ React.createElement(Avatar, { size: 40, src: row.avatar, radius: 40 }), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Text, { size: "sm", weight: 500 }, row.givenName && row.familyName ? `${row.givenName} ${row.familyName}` : row.email), /* @__PURE__ */ React.createElement(Text, { size: "xs", color: "dimmed" }, row.email))))), /* @__PURE__ */ React.createElement("td", null, row.badgesEarned), /* @__PURE__ */ React.createElement("td", null, row.lessonsCompleted), /* @__PURE__ */ React.createElement("td", null, row.hasAccount && /* @__PURE__ */ React.createElement(IconCheck, { color: "green" })), /* @__PURE__ */ React.createElement("td", null, row.lastActivity ? relativeTimeFromDates(row.lastActivity) : ""), /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(Group, { noWrap: true, spacing: 0, position: "right" }, !row.readonly && !!props.onDelete && /* @__PURE__ */ React.createElement(ActionIcon, { color: "red" }, /* @__PURE__ */ React.createElement(IconTrash, { onClick: () => openDeleteModal(row), size: 16, stroke: 1.5 }))))));
524
+ return /* @__PURE__ */ React.createElement(ScrollArea, null, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: 20, sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Student Name"), /* @__PURE__ */ React.createElement("th", null, "Badges Earned"), /* @__PURE__ */ React.createElement("th", null, "Lessons Completed"), /* @__PURE__ */ React.createElement("th", null, "Account Created?"), /* @__PURE__ */ React.createElement("th", null, "Last Active"), /* @__PURE__ */ React.createElement("th", null))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
525
+ }
526
+
527
+ var __defProp$3 = Object.defineProperty;
528
+ var __defProps = Object.defineProperties;
529
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
530
+ var __getOwnPropSymbols$3 = Object.getOwnPropertySymbols;
531
+ var __hasOwnProp$3 = Object.prototype.hasOwnProperty;
532
+ var __propIsEnum$3 = Object.prototype.propertyIsEnumerable;
533
+ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
534
+ var __spreadValues$3 = (a, b) => {
535
+ for (var prop in b || (b = {}))
536
+ if (__hasOwnProp$3.call(b, prop))
537
+ __defNormalProp$3(a, prop, b[prop]);
538
+ if (__getOwnPropSymbols$3)
539
+ for (var prop of __getOwnPropSymbols$3(b)) {
540
+ if (__propIsEnum$3.call(b, prop))
541
+ __defNormalProp$3(a, prop, b[prop]);
542
+ }
543
+ return a;
544
+ };
545
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
546
+ const useStyles$8 = createStyles((theme) => ({
547
+ title: {
548
+ fontSize: 34,
549
+ fontWeight: 900,
550
+ [theme.fn.smallerThan("sm")]: {
551
+ fontSize: 24
552
+ }
553
+ },
554
+ description: {
555
+ maxWidth: 600
556
+ },
557
+ wrapper: {
558
+ position: "relative",
559
+ marginBottom: 30
560
+ },
561
+ dropzone: {
562
+ borderWidth: 1,
563
+ paddingBottom: 50
564
+ },
565
+ icon: {
566
+ color: theme.colorScheme === "dark" ? theme.colors.dark[3] : theme.colors.gray[4]
567
+ },
568
+ control: {
569
+ position: "absolute",
570
+ width: 250,
571
+ left: "calc(50% - 125px)",
572
+ bottom: -20
573
+ }
574
+ }));
575
+ const Class = (props) => {
576
+ const { classes } = useStyles$8();
577
+ const form = useForm({
578
+ initialValues: {
579
+ classId: "",
580
+ studentId: "",
581
+ email: "",
582
+ givenName: "",
583
+ familyName: "",
584
+ avatar: "",
585
+ role: "",
586
+ readonly: false,
587
+ lastActivity: null,
588
+ hasAccount: false,
589
+ lessonsCompleted: 0,
590
+ badgesEarned: 0
591
+ },
592
+ validate: {
593
+ email: (value) => /^\S+@\S+$/.test(value) && props.students.filter((u) => u.email === value).length === 0 ? null : "Invalid email"
594
+ }
595
+ });
596
+ const [opened, setOpened] = useState(false);
597
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
598
+ Drawer,
599
+ {
600
+ opened,
601
+ onClose: () => setOpened(false),
602
+ title: /* @__PURE__ */ React.createElement(Title, { size: "h5" }, "Add students"),
603
+ padding: "xl",
604
+ size: "xl"
605
+ },
606
+ /* @__PURE__ */ React.createElement(Stack$1, { spacing: "md" }, /* @__PURE__ */ React.createElement(DropzoneButton, __spreadProps(__spreadValues$3({}, props), { close: () => setOpened(false) })), /* @__PURE__ */ React.createElement(Divider, { label: "or", labelPosition: "center", my: "md", variant: "dashed" }), /* @__PURE__ */ React.createElement("form", { onSubmit: form.onSubmit(() => {
607
+ const values = form.values;
608
+ form.reset();
609
+ setOpened(false);
610
+ props.onCreateStudents && props.onCreateStudents([values]);
611
+ }) }, /* @__PURE__ */ React.createElement(Stack$1, null, /* @__PURE__ */ React.createElement(
612
+ TextInput,
613
+ __spreadValues$3({
614
+ withAsterisk: true,
615
+ label: "Email",
616
+ placeholder: "Email"
617
+ }, form.getInputProps("email"))
618
+ ), /* @__PURE__ */ React.createElement(Group, { grow: true }, /* @__PURE__ */ React.createElement(
619
+ TextInput,
620
+ __spreadValues$3({
621
+ label: "Given name",
622
+ placeholder: "Given name"
623
+ }, form.getInputProps("givenName"))
624
+ ), /* @__PURE__ */ React.createElement(
625
+ TextInput,
626
+ __spreadValues$3({
627
+ label: "Family name",
628
+ placeholder: "Family name"
629
+ }, form.getInputProps("familyName"))
630
+ )), /* @__PURE__ */ React.createElement(Button, { type: "submit", fullWidth: true, mt: "md" }, "Submit"))))
631
+ ), /* @__PURE__ */ React.createElement(Container, { size: "lg", py: "xl" }, /* @__PURE__ */ React.createElement(Stack$1, { spacing: "md" }, /* @__PURE__ */ React.createElement(Grid, null, /* @__PURE__ */ React.createElement(Grid.Col, { sm: "auto" }, /* @__PURE__ */ React.createElement(
632
+ Badge$1,
633
+ {
634
+ variant: "filled",
635
+ leftSection: /* @__PURE__ */ React.createElement(ActionIcon, { onClick: props.onBackClick, color: "blue", size: "xs", radius: "xl", variant: "filled" }, /* @__PURE__ */ React.createElement(IconArrowLeft, { size: 14 })),
636
+ size: "lg"
637
+ },
638
+ "Classes"
639
+ ), /* @__PURE__ */ React.createElement(Title, { order: 2, className: classes.title, mt: "md" }, props.displayName || "Class"), /* @__PURE__ */ React.createElement(Text, { color: "dimmed", className: classes.description, mt: "sm" }, props.description || "No description")), /* @__PURE__ */ React.createElement(Grid.Col, { sm: "content" }, !props.loading && /* @__PURE__ */ React.createElement(
640
+ Button,
641
+ {
642
+ onClick: () => setOpened(true),
643
+ leftIcon: /* @__PURE__ */ React.createElement(IconPlaylistAdd, { size: 14 })
644
+ },
645
+ "Add students"
646
+ ))), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(LoadingOverlay, { visible: props.loading, overlayBlur: 2 }), /* @__PURE__ */ React.createElement(Stack$1, { spacing: "sm" }, /* @__PURE__ */ React.createElement(StatsGroup, { data: [
647
+ {
648
+ title: "# OF STUDENTS",
649
+ value: props.students.length
650
+ },
651
+ {
652
+ title: "ACCOUNT CREATION",
653
+ value: props.percentageOfAccountsCreated,
654
+ unit: "%"
655
+ },
656
+ {
657
+ title: "BADGE COMPLETION",
658
+ value: props.percentageOfBadgesEarned,
659
+ unit: "%"
660
+ },
661
+ {
662
+ title: "LESSON COMPLETION",
663
+ value: props.percentageOfLessonsCompleted,
664
+ unit: "%"
665
+ }
666
+ ] }), /* @__PURE__ */ React.createElement(
667
+ Table$7,
668
+ {
669
+ loading: props.loading,
670
+ items: props.students,
671
+ onDelete: props.onDeleteStudent,
672
+ onViewProfile: (student) => props.onStudentClick(student)
673
+ }
674
+ ))))));
675
+ };
676
+ const DropzoneButton = (props) => {
677
+ const { classes, theme } = useStyles$8();
678
+ const openRef = React.useRef(null);
679
+ const [loading, setLoading] = React.useState(false);
680
+ const onDrop = React.useCallback((acceptedFiles) => {
681
+ setLoading(true);
682
+ acceptedFiles.forEach((file) => {
683
+ papa.parse(file, {
684
+ download: true,
685
+ header: true,
686
+ dynamicTyping: true,
687
+ skipEmptyLines: true,
688
+ worker: true,
689
+ complete: function(results) {
690
+ const data = results.data.filter((v) => /^\S+@\S+$/.test(v.email) && props.students.filter((u) => u.email === v.email).length === 0);
691
+ data.length > 0 && props.onCreateStudents && props.onCreateStudents(data);
692
+ setLoading(false);
693
+ props.close();
694
+ }
695
+ });
696
+ });
697
+ }, []);
698
+ return /* @__PURE__ */ React.createElement("div", { className: classes.wrapper }, /* @__PURE__ */ React.createElement(
699
+ Dropzone,
700
+ {
701
+ loading,
702
+ openRef,
703
+ onDrop,
704
+ className: classes.dropzone,
705
+ radius: "md",
706
+ accept: [MIME_TYPES.csv],
707
+ maxSize: 5 * 1024 ** 2
708
+ },
709
+ /* @__PURE__ */ React.createElement("div", { style: { pointerEvents: "none" } }, /* @__PURE__ */ React.createElement(Group, { position: "center" }, /* @__PURE__ */ React.createElement(Dropzone.Accept, null, /* @__PURE__ */ React.createElement(IconDownload, { size: 50, color: theme.colors[theme.primaryColor][6], stroke: 1.5 })), /* @__PURE__ */ React.createElement(Dropzone.Reject, null, /* @__PURE__ */ React.createElement(IconX, { size: 50, color: theme.colors.red[6], stroke: 1.5 })), /* @__PURE__ */ React.createElement(Dropzone.Idle, null, /* @__PURE__ */ React.createElement(
710
+ IconCloudUpload,
711
+ {
712
+ size: 50,
713
+ color: theme.colorScheme === "dark" ? theme.colors.dark[0] : theme.black,
714
+ stroke: 1.5
715
+ }
716
+ ))), /* @__PURE__ */ React.createElement(Text, { align: "center", weight: 700, size: "lg", mt: "xl" }, /* @__PURE__ */ React.createElement(Dropzone.Accept, null, "Drop files here"), /* @__PURE__ */ React.createElement(Dropzone.Reject, null, "Csv file less than 5mb"), /* @__PURE__ */ React.createElement(Dropzone.Idle, null, "Upload multiple")), /* @__PURE__ */ React.createElement(Text, { align: "center", size: "sm", mt: "xs", color: "dimmed" }, "Drag'n'drop files here to upload. We can accept only ", /* @__PURE__ */ React.createElement("i", null, ".csv"), " files that are less than 5mb in size."))
717
+ ), /* @__PURE__ */ React.createElement(Button, { className: classes.control, size: "md", radius: "xl", onClick: () => {
718
+ var _a;
719
+ return (_a = openRef.current) == null ? void 0 : _a.call(openRef);
720
+ } }, "Select file"));
721
+ };
722
+
723
+ function Table$6(props) {
724
+ if (props.items.length === 0) {
725
+ return /* @__PURE__ */ React.createElement(
726
+ PlaceholderBanner,
727
+ {
728
+ title: "No classes to display",
729
+ description: "You don't have any classes yet. Try creating one first...",
730
+ loading: props.loading,
731
+ icon: "groups"
732
+ }
733
+ );
734
+ }
735
+ const openDeleteModal = (group) => openConfirmModal({
736
+ title: `Delete "${group.name}"?`,
737
+ centered: true,
738
+ children: /* @__PURE__ */ React.createElement(Text, { size: "sm" }, "Are you sure you want to delete this class? This action is destructive and you will have to contact support to restore your data."),
739
+ labels: { confirm: "Delete class", cancel: "No don't delete it" },
740
+ confirmProps: { color: "red" },
741
+ onConfirm: () => props.onDeleteClass(group)
742
+ });
743
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.classId }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(UnstyledButton, { onClick: () => props.onClick(row) }, /* @__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(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 }))))));
744
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 300 }, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: 20, sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Class Name"), /* @__PURE__ */ React.createElement("th", null, "Description"), /* @__PURE__ */ React.createElement("th", null))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
745
+ }
746
+
747
+ var __defProp$2 = Object.defineProperty;
748
+ var __getOwnPropSymbols$2 = Object.getOwnPropertySymbols;
749
+ var __hasOwnProp$2 = Object.prototype.hasOwnProperty;
750
+ var __propIsEnum$2 = Object.prototype.propertyIsEnumerable;
751
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
752
+ var __spreadValues$2 = (a, b) => {
753
+ for (var prop in b || (b = {}))
754
+ if (__hasOwnProp$2.call(b, prop))
755
+ __defNormalProp$2(a, prop, b[prop]);
756
+ if (__getOwnPropSymbols$2)
757
+ for (var prop of __getOwnPropSymbols$2(b)) {
758
+ if (__propIsEnum$2.call(b, prop))
759
+ __defNormalProp$2(a, prop, b[prop]);
760
+ }
761
+ return a;
762
+ };
763
+ const useStyles$7 = createStyles((theme) => ({
764
+ title: {
765
+ fontSize: 34,
766
+ fontWeight: 900,
767
+ [theme.fn.smallerThan("sm")]: {
768
+ fontSize: 24
769
+ }
770
+ },
771
+ description: {
772
+ maxWidth: 600
773
+ }
774
+ }));
775
+ const Classes = (props) => {
776
+ const { classes } = useStyles$7();
777
+ const form = useForm({
778
+ initialValues: {
779
+ classId: "",
780
+ name: "",
781
+ description: ""
782
+ },
783
+ validate: {
784
+ name: (val) => val.length <= 6 ? "Name should include at least 6 characters" : null
785
+ }
786
+ });
787
+ const [opened, setOpened] = useState(false);
788
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
789
+ Drawer,
790
+ {
791
+ opened,
792
+ onClose: () => setOpened(false),
793
+ title: /* @__PURE__ */ React.createElement(Group, { spacing: 0 }, /* @__PURE__ */ React.createElement(Title, { size: "h5" }, "Create a class"), /* @__PURE__ */ React.createElement(Tooltip, { label: "Classes settings cannot be modified once created" }, /* @__PURE__ */ React.createElement(ActionIcon, null, /* @__PURE__ */ React.createElement(IconInfoCircle, { color: "#3b82f6", size: 14 })))),
794
+ padding: "xl",
795
+ size: "xl"
796
+ },
797
+ /* @__PURE__ */ React.createElement("form", { onSubmit: form.onSubmit(() => {
798
+ const values = form.values;
799
+ form.reset();
800
+ setOpened(false);
801
+ props.onCreateClass && props.onCreateClass(values);
802
+ }) }, /* @__PURE__ */ React.createElement(Stack$1, null, /* @__PURE__ */ React.createElement(
803
+ TextInput,
804
+ __spreadValues$2({
805
+ withAsterisk: true,
806
+ label: "Name",
807
+ placeholder: "Class name"
808
+ }, form.getInputProps("name"))
809
+ ), /* @__PURE__ */ React.createElement(
810
+ TextInput,
811
+ __spreadValues$2({
812
+ label: "Description",
813
+ placeholder: "A class for my first period English students"
814
+ }, form.getInputProps("description"))
815
+ )), /* @__PURE__ */ React.createElement(Button, { type: "submit", fullWidth: true, mt: "md" }, "Submit"))
816
+ ), /* @__PURE__ */ React.createElement(Container, { size: "lg", py: "xl" }, /* @__PURE__ */ React.createElement(Stack$1, { spacing: "md" }, /* @__PURE__ */ React.createElement(Grid, null, /* @__PURE__ */ React.createElement(Grid.Col, { sm: "auto" }, /* @__PURE__ */ React.createElement(Badge$1, { variant: "filled", size: "lg" }, "Classes"), /* @__PURE__ */ React.createElement(Title, { order: 2, className: classes.title, mt: "md" }, "Organize students into classes"), /* @__PURE__ */ React.createElement(Text, { color: "dimmed", className: classes.description, mt: "sm" }, "A class can be for a specific period of time, grade, team, or other cohorts.")), /* @__PURE__ */ React.createElement(Grid.Col, { sm: "content" }, !props.loading && /* @__PURE__ */ React.createElement(
817
+ Button,
818
+ {
819
+ onClick: () => setOpened(true),
820
+ leftIcon: /* @__PURE__ */ React.createElement(IconPlaylistAdd, { size: 14 })
821
+ },
822
+ "Create class"
823
+ ))), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(LoadingOverlay, { visible: props.loading, overlayBlur: 2 }), /* @__PURE__ */ React.createElement(Stack$1, { spacing: "sm" }, /* @__PURE__ */ React.createElement(StatsGroup, { data: [
824
+ {
825
+ title: "# OF CLASSES",
826
+ value: props.classes.length
827
+ }
828
+ ] }), /* @__PURE__ */ React.createElement(
829
+ Table$6,
830
+ {
831
+ loading: props.loading,
832
+ items: props.classes,
833
+ onDeleteClass: props.onDeleteClass,
834
+ onClick: props.onClassClick
835
+ }
836
+ ))))));
837
+ };
838
+
839
+ const useStyles$6 = createStyles((theme) => ({
840
+ title: {
841
+ fontSize: 34,
842
+ fontWeight: 900,
843
+ marginTop: 16,
844
+ [theme.fn.smallerThan("sm")]: {
845
+ fontSize: 24
846
+ }
847
+ },
848
+ description: {
849
+ maxWidth: 600
850
+ }
851
+ }));
852
+ const UserInfo = (props) => {
853
+ const { classes } = useStyles$6();
854
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Title, { className: classes.title }, props.name), /* @__PURE__ */ React.createElement(Text, { color: "dimmed", className: classes.description, mt: "xs" }, props.impactStatement));
855
+ };
856
+
857
+ function Table$5(props) {
858
+ if (props.items.length === 0) {
859
+ return /* @__PURE__ */ React.createElement(
860
+ PlaceholderBanner,
861
+ {
862
+ title: "No badges to display",
863
+ description: "There has not been any badge progress just yet.",
864
+ loading: props.loading,
865
+ icon: "badges"
866
+ }
867
+ );
868
+ }
869
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.badgeName }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(UnstyledButton, { onClick: () => props.onClick(row) }, row.badgeName)), /* @__PURE__ */ React.createElement("td", null, !!row.isComplete && /* @__PURE__ */ React.createElement(Badge$1, { variant: "filled" }, "Complete"), !row.isComplete && /* @__PURE__ */ React.createElement(Badge$1, { color: "red", variant: "filled" }, "Incomplete"))));
870
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: "sm", sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Badge Name"), /* @__PURE__ */ React.createElement("th", null, "Status"))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
871
+ }
872
+
873
+ function Table$4(props) {
874
+ if (props.items.length === 0) {
875
+ return /* @__PURE__ */ React.createElement(
876
+ PlaceholderBanner,
877
+ {
878
+ title: "No answers to display",
879
+ description: "There has not been any lesson progress just yet.",
880
+ loading: props.loading,
881
+ icon: "lessons"
882
+ }
883
+ );
884
+ }
885
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.questionName }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(UnstyledButton, { onClick: () => props.onClick(row) }, row.lessonName)), /* @__PURE__ */ React.createElement("td", null, row.questionName), /* @__PURE__ */ React.createElement("td", null, row.answer.join(","))));
886
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: "sm", sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Lesson Name"), /* @__PURE__ */ React.createElement("th", null, "Question"), /* @__PURE__ */ React.createElement("th", null, "Answer"))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
887
+ }
888
+
889
+ function Table$3(props) {
890
+ if (props.items.length === 0) {
891
+ return /* @__PURE__ */ React.createElement(
892
+ PlaceholderBanner,
893
+ {
894
+ title: "No reflections to display",
895
+ description: "There has not been any lesson progress just yet.",
896
+ loading: props.loading,
897
+ icon: "lessons"
898
+ }
899
+ );
900
+ }
901
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.lessonName }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(UnstyledButton, { onClick: () => props.onClick(row) }, row.lessonName)), /* @__PURE__ */ React.createElement("td", null, row.reflection), /* @__PURE__ */ React.createElement("td", null, row.rating.toLocaleString())));
902
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: "sm", sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Lesson Name"), /* @__PURE__ */ React.createElement("th", null, "Reflection"), /* @__PURE__ */ React.createElement("th", null, "Rating"))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
903
+ }
904
+
905
+ const Student = (props) => {
906
+ const [tab, setTab] = useState("badges");
907
+ const numberOfBadges = props.badges.length;
908
+ const percentageOfBadgesEarned = numberOfBadges > 0 ? props.badges.filter((b) => b.isComplete).length / numberOfBadges : 0;
909
+ return /* @__PURE__ */ React.createElement(Container, { size: "lg", py: "xl" }, /* @__PURE__ */ React.createElement(Stack$1, { spacing: "md" }, /* @__PURE__ */ React.createElement(Grid, { gutter: "md" }, /* @__PURE__ */ React.createElement(Grid.Col, { sm: "auto" }, /* @__PURE__ */ React.createElement(
910
+ Badge$1,
911
+ {
912
+ variant: "filled",
913
+ leftSection: /* @__PURE__ */ React.createElement(ActionIcon, { onClick: props.onBackClick, color: "blue", size: "xs", radius: "xl", variant: "filled" }, /* @__PURE__ */ React.createElement(IconArrowLeft, { size: 14 })),
914
+ size: "lg"
915
+ },
916
+ "Students"
917
+ ), /* @__PURE__ */ React.createElement(
918
+ UserInfo,
919
+ {
920
+ variant: "compact",
921
+ name: props.name,
922
+ impactStatement: props.impactStatement
923
+ }
924
+ ))), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(LoadingOverlay, { visible: props.loading, overlayBlur: 2 }), /* @__PURE__ */ React.createElement(Stack$1, { spacing: "lg" }, /* @__PURE__ */ React.createElement(StatsGroup, { data: [
925
+ {
926
+ title: "PROBLEMS SOLVED",
927
+ value: props.numberOfProblemsSolved
928
+ },
929
+ {
930
+ title: "LESSON COMPLETION",
931
+ value: props.percentageOfLessonsCompleted,
932
+ unit: "%"
933
+ },
934
+ {
935
+ title: "BADGE COMPLETION",
936
+ value: percentageOfBadgesEarned,
937
+ unit: "%"
938
+ }
939
+ ] }), /* @__PURE__ */ React.createElement(Stack$1, { spacing: 0 }, /* @__PURE__ */ React.createElement(
940
+ Tabs,
941
+ {
942
+ value: tab,
943
+ data: [
944
+ { label: "My badges", value: "badges" },
945
+ { label: "My answers", value: "answers" },
946
+ { label: "My reflections", value: "reflections" }
947
+ ],
948
+ onChange: setTab
949
+ }
950
+ ), tab === "badges" && /* @__PURE__ */ React.createElement(
951
+ Table$5,
952
+ {
953
+ loading: props.loading,
954
+ items: props.badges,
955
+ onClick: props.onBadgeClick
956
+ }
957
+ ), tab === "answers" && /* @__PURE__ */ React.createElement(
958
+ Table$4,
959
+ {
960
+ loading: props.loading,
961
+ items: props.answers,
962
+ onClick: props.onAnswerClick
963
+ }
964
+ ), tab === "reflections" && /* @__PURE__ */ React.createElement(
965
+ Table$3,
966
+ {
967
+ loading: props.loading,
968
+ items: props.reflections,
969
+ onClick: props.onReflectionClick
970
+ }
971
+ ))))));
972
+ };
973
+
974
+ const useStyles$5 = createStyles((theme, props) => {
975
+ const from = props.from || "blue";
976
+ const to = props.to || "green";
977
+ return {
978
+ card: {
979
+ position: "relative",
980
+ cursor: "pointer",
981
+ overflow: "hidden",
982
+ transition: "transform 150ms ease, box-shadow 100ms ease",
983
+ padding: theme.spacing.xl,
984
+ paddingLeft: theme.spacing.xl * 2,
985
+ "&:hover": {
986
+ boxShadow: theme.shadows.md,
987
+ transform: "scale(1.02)"
988
+ },
989
+ "&::before": {
990
+ content: '""',
991
+ position: "absolute",
992
+ top: 0,
993
+ bottom: 0,
994
+ left: 0,
995
+ width: 6,
996
+ backgroundImage: theme.fn.linearGradient(0, theme.colors[from][6], theme.colors[to][6])
997
+ }
998
+ }
999
+ };
1000
+ });
1001
+ function CardGradient(props) {
1002
+ const { classes } = useStyles$5(props);
1003
+ const from = props.from || "blue";
1004
+ const to = props.to || "green";
1005
+ const icon = props.icon || /* @__PURE__ */ React.createElement(IconColorSwatch, { size: 28, stroke: 1.5 });
1006
+ return /* @__PURE__ */ React.createElement(Paper, { withBorder: true, radius: "md", className: classes.card, onClick: props.onClick }, /* @__PURE__ */ React.createElement(
1007
+ ThemeIcon,
1008
+ {
1009
+ size: "xl",
1010
+ radius: "md",
1011
+ variant: "gradient",
1012
+ gradient: { deg: 0, from, to }
1013
+ },
1014
+ icon
1015
+ ), /* @__PURE__ */ React.createElement(Text, { size: "xl", weight: 500, mt: "md" }, props.title), /* @__PURE__ */ React.createElement(Text, { size: "sm", mt: "sm", color: "dimmed" }, props.description));
1016
+ }
1017
+
1018
+ var __defProp$1 = Object.defineProperty;
1019
+ var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
1020
+ var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
1021
+ var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
1022
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1023
+ var __spreadValues$1 = (a, b) => {
1024
+ for (var prop in b || (b = {}))
1025
+ if (__hasOwnProp$1.call(b, prop))
1026
+ __defNormalProp$1(a, prop, b[prop]);
1027
+ if (__getOwnPropSymbols$1)
1028
+ for (var prop of __getOwnPropSymbols$1(b)) {
1029
+ if (__propIsEnum$1.call(b, prop))
1030
+ __defNormalProp$1(a, prop, b[prop]);
1031
+ }
1032
+ return a;
1033
+ };
1034
+ var __objRest = (source, exclude) => {
1035
+ var target = {};
1036
+ for (var prop in source)
1037
+ if (__hasOwnProp$1.call(source, prop) && exclude.indexOf(prop) < 0)
1038
+ target[prop] = source[prop];
1039
+ if (source != null && __getOwnPropSymbols$1)
1040
+ for (var prop of __getOwnPropSymbols$1(source)) {
1041
+ if (exclude.indexOf(prop) < 0 && __propIsEnum$1.call(source, prop))
1042
+ target[prop] = source[prop];
1043
+ }
1044
+ return target;
1045
+ };
1046
+ const useStyles$4 = createStyles((theme) => ({
1047
+ card: {
1048
+ height: 240,
1049
+ backgroundSize: "cover",
1050
+ backgroundPosition: "center"
1051
+ },
1052
+ content: {
1053
+ position: "absolute",
1054
+ padding: theme.spacing.xl,
1055
+ zIndex: 1,
1056
+ top: 0,
1057
+ bottom: 0,
1058
+ right: 0,
1059
+ left: 0
1060
+ },
1061
+ action: {
1062
+ position: "absolute",
1063
+ bottom: theme.spacing.xl,
1064
+ right: theme.spacing.xl
1065
+ },
1066
+ title: {
1067
+ color: theme.white,
1068
+ marginBottom: theme.spacing.xs / 2
1069
+ },
1070
+ description: {
1071
+ color: theme.white,
1072
+ maxWidth: 220
1073
+ }
1074
+ }));
1075
+ const TenantBanner = (_a) => {
1076
+ var _b = _a, {
1077
+ title,
1078
+ description,
1079
+ image,
1080
+ action,
1081
+ style,
1082
+ className
1083
+ } = _b, others = __objRest(_b, [
1084
+ "title",
1085
+ "description",
1086
+ "image",
1087
+ "action",
1088
+ "style",
1089
+ "className"
1090
+ ]);
1091
+ const { classes, cx, theme } = useStyles$4();
1092
+ return /* @__PURE__ */ React.createElement(
1093
+ Card,
1094
+ __spreadValues$1({
1095
+ radius: "md",
1096
+ style: __spreadValues$1({ backgroundImage: `url(${image})` }, style),
1097
+ className: cx(classes.card, className)
1098
+ }, others),
1099
+ /* @__PURE__ */ React.createElement(
1100
+ Overlay,
1101
+ {
1102
+ gradient: `linear-gradient(105deg, ${theme.black} 20%, #312f2f 50%, ${theme.colors.gray[4]} 100%)`,
1103
+ opacity: 0.55,
1104
+ zIndex: 0
1105
+ }
1106
+ ),
1107
+ /* @__PURE__ */ React.createElement("div", { className: classes.content }, /* @__PURE__ */ React.createElement(Text, { size: "lg", weight: 700, className: classes.title }, title), /* @__PURE__ */ React.createElement(Text, { size: "sm", className: classes.description }, description), /* @__PURE__ */ React.createElement(
1108
+ Button,
1109
+ {
1110
+ className: classes.action,
1111
+ variant: "white",
1112
+ color: "dark",
1113
+ component: "a",
1114
+ size: "xs",
1115
+ href: action.link,
1116
+ target: "_blank"
1117
+ },
1118
+ action.label
1119
+ ))
1120
+ );
1121
+ };
1122
+
1123
+ const Home = (props) => {
1124
+ return /* @__PURE__ */ React.createElement(Container, { size: "lg" }, /* @__PURE__ */ React.createElement(Stack$1, { spacing: "lg" }, /* @__PURE__ */ React.createElement(Grid, { gutter: "md" }, /* @__PURE__ */ React.createElement(Grid.Col, { md: 6 }, /* @__PURE__ */ React.createElement(
1125
+ UserInfo,
1126
+ {
1127
+ variant: "compact",
1128
+ name: props.name,
1129
+ impactStatement: props.impactStatement
1130
+ }
1131
+ )), /* @__PURE__ */ React.createElement(Grid.Col, { md: 6 }, /* @__PURE__ */ React.createElement(
1132
+ TenantBanner,
1133
+ {
1134
+ title: props.organization.name,
1135
+ description: props.organization.description,
1136
+ image: props.organization.image,
1137
+ action: {
1138
+ label: "Visit website",
1139
+ link: props.organization.website
1140
+ }
1141
+ }
1142
+ ))), /* @__PURE__ */ React.createElement(Grid, { gutter: "md" }, /* @__PURE__ */ React.createElement(Grid.Col, null, /* @__PURE__ */ React.createElement(
1143
+ CardGradient,
1144
+ {
1145
+ title: "Dashboard",
1146
+ description: "Track class performance across core areas of focus.",
1147
+ onClick: props.onDashboardClick
1148
+ }
1149
+ )), /* @__PURE__ */ React.createElement(Grid.Col, null, /* @__PURE__ */ React.createElement(
1150
+ CardGradient,
1151
+ {
1152
+ title: "Classes",
1153
+ description: "Organize students into classes.",
1154
+ onClick: props.onClassesClick
1155
+ }
1156
+ )), /* @__PURE__ */ React.createElement(Grid.Col, null, /* @__PURE__ */ React.createElement(
1157
+ CardGradient,
1158
+ {
1159
+ title: "Lessons",
1160
+ description: "Explore units of instruction and/or see corresponding class progress.",
1161
+ onClick: props.onLessonsClick
1162
+ }
1163
+ )), /* @__PURE__ */ React.createElement(Grid.Col, null, /* @__PURE__ */ React.createElement(
1164
+ CardGradient,
1165
+ {
1166
+ title: "Badges",
1167
+ description: "Project-sized skills acquisition and standards alignment.",
1168
+ onClick: props.onBadgesClick
1169
+ }
1170
+ )))));
1171
+ };
1172
+
1173
+ function Table$2(props) {
1174
+ if (props.items.length === 0) {
1175
+ return /* @__PURE__ */ React.createElement(
1176
+ PlaceholderBanner,
1177
+ {
1178
+ title: "No reflections to display",
1179
+ description: "There has not been any lesson progress just yet.",
1180
+ loading: props.loading,
1181
+ icon: "lessons"
1182
+ }
1183
+ );
1184
+ }
1185
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.studentName }, /* @__PURE__ */ React.createElement("td", null, row.studentName), /* @__PURE__ */ React.createElement("td", null, row.reflection), /* @__PURE__ */ React.createElement("td", null, row.rating.toLocaleString())));
1186
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: "sm", sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Student Name"), /* @__PURE__ */ React.createElement("th", null, "Reflection"), /* @__PURE__ */ React.createElement("th", null, "Rating"))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
1187
+ }
1188
+
1189
+ function Table$1(props) {
1190
+ if (props.items.length === 0) {
1191
+ return /* @__PURE__ */ React.createElement(
1192
+ PlaceholderBanner,
1193
+ {
1194
+ title: "No students to display",
1195
+ description: "You don't have any student data yet for this lesson",
1196
+ loading: props.loading,
1197
+ icon: "lessons"
1198
+ }
1199
+ );
1200
+ }
1201
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.name }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(UnstyledButton, { onClick: () => props.onClick && props.onClick(row) }, /* @__PURE__ */ React.createElement(Group, { spacing: "sm" }, /* @__PURE__ */ React.createElement(Avatar, { size: 40, src: row.avatar, radius: 40 }), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Text, { size: "sm", weight: 500 }, row.name), /* @__PURE__ */ React.createElement(Text, { size: "xs", color: "dimmed" }, row.email))))), /* @__PURE__ */ React.createElement("td", null, !!row.isComplete && /* @__PURE__ */ React.createElement(Badge$1, { variant: "filled" }, "Complete"), !row.isComplete && !row.isStarted && /* @__PURE__ */ React.createElement(Badge$1, { color: "red", variant: "filled" }, "Not started"), !row.isComplete && !!row.isStarted && /* @__PURE__ */ React.createElement(Badge$1, { color: "violet", variant: "filled" }, "In progress"))));
1202
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { verticalSpacing: "sm", sx: { minWidth: 700 }, highlightOnHover: true, striped: true }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, "Lesson Name"), /* @__PURE__ */ React.createElement("th", null, "Status"))), /* @__PURE__ */ React.createElement("tbody", null, rows)));
1203
+ }
1204
+
1205
+ function Stack(props) {
1206
+ const primaryAxis = React.useMemo(
1207
+ () => ({
1208
+ position: "left",
1209
+ getValue: (datum) => datum.primary
1210
+ }),
1211
+ []
1212
+ );
1213
+ const secondaryAxes = React.useMemo(
1214
+ () => [
1215
+ {
1216
+ position: "bottom",
1217
+ getValue: (datum) => datum.secondary
1218
+ }
1219
+ ],
1220
+ []
1221
+ );
1222
+ if (props.items.length === 0) {
1223
+ return /* @__PURE__ */ React.createElement(
1224
+ PlaceholderBanner,
1225
+ {
1226
+ title: "No questions to display",
1227
+ description: "There are no questions in this lesson.",
1228
+ loading: props.loading,
1229
+ icon: "lessons"
1230
+ }
1231
+ );
1232
+ }
1233
+ const rows = props.items.map((row) => {
1234
+ if (row.chart) {
1235
+ const labelMap = {};
1236
+ const choices = row.choices || [];
1237
+ choices.forEach((c) => {
1238
+ labelMap[c] = 0;
1239
+ });
1240
+ row.answers.forEach((a) => a.forEach((r) => {
1241
+ if (r in labelMap) {
1242
+ labelMap[r] = labelMap[r] ? labelMap[r] + 1 : 1;
1243
+ }
1244
+ }));
1245
+ return /* @__PURE__ */ React.createElement(Card, { key: row.question, withBorder: true, p: "xl", radius: "md" }, /* @__PURE__ */ React.createElement(Stack$1, { spacing: 4 }, /* @__PURE__ */ React.createElement(Title, { size: "lg" }, row.question), /* @__PURE__ */ React.createElement(Text, { size: "sm" }, row.answers.length, " answers"), /* @__PURE__ */ React.createElement("div", { style: { background: "white", height: "300px", width: "100%", position: "relative" } }, /* @__PURE__ */ React.createElement(
1246
+ Chart,
1247
+ {
1248
+ options: {
1249
+ data: [{
1250
+ label: "",
1251
+ data: choices.map((k) => {
1252
+ return {
1253
+ primary: truncateWithEllipses(k, 50),
1254
+ secondary: labelMap[k]
1255
+ };
1256
+ })
1257
+ }],
1258
+ primaryAxis,
1259
+ secondaryAxes
1260
+ }
1261
+ }
1262
+ ))));
1263
+ }
1264
+ return /* @__PURE__ */ React.createElement(Card, { key: row.question, withBorder: true, p: "xl", radius: "md" }, /* @__PURE__ */ React.createElement(Stack$1, { spacing: 4 }, /* @__PURE__ */ React.createElement(Title, { size: "lg" }, row.question), /* @__PURE__ */ React.createElement(Text, { size: "sm" }, row.answers.length, " answers"), /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Stack$1, { spacing: 4 }, row.answers.map((a) => {
1265
+ const answerText = a.join("\n");
1266
+ return /* @__PURE__ */ React.createElement(Card, { key: answerText, p: 5, radius: 0, bg: "gray.0" }, /* @__PURE__ */ React.createElement(Text, null, answerText));
1267
+ })))));
1268
+ });
1269
+ return /* @__PURE__ */ React.createElement(Stack$1, { py: 4, spacing: 10, sx: { minWidth: 700 } }, rows);
1270
+ }
1271
+ const truncateWithEllipses = (text, max) => text.substr(0, max - 1) + (text.length > max ? "&hellip;" : "");
1272
+
1273
+ const useStyles$3 = createStyles((theme) => ({
1274
+ title: {
1275
+ fontSize: 34,
1276
+ fontWeight: 900,
1277
+ [theme.fn.smallerThan("sm")]: {
1278
+ fontSize: 24
1279
+ }
1280
+ },
1281
+ description: {
1282
+ maxWidth: 600
1283
+ }
1284
+ }));
1285
+ const Lesson = (props) => {
1286
+ const { classes } = useStyles$3();
1287
+ const [tab, setTab] = useState("question");
1288
+ const numberOfStudents = props.students.length;
1289
+ const percentageOfLessonsCompleted = numberOfStudents > 0 ? props.students.filter((u) => u.isComplete).length / numberOfStudents : 0;
1290
+ return /* @__PURE__ */ React.createElement(Container, { size: "lg", py: "xl" }, /* @__PURE__ */ React.createElement(Stack$1, { spacing: "md" }, /* @__PURE__ */ React.createElement(Grid, null, /* @__PURE__ */ React.createElement(Grid.Col, { sm: "auto" }, /* @__PURE__ */ React.createElement(
1291
+ Badge$1,
1292
+ {
1293
+ variant: "filled",
1294
+ leftSection: /* @__PURE__ */ React.createElement(ActionIcon, { onClick: props.onBackClick, color: "blue", size: "xs", radius: "xl", variant: "filled" }, /* @__PURE__ */ React.createElement(IconArrowLeft, { size: 14 })),
1295
+ size: "lg"
1296
+ },
1297
+ "Lessons"
1298
+ ), /* @__PURE__ */ React.createElement(Group, null, /* @__PURE__ */ React.createElement(Stack$1, { spacing: 0 }, /* @__PURE__ */ React.createElement(Title, { order: 2, className: classes.title, mt: "md" }, props.displayName || "Lesson"), /* @__PURE__ */ React.createElement(Text, { color: "dimmed", className: classes.description, mt: "sm" }, props.description || "No description")), /* @__PURE__ */ React.createElement(Stack$1, { ml: "auto" }, /* @__PURE__ */ React.createElement(
1299
+ Button,
1300
+ {
1301
+ variant: "gradient",
1302
+ onClick: props.onPreviewClick
1303
+ },
1304
+ "Preview"
1305
+ ))))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(LoadingOverlay, { visible: props.loading, overlayBlur: 2 }), /* @__PURE__ */ React.createElement(Stack$1, null, /* @__PURE__ */ React.createElement(StatsGroup, { data: [
1306
+ {
1307
+ title: "LESSON COMPLETION",
1308
+ value: percentageOfLessonsCompleted,
1309
+ unit: "%"
1310
+ }
1311
+ ] }), /* @__PURE__ */ React.createElement(
1312
+ Select,
1313
+ {
1314
+ clearable: true,
1315
+ clearButtonLabel: "Clear class selection",
1316
+ size: "sm",
1317
+ placeholder: "Select a class",
1318
+ nothingFound: "No options",
1319
+ value: props.classId,
1320
+ onChange: props.onClassChange,
1321
+ icon: /* @__PURE__ */ React.createElement(IconCategory2, null),
1322
+ data: props.classes.map((g) => {
1323
+ return { value: g.classId, label: g.name };
1324
+ })
1325
+ }
1326
+ ), /* @__PURE__ */ React.createElement(Stack$1, { spacing: 0 }, /* @__PURE__ */ React.createElement(
1327
+ Tabs,
1328
+ {
1329
+ value: tab,
1330
+ data: [
1331
+ { label: "By question", value: "question" },
1332
+ { label: "By reflection", value: "reflections" },
1333
+ { label: "By student", value: "students" }
1334
+ ],
1335
+ onChange: setTab
1336
+ }
1337
+ ), tab === "question" && /* @__PURE__ */ React.createElement(
1338
+ Stack,
1339
+ {
1340
+ loading: props.loading,
1341
+ items: props.questions
1342
+ }
1343
+ ), tab === "reflections" && /* @__PURE__ */ React.createElement(
1344
+ Table$2,
1345
+ {
1346
+ loading: props.loading,
1347
+ items: props.reflections
1348
+ }
1349
+ ), tab === "students" && /* @__PURE__ */ React.createElement(
1350
+ Table$1,
1351
+ {
1352
+ loading: props.loading,
1353
+ items: props.students,
1354
+ onClick: props.onUserClick
1355
+ }
1356
+ )))))));
1357
+ };
1358
+
1359
+ function Table(props) {
1360
+ if (props.items.length === 0) {
1361
+ return /* @__PURE__ */ React.createElement(
1362
+ PlaceholderBanner,
1363
+ {
1364
+ title: "No lessons to display",
1365
+ description: "We don't have any lessons to show you just yet.",
1366
+ loading: props.loading,
1367
+ icon: "lessons"
1368
+ }
1369
+ );
1370
+ }
1371
+ const rows = props.items.map((row) => /* @__PURE__ */ React.createElement("tr", { key: row.lessonId }, /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement(
1372
+ UnstyledButton,
1373
+ {
1374
+ sx: (theme) => ({
1375
+ display: "block",
1376
+ width: "100%",
1377
+ padding: theme.spacing.md,
1378
+ color: theme.colorScheme === "dark" ? theme.colors.dark[0] : theme.black,
1379
+ "&:hover": {
1380
+ backgroundColor: theme.colorScheme === "dark" ? theme.colors.dark[8] : theme.colors.gray[1]
1381
+ }
1382
+ }),
1383
+ onClick: () => props.onClick && props.onClick(row)
1384
+ },
1385
+ /* @__PURE__ */ React.createElement(Group, null, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Text, { size: "sm", weight: 500 }, row.name), /* @__PURE__ */ React.createElement(Text, { size: "xs", color: "dimmed" }, row.description)))
1386
+ ))));
1387
+ return /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { maxHeight: 500 }, /* @__PURE__ */ React.createElement(Table$g, { horizontalSpacing: 0, verticalSpacing: 0, sx: { minWidth: 700 } }, /* @__PURE__ */ React.createElement("tbody", null, rows)));
1388
+ }
1389
+
1390
+ const useStyles$2 = createStyles((theme) => ({
1391
+ title: {
1392
+ fontSize: 34,
1393
+ fontWeight: 900,
1394
+ [theme.fn.smallerThan("sm")]: {
1395
+ fontSize: 24
1396
+ }
1397
+ },
1398
+ description: {
1399
+ maxWidth: 600
1400
+ }
1401
+ }));
1402
+ const Lessons = (props) => {
1403
+ const { classes } = useStyles$2();
1404
+ return /* @__PURE__ */ React.createElement(Container, { size: "lg", py: "xl" }, /* @__PURE__ */ React.createElement(Stack$1, { spacing: "md" }, /* @__PURE__ */ React.createElement(Grid, null, /* @__PURE__ */ React.createElement(Grid.Col, { sm: "auto" }, /* @__PURE__ */ React.createElement(Badge$1, { variant: "filled", size: "lg" }, "Lessons"), /* @__PURE__ */ React.createElement(Title, { order: 2, className: classes.title, mt: "md" }, "Lessons"), /* @__PURE__ */ React.createElement(Text, { color: "dimmed", className: classes.description, mt: "sm" }, "Explore units of instruction and/or see corresponding class progress."))), /* @__PURE__ */ React.createElement(
1405
+ Autocomplete,
1406
+ {
1407
+ placeholder: "Search for a lesson that fits your needs",
1408
+ data: props.lessons.map((item) => item.name),
1409
+ onChange: props.onAutocompleteChange
1410
+ }
1411
+ ), /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(LoadingOverlay, { visible: props.loading, overlayBlur: 2 }), /* @__PURE__ */ React.createElement(
1412
+ Table,
1413
+ {
1414
+ loading: props.loading,
1415
+ items: props.lessons,
1416
+ onClick: props.onLessonClick
1417
+ }
1418
+ ))));
1419
+ };
1420
+
1421
+ const mycCache = createEmotionCache({
1422
+ key: "mantine",
1423
+ prepend: false
1424
+ });
1425
+ const AdminProvider = (props) => {
1426
+ return /* @__PURE__ */ React.createElement(MantineProvider, { withNormalizeCSS: true, withGlobalStyles: true, emotionCache: mycCache, theme: { loader: "bars" } }, /* @__PURE__ */ React.createElement(NotificationsProvider, { limit: props.notificationLimit || 5 }, /* @__PURE__ */ React.createElement(ModalsProvider, null, props.children)));
1427
+ };
1428
+
1429
+ var __defProp = Object.defineProperty;
1430
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
1431
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
1432
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
1433
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1434
+ var __spreadValues = (a, b) => {
1435
+ for (var prop in b || (b = {}))
1436
+ if (__hasOwnProp.call(b, prop))
1437
+ __defNormalProp(a, prop, b[prop]);
1438
+ if (__getOwnPropSymbols)
1439
+ for (var prop of __getOwnPropSymbols(b)) {
1440
+ if (__propIsEnum.call(b, prop))
1441
+ __defNormalProp(a, prop, b[prop]);
1442
+ }
1443
+ return a;
1444
+ };
1445
+ const SwitchAccount = (props) => {
1446
+ const form = useForm({
1447
+ initialValues: {
1448
+ active: props.account
1449
+ }
1450
+ });
1451
+ return /* @__PURE__ */ React.createElement(
1452
+ Modal,
1453
+ {
1454
+ centered: true,
1455
+ opened: props.opened,
1456
+ onClose: () => props.onClose && props.onClose(),
1457
+ size: "sm",
1458
+ title: /* @__PURE__ */ React.createElement(Title, { size: "h5" }, "Accounts")
1459
+ },
1460
+ /* @__PURE__ */ React.createElement("form", { onSubmit: form.onSubmit(() => {
1461
+ props.onChange && props.onChange(form.values.active);
1462
+ }) }, /* @__PURE__ */ React.createElement(
1463
+ Select,
1464
+ __spreadValues({
1465
+ required: true,
1466
+ placeholder: "Select an account",
1467
+ defaultValue: props.account,
1468
+ data: props.accounts.map((a) => {
1469
+ return { value: a.accountId, label: a.name };
1470
+ })
1471
+ }, form.getInputProps("active"))
1472
+ ), /* @__PURE__ */ React.createElement(Button, { type: "submit", fullWidth: true, mt: "xl" }, "Switch"))
1473
+ );
1474
+ };
1475
+
1476
+ const useStyles$1 = createStyles((theme) => ({
1477
+ link: {
1478
+ width: 50,
1479
+ height: 50,
1480
+ borderRadius: theme.radius.md,
1481
+ display: "flex",
1482
+ alignItems: "center",
1483
+ justifyContent: "center",
1484
+ color: theme.colorScheme === "dark" ? theme.colors.dark[0] : theme.colors.gray[7],
1485
+ "&:hover": {
1486
+ backgroundColor: theme.colorScheme === "dark" ? theme.colors.dark[5] : theme.colors.gray[0]
1487
+ }
1488
+ },
1489
+ active: {
1490
+ "&, &:hover": {
1491
+ backgroundColor: theme.fn.variant({ variant: "light", color: theme.primaryColor }).background,
1492
+ color: theme.fn.variant({ variant: "light", color: theme.primaryColor }).color
1493
+ }
1494
+ }
1495
+ }));
1496
+ const data = [
1497
+ { icon: IconHome2, label: "Home", href: "/home" },
1498
+ { icon: IconGauge, label: "Dashboard", href: "/dashboard" },
1499
+ { icon: IconCategory2, label: "Classes", href: "/classes" },
1500
+ { icon: IconAlbum, label: "Badges", href: "/badges" },
1501
+ { icon: IconLambda, label: "Lessons", href: "/lessons" }
1502
+ ];
1503
+ const NavbarLink = ({ icon: Icon, label, active, onClick }) => {
1504
+ const { classes, cx } = useStyles$1();
1505
+ return /* @__PURE__ */ React.createElement(Tooltip, { label, position: "right", transitionDuration: 0 }, /* @__PURE__ */ React.createElement(UnstyledButton, { onClick, className: cx(classes.link, { [classes.active]: active }) }, /* @__PURE__ */ React.createElement(Icon, { stroke: 1.5 })));
1506
+ };
1507
+ const Navbar = (props) => {
1508
+ const links = data.map((link) => /* @__PURE__ */ React.createElement(
1509
+ NavbarLink,
1510
+ {
1511
+ key: link.label,
1512
+ label: link.label,
1513
+ icon: link.icon,
1514
+ active: link.label === props.active,
1515
+ onClick: () => props.navigate(link.href)
1516
+ }
1517
+ ));
1518
+ return /* @__PURE__ */ React.createElement(Navbar$1, { width: { base: 80 }, p: "md" }, /* @__PURE__ */ React.createElement(Center, null, /* @__PURE__ */ React.createElement(Avatar, { color: "blue", radius: "sm" }, /* @__PURE__ */ React.createElement("div", { style: { width: 15, marginLeft: "auto", marginRight: "auto" } }, /* @__PURE__ */ React.createElement(Image, { fit: "contain", src: "https://cdn.localcivics.io/brand/l.png" })))), /* @__PURE__ */ React.createElement(Navbar$1.Section, { grow: true, mt: 50 }, /* @__PURE__ */ React.createElement(Stack$1, { justify: "center", spacing: 0 }, links)), /* @__PURE__ */ React.createElement(Navbar$1.Section, null, /* @__PURE__ */ React.createElement(Stack$1, { justify: "center", spacing: 0 }, /* @__PURE__ */ React.createElement(
1519
+ NavbarLink,
1520
+ {
1521
+ icon: IconSwitchHorizontal,
1522
+ label: "Switch accounts",
1523
+ onClick: props.onSwitchAccounts
1524
+ }
1525
+ ), /* @__PURE__ */ React.createElement(
1526
+ NavbarLink,
1527
+ {
1528
+ icon: IconLogout,
1529
+ label: "Logout",
1530
+ onClick: props.onLogout
1531
+ }
1532
+ ))));
1533
+ };
1534
+
1535
+ const useStyles = createStyles((theme) => ({
1536
+ footer: {
1537
+ paddingTop: theme.spacing.xl * 2,
1538
+ paddingBottom: theme.spacing.xl * 2,
1539
+ paddingLeft: theme.spacing.xl * 3,
1540
+ backgroundColor: theme.colorScheme === "dark" ? theme.colors.dark[6] : theme.colors.gray[0],
1541
+ borderTop: `1px solid ${theme.colorScheme === "dark" ? theme.colors.dark[5] : theme.colors.gray[2]}`
1542
+ },
1543
+ logo: {
1544
+ maxWidth: 200,
1545
+ [theme.fn.smallerThan("sm")]: {
1546
+ display: "flex",
1547
+ flexDirection: "column",
1548
+ alignItems: "center"
1549
+ }
1550
+ },
1551
+ description: {
1552
+ marginTop: 5,
1553
+ [theme.fn.smallerThan("sm")]: {
1554
+ marginTop: theme.spacing.xs,
1555
+ textAlign: "center"
1556
+ }
1557
+ },
1558
+ inner: {
1559
+ display: "flex",
1560
+ justifyContent: "space-between",
1561
+ [theme.fn.smallerThan("sm")]: {
1562
+ flexDirection: "column",
1563
+ alignItems: "center"
1564
+ }
1565
+ },
1566
+ groups: {
1567
+ display: "flex",
1568
+ flexWrap: "wrap",
1569
+ [theme.fn.smallerThan("sm")]: {
1570
+ display: "none"
1571
+ }
1572
+ },
1573
+ wrapper: {
1574
+ width: 160
1575
+ },
1576
+ link: {
1577
+ display: "block",
1578
+ color: theme.colorScheme === "dark" ? theme.colors.dark[1] : theme.colors.gray[6],
1579
+ fontSize: theme.fontSizes.sm,
1580
+ paddingTop: 3,
1581
+ paddingBottom: 3,
1582
+ "&:hover": {
1583
+ textDecoration: "underline"
1584
+ }
1585
+ },
1586
+ title: {
1587
+ fontSize: theme.fontSizes.md,
1588
+ fontWeight: 700,
1589
+ fontFamily: `Greycliff CF, ${theme.fontFamily}`,
1590
+ marginBottom: theme.spacing.xs / 2,
1591
+ color: theme.colorScheme === "dark" ? theme.white : theme.black
1592
+ },
1593
+ afterFooter: {
1594
+ display: "flex",
1595
+ justifyContent: "space-between",
1596
+ alignItems: "center",
1597
+ marginTop: theme.spacing.xl,
1598
+ paddingTop: theme.spacing.xl,
1599
+ paddingBottom: theme.spacing.xl,
1600
+ borderTop: `1px solid ${theme.colorScheme === "dark" ? theme.colors.dark[4] : theme.colors.gray[2]}`,
1601
+ [theme.fn.smallerThan("sm")]: {
1602
+ flexDirection: "column"
1603
+ }
1604
+ },
1605
+ social: {
1606
+ [theme.fn.smallerThan("sm")]: {
1607
+ marginTop: theme.spacing.xs
1608
+ }
1609
+ }
1610
+ }));
1611
+ const App = (props) => {
1612
+ const { classes } = useStyles();
1613
+ const account = useAccount(props.account, props.accounts, props.onAccountChange);
1614
+ return /* @__PURE__ */ React.createElement(
1615
+ AppShell,
1616
+ {
1617
+ padding: "xs",
1618
+ navbar: /* @__PURE__ */ React.createElement(
1619
+ Navbar,
1620
+ {
1621
+ active: props.navbar.props.active,
1622
+ navigate: props.navbar.props.navigate,
1623
+ onLogout: props.navbar.props.onLogout,
1624
+ onSwitchAccounts: () => account.setChangeModalOpen(true)
1625
+ }
1626
+ ),
1627
+ footer: /* @__PURE__ */ React.createElement(React.Fragment, null, !account.opened && /* @__PURE__ */ React.createElement("footer", { className: classes.footer }, /* @__PURE__ */ React.createElement(Container, { className: classes.inner }, /* @__PURE__ */ React.createElement("div", { className: classes.logo }, /* @__PURE__ */ React.createElement(Group, { spacing: "xs" }, /* @__PURE__ */ React.createElement("div", { style: { width: 15 } }, /* @__PURE__ */ React.createElement(Image, { fit: "contain", src: "https://cdn.localcivics.io/brand/l.png" })), /* @__PURE__ */ React.createElement(Title, { color: "dimmed", size: "h5" }, "Local Civics")), /* @__PURE__ */ React.createElement(Text, { size: "xs", color: "dimmed", className: classes.description }, "We connect students to powerful civic learning experiences.")), /* @__PURE__ */ React.createElement("div", { className: classes.groups }, /* @__PURE__ */ React.createElement("div", { className: classes.wrapper }, /* @__PURE__ */ React.createElement(
1628
+ Text,
1629
+ {
1630
+ className: classes.link,
1631
+ component: "a",
1632
+ href: "https://www.localcivics.io",
1633
+ target: "_blank"
1634
+ },
1635
+ "About"
1636
+ ), /* @__PURE__ */ React.createElement(
1637
+ Text,
1638
+ {
1639
+ className: classes.link,
1640
+ component: "a",
1641
+ href: "https://www.localcivics.io/terms-of-service",
1642
+ target: "_blank"
1643
+ },
1644
+ "Terms"
1645
+ ), /* @__PURE__ */ React.createElement(
1646
+ Text,
1647
+ {
1648
+ className: classes.link,
1649
+ component: "a",
1650
+ href: "https://www.localcivics.io/privacy-policy",
1651
+ target: "_blank"
1652
+ },
1653
+ "Privacy"
1654
+ ), /* @__PURE__ */ React.createElement(
1655
+ Text,
1656
+ {
1657
+ className: classes.link,
1658
+ component: "a",
1659
+ href: "https://localcivics.notion.site/Help-Center-b52300f587b64fc0a61f512686e7626d",
1660
+ target: "_blank"
1661
+ },
1662
+ "Help Center"
1663
+ )))), /* @__PURE__ */ React.createElement(Container, { className: classes.afterFooter }, /* @__PURE__ */ React.createElement(Text, { color: "dimmed", size: "sm" }, "\xA9 ", new Date().getFullYear(), " Local Civics. All rights reserved."), /* @__PURE__ */ React.createElement(Group, { spacing: 0, className: classes.social, position: "right", noWrap: true }, /* @__PURE__ */ React.createElement(ActionIcon, { component: "a", target: "_blank", href: "https://www.instagram.com/localcivics/", size: "lg" }, /* @__PURE__ */ React.createElement(IconBrandInstagram, { size: 18, stroke: 1.5 })), /* @__PURE__ */ React.createElement(ActionIcon, { component: "a", target: "_blank", href: "https://www.linkedin.com/company/localcivics", size: "lg" }, /* @__PURE__ */ React.createElement(IconBrandLinkedin, { size: 18, stroke: 1.5 })), /* @__PURE__ */ React.createElement(ActionIcon, { component: "a", target: "_blank", href: "https://www.facebook.com/localcivics/", size: "lg" }, /* @__PURE__ */ React.createElement(IconBrandFacebook, { size: 18, stroke: 1.5 })))))),
1664
+ styles: (theme) => ({
1665
+ main: { backgroundColor: theme.colorScheme === "dark" ? theme.colors.dark[8] : theme.colors.gray[0] }
1666
+ })
1667
+ },
1668
+ /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, (props.loading || account.opened) && /* @__PURE__ */ React.createElement(Center, { style: { height: 400 } }, /* @__PURE__ */ React.createElement(Loader, null)), !props.loading && !account.opened && props.page),
1669
+ /* @__PURE__ */ React.createElement(
1670
+ SwitchAccount,
1671
+ {
1672
+ opened: account.opened,
1673
+ account: account.account,
1674
+ accounts: account.accounts,
1675
+ onChange: account.onAccountChange,
1676
+ onClose: () => account.setChangeModalOpen(false)
1677
+ }
1678
+ )
1679
+ );
1680
+ };
1681
+ const useAccount = (account, accounts, onAccountChange) => {
1682
+ const accountsKey = JSON.stringify(accounts);
1683
+ const [changeModalOpen, setChangeModalOpen] = useState(false);
1684
+ const [active, setActive] = useState(account);
1685
+ React.useEffect(() => {
1686
+ setActive(account);
1687
+ }, [account, accountsKey]);
1688
+ return {
1689
+ opened: changeModalOpen,
1690
+ account: active,
1691
+ accounts,
1692
+ setChangeModalOpen,
1693
+ onAccountChange: (account2) => {
1694
+ setActive(account2);
1695
+ setChangeModalOpen(false);
1696
+ onAccountChange(account2);
1697
+ }
1698
+ };
1699
+ };
1700
+
1701
+ export { AdminProvider, App, Badge, Badges, Class, Classes, Dashboard, Home, Lesson, Lessons, Navbar, Student, SwitchAccount };
1702
+ //# sourceMappingURL=index.mjs.map