@olinfo/quizms-firebase 4.0.1 → 4.1.1

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 (68) hide show
  1. package/dist/functions.d.ts +2 -11
  2. package/dist/functions.js +2 -2
  3. package/dist/package.json.js +1 -1
  4. package/dist/src/cli/files/functions-index.js +4 -4
  5. package/dist/src/cli/files/functions-index.js.map +1 -1
  6. package/dist/src/cli/import.js +85 -96
  7. package/dist/src/cli/import.js.map +1 -1
  8. package/dist/src/cli/init.js +8 -8
  9. package/dist/src/cli/init.js.map +1 -1
  10. package/dist/src/cli/update-scores.js +21 -32
  11. package/dist/src/cli/update-scores.js.map +1 -1
  12. package/dist/src/cli/utils/collection.js +20 -18
  13. package/dist/src/cli/utils/collection.js.map +1 -1
  14. package/dist/src/cli/utils/converters-admin.js +31 -32
  15. package/dist/src/cli/utils/converters-admin.js.map +1 -1
  16. package/dist/src/cli/utils/region-finder.js +6 -5
  17. package/dist/src/cli/utils/region-finder.js.map +1 -1
  18. package/dist/src/cli/utils/storage.js +26 -35
  19. package/dist/src/cli/utils/storage.js.map +1 -1
  20. package/dist/src/cli/utils/users.js +13 -12
  21. package/dist/src/cli/utils/users.js.map +1 -1
  22. package/dist/src/functions/api/admin.js +25 -10
  23. package/dist/src/functions/api/admin.js.map +1 -1
  24. package/dist/src/functions/api/index.js +26 -37
  25. package/dist/src/functions/api/index.js.map +1 -1
  26. package/dist/src/functions/api/queries.js +24 -25
  27. package/dist/src/functions/api/queries.js.map +1 -1
  28. package/dist/src/functions/api/student.js +62 -72
  29. package/dist/src/functions/api/student.js.map +1 -1
  30. package/dist/src/functions/api/teacher.js +104 -111
  31. package/dist/src/functions/api/teacher.js.map +1 -1
  32. package/dist/src/functions/api/trpc.js +18 -0
  33. package/dist/src/functions/api/trpc.js.map +1 -0
  34. package/dist/src/functions/submissions.js +1 -1
  35. package/dist/src/functions/submissions.js.map +1 -1
  36. package/dist/src/models/user.js +2 -2
  37. package/dist/src/models/user.js.map +1 -1
  38. package/dist/src/models/website.js +1 -1
  39. package/dist/src/models/website.js.map +1 -1
  40. package/dist/src/vite/preset.js +3 -4
  41. package/dist/src/vite/preset.js.map +1 -1
  42. package/dist/src/web/admin/announcements.js +9 -10
  43. package/dist/src/web/admin/announcements.js.map +1 -1
  44. package/dist/src/web/admin/school-table.js +7 -8
  45. package/dist/src/web/admin/school-table.js.map +1 -1
  46. package/dist/src/web/common/api.js +32 -35
  47. package/dist/src/web/common/api.js.map +1 -1
  48. package/dist/src/web/common/converters.js +35 -36
  49. package/dist/src/web/common/converters.js.map +1 -1
  50. package/dist/src/web/common/token-login.js +11 -11
  51. package/dist/src/web/common/token-login.js.map +1 -1
  52. package/dist/src/web/entry/admin.js +5 -7
  53. package/dist/src/web/entry/admin.js.map +1 -1
  54. package/dist/src/web/entry/student-restoring.js +18 -20
  55. package/dist/src/web/entry/student-restoring.js.map +1 -1
  56. package/dist/src/web/entry/student.js +70 -76
  57. package/dist/src/web/entry/student.js.map +1 -1
  58. package/dist/src/web/entry/teacher.js +68 -68
  59. package/dist/src/web/entry/teacher.js.map +1 -1
  60. package/dist/src/web/hooks/collection.js +29 -30
  61. package/dist/src/web/hooks/collection.js.map +1 -1
  62. package/dist/src/web/hooks/document.js +35 -36
  63. package/dist/src/web/hooks/document.js.map +1 -1
  64. package/dist/src/web/hooks/subscription.js +26 -22
  65. package/dist/src/web/hooks/subscription.js.map +1 -1
  66. package/package.json +29 -25
  67. package/dist/src/functions/api/schema.js +0 -42
  68. package/dist/src/functions/api/schema.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"submissions.js","sources":["../../../src/functions/submissions.ts"],"sourcesContent":["import type { DocumentSnapshot } from \"firebase-admin/firestore\";\nimport type { Change, FirestoreAuthEvent } from \"firebase-functions/firestore\";\n\nimport { db } from \"./common\";\n\nexport async function studentUpdatedHandler(\n event: FirestoreAuthEvent<Change<DocumentSnapshot> | undefined>,\n) {\n const data = event.data?.after;\n if (!data) return;\n\n await db.collection(\"submissions\").add({\n authType: event.authType,\n authId: event.authId,\n studentId: data.id,\n student: data.data(),\n submittedAt: event.time,\n });\n}\n"],"names":["studentUpdatedHandler","event","data","db"],"mappings":";AAKA,eAAsBA,EACpBC,GACA;AACA,QAAMC,IAAOD,EAAM,MAAM;AACzB,EAAKC,KAEL,MAAMC,EAAG,WAAW,aAAa,EAAE,IAAI;AAAA,IACrC,UAAUF,EAAM;AAAA,IAChB,QAAQA,EAAM;AAAA,IACd,WAAWC,EAAK;AAAA,IAChB,SAASA,EAAK,KAAA;AAAA,IACd,aAAaD,EAAM;AAAA,EAAA,CACpB;AACH;"}
1
+ {"version":3,"file":"submissions.js","sources":["../../../src/functions/submissions.ts"],"sourcesContent":["import type { DocumentSnapshot } from \"firebase-admin/firestore\";\nimport type { Change, FirestoreAuthEvent } from \"firebase-functions/firestore\";\n\nimport { db } from \"./common\";\n\nexport async function studentUpdatedHandler(\n event: FirestoreAuthEvent<Change<DocumentSnapshot> | undefined>,\n) {\n const data = event.data?.after;\n if (!data?.exists) return;\n\n await db.collection(\"submissions\").add({\n authType: event.authType,\n authId: event.authId,\n studentId: data.id,\n student: data.data(),\n submittedAt: event.time,\n });\n}\n"],"names":["studentUpdatedHandler","event","data","db"],"mappings":";AAKA,eAAsBA,EACpBC,GACA;AACA,QAAMC,IAAOD,EAAM,MAAM;AACzB,EAAKC,GAAM,UAEX,MAAMC,EAAG,WAAW,aAAa,EAAE,IAAI;AAAA,IACrC,UAAUF,EAAM;AAAA,IAChB,QAAQA,EAAM;AAAA,IACd,WAAWC,EAAK;AAAA,IAChB,SAASA,EAAK,KAAA;AAAA,IACd,aAAaD,EAAM;AAAA,EAAA,CACpB;AACH;"}
@@ -1,5 +1,5 @@
1
1
  import r from "zod";
