@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,9 +1,9 @@
1
- import { jsx as e, jsxs as o, Fragment as l } from "react/jsx-runtime";
2
- import { useEffect as d } from "react";
3
- import { Loading as p, Title as m } from "@olinfo/quizms/components";
4
- import { Navbar as u, NavbarBrand as f, NavbarContent as h, Dropdown as g, DropdownButton as b, DropdownMenu as v, DropdownItem as x } from "@olinfo/react-components";
1
+ import { jsx as e, jsxs as o, Fragment as d } from "react/jsx-runtime";
2
+ import { useEffect as l } from "react";
3
+ import { Loading as m, Title as p } from "@olinfo/quizms/components";
4
+ import { Navbar as u, NavbarBrand as f, NavbarContent as g, Dropdown as h, DropdownButton as v, DropdownMenu as x, DropdownItem as w } from "@olinfo/react-components";
5
5
  import { signOut as s, getAuth as a } from "firebase/auth";
6
- import { LogOut as w } from "lucide-react";
6
+ import { LogOut as b } from "lucide-react";
7
7
  import { useDb as c } from "../common/base-login.js";
8
8
  import { studentRestoreConvert as N } from "../common/converters.js";
9
9
  import "lodash-es";
@@ -11,36 +11,34 @@ import "react-error-boundary";
11
11
  import "swr";
12
12
  import "firebase/firestore";
13
13
  import { useDocumentOptional as D } from "../hooks/document.js";