2
- const n = r.object({
2
+ const t = r.strictObject({
3
3
  id: r.string(),
4
4
  name: r.string(),
5
5
  username: r.string(),
@@ -7,6 +7,6 @@ const n = r.object({
7
7
  role: r.enum(["admin", "teacher"])
8
8
  });
9
9
  export {
10
- n as userSchema
10
+ t as userSchema
11
11
  };
12
12
  //# sourceMappingURL=user.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"user.js","sources":["../../../src/models/user.ts"],"sourcesContent":["import z from \"zod\";\n\nexport const userSchema = z.object({\n id: z.string(),\n name: z.string(),\n username: z.string(),\n password: z.string(),\n role: z.enum([\"admin\", \"teacher\"]),\n});\n\nexport type User = z.infer<typeof userSchema>;\n"],"names":["userSchema","z"],"mappings":";AAEO,MAAMA,IAAaC,EAAE,OAAO;AAAA,EACjC,IAAIA,EAAE,OAAA;AAAA,EACN,MAAMA,EAAE,OAAA;AAAA,EACR,UAAUA,EAAE,OAAA;AAAA,EACZ,UAAUA,EAAE,OAAA;AAAA,EACZ,MAAMA,EAAE,KAAK,CAAC,SAAS,SAAS,CAAC;AACnC,CAAC;"}
1
+ {"version":3,"file":"user.js","sources":["../../../src/models/user.ts"],"sourcesContent":["import z from \"zod\";\n\nexport const userSchema = z.strictObject({\n id: z.string(),\n name: z.string(),\n username: z.string(),\n password: z.string(),\n role: z.enum([\"admin\", \"teacher\"]),\n});\n\nexport type User = z.infer<typeof userSchema>;\n"],"names":["userSchema","z"],"mappings":";AAEO,MAAMA,IAAaC,EAAE,aAAa;AAAA,EACvC,IAAIA,EAAE,OAAA;AAAA,EACN,MAAMA,EAAE,OAAA;AAAA,EACR,UAAUA,EAAE,OAAA;AAAA,EACZ,UAAUA,EAAE,OAAA;AAAA,EACZ,MAAMA,EAAE,KAAK,CAAC,SAAS,SAAS,CAAC;AACnC,CAAC;"}
@@ -1,5 +1,5 @@
1
1
  import t from "zod";
2
- const r = t.object({
2
+ const r = t.strictObject({
3
3
  id: t.string(),
4
4
  origin: t.string(),
5
5
  title: t.string(),
@@ -1 +1 @@
1
- {"version":3,"file":"website.js","sources":["../../../src/models/website.ts"],"sourcesContent":["import z from \"zod\";\n\nexport const websiteSchema = z.object({\n id: z.string(),\n origin: z.string(),\n title: z.string(),\n contests: z.string().array(),\n});\n\nexport type Website = z.infer<typeof websiteSchema>;\n"],"names":["websiteSchema","z"],"mappings":";AAEO,MAAMA,IAAgBC,EAAE,OAAO;AAAA,EACpC,IAAIA,EAAE,OAAA;AAAA,EACN,QAAQA,EAAE,OAAA;AAAA,EACV,OAAOA,EAAE,OAAA;AAAA,EACT,UAAUA,EAAE,OAAA,EAAS,MAAA;AACvB,CAAC;"}
1
+ {"version":3,"file":"website.js","sources":["../../../src/models/website.ts"],"sourcesContent":["import z from \"zod\";\n\nexport const websiteSchema = z.strictObject({\n id: z.string(),\n origin: z.string(),\n title: z.string(),\n contests: z.string().array(),\n});\n\nexport type Website = z.infer<typeof websiteSchema>;\n"],"names":["websiteSchema","z"],"mappings":";AAEO,MAAMA,IAAgBC,EAAE,aAAa;AAAA,EAC1C,IAAIA,EAAE,OAAA;AAAA,EACN,QAAQA,EAAE,OAAA;AAAA,EACV,OAAOA,EAAE,OAAA;AAAA,EACT,UAAUA,EAAE,OAAA,EAAS,MAAA;AACvB,CAAC;"}
@@ -1,5 +1,5 @@
1
1
  import { defaultClientMainFields as e, defaultClientConditions as i } from "vite";
2
- function r() {
2
+ function t() {
3
3
  return {
4
4
  name: "quizms:firebase-preset",
5
5
  apply: "build",
@@ -7,7 +7,6 @@ function r() {
7
7
  return {
8
8
  environments: {
9
9
  firebase: {
10
- ssr: !1,
11
10
  resolve: {
12
11
  conditions: [...i],
13
12
  mainFields: [...e],
@@ -16,7 +15,7 @@ function r() {
16
15
  build: {
17
16
  rollupOptions: {
18
17
  input: {
19
- index: "virtual:quizms-entry?@olinfo/quizms-firebase/entry"
18
+ index: "virtual:quizms-entry?id=@olinfo/quizms-firebase/entry"
20
19
  }
21
20
  }
22
21
  }
@@ -27,6 +26,6 @@ function r() {
27
26
  };
28
27
  }
29
28
  export {
30
- r as default
29
+ t as default
31
30
  };
32
31
  //# sourceMappingURL=preset.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"preset.js","sources":["../../../src/vite/preset.ts"],"sourcesContent":["import { defaultClientConditions, defaultClientMainFields, type PluginOption } from \"vite\";\n\nexport default function firebasePreset(): PluginOption {\n return {\n name: \"quizms:firebase-preset\",\n apply: \"build\",\n config() {\n return {\n environments: {\n firebase: {\n ssr: false,\n resolve: {\n conditions: [...defaultClientConditions],\n mainFields: [...defaultClientMainFields],\n noExternal: true,\n },\n build: {\n rollupOptions: {\n input: {\n index: \"virtual:quizms-entry?@olinfo/quizms-firebase/entry\",\n },\n },\n },\n },\n },\n };\n },\n } satisfies PluginOption;\n}\n"],"names":["firebasePreset","defaultClientConditions","defaultClientMainFields"],"mappings":";AAEA,SAAwBA,IAA+B;AACrD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AACP,aAAO;AAAA,QACL,cAAc;AAAA,UACZ,UAAU;AAAA,YACR,KAAK;AAAA,YACL,SAAS;AAAA,cACP,YAAY,CAAC,GAAGC,CAAuB;AAAA,cACvC,YAAY,CAAC,GAAGC,CAAuB;AAAA,cACvC,YAAY;AAAA,YAAA;AAAA,YAEd,OAAO;AAAA,cACL,eAAe;AAAA,gBACb,OAAO;AAAA,kBACL,OAAO;AAAA,gBAAA;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IAEJ;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"preset.js","sources":["../../../src/vite/preset.ts"],"sourcesContent":["import { defaultClientConditions, defaultClientMainFields, type PluginOption } from \"vite\";\n\nexport default function firebasePreset(): PluginOption {\n return {\n name: \"quizms:firebase-preset\",\n apply: \"build\",\n config() {\n return {\n environments: {\n firebase: {\n resolve: {\n conditions: [...defaultClientConditions],\n mainFields: [...defaultClientMainFields],\n noExternal: true,\n },\n build: {\n rollupOptions: {\n input: {\n index: \"virtual:quizms-entry?id=@olinfo/quizms-firebase/entry\",\n },\n },\n },\n },\n },\n };\n },\n } satisfies PluginOption;\n}\n"],"names":["firebasePreset","defaultClientConditions","defaultClientMainFields"],"mappings":";AAEA,SAAwBA,IAA+B;AACrD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AACP,aAAO;AAAA,QACL,cAAc;AAAA,UACZ,UAAU;AAAA,YACR,SAAS;AAAA,cACP,YAAY,CAAC,GAAGC,CAAuB;AAAA,cACvC,YAAY,CAAC,GAAGC,CAAuB;AAAA,cACvC,YAAY;AAAA,YAAA;AAAA,YAEd,OAAO;AAAA,cACL,eAAe;AAAA,gBACb,OAAO;AAAA,kBACL,OAAO;AAAA,gBAAA;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IAEJ;AAAA,EAAA;AAEJ;"}
@@ -1,7 +1,7 @@
1
- import { jsxs as o, Fragment as u, jsx as e } from "react/jsx-runtime";
2
- import { useRef as f, Fragment as p, forwardRef as g, useReducer as h } from "react";
1
+ import { jsxs as o, Fragment as f, jsx as e } from "react/jsx-runtime";
2
+ import { useRef as p, Fragment as u, forwardRef as g, useReducer as h } from "react";
3
3
  import { randomId as A } from "@olinfo/quizms/utils";
4
- import { DateTime as x, CardActions as w, Button as b, Modal as y, Form as N, SelectField as z, TextField as F, TextAreaField as I, SubmitButton as v } from "@olinfo/react-components";
4
+ import { DateTime as x, CardActions as w, Button as y, Modal as N, Form as b, SelectField as z, TextField as F, TextAreaField as I, SubmitButton as v } from "@olinfo/react-components";
5
5
  import { Info as C, TriangleAlert as M, CircleX as T } from "lucide-react";
6
6
  import S from "react-markdown";
7
7
  import { announcementConverter as B } from "../common/converters.js";
@@ -14,15 +14,14 @@ import { useCollection as R } from "../hooks/collection.js";
14
14
  import "firebase/firestore";
15
15
  import { useAdmin as s } from "./context.js";
16
16
  function W() {
17
- const { contest: a } = s(), t = f(null), [r, n] = R("announcements", B, {
17
+ const { contest: a } = s(), t = p(null), [r, n] = R("announcements", B, {
18
18
  arrayConstraints: {
19
19
  contestIds: a.id
20
20
  },
21
- orderBy: ["createdAt", "desc"],
22
- subscribe: !0
21
+ orderBy: ["createdAt", "desc"]
23
22
  });
24
- return /* @__PURE__ */ o(u, { children: [
25
- /* @__PURE__ */ e("div", { className: "prose mt-4 max-w-none", children: r.map((i) => /* @__PURE__ */ o(p, { children: [
23
+ return /* @__PURE__ */ o(f, { children: [
24
+ /* @__PURE__ */ e("div", { className: "prose mt-4 max-w-none", children: r.map((i) => /* @__PURE__ */ o(u, { children: [
26
25
  /* @__PURE__ */ o("h4", { className: "flex items-center gap-3", children: [
27
26
  i.level === "info" && /* @__PURE__ */ e(C, { size: 20, className: "text-info" }),
28
27
  i.level === "warning" && /* @__PURE__ */ e(M, { size: 20, className: "text-warning" }),
@@ -33,13 +32,13 @@ function W() {
33
32
  /* @__PURE__ */ e(S, { children: i.body }),
34
33
  /* @__PURE__ */ e("hr", { className: "my-5 last:hidden" })
35
34
  ] }, i.id)) }),
36
- /* @__PURE__ */ e(w, { children: /* @__PURE__ */ e(b, { className: "btn-primary", onClick: () => t.current?.showModal(), children: "Aggiungi comunicazione" }) }),
35
+ /* @__PURE__ */ e(w, { children: /* @__PURE__ */ e(y, { className: "btn-primary", onClick: () => t.current?.showModal(), children: "Aggiungi comunicazione" }) }),
37
36
  /* @__PURE__ */ e(j, { ref: t, addAnnouncement: n })
38
37
  ] });
39
38
  }
40
39
  const j = g(function({ addAnnouncement: t }, r) {
41
40
  const { contest: n } = s(), [i, c] = h((l) => l + 1, 0);
42
- return /* @__PURE__ */ e(y, { ref: r, title: "Aggiungi comunicazione", children: /* @__PURE__ */ o(N, { onSubmit: async ({
41
+ return /* @__PURE__ */ e(N, { ref: r, title: "Aggiungi comunicazione", children: /* @__PURE__ */ o(b, { onSubmit: async ({
43
42
  level: l,
44
43
  title: m,
45
44
  body: d
@@ -1 +1 @@
1
- {"version":3,"file":"announcements.js","sources":["../../../../src/web/admin/announcements.tsx"],"sourcesContent":["import { Fragment, forwardRef, type Ref, useReducer, useRef } from \"react\";\n\nimport type { Announcement } from \"@olinfo/quizms/models\";\nimport { randomId } from \"@olinfo/quizms/utils\";\nimport {\n Button,\n CardActions,\n DateTime,\n Form,\n Modal,\n SelectField,\n SubmitButton,\n TextAreaField,\n TextField,\n} from \"@olinfo/react-components\";\nimport { CircleX, Info, TriangleAlert } from \"lucide-react\";\nimport Markdown from \"react-markdown\";\n\nimport { announcementConverter } from \"~/web/common/converters\";\nimport { useCollection } from \"~/web/hooks\";\n\nimport { useAdmin } from \"./context\";\n\nexport default function Announcements() {\n const { contest } = useAdmin();\n const ref = useRef<HTMLDialogElement>(null);\n\n const [announcements, addAnnouncement] = useCollection(\"announcements\", announcementConverter, {\n arrayConstraints: {\n contestIds: contest.id,\n },\n orderBy: [\"createdAt\", \"desc\"],\n subscribe: true,\n });\n\n return (\n <>\n <div className=\"prose mt-4 max-w-none\">\n {announcements.map((announcement) => (\n <Fragment key={announcement.id}>\n <h4 className=\"flex items-center gap-3\">\n {announcement.level === \"info\" && <Info size={20} className=\"text-info\" />}\n {announcement.level === \"warning\" && (\n <TriangleAlert size={20} className=\"text-warning\" />\n )}\n {announcement.level === \"error\" && <CircleX size={20} className=\"text-error\" />}\n {announcement.title}\n </h4>\n <small>\n <DateTime date={announcement.createdAt} />\n </small>\n <Markdown>{announcement.body}</Markdown>\n <hr className=\"my-5 last:hidden\" />\n </Fragment>\n ))}\n </div>\n <CardActions>\n <Button className=\"btn-primary\" onClick={() => ref.current?.showModal()}>\n Aggiungi comunicazione\n </Button>\n </CardActions>\n <AnnouncementModal ref={ref} addAnnouncement={addAnnouncement} />\n </>\n );\n}\n\nconst AnnouncementModal = forwardRef(function AnnouncementModal(\n { addAnnouncement }: { addAnnouncement: (announcement: Announcement) => Promise<void> },\n ref: Ref<HTMLDialogElement>,\n) {\n const { contest } = useAdmin();\n const [id, nextId] = useReducer((prev) => prev + 1, 0);\n\n const finalize = async ({\n level,\n title,\n body,\n }: Pick<Announcement, \"level\" | \"title\" | \"body\">) => {\n try {\n await addAnnouncement({\n id: randomId(),\n createdAt: new Date(),\n contestIds: [contest.id],\n level,\n title,\n body,\n });\n nextId();\n } finally {\n if (ref && \"current\" in ref) {\n ref.current?.close();\n }\n }\n };\n\n return (\n <Modal ref={ref} title=\"Aggiungi comunicazione\">\n <Form key={id} onSubmit={finalize} className=\"!max-w-full\">\n <SelectField\n field=\"level\"\n label=\"Priorità\"\n options={{ info: \"Normale\", warning: \"Alta\" }}\n placeholder=\"Seleziona la priorità\"\n />\n <TextField field=\"title\" label=\"Titolo\" placeholder=\"Inserisci il titolo\" />\n <TextAreaField\n field=\"body\"\n label=\"Messaggio\"\n placeholder=\"Inserisci il messaggio\"\n rows={5}\n />\n <SubmitButton>Aggiungi</SubmitButton>\n </Form>\n </Modal>\n );\n});\n"],"names":["Announcements","contest","useAdmin","ref","useRef","announcements","addAnnouncement","useCollection","announcementConverter","jsxs","Fragment","jsx","announcement","Info","TriangleAlert","CircleX","DateTime","Markdown","CardActions","Button","AnnouncementModal","forwardRef","id","nextId","useReducer","prev","Modal","Form","level","title","body","randomId","SelectField","TextField","TextAreaField","SubmitButton"],"mappings":"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"}
1
+ {"version":3,"file":"announcements.js","sources":["../../../../src/web/admin/announcements.tsx"],"sourcesContent":["import { Fragment, forwardRef, type Ref, useReducer, useRef } from \"react\";\n\nimport type { Announcement } from \"@olinfo/quizms/models\";\nimport { randomId } from \"@olinfo/quizms/utils\";\nimport {\n Button,\n CardActions,\n DateTime,\n Form,\n Modal,\n SelectField,\n SubmitButton,\n TextAreaField,\n TextField,\n} from \"@olinfo/react-components\";\nimport { CircleX, Info, TriangleAlert } from \"lucide-react\";\nimport Markdown from \"react-markdown\";\n\nimport { announcementConverter } from \"~/web/common/converters\";\nimport { useCollection } from \"~/web/hooks\";\n\nimport { useAdmin } from \"./context\";\n\nexport default function Announcements() {\n const { contest } = useAdmin();\n const ref = useRef<HTMLDialogElement>(null);\n\n const [announcements, addAnnouncement] = useCollection(\"announcements\", announcementConverter, {\n arrayConstraints: {\n contestIds: contest.id,\n },\n orderBy: [\"createdAt\", \"desc\"],\n });\n\n return (\n <>\n <div className=\"prose mt-4 max-w-none\">\n {announcements.map((announcement) => (\n <Fragment key={announcement.id}>\n <h4 className=\"flex items-center gap-3\">\n {announcement.level === \"info\" && <Info size={20} className=\"text-info\" />}\n {announcement.level === \"warning\" && (\n <TriangleAlert size={20} className=\"text-warning\" />\n )}\n {announcement.level === \"error\" && <CircleX size={20} className=\"text-error\" />}\n {announcement.title}\n </h4>\n <small>\n <DateTime date={announcement.createdAt} />\n </small>\n <Markdown>{announcement.body}</Markdown>\n <hr className=\"my-5 last:hidden\" />\n </Fragment>\n ))}\n </div>\n <CardActions>\n <Button className=\"btn-primary\" onClick={() => ref.current?.showModal()}>\n Aggiungi comunicazione\n </Button>\n </CardActions>\n <AnnouncementModal ref={ref} addAnnouncement={addAnnouncement} />\n </>\n );\n}\n\nconst AnnouncementModal = forwardRef(function AnnouncementModal(\n { addAnnouncement }: { addAnnouncement: (announcement: Announcement) => Promise<void> },\n ref: Ref<HTMLDialogElement>,\n) {\n const { contest } = useAdmin();\n const [id, nextId] = useReducer((prev) => prev + 1, 0);\n\n const finalize = async ({\n level,\n title,\n body,\n }: Pick<Announcement, \"level\" | \"title\" | \"body\">) => {\n try {\n await addAnnouncement({\n id: randomId(),\n createdAt: new Date(),\n contestIds: [contest.id],\n level,\n title,\n body,\n });\n nextId();\n } finally {\n if (ref && \"current\" in ref) {\n ref.current?.close();\n }\n }\n };\n\n return (\n <Modal ref={ref} title=\"Aggiungi comunicazione\">\n <Form key={id} onSubmit={finalize} className=\"!max-w-full\">\n <SelectField\n field=\"level\"\n label=\"Priorità\"\n options={{ info: \"Normale\", warning: \"Alta\" }}\n placeholder=\"Seleziona la priorità\"\n />\n <TextField field=\"title\" label=\"Titolo\" placeholder=\"Inserisci il titolo\" />\n <TextAreaField\n field=\"body\"\n label=\"Messaggio\"\n placeholder=\"Inserisci il messaggio\"\n rows={5}\n />\n <SubmitButton>Aggiungi</SubmitButton>\n </Form>\n </Modal>\n );\n});\n"],"names":["Announcements","contest","useAdmin","ref","useRef","announcements","addAnnouncement","useCollection","announcementConverter","jsxs","Fragment","jsx","announcement","Info","TriangleAlert","CircleX","DateTime","Markdown","CardActions","Button","AnnouncementModal","forwardRef","id","nextId","useReducer","prev","Modal","Form","level","title","body","randomId","SelectField","TextField","TextAreaField","SubmitButton"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as e } from "react/jsx-runtime";
2
- import { useMemo as s, Suspense as r } from "react";
2
+ import { useMemo as s, Suspense as o } from "react";
3
3
  import { AgGrid as d, Loading as c } from "@olinfo/quizms/components";
4
4
  import { participationConverter as f } from "../common/converters.js";
5
5
  import "firebase/auth";
@@ -12,18 +12,17 @@ import "firebase/firestore";
12
12
  import { useCount as p } from "../hooks/count.js";
13
13
  import { useAdmin as u } from "./context.js";
14
14
  function T() {
15
- const { contest: i } = u(), [o, n] = m(
15
+ const { contest: i } = u(), [r, n] = m(
16
16
  "participations",
17
17
  f,
18
18
  {
19
- constraints: { contestId: i.id },
20
- subscribe: !0
19
+ constraints: { contestId: i.id }
21
20
  }
22
21
  ), a = s(() => h(), []);
23
- return /* @__PURE__ */ e(r, { fallback: /* @__PURE__ */ e(c, {}), children: /* @__PURE__ */ e("div", { className: "relative grow p-2", children: /* @__PURE__ */ e("div", { className: "absolute inset-0", children: /* @__PURE__ */ e(
22
+ return /* @__PURE__ */ e(o, { fallback: /* @__PURE__ */ e(c, {}), children: /* @__PURE__ */ e("div", { className: "relative grow p-2", children: /* @__PURE__ */ e("div", { className: "absolute inset-0", children: /* @__PURE__ */ e(
24
23
  d,
25
24
  {
26
- rowData: o,
25
+ rowData: r,
27
26
  getRowId: (t) => t.data.id,
28
27
  columnDefs: a,
29
28
  onCellEditRequest: async (t) => {
@@ -55,7 +54,7 @@ function h() {
55
54
  sortable: !1,
56
55
  cellRenderer: ({ data: i }) => {
57
56
  if (i)
58
- return /* @__PURE__ */ e(r, { children: /* @__PURE__ */ e(b, { participationId: i.id }) });
57
+ return /* @__PURE__ */ e(o, { children: /* @__PURE__ */ e(w, { participationId: i.id }) });
59
58
  }
60
59
  },
61
60
  {
@@ -68,7 +67,7 @@ function h() {
68
67
  }
69
68
  ];
70
69
  }
71
- function b({ participationId: i }) {
70
+ function w({ participationId: i }) {
72
71
  return p(`participations/${i}/students`, {
73
72
  constraints: { disabled: !1 }
74
73
  });
@@ -1 +1 @@
1
- {"version":3,"file":"school-table.js","sources":["../../../../src/web/admin/school-table.tsx"],"sourcesContent":["import { Suspense, useMemo } from \"react\";\n\nimport { AgGrid, Loading } from \"@olinfo/quizms/components\";\nimport type { Participation, Student } from \"@olinfo/quizms/models\";\nimport type { CellEditRequestEvent, ColDef, ICellRendererParams } from \"ag-grid-community\";\n\nimport { participationConverter } from \"~/web/common/converters\";\nimport { useCollection } from \"~/web/hooks\";\nimport { useCount } from \"~/web/hooks/count\";\n\nimport { useAdmin } from \"./context\";\n\nexport function SchoolTable() {\n const { contest } = useAdmin();\n\n const [participations, setParticipation] = useCollection(\n \"participations\",\n participationConverter,\n {\n constraints: { contestId: contest.id },\n subscribe: true,\n },\n );\n\n const colDefs = useMemo(() => columnDefinition(), []);\n\n const onCellEditRequest = async (ev: CellEditRequestEvent) => {\n const participation = ev.data as Participation;\n if (ev.colDef.field === \"finalized\") {\n await setParticipation({ ...participation, finalized: ev.newValue });\n }\n ev.api.refreshCells({ force: true });\n };\n\n return (\n <Suspense fallback={<Loading />}>\n <div className=\"relative grow p-2\">\n <div className=\"absolute inset-0\">\n <AgGrid\n rowData={participations}\n getRowId={(row) => (row.data as Participation).id}\n columnDefs={colDefs}\n onCellEditRequest={onCellEditRequest}\n />\n </div>\n </div>\n </Suspense>\n );\n}\n\nfunction columnDefinition(): ColDef[] {\n return [\n {\n field: \"schoolId\",\n headerName: \"ID\",\n width: 150,\n filter: true,\n },\n {\n field: \"name\",\n headerName: \"Nome\",\n minWidth: 200,\n flex: 1,\n filter: true,\n },\n {\n field: \"count\",\n headerName: \"Studenti\",\n width: 100,\n sortable: false,\n cellRenderer: ({ data }: ICellRendererParams<Participation>) => {\n if (!data) return;\n return (\n <Suspense>\n <Count participationId={data.id} />\n </Suspense>\n );\n },\n },\n {\n field: \"finalized\",\n headerName: \"Finalizzato\",\n width: 100,\n filter: true,\n cellDataType: \"boolean\",\n editable: true,\n },\n ];\n}\n\nfunction Count({ participationId }: { participationId: string }) {\n return useCount<Student>(`participations/${participationId}/students`, {\n constraints: { disabled: false },\n });\n}\n"],"names":["SchoolTable","contest","useAdmin","participations","setParticipation","useCollection","participationConverter","colDefs","useMemo","columnDefinition","jsx","Suspense","Loading","AgGrid","row","ev","participation","data","Count","participationId","useCount"],"mappings":";;;;;;;;;;;;;AAYO,SAASA,IAAc;AAC5B,QAAM,EAAE,SAAAC,EAAA,IAAYC,EAAA,GAEd,CAACC,GAAgBC,CAAgB,IAAIC;AAAA,IACzC;AAAA,IACAC;AAAA,IACA;AAAA,MACE,aAAa,EAAE,WAAWL,EAAQ,GAAA;AAAA,MAClC,WAAW;AAAA,IAAA;AAAA,EACb,GAGIM,IAAUC,EAAQ,MAAMC,EAAA,GAAoB,CAAA,CAAE;AAUpD,SACE,gBAAAC,EAACC,GAAA,EAAS,UAAU,gBAAAD,EAACE,GAAA,CAAA,CAAQ,GAC3B,UAAA,gBAAAF,EAAC,OAAA,EAAI,WAAU,qBACb,UAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,oBACb,UAAA,gBAAAA;AAAA,IAACG;AAAA,IAAA;AAAA,MACC,SAASV;AAAA,MACT,UAAU,CAACW,MAASA,EAAI,KAAuB;AAAA,MAC/C,YAAYP;AAAA,MACZ,mBAhBgB,OAAOQ,MAA6B;AAC5D,cAAMC,IAAgBD,EAAG;AACzB,QAAIA,EAAG,OAAO,UAAU,eACtB,MAAMX,EAAiB,EAAE,GAAGY,GAAe,WAAWD,EAAG,UAAU,GAErEA,EAAG,IAAI,aAAa,EAAE,OAAO,IAAM;AAAA,MACrC;AAAA,IAUU;AAAA,EAAA,EACF,CACF,GACF,GACF;AAEJ;AAEA,SAASN,IAA6B;AACpC,SAAO;AAAA,IACL;AAAA,MACE,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,QAAQ;AAAA,IAAA;AAAA,IAEV;AAAA,MACE,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,MAAM;AAAA,MACN,QAAQ;AAAA,IAAA;AAAA,IAEV;AAAA,MACE,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,UAAU;AAAA,MACV,cAAc,CAAC,EAAE,MAAAQ,QAA+C;AAC9D,YAAKA;AACL,mCACGN,GAAA,EACC,UAAA,gBAAAD,EAACQ,KAAM,iBAAiBD,EAAK,IAAI,EAAA,CACnC;AAAA,MAEJ;AAAA,IAAA;AAAA,IAEF;AAAA,MACE,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,UAAU;AAAA,IAAA;AAAA,EACZ;AAEJ;AAEA,SAASC,EAAM,EAAE,iBAAAC,KAAgD;AAC/D,SAAOC,EAAkB,kBAAkBD,CAAe,aAAa;AAAA,IACrE,aAAa,EAAE,UAAU,GAAA;AAAA,EAAM,CAChC;AACH;"}
1
+ {"version":3,"file":"school-table.js","sources":["../../../../src/web/admin/school-table.tsx"],"sourcesContent":["import { Suspense, useMemo } from \"react\";\n\nimport { AgGrid, Loading } from \"@olinfo/quizms/components\";\nimport type { Participation, Student } from \"@olinfo/quizms/models\";\nimport type { CellEditRequestEvent, ColDef, ICellRendererParams } from \"ag-grid-community\";\n\nimport { participationConverter } from \"~/web/common/converters\";\nimport { useCollection } from \"~/web/hooks\";\nimport { useCount } from \"~/web/hooks/count\";\n\nimport { useAdmin } from \"./context\";\n\nexport function SchoolTable() {\n const { contest } = useAdmin();\n\n const [participations, setParticipation] = useCollection(\n \"participations\",\n participationConverter,\n {\n constraints: { contestId: contest.id },\n },\n );\n\n const colDefs = useMemo(() => columnDefinition(), []);\n\n const onCellEditRequest = async (ev: CellEditRequestEvent) => {\n const participation = ev.data as Participation;\n if (ev.colDef.field === \"finalized\") {\n await setParticipation({ ...participation, finalized: ev.newValue });\n }\n ev.api.refreshCells({ force: true });\n };\n\n return (\n <Suspense fallback={<Loading />}>\n <div className=\"relative grow p-2\">\n <div className=\"absolute inset-0\">\n <AgGrid\n rowData={participations}\n getRowId={(row) => (row.data as Participation).id}\n columnDefs={colDefs}\n onCellEditRequest={onCellEditRequest}\n />\n </div>\n </div>\n </Suspense>\n );\n}\n\nfunction columnDefinition(): ColDef[] {\n return [\n {\n field: \"schoolId\",\n headerName: \"ID\",\n width: 150,\n filter: true,\n },\n {\n field: \"name\",\n headerName: \"Nome\",\n minWidth: 200,\n flex: 1,\n filter: true,\n },\n {\n field: \"count\",\n headerName: \"Studenti\",\n width: 100,\n sortable: false,\n cellRenderer: ({ data }: ICellRendererParams<Participation>) => {\n if (!data) return;\n return (\n <Suspense>\n <Count participationId={data.id} />\n </Suspense>\n );\n },\n },\n {\n field: \"finalized\",\n headerName: \"Finalizzato\",\n width: 100,\n filter: true,\n cellDataType: \"boolean\",\n editable: true,\n },\n ];\n}\n\nfunction Count({ participationId }: { participationId: string }) {\n return useCount<Student>(`participations/${participationId}/students`, {\n constraints: { disabled: false },\n });\n}\n"],"names":["SchoolTable","contest","useAdmin","participations","setParticipation","useCollection","participationConverter","colDefs","useMemo","columnDefinition","jsx","Suspense","Loading","AgGrid","row","ev","participation","data","Count","participationId","useCount"],"mappings":";;;;;;;;;;;;;AAYO,SAASA,IAAc;AAC5B,QAAM,EAAE,SAAAC,EAAA,IAAYC,EAAA,GAEd,CAACC,GAAgBC,CAAgB,IAAIC;AAAA,IACzC;AAAA,IACAC;AAAA,IACA;AAAA,MACE,aAAa,EAAE,WAAWL,EAAQ,GAAA;AAAA,IAAG;AAAA,EACvC,GAGIM,IAAUC,EAAQ,MAAMC,EAAA,GAAoB,CAAA,CAAE;AAUpD,SACE,gBAAAC,EAACC,GAAA,EAAS,UAAU,gBAAAD,EAACE,GAAA,CAAA,CAAQ,GAC3B,UAAA,gBAAAF,EAAC,OAAA,EAAI,WAAU,qBACb,UAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,oBACb,UAAA,gBAAAA;AAAA,IAACG;AAAA,IAAA;AAAA,MACC,SAASV;AAAA,MACT,UAAU,CAACW,MAASA,EAAI,KAAuB;AAAA,MAC/C,YAAYP;AAAA,MACZ,mBAhBgB,OAAOQ,MAA6B;AAC5D,cAAMC,IAAgBD,EAAG;AACzB,QAAIA,EAAG,OAAO,UAAU,eACtB,MAAMX,EAAiB,EAAE,GAAGY,GAAe,WAAWD,EAAG,UAAU,GAErEA,EAAG,IAAI,aAAa,EAAE,OAAO,IAAM;AAAA,MACrC;AAAA,IAUU;AAAA,EAAA,EACF,CACF,GACF,GACF;AAEJ;AAEA,SAASN,IAA6B;AACpC,SAAO;AAAA,IACL;AAAA,MACE,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,QAAQ;AAAA,IAAA;AAAA,IAEV;AAAA,MACE,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,MAAM;AAAA,MACN,QAAQ;AAAA,IAAA;AAAA,IAEV;AAAA,MACE,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,UAAU;AAAA,MACV,cAAc,CAAC,EAAE,MAAAQ,QAA+C;AAC9D,YAAKA;AACL,mCACGN,GAAA,EACC,UAAA,gBAAAD,EAACQ,KAAM,iBAAiBD,EAAK,IAAI,EAAA,CACnC;AAAA,MAEJ;AAAA,IAAA;AAAA,IAEF;AAAA,MACE,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,UAAU;AAAA,IAAA;AAAA,EACZ;AAEJ;AAEA,SAASC,EAAM,EAAE,iBAAAC,KAAgD;AAC/D,SAAOC,EAAkB,kBAAkBD,CAAe,aAAa;AAAA,IACrE,aAAa,EAAE,UAAU,GAAA;AAAA,EAAM,CAChC;AACH;"}
@@ -1,43 +1,40 @@
1
- import { validate as c } from "@olinfo/quizms/utils";
2
- import { getFunctions as f, httpsCallable as g } from "firebase/functions";
3
- import n from "zod";
4
- const d = n.discriminatedUnion("success", [
5
- n.object({ success: n.literal(!0), data: n.any() }),
6
- n.object({ success: n.literal(!1), errorCode: n.string(), error: n.string() })
7
- ]);
8
- async function a(t, i, r, s) {
9
- const p = f(t.app, window.location.origin), u = g(p, "api");
10
- let e;
11
- try {
12
- e = await u({ action: i, ...r });
13
- } catch (l) {
14
- throw new Error("Server non raggiungibile", { cause: l });
15
- }
16
- const o = c(d, e.data);
17
- if (!o.success)
18
- throw new Error(o.error);
19
- return c(n.object(s), o.data);
1
+ import { createTRPCClient as a, httpLink as e } from "@trpc/client";
2
+ import { getIdToken as r } from "firebase/auth";
3
+ import o from "superjson";
4
+ const i = a({
5
+ links: [
6
+ e({
7
+ url: "/api",
8
+ transformer: o,
9
+ async headers({ op: t }) {
10
+ const n = t.context?.user;
11
+ return n ? {
12
+ Authorization: `Bearer ${await r(n)}`
13
+ } : {};
14
+ }
15
+ })
16
+ ]
17
+ });
18
+ function m(t) {
19
+ return i.teacherLogin.mutate(t);
20
20
  }
21
- function b(t, i, r) {
22
- return a(t, `${i}Login`, r, { token: n.string() });
21
+ function f(t) {
22
+ return i.studentLogin.mutate(t);
23
23
  }
24
- function y(t, i) {
25
- return a(t, "studentLogin", i, { token: n.string() });
24
+ async function h(t, n) {
25
+ await i.teacherStartParticipation.mutate({ participationId: n }, { context: { user: t } });
26
26
  }
27
- async function P(t, i) {
28
- await a(t, "teacherStartParticipation", { participationId: i }, {});
27
+ async function P(t, n) {
28
+ await i.teacherStopParticipation.mutate({ participationId: n }, { context: { user: t } });
29
29
  }
30
- async function S(t, i) {
31
- await a(t, "teacherStopParticipation", { participationId: i }, {});
32
- }
33
- async function j(t, i) {
34
- await a(t, "teacherFinalizeParticipation", { participationId: i }, {});
30
+ async function g(t, n) {
31
+ await i.teacherFinalizeParticipation.mutate({ participationId: n }, { context: { user: t } });
35
32
  }
36
33
  export {
37
- j as finalizeParticipation,
38
- b as login,
39
- P as startParticipation,
40
- S as stopParticipation,
41
- y as studentLogin
34
+ g as finalizeParticipation,
35
+ h as startParticipation,
36
+ P as stopParticipation,
37
+ f as studentLogin,
38
+ m as teacherLogin
42
39
  };
43
40
  //# sourceMappingURL=api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sources":["../../../../src/web/common/api.ts"],"sourcesContent":["import type { Student } from \"@olinfo/quizms/models\";\nimport { validate } from \"@olinfo/quizms/utils\";\nimport type { Firestore } from \"firebase/firestore\";\nimport { getFunctions, type HttpsCallableResult, httpsCallable } from \"firebase/functions\";\nimport z, { type ZodObject, type ZodRawShape } from \"zod\";\n\nconst responseSchema = z.discriminatedUnion(\"success\", [\n z.object({ success: z.literal(true), data: z.any() }),\n z.object({ success: z.literal(false), errorCode: z.string(), error: z.string() }),\n]);\n\nasync function api<Shape extends ZodRawShape>(\n db: Firestore,\n action: string,\n body: any,\n shape: Shape,\n): Promise<z.infer<ZodObject<Shape>>> {\n const functions = getFunctions(db.app, window.location.origin);\n const apiCallable = httpsCallable(functions, \"api\");\n\n let resp: HttpsCallableResult;\n try {\n resp = await apiCallable({ action, ...body });\n } catch (err: any) {\n throw new Error(\"Server non raggiungibile\", { cause: err });\n }\n\n const data = validate(responseSchema, resp.data);\n if (!data.success) {\n throw new Error(data.error);\n }\n return validate(z.object(shape), data.data);\n}\n\nexport function login(db: Firestore, role: string, body: { username: string; password: string }) {\n return api(db, `${role}Login`, body, { token: z.string() });\n}\n\nexport function studentLogin(\n db: Firestore,\n body: {\n contestId: string;\n token: string;\n userData: Student[\"userData\"];\n extraData: Record<string, any>;\n },\n) {\n return api(db, \"studentLogin\", body, { token: z.string() });\n}\n\nexport async function startParticipation(db: Firestore, participationId: string) {\n await api(db, \"teacherStartParticipation\", { participationId }, {});\n}\n\nexport async function stopParticipation(db: Firestore, participationId: string) {\n await api(db, \"teacherStopParticipation\", { participationId }, {});\n}\n\nexport async function finalizeParticipation(db: Firestore, participationId: string) {\n await api(db, \"teacherFinalizeParticipation\", { participationId }, {});\n}\n"],"names":["responseSchema","z","api","db","action","body","shape","functions","getFunctions","apiCallable","httpsCallable","resp","err","data","validate","login","role","studentLogin","startParticipation","participationId","stopParticipation","finalizeParticipation"],"mappings":";;;AAMA,MAAMA,IAAiBC,EAAE,mBAAmB,WAAW;AAAA,EACrDA,EAAE,OAAO,EAAE,SAASA,EAAE,QAAQ,EAAI,GAAG,MAAMA,EAAE,IAAA,EAAI,CAAG;AAAA,EACpDA,EAAE,OAAO,EAAE,SAASA,EAAE,QAAQ,EAAK,GAAG,WAAWA,EAAE,UAAU,OAAOA,EAAE,OAAA,GAAU;AAClF,CAAC;AAED,eAAeC,EACbC,GACAC,GACAC,GACAC,GACoC;AACpC,QAAMC,IAAYC,EAAaL,EAAG,KAAK,OAAO,SAAS,MAAM,GACvDM,IAAcC,EAAcH,GAAW,KAAK;AAElD,MAAII;AACJ,MAAI;AACF,IAAAA,IAAO,MAAMF,EAAY,EAAE,QAAAL,GAAQ,GAAGC,GAAM;AAAA,EAC9C,SAASO,GAAU;AACjB,UAAM,IAAI,MAAM,4BAA4B,EAAE,OAAOA,GAAK;AAAA,EAC5D;AAEA,QAAMC,IAAOC,EAASd,GAAgBW,EAAK,IAAI;AAC/C,MAAI,CAACE,EAAK;AACR,UAAM,IAAI,MAAMA,EAAK,KAAK;AAE5B,SAAOC,EAASb,EAAE,OAAOK,CAAK,GAAGO,EAAK,IAAI;AAC5C;AAEO,SAASE,EAAMZ,GAAea,GAAcX,GAA8C;AAC/F,SAAOH,EAAIC,GAAI,GAAGa,CAAI,SAASX,GAAM,EAAE,OAAOJ,EAAE,OAAA,EAAO,CAAG;AAC5D;AAEO,SAASgB,EACdd,GACAE,GAMA;AACA,SAAOH,EAAIC,GAAI,gBAAgBE,GAAM,EAAE,OAAOJ,EAAE,OAAA,GAAU;AAC5D;AAEA,eAAsBiB,EAAmBf,GAAegB,GAAyB;AAC/E,QAAMjB,EAAIC,GAAI,6BAA6B,EAAE,iBAAAgB,EAAA,GAAmB,CAAA,CAAE;AACpE;AAEA,eAAsBC,EAAkBjB,GAAegB,GAAyB;AAC9E,QAAMjB,EAAIC,GAAI,4BAA4B,EAAE,iBAAAgB,EAAA,GAAmB,CAAA,CAAE;AACnE;AAEA,eAAsBE,EAAsBlB,GAAegB,GAAyB;AAClF,QAAMjB,EAAIC,GAAI,gCAAgC,EAAE,iBAAAgB,EAAA,GAAmB,CAAA,CAAE;AACvE;"}
1
+ {"version":3,"file":"api.js","sources":["../../../../src/web/common/api.ts"],"sourcesContent":["import type { Student } from \"@olinfo/quizms/models\";\nimport { createTRPCClient, httpLink } from \"@trpc/client\";\nimport { getIdToken, type User } from \"firebase/auth\";\nimport superjson from \"superjson\";\n\nimport type { AppRouter } from \"~/functions/api\";\n\nconst trpc = createTRPCClient<AppRouter>({\n links: [\n httpLink({\n url: \"/api\",\n transformer: superjson,\n async headers({ op }) {\n const user = op.context?.user as User | undefined;\n if (user) {\n const token = await getIdToken(user);\n return {\n Authorization: `Bearer ${token}`,\n };\n }\n return {};\n },\n }),\n ],\n});\n\nexport function teacherLogin(body: { username: string; password: string }) {\n return trpc.teacherLogin.mutate(body);\n}\n\nexport function studentLogin(body: {\n contestId: string;\n token: string;\n userData: Required<NonNullable<Student[\"userData\"]>>;\n extraData: Record<string, any>;\n}) {\n return trpc.studentLogin.mutate(body);\n}\n\nexport async function startParticipation(user: User, participationId: string) {\n await trpc.teacherStartParticipation.mutate({ participationId }, { context: { user } });\n}\n\nexport async function stopParticipation(user: User, participationId: string) {\n await trpc.teacherStopParticipation.mutate({ participationId }, { context: { user } });\n}\n\nexport async function finalizeParticipation(user: User, participationId: string) {\n await trpc.teacherFinalizeParticipation.mutate({ participationId }, { context: { user } });\n}\n"],"names":["trpc","createTRPCClient","httpLink","superjson","op","user","getIdToken","teacherLogin","body","studentLogin","startParticipation","participationId","stopParticipation","finalizeParticipation"],"mappings":";;;AAOA,MAAMA,IAAOC,EAA4B;AAAA,EACvC,OAAO;AAAA,IACLC,EAAS;AAAA,MACP,KAAK;AAAA,MACL,aAAaC;AAAA,MACb,MAAM,QAAQ,EAAE,IAAAC,KAAM;AACpB,cAAMC,IAAOD,EAAG,SAAS;AACzB,eAAIC,IAEK;AAAA,UACL,eAAe,UAFH,MAAMC,EAAWD,CAAI,CAEH;AAAA,QAAA,IAG3B,CAAA;AAAA,MACT;AAAA,IAAA,CACD;AAAA,EAAA;AAEL,CAAC;AAEM,SAASE,EAAaC,GAA8C;AACzE,SAAOR,EAAK,aAAa,OAAOQ,CAAI;AACtC;AAEO,SAASC,EAAaD,GAK1B;AACD,SAAOR,EAAK,aAAa,OAAOQ,CAAI;AACtC;AAEA,eAAsBE,EAAmBL,GAAYM,GAAyB;AAC5E,QAAMX,EAAK,0BAA0B,OAAO,EAAE,iBAAAW,EAAA,GAAmB,EAAE,SAAS,EAAE,MAAAN,EAAA,GAAQ;AACxF;AAEA,eAAsBO,EAAkBP,GAAYM,GAAyB;AAC3E,QAAMX,EAAK,yBAAyB,OAAO,EAAE,iBAAAW,EAAA,GAAmB,EAAE,SAAS,EAAE,MAAAN,EAAA,GAAQ;AACvF;AAEA,eAAsBQ,EAAsBR,GAAYM,GAAyB;AAC/E,QAAMX,EAAK,6BAA6B,OAAO,EAAE,iBAAAW,EAAA,GAAmB,EAAE,SAAS,EAAE,MAAAN,EAAA,GAAQ;AAC3F;"}
@@ -1,59 +1,58 @@
1
- import { announcementSchema as u, contestSchema as c, participationSchema as d, studentRestoreSchema as m, variantSchema as v, studentSchema as S } from "@olinfo/quizms/models";
2
- import { validate as D } from "@olinfo/quizms/utils";
3
- import { serverTimestamp as f, Timestamp as s } from "firebase/firestore";
4
- import { cloneDeepWith as F, omit as Z, isDate as l, isString as C, mapValues as b } from "lodash-es";
5
- import n, { ZodDate as T, ZodObject as w, ZodRecord as A, ZodArray as R, ZodOptional as U, ZodDefault as j, ZodUnion as y, ZodDiscriminatedUnion as O } from "zod";
6
- import { websiteSchema as g } from "../../models/website.js";
7
- function i(t) {
8
- return F(Z(t, "id"), (r) => {
9
- if (l(r))
10
- return s.fromDate(r);
1
+ import { contestSchema as a, participationSchema as f, studentRestoreSchema as u, variantSchema as d, announcementSchema as p, studentSchema as v } from "@olinfo/quizms/models";
2
+ import { validate as F } from "@olinfo/quizms/utils";
3
+ import { serverTimestamp as i, Timestamp as c } from "firebase/firestore";
4
+ import { cloneDeepWith as s, omit as S, isDate as h, isString as C } from "lodash-es";
5
+ import { websiteSchema as D } from "../../models/website.js";
6
+ function o(t) {
7
+ return s(S(t, "id"), (r) => {
8
+ if (h(r))
9
+ return c.fromDate(r);
11
10
  if (C(r))
12
11
  return r.trim();
13
12
  if (r === void 0)
14
13
  return null;
15
14
  });
16
15
  }
17
- function e(t) {
18
- return t instanceof T ? n.instanceof(s).transform((r) => r.toDate()).pipe(t) : t instanceof w ? n.object(b(t.shape, (r) => e(r))) : t instanceof A ? n.record(e(t.element)) : t instanceof R ? e(t.element).array() : t instanceof U ? n.preprocess((r) => r ?? void 0, e(t.unwrap()).optional()) : t instanceof j ? e(t.removeDefault()).pipe(t) : t instanceof y ? n.union(t.options.map((r) => e(r))) : t instanceof O ? n.discriminatedUnion(
19
- t.discriminator,
20
- t.options.map((r) => e(r))
21
- ) : t;
16
+ function T(t) {
17
+ return s(t, (r) => {
18
+ if (r instanceof c)
19
+ return r.toDate();
20
+ });
22
21
  }
23
- function a(t, r) {
24
- const p = { ...r.data({ serverTimestamps: "estimate" }), id: r.id };
25
- return D(e(t), p);
22
+ function n(t, r) {
23
+ const m = T({ ...r.data(), id: r.id });
24
+ return F(t, m);
26
25
  }
27
- function o(t) {
26
+ function e(t) {
28
27
  return {
29
- toFirestore: (r) => i(r),
30
- fromFirestore: (r) => a(t, r)
28
+ toFirestore: (r) => o(r),
29
+ fromFirestore: (r) => n(t, r)
31
30
  };
32
31
  }
33
- const B = o(c), E = o(d), G = o(m), H = o(v), I = o(g), J = {
32
+ const x = e(a), W = e(f), j = e(u), k = e(d), q = e(D), y = {
34
33
  toFirestore(t) {
35
34
  return {
36
- ...i(t),
37
- createdAt: f()
35
+ ...o(t),
36
+ createdAt: i()
38
37
  };
39
38
  },
40
- fromFirestore: (t) => a(u, t)
41
- }, K = {
39
+ fromFirestore: (t) => n(p, t)
40
+ }, z = {
42
41
  toFirestore(t) {
43
42
  return {
44
- ...i(t),
45
- updatedAt: f()
43
+ ...o(t),
44
+ updatedAt: i()
46
45
  };
47
46
  },
48
- fromFirestore: (t) => a(S, t)
47
+ fromFirestore: (t) => n(v, t)
49
48
  };
50
49
  export {
51
- J as announcementConverter,
52
- B as contestConverter,
53
- E as participationConverter,
54
- K as studentConverter,
55
- G as studentRestoreConvert,
56
- H as variantConverter,
57
- I as websiteConverter
50
+ y as announcementConverter,
51
+ x as contestConverter,
52
+ W as participationConverter,
53
+ z as studentConverter,
54
+ j as studentRestoreConvert,
55
+ k as variantConverter,
56
+ q as websiteConverter
58
57
  };
59
58
  //# sourceMappingURL=converters.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"converters.js","sources":["../../../../src/web/common/converters.ts"],"sourcesContent":["import {\n type Announcement,\n announcementSchema,\n contestSchema,\n participationSchema,\n type Student,\n studentRestoreSchema,\n studentSchema,\n variantSchema,\n} from \"@olinfo/quizms/models\";\nimport { validate } from \"@olinfo/quizms/utils\";\nimport {\n type DocumentSnapshot,\n type FirestoreDataConverter,\n serverTimestamp,\n Timestamp,\n} from \"firebase/firestore\";\nimport { cloneDeepWith, isDate, isString, mapValues, omit } from \"lodash-es\";\nimport z, {\n ZodArray,\n ZodDate,\n ZodDefault,\n ZodDiscriminatedUnion,\n ZodObject,\n ZodOptional,\n ZodRecord,\n type ZodType,\n type ZodTypeAny,\n ZodUnion,\n} from \"zod\";\n\nimport { websiteSchema } from \"~/models/website\";\n\nfunction convertToFirestore(data: Record<string, any>) {\n return cloneDeepWith(omit(data, \"id\"), (value) => {\n if (isDate(value)) {\n return Timestamp.fromDate(value);\n }\n if (isString(value)) {\n return value.trim();\n }\n if (value === undefined) {\n return null;\n }\n });\n}\n\nfunction toFirebaseSchema(schema: ZodTypeAny): ZodTypeAny {\n if (schema instanceof ZodDate) {\n return z\n .instanceof(Timestamp)\n .transform((ts) => ts.toDate())\n .pipe(schema);\n }\n if (schema instanceof ZodObject) {\n return z.object(mapValues(schema.shape, (field) => toFirebaseSchema(field)));\n }\n if (schema instanceof ZodRecord) {\n return z.record(toFirebaseSchema(schema.element));\n }\n if (schema instanceof ZodArray) {\n return toFirebaseSchema(schema.element).array();\n }\n if (schema instanceof ZodOptional) {\n return z.preprocess((val) => val ?? undefined, toFirebaseSchema(schema.unwrap()).optional());\n }\n if (schema instanceof ZodDefault) {\n return toFirebaseSchema(schema.removeDefault()).pipe(schema);\n }\n if (schema instanceof ZodUnion) {\n return z.union(schema.options.map((option: ZodTypeAny) => toFirebaseSchema(option)));\n }\n if (schema instanceof ZodDiscriminatedUnion) {\n return z.discriminatedUnion(\n schema.discriminator,\n schema.options.map((option: ZodTypeAny) => toFirebaseSchema(option)),\n );\n }\n return schema;\n}\n\nfunction parse<T>(schema: ZodType<T, any, any>, snapshot: DocumentSnapshot): T {\n const data = { ...snapshot.data({ serverTimestamps: \"estimate\" }), id: snapshot.id };\n return validate(toFirebaseSchema(schema), data);\n}\n\nfunction converter<T extends object>(schema: ZodType<T, any, any>): FirestoreDataConverter<T> {\n return {\n toFirestore: (data) => convertToFirestore(data),\n fromFirestore: (snapshot) => parse(schema, snapshot),\n };\n}\n\nexport const contestConverter = converter(contestSchema);\nexport const participationConverter = converter(participationSchema);\nexport const studentRestoreConvert = converter(studentRestoreSchema);\nexport const variantConverter = converter(variantSchema);\nexport const websiteConverter = converter(websiteSchema);\n\nexport const announcementConverter: FirestoreDataConverter<Announcement> = {\n toFirestore(data) {\n return {\n ...convertToFirestore(data),\n createdAt: serverTimestamp(),\n };\n },\n fromFirestore: (snapshot) => parse(announcementSchema, snapshot),\n};\n\nexport const studentConverter: FirestoreDataConverter<Student> = {\n toFirestore(data) {\n return {\n ...convertToFirestore(data),\n updatedAt: serverTimestamp(),\n };\n },\n fromFirestore: (snapshot) => parse(studentSchema, snapshot),\n};\n"],"names":["convertToFirestore","data","cloneDeepWith","omit","value","isDate","Timestamp","isString","toFirebaseSchema","schema","ZodDate","z","ts","ZodObject","mapValues","field","ZodRecord","ZodArray","ZodOptional","val","ZodDefault","ZodUnion","option","ZodDiscriminatedUnion","parse","snapshot","validate","converter","contestConverter","contestSchema","participationConverter","participationSchema","studentRestoreConvert","studentRestoreSchema","variantConverter","variantSchema","websiteConverter","websiteSchema","announcementConverter","serverTimestamp","announcementSchema","studentConverter","studentSchema"],"mappings":";;;;;;AAiCA,SAASA,EAAmBC,GAA2B;AACrD,SAAOC,EAAcC,EAAKF,GAAM,IAAI,GAAG,CAACG,MAAU;AAChD,QAAIC,EAAOD,CAAK;AACd,aAAOE,EAAU,SAASF,CAAK;AAEjC,QAAIG,EAASH,CAAK;AAChB,aAAOA,EAAM,KAAA;AAEf,QAAIA,MAAU;AACZ,aAAO;AAAA,EAEX,CAAC;AACH;AAEA,SAASI,EAAiBC,GAAgC;AACxD,SAAIA,aAAkBC,IACbC,EACJ,WAAWL,CAAS,EACpB,UAAU,CAACM,MAAOA,EAAG,OAAA,CAAQ,EAC7B,KAAKH,CAAM,IAEZA,aAAkBI,IACbF,EAAE,OAAOG,EAAUL,EAAO,OAAO,CAACM,MAAUP,EAAiBO,CAAK,CAAC,CAAC,IAEzEN,aAAkBO,IACbL,EAAE,OAAOH,EAAiBC,EAAO,OAAO,CAAC,IAE9CA,aAAkBQ,IACbT,EAAiBC,EAAO,OAAO,EAAE,MAAA,IAEtCA,aAAkBS,IACbP,EAAE,WAAW,CAACQ,MAAQA,KAAO,QAAWX,EAAiBC,EAAO,QAAQ,EAAE,SAAA,CAAU,IAEzFA,aAAkBW,IACbZ,EAAiBC,EAAO,cAAA,CAAe,EAAE,KAAKA,CAAM,IAEzDA,aAAkBY,IACbV,EAAE,MAAMF,EAAO,QAAQ,IAAI,CAACa,MAAuBd,EAAiBc,CAAM,CAAC,CAAC,IAEjFb,aAAkBc,IACbZ,EAAE;AAAA,IACPF,EAAO;AAAA,IACPA,EAAO,QAAQ,IAAI,CAACa,MAAuBd,EAAiBc,CAAM,CAAC;AAAA,EAAA,IAGhEb;AACT;AAEA,SAASe,EAASf,GAA8BgB,GAA+B;AAC7E,QAAMxB,IAAO,EAAE,GAAGwB,EAAS,KAAK,EAAE,kBAAkB,YAAY,GAAG,IAAIA,EAAS,GAAA;AAChF,SAAOC,EAASlB,EAAiBC,CAAM,GAAGR,CAAI;AAChD;AAEA,SAAS0B,EAA4BlB,GAAyD;AAC5F,SAAO;AAAA,IACL,aAAa,CAACR,MAASD,EAAmBC,CAAI;AAAA,IAC9C,eAAe,CAACwB,MAAaD,EAAMf,GAAQgB,CAAQ;AAAA,EAAA;AAEvD;AAEO,MAAMG,IAAmBD,EAAUE,CAAa,GAC1CC,IAAyBH,EAAUI,CAAmB,GACtDC,IAAwBL,EAAUM,CAAoB,GACtDC,IAAmBP,EAAUQ,CAAa,GAC1CC,IAAmBT,EAAUU,CAAa,GAE1CC,IAA8D;AAAA,EACzE,YAAYrC,GAAM;AAChB,WAAO;AAAA,MACL,GAAGD,EAAmBC,CAAI;AAAA,MAC1B,WAAWsC,EAAA;AAAA,IAAgB;AAAA,EAE/B;AAAA,EACA,eAAe,CAACd,MAAaD,EAAMgB,GAAoBf,CAAQ;AACjE,GAEagB,IAAoD;AAAA,EAC/D,YAAYxC,GAAM;AAChB,WAAO;AAAA,MACL,GAAGD,EAAmBC,CAAI;AAAA,MAC1B,WAAWsC,EAAA;AAAA,IAAgB;AAAA,EAE/B;AAAA,EACA,eAAe,CAACd,MAAaD,EAAMkB,GAAejB,CAAQ;AAC5D;"}
1
+ {"version":3,"file":"converters.js","sources":["../../../../src/web/common/converters.ts"],"sourcesContent":["import {\n type Announcement,\n announcementSchema,\n contestSchema,\n participationSchema,\n type Student,\n studentRestoreSchema,\n studentSchema,\n variantSchema,\n} from \"@olinfo/quizms/models\";\nimport { validate } from \"@olinfo/quizms/utils\";\nimport {\n type DocumentSnapshot,\n type FirestoreDataConverter,\n serverTimestamp,\n Timestamp,\n} from \"firebase/firestore\";\nimport { cloneDeepWith, isDate, isString, omit } from \"lodash-es\";\nimport type z from \"zod\";\n\nimport { websiteSchema } from \"~/models/website\";\n\nfunction convertToFirestore(data: object) {\n return cloneDeepWith(omit(data, \"id\"), (value) => {\n if (isDate(value)) {\n return Timestamp.fromDate(value);\n }\n if (isString(value)) {\n return value.trim();\n }\n if (value === undefined) {\n return null;\n }\n });\n}\n\nfunction convertFromFirestore(data: object) {\n return cloneDeepWith(data, (value) => {\n if (value instanceof Timestamp) {\n return value.toDate();\n }\n });\n}\n\nfunction parse<T>(schema: z.core.$ZodType<T>, snapshot: DocumentSnapshot): T {\n const data = convertFromFirestore({ ...snapshot.data(), id: snapshot.id });\n return validate(schema, data);\n}\n\nfunction converter<T extends object>(schema: z.core.$ZodType<T>): FirestoreDataConverter<T> {\n return {\n toFirestore: (data) => convertToFirestore(data),\n fromFirestore: (snapshot) => parse(schema, snapshot),\n };\n}\n\nexport const contestConverter = converter(contestSchema);\nexport const participationConverter = converter(participationSchema);\nexport const studentRestoreConvert = converter(studentRestoreSchema);\nexport const variantConverter = converter(variantSchema);\nexport const websiteConverter = converter(websiteSchema);\n\nexport const announcementConverter: FirestoreDataConverter<Announcement> = {\n toFirestore(data) {\n return {\n ...convertToFirestore(data),\n createdAt: serverTimestamp(),\n };\n },\n fromFirestore: (snapshot) => parse(announcementSchema, snapshot),\n};\n\nexport const studentConverter: FirestoreDataConverter<Student> = {\n toFirestore(data) {\n return {\n ...convertToFirestore(data),\n updatedAt: serverTimestamp(),\n };\n },\n fromFirestore: (snapshot) => parse(studentSchema, snapshot),\n};\n"],"names":["convertToFirestore","data","cloneDeepWith","omit","value","isDate","Timestamp","isString","convertFromFirestore","parse","schema","snapshot","validate","converter","contestConverter","contestSchema","participationConverter","participationSchema","studentRestoreConvert","studentRestoreSchema","variantConverter","variantSchema","websiteConverter","websiteSchema","announcementConverter","serverTimestamp","announcementSchema","studentConverter","studentSchema"],"mappings":";;;;;AAsBA,SAASA,EAAmBC,GAAc;AACxC,SAAOC,EAAcC,EAAKF,GAAM,IAAI,GAAG,CAACG,MAAU;AAChD,QAAIC,EAAOD,CAAK;AACd,aAAOE,EAAU,SAASF,CAAK;AAEjC,QAAIG,EAASH,CAAK;AAChB,aAAOA,EAAM,KAAA;AAEf,QAAIA,MAAU;AACZ,aAAO;AAAA,EAEX,CAAC;AACH;AAEA,SAASI,EAAqBP,GAAc;AAC1C,SAAOC,EAAcD,GAAM,CAACG,MAAU;AACpC,QAAIA,aAAiBE;AACnB,aAAOF,EAAM,OAAA;AAAA,EAEjB,CAAC;AACH;AAEA,SAASK,EAASC,GAA4BC,GAA+B;AAC3E,QAAMV,IAAOO,EAAqB,EAAE,GAAGG,EAAS,QAAQ,IAAIA,EAAS,IAAI;AACzE,SAAOC,EAASF,GAAQT,CAAI;AAC9B;AAEA,SAASY,EAA4BH,GAAuD;AAC1F,SAAO;AAAA,IACL,aAAa,CAACT,MAASD,EAAmBC,CAAI;AAAA,IAC9C,eAAe,CAACU,MAAaF,EAAMC,GAAQC,CAAQ;AAAA,EAAA;AAEvD;AAEO,MAAMG,IAAmBD,EAAUE,CAAa,GAC1CC,IAAyBH,EAAUI,CAAmB,GACtDC,IAAwBL,EAAUM,CAAoB,GACtDC,IAAmBP,EAAUQ,CAAa,GAC1CC,IAAmBT,EAAUU,CAAa,GAE1CC,IAA8D;AAAA,EACzE,YAAYvB,GAAM;AAChB,WAAO;AAAA,MACL,GAAGD,EAAmBC,CAAI;AAAA,MAC1B,WAAWwB,EAAA;AAAA,IAAgB;AAAA,EAE/B;AAAA,EACA,eAAe,CAACd,MAAaF,EAAMiB,GAAoBf,CAAQ;AACjE,GAEagB,IAAoD;AAAA,EAC/D,YAAY1B,GAAM;AAChB,WAAO;AAAA,MACL,GAAGD,EAAmBC,CAAI;AAAA,MAC1B,WAAWwB,EAAA;AAAA,IAAgB;AAAA,EAE/B;AAAA,EACA,eAAe,CAACd,MAAaF,EAAMmB,GAAejB,CAAQ;AAC5D;"}
@@ -1,4 +1,4 @@
1
- import { jsxs as n, Fragment as u, jsx as e } from "react/jsx-runtime";
1
+ import { jsxs as a, Fragment as u, jsx as e } from "react/jsx-runtime";
2
2
  import { useEffect as f } from "react";
3
3
  import { Title as p } from "@olinfo/quizms/components";
4
4
  import { Navbar as h, NavbarBrand as b, Form as w, UsernameField as g, CurrentPasswordField as x, SubmitButton as N } from "@olinfo/react-components";
@@ -9,24 +9,24 @@ import "firebase/firestore";
9
9
  import "lodash-es";
10
10
  import "react-error-boundary";
11
11
  import "swr";
12
- import { useDb as P } from "./base-login.js";
13
- import { login as k } from "./api.js";
14
- function q({ allowedRole: t, children: i }) {
15
- const o = P(), a = new URLSearchParams(A());
12
+ import { useDb as L } from "./base-login.js";
13
+ import { teacherLogin as P } from "./api.js";
14
+ function q({ allowedRole: s, children: n }) {
15
+ const i = L(), t = new URLSearchParams(A());
16
16
  f(() => {
17
17
  const r = new URL(window.location.href);
18
18
  r.searchParams.delete("username"), r.searchParams.delete("password"), window.history.replaceState(void 0, "", r.href);
19
19
  }, []);
20
20
  const m = {
21
- username: a.get("username") ?? "",
22
- password: a.get("password") ?? ""
21
+ username: t.get("username") ?? "",
22
+ password: t.get("password") ?? ""
23
23
  }, c = async (r) => {
24
- const { token: l } = await k(o, t, r), d = S(o.app);
24
+ const l = await P(r), d = S(i.app);
25
25
  await v(d, l);
26
- }, s = F(t);
27
- return s ? i(s) : /* @__PURE__ */ n(u, { children: [
26
+ }, o = F(s);
27
+ return o ? n(o) : /* @__PURE__ */ a(u, { children: [
28
28
  /* @__PURE__ */ e(h, { color: "bg-base-300 text-base-content", children: /* @__PURE__ */ e(b, { children: /* @__PURE__ */ e("div", { className: "flex items-center h-full font-bold", children: /* @__PURE__ */ e(p, {}) }) }) }),
29
- /* @__PURE__ */ n(w, { defaultValue: m, onSubmit: c, className: "p-4 pb-8", children: [
29
+ /* @__PURE__ */ a(w, { defaultValue: m, onSubmit: c, className: "p-4 pb-8", children: [
30
30
  /* @__PURE__ */ e("h1", { className: "mb-2 text-xl font-bold", children: "Accedi alla gestione gara" }),
31
31
  /* @__PURE__ */ e(g, { field: "username" }),
32
32
  /* @__PURE__ */ e(x, { field: "password" }),
@@ -1 +1 @@
1
- {"version":3,"file":"token-login.js","sources":["../../../../src/web/common/token-login.tsx"],"sourcesContent":["import { type ReactNode, useEffect } from \"react\";\n\nimport { Title } from \"@olinfo/quizms/components\";\nimport {\n CurrentPasswordField,\n Form,\n Navbar,\n NavbarBrand,\n SubmitButton,\n UsernameField,\n} from \"@olinfo/react-components\";\nimport { getAuth, signInWithCustomToken, type User } from \"firebase/auth\";\nimport { useSearch } from \"wouter\";\n\nimport { useAuth } from \"~/web/hooks\";\n\nimport { login } from \"./api\";\nimport { useDb } from \"./base-login\";\n\ntype Props = {\n allowedRole: \"teacher\" | \"admin\";\n children: (auth: { user: User; claims: Record<string, string> }) => ReactNode;\n};\n\nexport default function TokenLogin({ allowedRole, children }: Props) {\n const db = useDb();\n const params = new URLSearchParams(useSearch());\n\n useEffect(() => {\n const url = new URL(window.location.href);\n url.searchParams.delete(\"username\");\n url.searchParams.delete(\"password\");\n window.history.replaceState(undefined, \"\", url.href);\n }, []);\n\n const defaultCredential = {\n username: params.get(\"username\") ?? \"\",\n password: params.get(\"password\") ?? \"\",\n };\n\n const signIn = async (credential: { username: string; password: string }) => {\n const { token } = await login(db, allowedRole, credential);\n\n const auth = getAuth(db.app);\n await signInWithCustomToken(auth, token);\n };\n\n const auth = useAuth(allowedRole);\n if (auth) {\n return children(auth);\n }\n\n return (\n <>\n <Navbar color=\"bg-base-300 text-base-content\">\n <NavbarBrand>\n <div className=\"flex items-center h-full font-bold\">\n <Title />\n </div>\n </NavbarBrand>\n </Navbar>\n <Form defaultValue={defaultCredential} onSubmit={signIn} className=\"p-4 pb-8\">\n <h1 className=\"mb-2 text-xl font-bold\">Accedi alla gestione gara</h1>\n <UsernameField field=\"username\" />\n <CurrentPasswordField field=\"password\" />\n <SubmitButton>Accedi</SubmitButton>\n </Form>\n </>\n );\n}\n"],"names":["TokenLogin","allowedRole","children","db","useDb","params","useSearch","useEffect","url","defaultCredential","signIn","credential","token","login","auth","getAuth","signInWithCustomToken","useAuth","jsxs","Fragment","jsx","Navbar","NavbarBrand","Title","Form","UsernameField","CurrentPasswordField","SubmitButton"],"mappings":";;;;;;;;;;;;;AAwBA,SAAwBA,EAAW,EAAE,aAAAC,GAAa,UAAAC,KAAmB;AACnE,QAAMC,IAAKC,EAAA,GACLC,IAAS,IAAI,gBAAgBC,GAAW;AAE9C,EAAAC,EAAU,MAAM;AACd,UAAMC,IAAM,IAAI,IAAI,OAAO,SAAS,IAAI;AACxC,IAAAA,EAAI,aAAa,OAAO,UAAU,GAClCA,EAAI,aAAa,OAAO,UAAU,GAClC,OAAO,QAAQ,aAAa,QAAW,IAAIA,EAAI,IAAI;AAAA,EACrD,GAAG,CAAA,CAAE;AAEL,QAAMC,IAAoB;AAAA,IACxB,UAAUJ,EAAO,IAAI,UAAU,KAAK;AAAA,IACpC,UAAUA,EAAO,IAAI,UAAU,KAAK;AAAA,EAAA,GAGhCK,IAAS,OAAOC,MAAuD;AAC3E,UAAM,EAAE,OAAAC,EAAA,IAAU,MAAMC,EAAMV,GAAIF,GAAaU,CAAU,GAEnDG,IAAOC,EAAQZ,EAAG,GAAG;AAC3B,UAAMa,EAAsBF,GAAMF,CAAK;AAAA,EACzC,GAEME,IAAOG,EAAQhB,CAAW;AAChC,SAAIa,IACKZ,EAASY,CAAI,IAIpB,gBAAAI,EAAAC,GAAA,EACE,UAAA;AAAA,IAAA,gBAAAC,EAACC,GAAA,EAAO,OAAM,iCACZ,UAAA,gBAAAD,EAACE,GAAA,EACC,UAAA,gBAAAF,EAAC,OAAA,EAAI,WAAU,sCACb,UAAA,gBAAAA,EAACG,GAAA,EAAM,EAAA,CACT,GACF,GACF;AAAA,sBACCC,GAAA,EAAK,cAAcf,GAAmB,UAAUC,GAAQ,WAAU,YACjE,UAAA;AAAA,MAAA,gBAAAU,EAAC,MAAA,EAAG,WAAU,0BAAyB,UAAA,6BAAyB;AAAA,MAChE,gBAAAA,EAACK,GAAA,EAAc,OAAM,WAAA,CAAW;AAAA,MAChC,gBAAAL,EAACM,GAAA,EAAqB,OAAM,WAAA,CAAW;AAAA,MACvC,gBAAAN,EAACO,KAAa,UAAA,SAAA,CAAM;AAAA,IAAA,EAAA,CACtB;AAAA,EAAA,GACF;AAEJ;"}
1
+ {"version":3,"file":"token-login.js","sources":["../../../../src/web/common/token-login.tsx"],"sourcesContent":["import { type ReactNode, useEffect } from \"react\";\n\nimport { Title } from \"@olinfo/quizms/components\";\nimport {\n CurrentPasswordField,\n Form,\n Navbar,\n NavbarBrand,\n SubmitButton,\n UsernameField,\n} from \"@olinfo/react-components\";\nimport { getAuth, signInWithCustomToken, type User } from \"firebase/auth\";\nimport { useSearch } from \"wouter\";\n\nimport { useAuth } from \"~/web/hooks\";\n\nimport { teacherLogin } from \"./api\";\nimport { useDb } from \"./base-login\";\n\ntype Props = {\n allowedRole: \"teacher\" | \"admin\";\n children: (auth: { user: User; claims: Record<string, string> }) => ReactNode;\n};\n\nexport default function TokenLogin({ allowedRole, children }: Props) {\n const db = useDb();\n const params = new URLSearchParams(useSearch());\n\n useEffect(() => {\n const url = new URL(window.location.href);\n url.searchParams.delete(\"username\");\n url.searchParams.delete(\"password\");\n window.history.replaceState(undefined, \"\", url.href);\n }, []);\n\n const defaultCredential = {\n username: params.get(\"username\") ?? \"\",\n password: params.get(\"password\") ?? \"\",\n };\n\n const signIn = async (credential: { username: string; password: string }) => {\n const token = await teacherLogin(credential);\n\n const auth = getAuth(db.app);\n await signInWithCustomToken(auth, token);\n };\n\n const auth = useAuth(allowedRole);\n if (auth) {\n return children(auth);\n }\n\n return (\n <>\n <Navbar color=\"bg-base-300 text-base-content\">\n <NavbarBrand>\n <div className=\"flex items-center h-full font-bold\">\n <Title />\n </div>\n </NavbarBrand>\n </Navbar>\n <Form defaultValue={defaultCredential} onSubmit={signIn} className=\"p-4 pb-8\">\n <h1 className=\"mb-2 text-xl font-bold\">Accedi alla gestione gara</h1>\n <UsernameField field=\"username\" />\n <CurrentPasswordField field=\"password\" />\n <SubmitButton>Accedi</SubmitButton>\n </Form>\n </>\n );\n}\n"],"names":["TokenLogin","allowedRole","children","db","useDb","params","useSearch","useEffect","url","defaultCredential","signIn","credential","token","teacherLogin","auth","getAuth","signInWithCustomToken","useAuth","jsxs","Fragment","jsx","Navbar","NavbarBrand","Title","Form","UsernameField","CurrentPasswordField","SubmitButton"],"mappings":";;;;;;;;;;;;;AAwBA,SAAwBA,EAAW,EAAE,aAAAC,GAAa,UAAAC,KAAmB;AACnE,QAAMC,IAAKC,EAAA,GACLC,IAAS,IAAI,gBAAgBC,GAAW;AAE9C,EAAAC,EAAU,MAAM;AACd,UAAMC,IAAM,IAAI,IAAI,OAAO,SAAS,IAAI;AACxC,IAAAA,EAAI,aAAa,OAAO,UAAU,GAClCA,EAAI,aAAa,OAAO,UAAU,GAClC,OAAO,QAAQ,aAAa,QAAW,IAAIA,EAAI,IAAI;AAAA,EACrD,GAAG,CAAA,CAAE;AAEL,QAAMC,IAAoB;AAAA,IACxB,UAAUJ,EAAO,IAAI,UAAU,KAAK;AAAA,IACpC,UAAUA,EAAO,IAAI,UAAU,KAAK;AAAA,EAAA,GAGhCK,IAAS,OAAOC,MAAuD;AAC3E,UAAMC,IAAQ,MAAMC,EAAaF,CAAU,GAErCG,IAAOC,EAAQZ,EAAG,GAAG;AAC3B,UAAMa,EAAsBF,GAAMF,CAAK;AAAA,EACzC,GAEME,IAAOG,EAAQhB,CAAW;AAChC,SAAIa,IACKZ,EAASY,CAAI,IAIpB,gBAAAI,EAAAC,GAAA,EACE,UAAA;AAAA,IAAA,gBAAAC,EAACC,GAAA,EAAO,OAAM,iCACZ,UAAA,gBAAAD,EAACE,GAAA,EACC,UAAA,gBAAAF,EAAC,OAAA,EAAI,WAAU,sCACb,UAAA,gBAAAA,EAACG,GAAA,EAAM,EAAA,CACT,GACF,GACF;AAAA,sBACCC,GAAA,EAAK,cAAcf,GAAmB,UAAUC,GAAQ,WAAU,YACjE,UAAA;AAAA,MAAA,gBAAAU,EAAC,MAAA,EAAG,WAAU,0BAAyB,UAAA,6BAAyB;AAAA,MAChE,gBAAAA,EAACK,GAAA,EAAc,OAAM,WAAA,CAAW;AAAA,MAChC,gBAAAL,EAACM,GAAA,EAAqB,OAAM,WAAA,CAAW;AAAA,MACvC,gBAAAN,EAACO,KAAa,UAAA,SAAA,CAAM;AAAA,IAAA,EAAA,CACtB;AAAA,EAAA,GACF;AAEJ;"}
@@ -2,8 +2,8 @@ import { jsx as t } from "react/jsx-runtime";
2
2
  import { useCallback as s } from "react";
3
3
  import { signOut as a, getAuth as p } from "firebase/auth";
4
4
  import { AdminProvider as c } from "../admin/provider.js";
5
- import { useDb as u } from "../common/base-login.js";
6
- import { contestConverter as l } from "../common/converters.js";
5
+ import { useDb as l } from "../common/base-login.js";
6
+ import { contestConverter as u } from "../common/converters.js";
7
7
  import d from "../common/token-login.js";
8
8
  import "lodash-es";
9
9
  import "react-error-boundary";
@@ -11,12 +11,10 @@ import "swr";
11
11
  import { useCollection as f } from "../hooks/collection.js";
12
12
  import "firebase/firestore";
13
13
  function I() {
14
- return /* @__PURE__ */ t(d, { allowedRole: "admin", children: (o) => /* @__PURE__ */ t(b, { user: o.user }) });
14
+ return /* @__PURE__ */ t(d, { allowedRole: "admin", children: (o) => /* @__PURE__ */ t(g, { user: o.user }) });
15
15
  }
16
- function b({ user: o }) {
17
- const r = u(), n = o.displayName ?? "Utente anonimo", [e, i] = f("contests", l, {
18
- subscribe: !0
19
- }), m = s(async () => {
16
+ function g({ user: o }) {
17
+ const r = l(), n = o.displayName ?? "Utente anonimo", [e, i] = f("contests", u), m = s(async () => {
20
18
  await a(p(r.app)), window.location.reload();
21
19
  }, [r]);
22
20
  return /* @__PURE__ */ t(c, { name: n, contests: e, setContest: i, logout: m });
@@ -1 +1 @@
1
- {"version":3,"file":"admin.js","sources":["../../../../src/web/entry/admin.tsx"],"sourcesContent":["import { useCallback } from \"react\";\n\nimport { getAuth, signOut, type User } from \"firebase/auth\";\n\nimport { AdminProvider } from \"~/web/admin/provider\";\nimport { useDb } from \"~/web/common/base-login\";\nimport { contestConverter } from \"~/web/common/converters\";\nimport TokenLogin from \"~/web/common/token-login\";\nimport { useCollection } from \"~/web/hooks\";\n\nexport default function AdminEntry() {\n return <TokenLogin allowedRole=\"admin\">{(auth) => <AdminInner user={auth.user} />}</TokenLogin>;\n}\n\nfunction AdminInner({ user }: { user: User }) {\n const db = useDb();\n const name = user.displayName ?? \"Utente anonimo\";\n\n const [contests, setContest] = useCollection(\"contests\", contestConverter, {\n subscribe: true,\n });\n\n const logout = useCallback(async () => {\n await signOut(getAuth(db.app));\n window.location.reload();\n }, [db]);\n\n return <AdminProvider name={name} contests={contests} setContest={setContest} logout={logout} />;\n}\n"],"names":["AdminEntry","jsx","TokenLogin","auth","AdminInner","user","db","useDb","name","contests","setContest","useCollection","contestConverter","logout","useCallback","signOut","getAuth","AdminProvider"],"mappings":";;;;;;;;;;;;AAUA,SAAwBA,IAAa;AACnC,SAAO,gBAAAC,EAACC,GAAA,EAAW,aAAY,SAAS,UAAA,CAACC,MAAS,gBAAAF,EAACG,GAAA,EAAW,MAAMD,EAAK,KAAA,CAAM,EAAA,CAAG;AACpF;AAEA,SAASC,EAAW,EAAE,MAAAC,KAAwB;AAC5C,QAAMC,IAAKC,EAAA,GACLC,IAAOH,EAAK,eAAe,kBAE3B,CAACI,GAAUC,CAAU,IAAIC,EAAc,YAAYC,GAAkB;AAAA,IACzE,WAAW;AAAA,EAAA,CACZ,GAEKC,IAASC,EAAY,YAAY;AACrC,UAAMC,EAAQC,EAAQV,EAAG,GAAG,CAAC,GAC7B,OAAO,SAAS,OAAA;AAAA,EAClB,GAAG,CAACA,CAAE,CAAC;AAEP,SAAO,gBAAAL,EAACgB,GAAA,EAAc,MAAAT,GAAY,UAAAC,GAAoB,YAAAC,GAAwB,QAAAG,GAAgB;AAChG;"}
1
+ {"version":3,"file":"admin.js","sources":["../../../../src/web/entry/admin.tsx"],"sourcesContent":["import { useCallback } from \"react\";\n\nimport { getAuth, signOut, type User } from \"firebase/auth\";\n\nimport { AdminProvider } from \"~/web/admin/provider\";\nimport { useDb } from \"~/web/common/base-login\";\nimport { contestConverter } from \"~/web/common/converters\";\nimport TokenLogin from \"~/web/common/token-login\";\nimport { useCollection } from \"~/web/hooks\";\n\nexport default function AdminEntry() {\n return <TokenLogin allowedRole=\"admin\">{(auth) => <AdminInner user={auth.user} />}</TokenLogin>;\n}\n\nfunction AdminInner({ user }: { user: User }) {\n const db = useDb();\n const name = user.displayName ?? \"Utente anonimo\";\n\n const [contests, setContest] = useCollection(\"contests\", contestConverter);\n\n const logout = useCallback(async () => {\n await signOut(getAuth(db.app));\n window.location.reload();\n }, [db]);\n\n return <AdminProvider name={name} contests={contests} setContest={setContest} logout={logout} />;\n}\n"],"names":["AdminEntry","jsx","TokenLogin","auth","AdminInner","user","db","useDb","name","contests","setContest","useCollection","contestConverter","logout","useCallback","signOut","getAuth","AdminProvider"],"mappings":";;;;;;;;;;;;AAUA,SAAwBA,IAAa;AACnC,SAAO,gBAAAC,EAACC,GAAA,EAAW,aAAY,SAAS,UAAA,CAACC,MAAS,gBAAAF,EAACG,GAAA,EAAW,MAAMD,EAAK,KAAA,CAAM,EAAA,CAAG;AACpF;AAEA,SAASC,EAAW,EAAE,MAAAC,KAAwB;AAC5C,QAAMC,IAAKC,EAAA,GACLC,IAAOH,EAAK,eAAe,kBAE3B,CAACI,GAAUC,CAAU,IAAIC,EAAc,YAAYC,CAAgB,GAEnEC,IAASC,EAAY,YAAY;AACrC,UAAMC,EAAQC,EAAQV,EAAG,GAAG,CAAC,GAC7B,OAAO,SAAS,OAAA;AAAA,EAClB,GAAG,CAACA,CAAE,CAAC;AAEP,SAAO,gBAAAL,EAACgB,GAAA,EAAc,MAAAT,GAAY,UAAAC,GAAoB,YAAAC,GAAwB,QAAAG,GAAgB;AAChG;"}