14
- function M({ user: r, children: n }) {
15
- const i = c(), [t] = D("studentRestores", r.uid, N, {
16
- subscribe: !0
17
- });
18
- return d(() => {
14
+ function F({ user: r, children: n }) {
15
+ const i = c(), [t] = D("studentRestores", r.uid, N);
16
+ return l(() => {
19
17
  t?.status === "revoked" && s(a(i.app)).then(() => window.location.reload());
20
- }, [i, t]), !t || t.status === "approved" ? n : t.status === "revoked" ? /* @__PURE__ */ e(p, {}) : /* @__PURE__ */ o(l, { children: [
18
+ }, [i, t]), !t || t.status === "approved" ? n : t.status === "revoked" ? /* @__PURE__ */ e(m, {}) : /* @__PURE__ */ o(d, { children: [
21
19
  /* @__PURE__ */ o(u, { color: "bg-base-300 text-base-content", children: [
22
- /* @__PURE__ */ e(f, { children: /* @__PURE__ */ e("div", { className: "flex items-center h-full font-bold", children: /* @__PURE__ */ e(m, {}) }) }),
23
- /* @__PURE__ */ e(h, { children: /* @__PURE__ */ e(y, { name: r.displayName }) })
20
+ /* @__PURE__ */ e(f, { children: /* @__PURE__ */ e("div", { className: "flex items-center h-full font-bold", children: /* @__PURE__ */ e(p, {}) }) }),
21
+ /* @__PURE__ */ e(g, { children: /* @__PURE__ */ e(j, { name: `${t.name} ${t.surname}` }) })
24
22
  ] }),
25
23
  /* @__PURE__ */ o("div", { className: "flex flex-col justify-center items-center grow text-center m-4", children: [
26
- /* @__PURE__ */ e("p", { className: "text-lg max-w-3xl", children: "Il tuo account è già presente su un'altro dispositivo. Per trasferire l'accesso al dispositivo corrente comunica al tuo insegnante il codice seguente:" }),
24
+ /* @__PURE__ */ e("p", { className: "text-lg max-w-3xl", children: `Il tuo account è già presente su un'altro dispositivo. Per trasferire l'accesso al dispositivo corrente comunica al tuo insegnante di inserire il codice seguente in fondo alla pagina di gestione gara, sezione "richieste di accesso":` }),
27
25
  /* @__PURE__ */ e("div", { className: "flex justify-center pt-3", children: /* @__PURE__ */ e("span", { className: "pt-1 font-mono text-3xl", children: String(t?.approvalCode).padStart(3, "0") }) })
28
26
  ] })
29
27
  ] });
30
28
  }
31
- function y({ name: r }) {
29
+ function j({ name: r }) {
32
30
  const n = c();
33
- return /* @__PURE__ */ o(g, { className: "dropdown-end", children: [
34
- /* @__PURE__ */ e(b, { children: /* @__PURE__ */ e("div", { className: "truncate uppercase", children: r }) }),
35
- /* @__PURE__ */ e(v, { children: /* @__PURE__ */ e(x, { children: /* @__PURE__ */ o("button", { type: "button", className: "flex justify-between gap-4", onClick: async () => {
31
+ return /* @__PURE__ */ o(h, { className: "dropdown-end", children: [
32
+ /* @__PURE__ */ e(v, { children: /* @__PURE__ */ e("div", { className: "truncate uppercase", children: r }) }),
33
+ /* @__PURE__ */ e(x, { children: /* @__PURE__ */ e(w, { children: /* @__PURE__ */ o("button", { type: "button", className: "flex justify-between gap-4", onClick: async () => {
36
34
  await s(a(n.app)), window.location.reload();
37
35
  }, children: [
38
36
  "Esci ",
39
- /* @__PURE__ */ e(w, { size: 20 })
37
+ /* @__PURE__ */ e(b, { size: 20 })
40
38
  ] }) }) })
41
39
  ] });
42
40
  }
43
41
  export {
44
- M as StudentRestoring
42
+ F as StudentRestoring
45
43
  };
46
44
  //# sourceMappingURL=student-restoring.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"student-restoring.js","sources":["../../../../src/web/entry/student-restoring.tsx"],"sourcesContent":["import { type ReactNode, useEffect } from \"react\";\n\nimport { Loading, Title } from \"@olinfo/quizms/components\";\nimport {\n Dropdown,\n DropdownButton,\n DropdownItem,\n DropdownMenu,\n Navbar,\n NavbarBrand,\n NavbarContent,\n} from \"@olinfo/react-components\";\nimport { getAuth, signOut, type User } from \"firebase/auth\";\nimport { LogOut } from \"lucide-react\";\n\nimport { useDb } from \"~/web/common/base-login\";\nimport { studentRestoreConvert } from \"~/web/common/converters\";\nimport { useDocumentOptional } from \"~/web/hooks\";\n\nexport function StudentRestoring({ user, children }: { user: User; children: ReactNode }) {\n const db = useDb();\n\n const [studentRestore] = useDocumentOptional(\"studentRestores\", user.uid, studentRestoreConvert, {\n subscribe: true,\n });\n\n useEffect(() => {\n if (studentRestore?.status === \"revoked\") {\n void signOut(getAuth(db.app)).then(() => window.location.reload());\n }\n }, [db, studentRestore]);\n\n if (!studentRestore || studentRestore.status === \"approved\") {\n return children;\n }\n\n if (studentRestore.status === \"revoked\") {\n return <Loading />;\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 <NavbarContent>\n <UserDropdown name={user.displayName} />\n </NavbarContent>\n </Navbar>\n <div className=\"flex flex-col justify-center items-center grow text-center m-4\">\n <p className=\"text-lg max-w-3xl\">\n Il tuo account è già presente su un&apos;altro dispositivo. Per trasferire l&apos;accesso\n al dispositivo corrente comunica al tuo insegnante il codice seguente:\n </p>\n <div className=\"flex justify-center pt-3\">\n <span className=\"pt-1 font-mono text-3xl\">\n {String(studentRestore?.approvalCode).padStart(3, \"0\")}\n </span>\n </div>\n </div>\n </>\n );\n}\n\nfunction UserDropdown({ name }: { name: ReactNode }) {\n const db = useDb();\n\n const logout = async () => {\n await signOut(getAuth(db.app));\n window.location.reload();\n };\n\n return (\n <Dropdown className=\"dropdown-end\">\n <DropdownButton>\n <div className=\"truncate uppercase\">{name}</div>\n </DropdownButton>\n <DropdownMenu>\n <DropdownItem>\n <button type=\"button\" className=\"flex justify-between gap-4\" onClick={logout}>\n Esci <LogOut size={20} />\n </button>\n </DropdownItem>\n </DropdownMenu>\n </Dropdown>\n );\n}\n"],"names":["StudentRestoring","user","children","db","useDb","studentRestore","useDocumentOptional","studentRestoreConvert","useEffect","signOut","getAuth","Loading","jsxs","Fragment","Navbar","jsx","NavbarBrand","Title","NavbarContent","UserDropdown","name","Dropdown","DropdownButton","DropdownMenu","DropdownItem","LogOut"],"mappings":";;;;;;;;;;;;;AAmBO,SAASA,EAAiB,EAAE,MAAAC,GAAM,UAAAC,KAAiD;AACxF,QAAMC,IAAKC,EAAA,GAEL,CAACC,CAAc,IAAIC,EAAoB,mBAAmBL,EAAK,KAAKM,GAAuB;AAAA,IAC/F,WAAW;AAAA,EAAA,CACZ;AAQD,SANAC,EAAU,MAAM;AACd,IAAIH,GAAgB,WAAW,aACxBI,EAAQC,EAAQP,EAAG,GAAG,CAAC,EAAE,KAAK,MAAM,OAAO,SAAS,QAAQ;AAAA,EAErE,GAAG,CAACA,GAAIE,CAAc,CAAC,GAEnB,CAACA,KAAkBA,EAAe,WAAW,aACxCH,IAGLG,EAAe,WAAW,8BACpBM,GAAA,EAAQ,IAIhB,gBAAAC,EAAAC,GAAA,EACE,UAAA;AAAA,IAAA,gBAAAD,EAACE,GAAA,EAAO,OAAM,iCACZ,UAAA;AAAA,MAAA,gBAAAC,EAACC,GAAA,EACC,4BAAC,OAAA,EAAI,WAAU,sCACb,UAAA,gBAAAD,EAACE,GAAA,CAAA,CAAM,GACT,EAAA,CACF;AAAA,wBACCC,GAAA,EACC,UAAA,gBAAAH,EAACI,KAAa,MAAMlB,EAAK,aAAa,EAAA,CACxC;AAAA,IAAA,GACF;AAAA,IACA,gBAAAW,EAAC,OAAA,EAAI,WAAU,kEACb,UAAA;AAAA,MAAA,gBAAAG,EAAC,KAAA,EAAE,WAAU,qBAAoB,UAAA,0JAGjC;AAAA,wBACC,OAAA,EAAI,WAAU,4BACb,UAAA,gBAAAA,EAAC,UAAK,WAAU,2BACb,UAAA,OAAOV,GAAgB,YAAY,EAAE,SAAS,GAAG,GAAG,GACvD,EAAA,CACF;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;AAEA,SAASc,EAAa,EAAE,MAAAC,KAA6B;AACnD,QAAMjB,IAAKC,EAAA;AAOX,SACE,gBAAAQ,EAACS,GAAA,EAAS,WAAU,gBAClB,UAAA;AAAA,IAAA,gBAAAN,EAACO,KACC,UAAA,gBAAAP,EAAC,OAAA,EAAI,WAAU,sBAAsB,aAAK,EAAA,CAC5C;AAAA,IACA,gBAAAA,EAACQ,GAAA,EACC,UAAA,gBAAAR,EAACS,GAAA,EACC,UAAA,gBAAAZ,EAAC,UAAA,EAAO,MAAK,UAAS,WAAU,8BAA6B,SAZtD,YAAY;AACzB,YAAMH,EAAQC,EAAQP,EAAG,GAAG,CAAC,GAC7B,OAAO,SAAS,OAAA;AAAA,IAClB,GASsF,UAAA;AAAA,MAAA;AAAA,MACvE,gBAAAY,EAACU,GAAA,EAAO,MAAM,GAAA,CAAI;AAAA,IAAA,EAAA,CACzB,GACF,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;"}
1
+ {"version":3,"file":"student-restoring.js","sources":["../../../../src/web/entry/student-restoring.tsx"],"sourcesContent":["import { type ReactNode, useEffect } from \"react\";\n\nimport { Loading, Title } from \"@olinfo/quizms/components\";\nimport {\n Dropdown,\n DropdownButton,\n DropdownItem,\n DropdownMenu,\n Navbar,\n NavbarBrand,\n NavbarContent,\n} from \"@olinfo/react-components\";\nimport { getAuth, signOut, type User } from \"firebase/auth\";\nimport { LogOut } from \"lucide-react\";\n\nimport { useDb } from \"~/web/common/base-login\";\nimport { studentRestoreConvert } from \"~/web/common/converters\";\nimport { useDocumentOptional } from \"~/web/hooks\";\n\nexport function StudentRestoring({ user, children }: { user: User; children: ReactNode }) {\n const db = useDb();\n\n const [studentRestore] = useDocumentOptional(\"studentRestores\", user.uid, studentRestoreConvert);\n\n useEffect(() => {\n if (studentRestore?.status === \"revoked\") {\n void signOut(getAuth(db.app)).then(() => window.location.reload());\n }\n }, [db, studentRestore]);\n\n if (!studentRestore || studentRestore.status === \"approved\") {\n return children;\n }\n\n if (studentRestore.status === \"revoked\") {\n return <Loading />;\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 <NavbarContent>\n <UserDropdown name={`${studentRestore.name} ${studentRestore.surname}`} />\n </NavbarContent>\n </Navbar>\n <div className=\"flex flex-col justify-center items-center grow text-center m-4\">\n <p className=\"text-lg max-w-3xl\">\n Il tuo account è già presente su un&apos;altro dispositivo. Per trasferire l&apos;accesso\n al dispositivo corrente comunica al tuo insegnante di inserire il codice seguente in fondo\n alla pagina di gestione gara, sezione &quot;richieste di accesso&quot;:\n </p>\n <div className=\"flex justify-center pt-3\">\n <span className=\"pt-1 font-mono text-3xl\">\n {String(studentRestore?.approvalCode).padStart(3, \"0\")}\n </span>\n </div>\n </div>\n </>\n );\n}\n\nfunction UserDropdown({ name }: { name: ReactNode }) {\n const db = useDb();\n\n const logout = async () => {\n await signOut(getAuth(db.app));\n window.location.reload();\n };\n\n return (\n <Dropdown className=\"dropdown-end\">\n <DropdownButton>\n <div className=\"truncate uppercase\">{name}</div>\n </DropdownButton>\n <DropdownMenu>\n <DropdownItem>\n <button type=\"button\" className=\"flex justify-between gap-4\" onClick={logout}>\n Esci <LogOut size={20} />\n </button>\n </DropdownItem>\n </DropdownMenu>\n </Dropdown>\n );\n}\n"],"names":["StudentRestoring","user","children","db","useDb","studentRestore","useDocumentOptional","studentRestoreConvert","useEffect","signOut","getAuth","Loading","jsxs","Fragment","Navbar","jsx","NavbarBrand","Title","NavbarContent","UserDropdown","name","Dropdown","DropdownButton","DropdownMenu","DropdownItem","LogOut"],"mappings":";;;;;;;;;;;;;AAmBO,SAASA,EAAiB,EAAE,MAAAC,GAAM,UAAAC,KAAiD;AACxF,QAAMC,IAAKC,EAAA,GAEL,CAACC,CAAc,IAAIC,EAAoB,mBAAmBL,EAAK,KAAKM,CAAqB;AAQ/F,SANAC,EAAU,MAAM;AACd,IAAIH,GAAgB,WAAW,aACxBI,EAAQC,EAAQP,EAAG,GAAG,CAAC,EAAE,KAAK,MAAM,OAAO,SAAS,QAAQ;AAAA,EAErE,GAAG,CAACA,GAAIE,CAAc,CAAC,GAEnB,CAACA,KAAkBA,EAAe,WAAW,aACxCH,IAGLG,EAAe,WAAW,8BACpBM,GAAA,EAAQ,IAIhB,gBAAAC,EAAAC,GAAA,EACE,UAAA;AAAA,IAAA,gBAAAD,EAACE,GAAA,EAAO,OAAM,iCACZ,UAAA;AAAA,MAAA,gBAAAC,EAACC,GAAA,EACC,4BAAC,OAAA,EAAI,WAAU,sCACb,UAAA,gBAAAD,EAACE,GAAA,CAAA,CAAM,GACT,EAAA,CACF;AAAA,MACA,gBAAAF,EAACG,GAAA,EACC,UAAA,gBAAAH,EAACI,GAAA,EAAa,MAAM,GAAGd,EAAe,IAAI,IAAIA,EAAe,OAAO,GAAA,CAAI,EAAA,CAC1E;AAAA,IAAA,GACF;AAAA,IACA,gBAAAO,EAAC,OAAA,EAAI,WAAU,kEACb,UAAA;AAAA,MAAA,gBAAAG,EAAC,KAAA,EAAE,WAAU,qBAAoB,UAAA,4OAIjC;AAAA,wBACC,OAAA,EAAI,WAAU,4BACb,UAAA,gBAAAA,EAAC,UAAK,WAAU,2BACb,UAAA,OAAOV,GAAgB,YAAY,EAAE,SAAS,GAAG,GAAG,GACvD,EAAA,CACF;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;AAEA,SAASc,EAAa,EAAE,MAAAC,KAA6B;AACnD,QAAMjB,IAAKC,EAAA;AAOX,SACE,gBAAAQ,EAACS,GAAA,EAAS,WAAU,gBAClB,UAAA;AAAA,IAAA,gBAAAN,EAACO,KACC,UAAA,gBAAAP,EAAC,OAAA,EAAI,WAAU,sBAAsB,aAAK,EAAA,CAC5C;AAAA,IACA,gBAAAA,EAACQ,GAAA,EACC,UAAA,gBAAAR,EAACS,GAAA,EACC,UAAA,gBAAAZ,EAAC,UAAA,EAAO,MAAK,UAAS,WAAU,8BAA6B,SAZtD,YAAY;AACzB,YAAMH,EAAQC,EAAQP,EAAG,GAAG,CAAC,GAC7B,OAAO,SAAS,OAAA;AAAA,IAClB,GASsF,UAAA;AAAA,MAAA;AAAA,MACvE,gBAAAY,EAACU,GAAA,EAAO,MAAM,GAAA,CAAI;AAAA,IAAA,EAAA,CACzB,GACF,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;"}
@@ -1,47 +1,44 @@
1
- import { jsx as t, jsxs as u, Fragment as b } from "react/jsx-runtime";
1
+ import { jsx as t, jsxs as u, Fragment as f } from "react/jsx-runtime";
2
2
  import { useMemo as w, useCallback as p, useEffect as v } from "react";
3
- import { Title as x, StudentFormField as F } from "@olinfo/quizms/components";
3
+ import { Title as F, StudentFormField as x } from "@olinfo/quizms/components";
4
4
  import { StudentProvider as I } from "@olinfo/quizms/student";
5
- import { Navbar as k, NavbarBrand as A, Form as C, SelectField as D, TextField as W, SubmitButton as y } from "@olinfo/react-components";
6
- import { signOut as E, getAuth as g, signInWithCustomToken as H } from "firebase/auth";
7
- import { waitForPendingWrites as f } from "firebase/firestore";
8
- import { isEqual as N } from "lodash-es";
5
+ import { useUserAgent as k } from "@olinfo/quizms/utils";
6
+ import { Navbar as A, NavbarBrand as C, Form as W, SelectField as y, TextField as E, SubmitButton as H } from "@olinfo/react-components";
7
+ import { signOut as N, getAuth as g, signInWithCustomToken as D } from "firebase/auth";
8
+ import { waitForPendingWrites as h } from "firebase/firestore";
9
+ import { isEqual as T } from "lodash-es";
9
10
  import { studentLogin as j } from "../common/api.js";
10
11
  import { useDb as S } from "../common/base-login.js";
11
12
  import { contestConverter as M, studentConverter as P, participationConverter as R } from "../common/converters.js";
12
- import { useWebsite as T } from "../common/website.js";
13
- import { useAuth as q } from "../hooks/auth.js";
14
- import { useCollection as z } from "../hooks/collection.js";
15
- import { useDocument as h } from "../hooks/document.js";
16
- import { StudentRestoring as B } from "./student-restoring.js";
17
- import { FirebaseStatement as O } from "./student-statement.js";
18
- import G from "virtual:quizms-firebase-header";
19
- function ut() {
20
- const e = q("student"), o = T(), [a] = z("contests", M, {
21
- constraints: { id: o.contests },
22
- subscribe: !0
13
+ import { useWebsite as q } from "../common/website.js";
14
+ import { useAuth as z } from "../hooks/auth.js";
15
+ import { useCollection as B } from "../hooks/collection.js";
16
+ import { useDocument as b } from "../hooks/document.js";
17
+ import { StudentRestoring as O } from "./student-restoring.js";
18
+ import { FirebaseStatement as G } from "./student-statement.js";
19
+ import L from "virtual:quizms-firebase-header";
20
+ function lt() {
21
+ const e = z("student"), s = q(), [o] = B("contests", M, {
22
+ constraints: { id: s.contests }
23
23
  });
24
- return e ? /* @__PURE__ */ t(B, { user: e.user, children: /* @__PURE__ */ t(
24
+ return e ? /* @__PURE__ */ t(O, { user: e.user, children: /* @__PURE__ */ t(
25
25
  V,
26
26
  {
27
- contests: a,
27
+ contests: o,
28
28
  participationId: e.claims.participationId,
29
29
  studentId: e.claims.studentId
30
30
  }
31
- ) }) : /* @__PURE__ */ t(L, { contests: a });
31
+ ) }) : /* @__PURE__ */ t(U, { contests: o });
32
32
  }
33
- function L({ contests: e }) {
34
- const o = S(), a = p(
35
- async ({ contestId: n, token: s, ...i }) => {
36
- const d = await j(o, {
37
- contestId: n,
38
- token: s,
39
- userData: {
40
- ...i,
41
- birthDate: i.birthDate ? new Date(i.birthDate) : void 0
42
- },
33
+ function U({ contests: e }) {
34
+ const s = S(), o = k(), d = p(
35
+ async ({ contestId: r, token: a, ...n }) => {
36
+ const l = await j({
37
+ contestId: r,
38
+ token: a,
39
+ userData: n,
43
40
  extraData: {
44
- userAgent: navigator.userAgent,
41
+ userAgent: o,
45
42
  windowWidth: window.innerWidth,
46
43
  windowHeight: window.innerHeight,
47
44
  screenWidth: window.screen.availWidth,
@@ -49,36 +46,36 @@ function L({ contests: e }) {
49
46
  pixelRatio: window.devicePixelRatio,
50
47
  darkMode: window.matchMedia("(prefers-color-scheme: dark)").matches
51
48
  }
52
- }), m = g(o.app);
53
- await H(m, d.token), await document.documentElement.requestFullscreen?.();
49
+ }), c = g(s.app);
50
+ await D(c, l), o.hasFullscreen && await document.documentElement.requestFullscreen?.();
54
51
  },
55
- [o]
56
- ), r = w(
52
+ [s, o]
53
+ ), i = w(
57
54
  () => ({
58
55
  contestId: e.length === 1 ? e[0].id : void 0
59
56
  }),
60
57
  [e]
61
58
  );
62
- return /* @__PURE__ */ u(b, { children: [
63
- /* @__PURE__ */ t(k, { color: "bg-base-300 text-base-content", children: /* @__PURE__ */ t(A, { children: /* @__PURE__ */ t("div", { className: "flex items-center h-full font-bold", children: /* @__PURE__ */ t(x, {}) }) }) }),
64
- /* @__PURE__ */ u(C, { defaultValue: r, onSubmit: a, className: "p-4 pb-8", children: [
59
+ return /* @__PURE__ */ u(f, { children: [
60
+ /* @__PURE__ */ t(A, { color: "bg-base-300 text-base-content", children: /* @__PURE__ */ t(C, { children: /* @__PURE__ */ t("div", { className: "flex items-center h-full font-bold", children: /* @__PURE__ */ t(F, {}) }) }) }),
61
+ /* @__PURE__ */ u(W, { defaultValue: i, onSubmit: d, className: "p-4 pb-8", children: [
65
62
  /* @__PURE__ */ t("h1", { className: "mb-2 text-xl font-bold", children: "Accedi alla gara" }),
66
63
  /* @__PURE__ */ t(
67
- D,
64
+ y,
68
65
  {
69
66
  field: "contestId",
70
67
  label: "Gara",
71
- options: Object.fromEntries(e.map((n) => [n.id, n.name])),
68
+ options: Object.fromEntries(e.map((r) => [r.id, r.name])),
72
69
  placeholder: "Seleziona una gara"
73
70
  }
74
71
  ),
75
- ({ contestId: n }) => {
76
- const s = e.find((i) => i.id === n);
77
- if (s)
78
- return /* @__PURE__ */ u(b, { children: [
79
- s.userData.map((i) => /* @__PURE__ */ t(F, { field: i }, i.name)),
80
- /* @__PURE__ */ t(W, { field: "token", label: "Codice prova", placeholder: "aaaaa-bbbbb-ccccc" }),
81
- /* @__PURE__ */ t(y, { children: "Inizia la prova" })
72
+ ({ contestId: r }) => {
73
+ const a = e.find((n) => n.id === r);
74
+ if (a)
75
+ return /* @__PURE__ */ u(f, { children: [
76
+ a.userData.map((n) => /* @__PURE__ */ t(x, { field: n }, n.name)),
77
+ /* @__PURE__ */ t(E, { field: "token", label: "Codice prova", placeholder: "aaaaa-bbbbb-ccccc" }),
78
+ /* @__PURE__ */ t(H, { children: "Inizia la prova" })
82
79
  ] });
83
80
  }
84
81
  ] })
@@ -86,46 +83,43 @@ function L({ contests: e }) {
86
83
  }
87
84
  function V({
88
85
  contests: e,
89
- participationId: o,
90
- studentId: a
86
+ participationId: s,
87
+ studentId: o
91
88
  }) {
92
- const r = S(), [n, s] = h(
93
- `participations/${o}/students`,
94
- a,
95
- P,
96
- { subscribe: !0 }
97
- ), [i] = h("participations", o, R, {
98
- subscribe: !0
99
- }), d = w(
100
- () => e.find((c) => c.id === n.contestId),
101
- [e, n.contestId]
102
- ), m = p(async () => {
103
- await f(r);
104
- }, [r]), l = p(async () => {
105
- await f(r), await E(g(r.app)), window.location.reload();
106
- }, [r]);
89
+ const d = S(), [i, r] = b(
90
+ `participations/${s}/students`,
91
+ o,
92
+ P
93
+ ), [a] = b("participations", s, R), n = w(
94
+ () => e.find((m) => m.id === i.contestId),
95
+ [e, i.contestId]
96
+ ), l = p(async () => {
97
+ await h(d);
98
+ }, [d]), c = p(async () => {
99
+ await h(d), await N(g(d.app)), window.location.reload();
100
+ }, [d]);
107
101
  return v(() => {
108
- i.token !== n.token && l();
109
- }, [i.token, n.token, l]), /* @__PURE__ */ u(
102
+ a.token !== i.token && c();
103
+ }, [a.token, i.token, c]), /* @__PURE__ */ u(
110
104
  I,
111
105
  {
112
- contest: d,
113
- participation: i,
114
- student: n,
115
- setStudent: async (c) => {
116
- N(n, c) || await s({ ...c });
106
+ contest: n,
107
+ participation: a,
108
+ student: i,
109
+ setStudent: async (m) => {
110
+ T(i, m) || await r({ ...m });
117
111
  },
118
- logout: l,
119
- onSubmit: m,
112
+ logout: c,
113
+ onSubmit: l,
120
114
  enforceFullscreen: !0,
121
115
  children: [
122
- /* @__PURE__ */ t(G, { contestId: d.id }),
123
- /* @__PURE__ */ t(O, {})
116
+ /* @__PURE__ */ t(L, { contestId: n.id }),
117
+ /* @__PURE__ */ t(G, {})
124
118
  ]
125
119
  }
126
120
  );
127
121
  }
128
122
  export {
129
- ut as default
123
+ lt as default
130
124
  };
131
125
  //# sourceMappingURL=student.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"student.js","sources":["../../../../src/web/entry/student.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo } from \"react\";\n\nimport { StudentFormField, Title } from \"@olinfo/quizms/components\";\nimport type { Contest, Student } from \"@olinfo/quizms/models\";\nimport { StudentProvider } from \"@olinfo/quizms/student\";\nimport {\n Form,\n Navbar,\n NavbarBrand,\n SelectField,\n SubmitButton,\n TextField,\n} from \"@olinfo/react-components\";\nimport { getAuth, signInWithCustomToken, signOut } from \"firebase/auth\";\nimport { waitForPendingWrites } from \"firebase/firestore\";\nimport { isEqual } from \"lodash-es\";\n\nimport { studentLogin } from \"~/web/common/api\";\nimport { useDb } from \"~/web/common/base-login\";\nimport {\n contestConverter,\n participationConverter,\n studentConverter,\n} from \"~/web/common/converters\";\nimport { useWebsite } from \"~/web/common/website\";\nimport { useAuth, useCollection, useDocument } from \"~/web/hooks\";\n\nimport { StudentRestoring } from \"./student-restoring\";\nimport { FirebaseStatement } from \"./student-statement\";\n// @ts-expect-error\nimport Header from \"virtual:quizms-firebase-header\";\n\nexport default function StudentEntry() {\n const auth = useAuth(\"student\");\n\n const website = useWebsite();\n const [contests] = useCollection(\"contests\", contestConverter, {\n constraints: { id: website.contests },\n subscribe: true,\n });\n\n if (auth) {\n return (\n <StudentRestoring user={auth.user}>\n <StudentInner\n contests={contests}\n participationId={auth.claims.participationId}\n studentId={auth.claims.studentId}\n />\n </StudentRestoring>\n );\n }\n\n return <StudentForm contests={contests} />;\n}\n\ntype FormStudent = {\n contestId: string;\n token: string;\n} & Student[\"userData\"];\n\nfunction StudentForm({ contests }: { contests: Contest[] }) {\n const db = useDb();\n\n const submit = useCallback(\n async ({ contestId, token, ...userData }: FormStudent) => {\n const data = await studentLogin(db, {\n contestId,\n token,\n userData: {\n ...userData,\n birthDate: userData.birthDate ? new Date(userData.birthDate) : undefined,\n },\n extraData: {\n userAgent: navigator.userAgent,\n windowWidth: window.innerWidth,\n windowHeight: window.innerHeight,\n screenWidth: window.screen.availWidth,\n screenHeight: window.screen.availHeight,\n pixelRatio: window.devicePixelRatio,\n darkMode: window.matchMedia(\"(prefers-color-scheme: dark)\").matches,\n },\n });\n\n const auth = getAuth(db.app);\n await signInWithCustomToken(auth, data.token);\n await document.documentElement.requestFullscreen?.();\n },\n [db],\n );\n\n const defaultValue = useMemo(\n () => ({\n contestId: contests.length === 1 ? contests[0].id : undefined,\n }),\n [contests],\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={defaultValue} onSubmit={submit} className=\"p-4 pb-8\">\n <h1 className=\"mb-2 text-xl font-bold\">Accedi alla gara</h1>\n <SelectField\n field=\"contestId\"\n label=\"Gara\"\n options={Object.fromEntries(contests.map((c) => [c.id, c.name]))}\n placeholder=\"Seleziona una gara\"\n />\n {({ contestId }) => {\n const contest = contests.find((c) => c.id === contestId);\n if (!contest) return;\n return (\n <>\n {contest.userData.map((field) => (\n <StudentFormField key={field.name} field={field} />\n ))}\n <TextField field=\"token\" label=\"Codice prova\" placeholder=\"aaaaa-bbbbb-ccccc\" />\n <SubmitButton>Inizia la prova</SubmitButton>\n </>\n );\n }}\n </Form>\n </>\n );\n}\n\nfunction StudentInner({\n contests,\n participationId,\n studentId,\n}: {\n contests: Contest[];\n participationId: string;\n studentId: string;\n}) {\n const db = useDb();\n\n const [student, setStudent] = useDocument(\n `participations/${participationId}/students`,\n studentId,\n studentConverter,\n { subscribe: true },\n );\n const [participation] = useDocument(\"participations\", participationId, participationConverter, {\n subscribe: true,\n });\n\n const contest = useMemo(\n () => contests.find((c) => c.id === student.contestId)!,\n [contests, student.contestId],\n );\n\n const onSubmit = useCallback(async () => {\n await waitForPendingWrites(db);\n }, [db]);\n\n const logout = useCallback(async () => {\n await waitForPendingWrites(db);\n await signOut(getAuth(db.app));\n window.location.reload();\n }, [db]);\n\n useEffect(() => {\n if (participation.token !== student.token) {\n void logout();\n }\n }, [participation.token, student.token, logout]);\n\n const setStudentAndSubmission = async (newStudent: Student) => {\n if (!isEqual(student, newStudent)) {\n await setStudent({ ...newStudent });\n }\n };\n\n return (\n <StudentProvider\n contest={contest}\n participation={participation}\n student={student}\n setStudent={setStudentAndSubmission}\n logout={logout}\n onSubmit={onSubmit}\n enforceFullscreen={true}>\n <Header contestId={contest.id} />\n <FirebaseStatement />\n </StudentProvider>\n );\n}\n"],"names":["StudentEntry","auth","useAuth","website","useWebsite","contests","useCollection","contestConverter","jsx","StudentRestoring","StudentInner","StudentForm","db","useDb","submit","useCallback","contestId","token","userData","data","studentLogin","getAuth","signInWithCustomToken","defaultValue","useMemo","jsxs","Fragment","Navbar","NavbarBrand","Title","Form","SelectField","c","contest","field","StudentFormField","TextField","SubmitButton","participationId","studentId","student","setStudent","useDocument","studentConverter","participation","participationConverter","onSubmit","waitForPendingWrites","logout","signOut","useEffect","StudentProvider","newStudent","isEqual","Header","FirebaseStatement"],"mappings":"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"}
1
+ {"version":3,"file":"student.js","sources":["../../../../src/web/entry/student.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo } from \"react\";\n\nimport { StudentFormField, Title } from \"@olinfo/quizms/components\";\nimport type { Contest, Student } from \"@olinfo/quizms/models\";\nimport { StudentProvider } from \"@olinfo/quizms/student\";\nimport { useUserAgent } from \"@olinfo/quizms/utils\";\nimport {\n Form,\n Navbar,\n NavbarBrand,\n SelectField,\n SubmitButton,\n TextField,\n} from \"@olinfo/react-components\";\nimport { getAuth, signInWithCustomToken, signOut } from \"firebase/auth\";\nimport { waitForPendingWrites } from \"firebase/firestore\";\nimport { isEqual } from \"lodash-es\";\n\nimport { studentLogin } from \"~/web/common/api\";\nimport { useDb } from \"~/web/common/base-login\";\nimport {\n contestConverter,\n participationConverter,\n studentConverter,\n} from \"~/web/common/converters\";\nimport { useWebsite } from \"~/web/common/website\";\nimport { useAuth, useCollection, useDocument } from \"~/web/hooks\";\n\nimport { StudentRestoring } from \"./student-restoring\";\nimport { FirebaseStatement } from \"./student-statement\";\n// @ts-expect-error\nimport Header from \"virtual:quizms-firebase-header\";\n\nexport default function StudentEntry() {\n const auth = useAuth(\"student\");\n\n const website = useWebsite();\n const [contests] = useCollection(\"contests\", contestConverter, {\n constraints: { id: website.contests },\n });\n\n if (auth) {\n return (\n <StudentRestoring user={auth.user}>\n <StudentInner\n contests={contests}\n participationId={auth.claims.participationId}\n studentId={auth.claims.studentId}\n />\n </StudentRestoring>\n );\n }\n\n return <StudentForm contests={contests} />;\n}\n\ntype FormStudent = {\n contestId: string;\n token: string;\n} & Required<NonNullable<Student[\"userData\"]>>;\n\nfunction StudentForm({ contests }: { contests: Contest[] }) {\n const db = useDb();\n const ua = useUserAgent();\n\n const submit = useCallback(\n async ({ contestId, token, ...userData }: FormStudent) => {\n const authToken = await studentLogin({\n contestId,\n token,\n userData,\n extraData: {\n userAgent: ua,\n windowWidth: window.innerWidth,\n windowHeight: window.innerHeight,\n screenWidth: window.screen.availWidth,\n screenHeight: window.screen.availHeight,\n pixelRatio: window.devicePixelRatio,\n darkMode: window.matchMedia(\"(prefers-color-scheme: dark)\").matches,\n },\n });\n\n const auth = getAuth(db.app);\n await signInWithCustomToken(auth, authToken);\n if (ua.hasFullscreen) {\n await document.documentElement.requestFullscreen?.();\n }\n },\n [db, ua],\n );\n\n const defaultValue = useMemo(\n () => ({\n contestId: contests.length === 1 ? contests[0].id : undefined,\n }),\n [contests],\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={defaultValue} onSubmit={submit} className=\"p-4 pb-8\">\n <h1 className=\"mb-2 text-xl font-bold\">Accedi alla gara</h1>\n <SelectField\n field=\"contestId\"\n label=\"Gara\"\n options={Object.fromEntries(contests.map((c) => [c.id, c.name]))}\n placeholder=\"Seleziona una gara\"\n />\n {({ contestId }) => {\n const contest = contests.find((c) => c.id === contestId);\n if (!contest) return;\n return (\n <>\n {contest.userData.map((field) => (\n <StudentFormField key={field.name} field={field} />\n ))}\n <TextField field=\"token\" label=\"Codice prova\" placeholder=\"aaaaa-bbbbb-ccccc\" />\n <SubmitButton>Inizia la prova</SubmitButton>\n </>\n );\n }}\n </Form>\n </>\n );\n}\n\nfunction StudentInner({\n contests,\n participationId,\n studentId,\n}: {\n contests: Contest[];\n participationId: string;\n studentId: string;\n}) {\n const db = useDb();\n\n const [student, setStudent] = useDocument(\n `participations/${participationId}/students`,\n studentId,\n studentConverter,\n );\n const [participation] = useDocument(\"participations\", participationId, participationConverter);\n\n const contest = useMemo(\n () => contests.find((c) => c.id === student.contestId)!,\n [contests, student.contestId],\n );\n\n const onSubmit = useCallback(async () => {\n await waitForPendingWrites(db);\n }, [db]);\n\n const logout = useCallback(async () => {\n await waitForPendingWrites(db);\n await signOut(getAuth(db.app));\n window.location.reload();\n }, [db]);\n\n useEffect(() => {\n if (participation.token !== student.token) {\n void logout();\n }\n }, [participation.token, student.token, logout]);\n\n const setStudentAndSubmission = async (newStudent: Student) => {\n if (!isEqual(student, newStudent)) {\n await setStudent({ ...newStudent });\n }\n };\n\n return (\n <StudentProvider\n contest={contest}\n participation={participation}\n student={student}\n setStudent={setStudentAndSubmission}\n logout={logout}\n onSubmit={onSubmit}\n enforceFullscreen={true}>\n <Header contestId={contest.id} />\n <FirebaseStatement />\n </StudentProvider>\n );\n}\n"],"names":["StudentEntry","auth","useAuth","website","useWebsite","contests","useCollection","contestConverter","jsx","StudentRestoring","StudentInner","StudentForm","db","useDb","ua","useUserAgent","submit","useCallback","contestId","token","userData","authToken","studentLogin","getAuth","signInWithCustomToken","defaultValue","useMemo","jsxs","Fragment","Navbar","NavbarBrand","Title","Form","SelectField","c","contest","field","StudentFormField","TextField","SubmitButton","participationId","studentId","student","setStudent","useDocument","studentConverter","participation","participationConverter","onSubmit","waitForPendingWrites","logout","signOut","useEffect","StudentProvider","newStudent","isEqual","Header","FirebaseStatement"],"mappings":"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"}
@@ -1,88 +1,88 @@
1
- import { jsx as u } from "react/jsx-runtime";
2
- import { useCallback as v } from "react";
3
- import { TeacherProvider as g } from "@olinfo/quizms/teacher";
4
- import { signOut as h, getAuth as w } from "firebase/auth";
5
- import { writeBatch as f, doc as d, serverTimestamp as P } from "firebase/firestore";
6
- import { getStorage as y, getBytes as C, ref as R } from "firebase/storage";
7
- import { finalizeParticipation as I, stopParticipation as S, startParticipation as k } from "../common/api.js";
8
- import { useDb as b } from "../common/base-login.js";
9
- import { contestConverter as A, participationConverter as l, studentRestoreConvert as T, studentConverter as B, variantConverter as $ } from "../common/converters.js";
10
- import j from "../common/token-login.js";
11
- import { useWebsite as x } from "../common/website.js";
1
+ import { jsx as d } from "react/jsx-runtime";
2
+ import { useCallback as g } from "react";
3
+ import { TeacherProvider as h } from "@olinfo/quizms/teacher";
4
+ import { signOut as w, getAuth as y } from "firebase/auth";
5
+ import { writeBatch as f, doc as u, serverTimestamp as b } from "firebase/firestore";
6
+ import { getStorage as C, getBytes as P, ref as R } from "firebase/storage";
7
+ import { finalizeParticipation as A, stopParticipation as I, startParticipation as S } from "../common/api.js";
8
+ import { useDb as l } from "../common/base-login.js";
9
+ import { contestConverter as k, participationConverter as v, studentRestoreConvert as B, studentConverter as T, announcementConverter as $, variantConverter as j } from "../common/converters.js";
10
+ import x from "../common/token-login.js";
11
+ import { useWebsite as z } from "../common/website.js";
12
12
  import "lodash-es";
13
13
  import "react-error-boundary";
14
14
  import "swr";
15
15
  import { useCollection as c } from "../hooks/collection.js";
16
- import { useDocument as z } from "../hooks/document.js";
17
- import { FirebaseStatement as D } from "./student-statement.js";
16
+ import { useDocument as D } from "../hooks/document.js";
17
+ import { FirebaseStatement as E } from "./student-statement.js";
18
18
  function rt() {
19
- return /* @__PURE__ */ u(j, { allowedRole: "teacher", children: ({ claims: o }) => /* @__PURE__ */ u(E, { schoolId: o.schoolId }) });
19
+ return /* @__PURE__ */ d(x, { allowedRole: "teacher", children: ({ user: t, claims: e }) => /* @__PURE__ */ d(O, { user: t, schoolId: e.schoolId }) });
20
20
  }
21
- function E({ schoolId: o }) {
22
- const t = b(), n = x(), [s] = c("contests", A, {
23
- constraints: { id: n.contests },
24
- subscribe: !0
25
- }), [i] = c("participations", l, {
26
- constraints: { contestId: n.contests, schoolId: o },
27
- subscribe: !0
28
- }), [a] = c("variants", $), r = v(async () => {
29
- await h(w(t.app)), window.location.reload();
30
- }, [t]);
31
- return /* @__PURE__ */ u(
32
- g,
21
+ function O({ user: t, schoolId: e }) {
22
+ const r = l(), s = z(), [a] = c("contests", k, {
23
+ constraints: { id: s.contests }
24
+ }), [m] = c("participations", v, {
25
+ constraints: { contestId: s.contests, schoolId: e }
26
+ }), [n] = c("variants", j), i = g(async () => {
27
+ await w(y(r.app)), window.location.reload();
28
+ }, [r]);
29
+ return /* @__PURE__ */ d(
30
+ h,
33
31
  {
34
- participations: i,
35
- contests: s,
36
- startParticipation: (...e) => k(t, ...e),
37
- stopParticipation: (...e) => S(t, ...e),
38
- finalizeParticipation: (...e) => I(t, ...e),
39
- variants: a,
40
- logout: r,
41
- statementComponent: () => /* @__PURE__ */ u(D, {}),
42
- getPdfStatements: (e, p) => O(t, e, p),
43
- useAnnouncements: F,
44
- useStudents: L,
45
- useStudentRestores: W
32
+ participations: m,
33
+ contests: a,
34
+ startParticipation: (...o) => S(t, ...o),
35
+ stopParticipation: (...o) => I(t, ...o),
36
+ finalizeParticipation: (...o) => A(t, ...o),
37
+ variants: n,
38
+ logout: i,
39
+ statementComponent: () => /* @__PURE__ */ d(E, {}),
40
+ getPdfStatements: (o, p) => F(r, o, p),
41
+ useAnnouncements: L,
42
+ useStudents: W,
43
+ useStudentRestores: G
46
44
  }
47
45
  );
48
46
  }
49
- async function O(o, t, n) {
50
- const s = y(o.app), i = await Promise.all(
51
- n.map(
52
- async (a) => [a, await C(R(s, `statements/${t}/${a}/statement.pdf`))]
47
+ async function F(t, e, r) {
48
+ const s = C(t.app), a = await Promise.all(
49
+ r.map(
50
+ async (m) => [m, await P(R(s, `statements/${e}/${m}/statement.pdf`))]
53
51
  )
54
52
  );
55
- return Object.fromEntries(i);
53
+ return Object.fromEntries(a);
56
54
  }
57
- function F(o) {
58
- return [];
55
+ function L(t) {
56
+ const [e] = c("announcements", $, {
57
+ arrayConstraints: {
58
+ contestIds: t
59
+ },
60
+ orderBy: ["createdAt", "desc"]
61
+ });
62
+ return e;
59
63
  }
60
- function L(o) {
61
- return c(`participations/${o}/students`, B, {
62
- orderBy: "createdAt",
63
- subscribe: !0
64
+ function W(t) {
65
+ return c(`participations/${t}/students`, T, {
66
+ orderBy: "createdAt"
64
67
  });
65
68
  }
66
- function W(o) {
67
- const t = b(), [n] = z("participations", o, l, {
68
- subscribe: !0
69
- }), [s] = c("studentRestores", T, {
70
- constraints: { participationId: o, token: n.token, status: "pending" },
71
- orderBy: "createdAt",
72
- subscribe: !0
73
- }), i = async (r, e) => {
74
- const p = f(t);
75
- for (const m of s)
76
- m.studentId === r && m.id !== e && p.update(d(t, "studentRestores", m.id), { status: "revoked" });
77
- await p.commit();
69
+ function G(t) {
70
+ const e = l(), [r] = D("participations", t, v), [s] = c("studentRestores", B, {
71
+ constraints: { participationId: t, token: r.token, status: "pending" },
72
+ orderBy: "createdAt"
73
+ }), a = async (n, i) => {
74
+ const o = f(e);
75
+ for (const p of s)
76
+ p.studentId === n && p.id !== i && o.update(u(e, "studentRestores", p.id), { status: "revoked" });
77
+ await o.commit();
78
78
  };
79
- return [s, async (r) => {
80
- const e = f(t);
81
- e.update(d(t, `/participations/${o}/students`, r.studentId), {
82
- uid: r.id,
83
- updatedAt: P()
84
- }), e.delete(d(t, "studentRestores", r.id)), await e.commit(), await i(r.studentId, r.id);
85
- }, i];
79
+ return [s, async (n) => {
80
+ const i = f(e);
81
+ i.update(u(e, `/participations/${t}/students`, n.studentId), {
82
+ uid: n.id,
83
+ updatedAt: b()
84
+ }), i.delete(u(e, "studentRestores", n.id)), await i.commit(), await a(n.studentId, n.id);
85
+ }, a];
86
86
  }
87
87
  export {
88
88
  rt as default
@@ -1 +1 @@
1
- {"version":3,"file":"teacher.js","sources":["../../../../src/web/entry/teacher.tsx"],"sourcesContent":["import { useCallback } from \"react\";\n\nimport type { StudentRestore } from \"@olinfo/quizms/models\";\nimport { TeacherProvider } from \"@olinfo/quizms/teacher\";\nimport { getAuth, signOut } from \"firebase/auth\";\nimport { doc, type Firestore, serverTimestamp, writeBatch } from \"firebase/firestore\";\nimport { getBytes, getStorage, ref } from \"firebase/storage\";\n\nimport { finalizeParticipation, startParticipation, stopParticipation } from \"~/web/common/api\";\nimport { useDb } from \"~/web/common/base-login\";\nimport {\n contestConverter,\n participationConverter,\n studentConverter,\n studentRestoreConvert,\n variantConverter,\n} from \"~/web/common/converters\";\nimport TokenLogin from \"~/web/common/token-login\";\nimport { useWebsite } from \"~/web/common/website\";\nimport { useCollection, useDocument } from \"~/web/hooks\";\n\nimport { FirebaseStatement } from \"./student-statement\";\n\nexport default function TeacherEntry() {\n return (\n <TokenLogin allowedRole=\"teacher\">\n {({ claims }) => <TeacherInner schoolId={claims.schoolId} />}\n </TokenLogin>\n );\n}\n\nfunction TeacherInner({ schoolId }: { schoolId: string }) {\n const db = useDb();\n\n const website = useWebsite();\n const [contests] = useCollection(\"contests\", contestConverter, {\n constraints: { id: website.contests },\n subscribe: true,\n });\n const [participations] = useCollection(\"participations\", participationConverter, {\n constraints: { contestId: website.contests, schoolId: schoolId },\n subscribe: true,\n });\n const [variants] = useCollection(\"variants\", variantConverter);\n\n const logout = useCallback(async () => {\n await signOut(getAuth(db.app));\n window.location.reload();\n }, [db]);\n\n return (\n <TeacherProvider\n participations={participations}\n contests={contests}\n startParticipation={(...args) => startParticipation(db, ...args)}\n stopParticipation={(...args) => stopParticipation(db, ...args)}\n finalizeParticipation={(...args) => finalizeParticipation(db, ...args)}\n variants={variants}\n logout={logout}\n statementComponent={() => <FirebaseStatement />}\n getPdfStatements={(contestId, variantIds) => getPdfStatements(db, contestId, variantIds)}\n useAnnouncements={useAnnouncements}\n useStudents={useStudents}\n useStudentRestores={useStudentRestores}\n />\n );\n}\n\nasync function getPdfStatements(\n db: Firestore,\n contestId: string,\n variantIds: string[],\n): Promise<Record<string, ArrayBuffer>> {\n const storage = getStorage(db.app);\n\n const files = await Promise.all(\n variantIds.map(\n async (id) =>\n [id, await getBytes(ref(storage, `statements/${contestId}/${id}/statement.pdf`))] as const,\n ),\n );\n return Object.fromEntries(files);\n}\n\nfunction useAnnouncements(_participationId: string) {\n return [];\n}\n\nfunction useStudents(participationId: string) {\n return useCollection(`participations/${participationId}/students`, studentConverter, {\n orderBy: \"createdAt\",\n subscribe: true,\n });\n}\n\nfunction useStudentRestores(\n participationId: string,\n): readonly [\n StudentRestore[],\n (request: StudentRestore) => Promise<void>,\n (studentId: string) => Promise<void>,\n] {\n const db = useDb();\n\n const [participation] = useDocument(\"participations\", participationId, participationConverter, {\n subscribe: true,\n });\n\n const [studentRestores] = useCollection(\"studentRestores\", studentRestoreConvert, {\n constraints: { participationId, token: participation.token, status: \"pending\" },\n orderBy: \"createdAt\",\n subscribe: true,\n });\n\n const reject = async (studentId: string, excludeRestoreId?: string) => {\n const batch = writeBatch(db);\n\n for (const studentRestore of studentRestores) {\n if (studentRestore.studentId === studentId && studentRestore.id !== excludeRestoreId) {\n batch.update(doc(db, \"studentRestores\", studentRestore.id), { status: \"revoked\" });\n }\n }\n\n await batch.commit();\n };\n\n const approve = async (request: StudentRestore) => {\n const batch = writeBatch(db);\n batch.update(doc(db, `/participations/${participationId}/students`, request.studentId), {\n uid: request.id,\n updatedAt: serverTimestamp(),\n });\n batch.delete(doc(db, \"studentRestores\", request.id));\n await batch.commit();\n\n await reject(request.studentId, request.id);\n };\n\n return [studentRestores, approve, reject];\n}\n"],"names":["TeacherEntry","jsx","TokenLogin","claims","TeacherInner","schoolId","db","useDb","website","useWebsite","contests","useCollection","contestConverter","participations","participationConverter","variants","variantConverter","logout","useCallback","signOut","getAuth","TeacherProvider","args","startParticipation","stopParticipation","finalizeParticipation","FirebaseStatement","contestId","variantIds","getPdfStatements","useAnnouncements","useStudents","useStudentRestores","storage","getStorage","files","id","getBytes","ref","_participationId","participationId","studentConverter","participation","useDocument","studentRestores","studentRestoreConvert","reject","studentId","excludeRestoreId","batch","writeBatch","studentRestore","doc","request","serverTimestamp"],"mappings":"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"}
1
+ {"version":3,"file":"teacher.js","sources":["../../../../src/web/entry/teacher.tsx"],"sourcesContent":["import { useCallback } from \"react\";\n\nimport type { StudentRestore } from \"@olinfo/quizms/models\";\nimport { TeacherProvider } from \"@olinfo/quizms/teacher\";\nimport { getAuth, signOut, type User } from \"firebase/auth\";\nimport { doc, type Firestore, serverTimestamp, writeBatch } from \"firebase/firestore\";\nimport { getBytes, getStorage, ref } from \"firebase/storage\";\n\nimport { finalizeParticipation, startParticipation, stopParticipation } from \"~/web/common/api\";\nimport { useDb } from \"~/web/common/base-login\";\nimport {\n announcementConverter,\n contestConverter,\n participationConverter,\n studentConverter,\n studentRestoreConvert,\n variantConverter,\n} from \"~/web/common/converters\";\nimport TokenLogin from \"~/web/common/token-login\";\nimport { useWebsite } from \"~/web/common/website\";\nimport { useCollection, useDocument } from \"~/web/hooks\";\n\nimport { FirebaseStatement } from \"./student-statement\";\n\nexport default function TeacherEntry() {\n return (\n <TokenLogin allowedRole=\"teacher\">\n {({ user, claims }) => <TeacherInner user={user} schoolId={claims.schoolId} />}\n </TokenLogin>\n );\n}\n\nfunction TeacherInner({ user, schoolId }: { user: User; schoolId: string }) {\n const db = useDb();\n\n const website = useWebsite();\n const [contests] = useCollection(\"contests\", contestConverter, {\n constraints: { id: website.contests },\n });\n const [participations] = useCollection(\"participations\", participationConverter, {\n constraints: { contestId: website.contests, schoolId: schoolId },\n });\n const [variants] = useCollection(\"variants\", variantConverter);\n\n const logout = useCallback(async () => {\n await signOut(getAuth(db.app));\n window.location.reload();\n }, [db]);\n\n return (\n <TeacherProvider\n participations={participations}\n contests={contests}\n startParticipation={(...args) => startParticipation(user, ...args)}\n stopParticipation={(...args) => stopParticipation(user, ...args)}\n finalizeParticipation={(...args) => finalizeParticipation(user, ...args)}\n variants={variants}\n logout={logout}\n statementComponent={() => <FirebaseStatement />}\n getPdfStatements={(contestId, variantIds) => getPdfStatements(db, contestId, variantIds)}\n useAnnouncements={useAnnouncements}\n useStudents={useStudents}\n useStudentRestores={useStudentRestores}\n />\n );\n}\n\nasync function getPdfStatements(\n db: Firestore,\n contestId: string,\n variantIds: string[],\n): Promise<Record<string, ArrayBuffer>> {\n const storage = getStorage(db.app);\n\n const files = await Promise.all(\n variantIds.map(\n async (id) =>\n [id, await getBytes(ref(storage, `statements/${contestId}/${id}/statement.pdf`))] as const,\n ),\n );\n return Object.fromEntries(files);\n}\n\nfunction useAnnouncements(contestId: string) {\n const [announcements] = useCollection(\"announcements\", announcementConverter, {\n arrayConstraints: {\n contestIds: contestId,\n },\n orderBy: [\"createdAt\", \"desc\"],\n });\n return announcements;\n}\n\nfunction useStudents(participationId: string) {\n return useCollection(`participations/${participationId}/students`, studentConverter, {\n orderBy: \"createdAt\",\n });\n}\n\nfunction useStudentRestores(\n participationId: string,\n): readonly [\n StudentRestore[],\n (request: StudentRestore) => Promise<void>,\n (studentId: string) => Promise<void>,\n] {\n const db = useDb();\n\n const [participation] = useDocument(\"participations\", participationId, participationConverter);\n\n const [studentRestores] = useCollection(\"studentRestores\", studentRestoreConvert, {\n constraints: { participationId, token: participation.token, status: \"pending\" },\n orderBy: \"createdAt\",\n });\n\n const reject = async (studentId: string, excludeRestoreId?: string) => {\n const batch = writeBatch(db);\n\n for (const studentRestore of studentRestores) {\n if (studentRestore.studentId === studentId && studentRestore.id !== excludeRestoreId) {\n batch.update(doc(db, \"studentRestores\", studentRestore.id), { status: \"revoked\" });\n }\n }\n\n await batch.commit();\n };\n\n const approve = async (request: StudentRestore) => {\n const batch = writeBatch(db);\n batch.update(doc(db, `/participations/${participationId}/students`, request.studentId), {\n uid: request.id,\n updatedAt: serverTimestamp(),\n });\n batch.delete(doc(db, \"studentRestores\", request.id));\n await batch.commit();\n\n await reject(request.studentId, request.id);\n };\n\n return [studentRestores, approve, reject];\n}\n"],"names":["TeacherEntry","jsx","TokenLogin","user","claims","TeacherInner","schoolId","db","useDb","website","useWebsite","contests","useCollection","contestConverter","participations","participationConverter","variants","variantConverter","logout","useCallback","signOut","getAuth","TeacherProvider","args","startParticipation","stopParticipation","finalizeParticipation","FirebaseStatement","contestId","variantIds","getPdfStatements","useAnnouncements","useStudents","useStudentRestores","storage","getStorage","files","id","getBytes","ref","announcements","announcementConverter","participationId","studentConverter","participation","useDocument","studentRestores","studentRestoreConvert","reject","studentId","excludeRestoreId","batch","writeBatch","studentRestore","doc","request","serverTimestamp"],"mappings":"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"}