@olinfo/quizms-firebase 4.1.2 → 4.1.3

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.
@@ -1,4 +1,4 @@
1
- const e = "4.1.2", n = { "firebase-functions": "^7.0.5" }, s = {
1
+ const e = "4.1.3", n = { "firebase-functions": "^7.0.5" }, s = {
2
2
  version: e,
3
3
  dependencies: n
4
4
  };
@@ -1,16 +1,24 @@
1
- import { jsxs as a, jsx as e } from "react/jsx-runtime";
2
- import { Form as i, TextAreaField as n, SubmitButton as r } from "@olinfo/react-components";
3
- import { useAdmin as l } from "./context.js";
4
- function f() {
5
- const { contest: t, setContest: s } = l();
6
- return /* @__PURE__ */ a(i, { defaultValue: t, onSubmit: async (o) => {
7
- await s({ ...t, ...o });
1
+ import { jsxs as o, jsx as t } from "react/jsx-runtime";
2
+ import { Form as l, SelectField as s, TextAreaField as n, SubmitButton as r } from "@olinfo/react-components";
3
+ import { useAdmin as u } from "./context.js";
4
+ function p() {
5
+ const { contest: e, setContest: i } = u();
6
+ return /* @__PURE__ */ o(l, { defaultValue: e, onSubmit: async (a) => {
7
+ await i({ ...e, ...a });
8
8
  }, className: "!max-w-full", children: [
9
- /* @__PURE__ */ e(n, { field: "instructions", label: "Istruzioni gara", rows: 8 }),
10
- /* @__PURE__ */ e(r, { children: "Salva" })
11
- ] }, t.id);
9
+ /* @__PURE__ */ t(
10
+ s,
11
+ {
12
+ field: "scoreVisibility",
13
+ label: "Mostra punteggi",
14
+ options: { never: "Mai", always: "Sempre", finalized: "Solo alle scuole finalizzate" }
15
+ }
16
+ ),
17
+ /* @__PURE__ */ t(n, { field: "instructions", label: "Istruzioni gara", rows: 8, optional: !0 }),
18
+ /* @__PURE__ */ t(r, { children: "Salva" })
19
+ ] }, e.id);
12
20
  }
13
21
  export {
14
- f as default
22
+ p as default
15
23
  };
16
24
  //# sourceMappingURL=contest-settings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"contest-settings.js","sources":["../../../../src/web/admin/contest-settings.tsx"],"sourcesContent":["import type { Contest } from \"@olinfo/quizms/models\";\nimport { Form, SubmitButton, TextAreaField } from \"@olinfo/react-components\";\n\nimport { useAdmin } from \"./context\";\n\nexport default function ContestSettings() {\n const { contest, setContest } = useAdmin();\n\n const save = async (newContest: Pick<Contest, \"instructions\">) => {\n await setContest({ ...contest, ...newContest });\n };\n\n return (\n <Form key={contest.id} defaultValue={contest} onSubmit={save} className=\"!max-w-full\">\n <TextAreaField field=\"instructions\" label=\"Istruzioni gara\" rows={8} />\n <SubmitButton>Salva</SubmitButton>\n </Form>\n );\n}\n"],"names":["ContestSettings","contest","setContest","useAdmin","Form","newContest","jsx","TextAreaField","SubmitButton"],"mappings":";;;AAKA,SAAwBA,IAAkB;AACxC,QAAM,EAAE,SAAAC,GAAS,YAAAC,EAAA,IAAeC,EAAA;AAMhC,2BACGC,GAAA,EAAsB,cAAcH,GAAS,UALnC,OAAOI,MAA8C;AAChE,UAAMH,EAAW,EAAE,GAAGD,GAAS,GAAGI,GAAY;AAAA,EAChD,GAGgE,WAAU,eACtE,UAAA;AAAA,IAAA,gBAAAC,EAACC,KAAc,OAAM,gBAAe,OAAM,mBAAkB,MAAM,GAAG;AAAA,IACrE,gBAAAD,EAACE,KAAa,UAAA,QAAA,CAAK;AAAA,EAAA,EAAA,GAFVP,EAAQ,EAGnB;AAEJ;"}
1
+ {"version":3,"file":"contest-settings.js","sources":["../../../../src/web/admin/contest-settings.tsx"],"sourcesContent":["import type { Contest } from \"@olinfo/quizms/models\";\nimport { Form, SelectField, SubmitButton, TextAreaField } from \"@olinfo/react-components\";\n\nimport { useAdmin } from \"./context\";\n\nexport default function ContestSettings() {\n const { contest, setContest } = useAdmin();\n\n const save = async (newContest: Pick<Contest, \"instructions\" | \"scoreVisibility\">) => {\n await setContest({ ...contest, ...newContest });\n };\n\n return (\n <Form key={contest.id} defaultValue={contest} onSubmit={save} className=\"!max-w-full\">\n <SelectField\n field=\"scoreVisibility\"\n label=\"Mostra punteggi\"\n options={{ never: \"Mai\", always: \"Sempre\", finalized: \"Solo alle scuole finalizzate\" }}\n />\n <TextAreaField field=\"instructions\" label=\"Istruzioni gara\" rows={8} optional={true} />\n <SubmitButton>Salva</SubmitButton>\n </Form>\n );\n}\n"],"names":["ContestSettings","contest","setContest","useAdmin","Form","newContest","jsx","SelectField","TextAreaField","SubmitButton"],"mappings":";;;AAKA,SAAwBA,IAAkB;AACxC,QAAM,EAAE,SAAAC,GAAS,YAAAC,EAAA,IAAeC,EAAA;AAMhC,2BACGC,GAAA,EAAsB,cAAcH,GAAS,UALnC,OAAOI,MAAkE;AACpF,UAAMH,EAAW,EAAE,GAAGD,GAAS,GAAGI,GAAY;AAAA,EAChD,GAGgE,WAAU,eACtE,UAAA;AAAA,IAAA,gBAAAC;AAAA,MAACC;AAAA,MAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAM;AAAA,QACN,SAAS,EAAE,OAAO,OAAO,QAAQ,UAAU,WAAW,+BAAA;AAAA,MAA+B;AAAA,IAAA;AAAA,IAEvF,gBAAAD,EAACE,KAAc,OAAM,gBAAe,OAAM,mBAAkB,MAAM,GAAG,UAAU,GAAA,CAAM;AAAA,IACrF,gBAAAF,EAACG,KAAa,UAAA,QAAA,CAAK;AAAA,EAAA,EAAA,GAPVR,EAAQ,EAQnB;AAEJ;"}
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sources":["../../../../src/web/admin/context.tsx"],"sourcesContent":["import { createContext, use } from \"react\";\n\nimport type { Contest } from \"@olinfo/quizms/models\";\n\nexport type AdminContextProps = {\n name: string;\n contest: Contest;\n contests: Contest[];\n setContest: (contest: Contest) => Promise<void>;\n};\n\nexport const AdminContext = createContext<AdminContextProps>({} as AdminContextProps);\nAdminContext.displayName = \"AdminContext\";\n\nexport function useAdmin() {\n return use(AdminContext);\n}\n"],"names":["AdminContext","createContext","useAdmin","use"],"mappings":";AAWO,MAAMA,IAAeC,EAAiC,CAAA,CAAuB;AACpFD,EAAa,cAAc;AAEpB,SAASE,IAAW;AACzB,SAAOC,EAAIH,CAAY;AACzB;"}
1
+ {"version":3,"file":"context.js","sources":["../../../../src/web/admin/context.tsx"],"sourcesContent":["import { createContext, use } from \"react\";\n\nimport type { Contest, Variant } from \"@olinfo/quizms/models\";\n\nexport type AdminContextProps = {\n name: string;\n contest: Contest;\n contests: Contest[];\n variants: Record<string, Variant>;\n setContest: (contest: Contest) => Promise<void>;\n};\n\nexport const AdminContext = createContext<AdminContextProps>({} as AdminContextProps);\nAdminContext.displayName = \"AdminContext\";\n\nexport function useAdmin() {\n return use(AdminContext);\n}\n"],"names":["AdminContext","createContext","useAdmin","use"],"mappings":";AAYO,MAAMA,IAAeC,EAAiC,CAAA,CAAuB;AACpFD,EAAa,cAAc;AAEpB,SAASE,IAAW;AACzB,SAAOC,EAAIH,CAAY;AACzB;"}
@@ -1,81 +1,102 @@
1
- import { jsxs as i, jsx as t } from "react/jsx-runtime";
2
- import { Suspense as u } from "react";
3
- import { Loading as m } from "@olinfo/quizms/components";
4
- import { Card as e, CardBody as n, CardActions as s } from "@olinfo/react-components";
5
- import { Link as f } from "wouter";
6
- import { participationConverter as h, studentConverter as b } from "../common/converters.js";
7
- import { useCount as r } from "../hooks/count.js";
1
+ import { jsxs as o, jsx as t } from "react/jsx-runtime";
2
+ import { useCallback as f, Suspense as p } from "react";
3
+ import { Loading as h } from "@olinfo/quizms/components";
4
+ import { Card as n, CardBody as s, CardActions as l } from "@olinfo/react-components";
5
+ import { Link as b } from "wouter";
6
+ import { participationConverter as g, studentConverter as m } from "../common/converters.js";
7
+ import { useCount as c } from "../hooks/count.js";
8
8
  import v from "./announcements.js";
9
- import C from "./contest-settings.js";
10
- import { useAdmin as c } from "./context.js";
11
- import l from "./export.js";
9
+ import S from "./contest-settings.js";
10
+ import { useAdmin as u } from "./context.js";
11
+ import d, { scoreboardFormatter as C, scoreboradHeader as I } from "./export.js";
12
12
  function B() {
13
- const { contest: o } = c();
14
- return /* @__PURE__ */ i("div", { className: "flex flex-col gap-4", children: [
15
- /* @__PURE__ */ t(e, { children: /* @__PURE__ */ t(n, { title: "Impostazioni gara", children: /* @__PURE__ */ t(C, {}) }) }),
16
- /* @__PURE__ */ t(e, { children: /* @__PURE__ */ t(n, { title: "Comunicazioni", children: /* @__PURE__ */ t(v, {}) }) }),
17
- /* @__PURE__ */ t(e, { children: /* @__PURE__ */ i(n, { title: "Scuole", children: [
18
- /* @__PURE__ */ t(u, { fallback: /* @__PURE__ */ t(m, {}), children: /* @__PURE__ */ t(S, {}) }),
19
- /* @__PURE__ */ t(s, { children: /* @__PURE__ */ t(f, { href: "/schools", className: "btn btn-primary", children: "Gestione scuole" }) })
13
+ const { contest: i, variants: r } = u(), a = f(
14
+ (e) => C(i, r, e),
15
+ [i, r]
16
+ );
17
+ return /* @__PURE__ */ o("div", { className: "flex flex-col gap-4", children: [
18
+ /* @__PURE__ */ t(n, { children: /* @__PURE__ */ t(s, { title: "Impostazioni gara", children: /* @__PURE__ */ t(S, {}) }) }),
19
+ /* @__PURE__ */ t(n, { children: /* @__PURE__ */ t(s, { title: "Comunicazioni", children: /* @__PURE__ */ t(v, {}) }) }),
20
+ /* @__PURE__ */ t(n, { children: /* @__PURE__ */ o(s, { title: "Scuole", children: [
21
+ /* @__PURE__ */ t(p, { fallback: /* @__PURE__ */ t(h, {}), children: /* @__PURE__ */ t(z, {}) }),
22
+ /* @__PURE__ */ t(l, { children: /* @__PURE__ */ t(b, { href: "/schools", className: "btn btn-primary", children: "Gestione scuole" }) })
20
23
  ] }) }),
21
- /* @__PURE__ */ t(e, { children: /* @__PURE__ */ t(n, { title: "Esportazione", children: /* @__PURE__ */ i(s, { children: [
24
+ /* @__PURE__ */ t(n, { children: /* @__PURE__ */ t(s, { title: "Esportazione", children: /* @__PURE__ */ o(l, { children: [
22
25
  /* @__PURE__ */ t(
23
- l,
26
+ d,
24
27
  {
25
28
  label: "scuole",
26
- description: "tutte le scuole",
29
+ description: "i dati di tutte le scuole",
27
30
  collection: "participations",
28
- converter: h,
29
- options: { constraints: { contestId: o.id, disabled: !1 } }
31
+ converter: g,
32
+ options: { constraints: { contestId: i.id, disabled: !1 } },
33
+ formatter: (e) => JSON.stringify(e)
30
34
  }
31
35
  ),
32
36
  /* @__PURE__ */ t(
33
- l,
37
+ d,
34
38
  {
35
39
  label: "studenti",
36
- description: "tutti gli studenti",
40
+ description: "i dati di tutti gli studenti",
37
41
  collection: "students",
38
- converter: b,
42
+ converter: m,
39
43
  options: {
40
- constraints: { contestId: o.id, disabled: !1 },
44
+ constraints: { contestId: i.id, disabled: !1 },
41
45
  group: !0
42
- }
46
+ },
47
+ formatter: (e) => JSON.stringify(e)
48
+ }
49
+ ),
50
+ /* @__PURE__ */ t(
51
+ d,
52
+ {
53
+ label: "classifica",
54
+ description: "la classifica",
55
+ collection: "students",
56
+ suggestedName: `scoreboard-${i.id}.csv`,
57
+ converter: m,
58
+ options: {
59
+ constraints: { contestId: i.id, disabled: !1 },
60
+ group: !0
61
+ },
62
+ formatter: a,
63
+ header: I(i)
43
64
  }
44
65
  )
45
66
  ] }) }) })
46
67
  ] });
47
68
  }
48
- function S() {
49
- const { contest: o } = c(), d = r("participations", {
69
+ function z() {
70
+ const { contest: i } = u(), r = c("participations", {
50
71
  constraints: {
51
- contestId: o.id,
72
+ contestId: i.id,
52
73
  disabled: !1
53
74
  }
54
- }), a = r("participations", {
75
+ }), a = c("participations", {
55
76
  constraints: {
56
- contestId: o.id,
77
+ contestId: i.id,
57
78
  finalized: !0,
58
79
  disabled: !1
59
80
  }
60
- }), p = r("students", {
81
+ }), e = c("students", {
61
82
  constraints: {
62
- contestId: o.id,
83
+ contestId: i.id,
63
84
  disabled: !1
64
85
  },
65
86
  group: !0
66
87
  });
67
- return /* @__PURE__ */ i("div", { children: [
68
- /* @__PURE__ */ i("div", { children: [
88
+ return /* @__PURE__ */ o("div", { children: [
89
+ /* @__PURE__ */ o("div", { children: [
69
90
  "Scuole totali: ",
70
- d
91
+ r
71
92
  ] }),
72
- /* @__PURE__ */ i("div", { children: [
93
+ /* @__PURE__ */ o("div", { children: [
73
94
  "Scuole finalizzate: ",
74
95
  a
75
96
  ] }),
76
- /* @__PURE__ */ i("div", { children: [
97
+ /* @__PURE__ */ o("div", { children: [
77
98
  "Studenti totali: ",
78
- p
99
+ e
79
100
  ] })
80
101
  ] });
81
102
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.js","sources":["../../../../src/web/admin/dashboard.tsx"],"sourcesContent":["import { Suspense } from \"react\";\n\nimport { Loading } from \"@olinfo/quizms/components\";\nimport type { Participation, Student } from \"@olinfo/quizms/models\";\nimport { Card, CardActions, CardBody } from \"@olinfo/react-components\";\nimport { Link } from \"wouter\";\n\nimport { participationConverter, studentConverter } from \"~/web/common/converters\";\nimport { useCount } from \"~/web/hooks/count\";\n\nimport Announcements from \"./announcements\";\nimport ContestSettings from \"./contest-settings\";\nimport { useAdmin } from \"./context\";\nimport Export from \"./export\";\n\nexport default function Dashboard() {\n const { contest } = useAdmin();\n\n return (\n <div className=\"flex flex-col gap-4\">\n <Card>\n <CardBody title=\"Impostazioni gara\">\n <ContestSettings />\n </CardBody>\n </Card>\n <Card>\n <CardBody title=\"Comunicazioni\">\n <Announcements />\n </CardBody>\n </Card>\n <Card>\n <CardBody title=\"Scuole\">\n <Suspense fallback={<Loading />}>\n <ContestInformation />\n </Suspense>\n <CardActions>\n <Link href=\"/schools\" className=\"btn btn-primary\">\n Gestione scuole\n </Link>\n </CardActions>\n </CardBody>\n </Card>\n <Card>\n <CardBody title=\"Esportazione\">\n <CardActions>\n <Export\n label=\"scuole\"\n description=\"tutte le scuole\"\n collection=\"participations\"\n converter={participationConverter}\n options={{ constraints: { contestId: contest.id, disabled: false } }}\n />\n <Export\n label=\"studenti\"\n description=\"tutti gli studenti\"\n collection=\"students\"\n converter={studentConverter}\n options={{\n constraints: { contestId: contest.id, disabled: false },\n group: true,\n }}\n />\n </CardActions>\n </CardBody>\n </Card>\n </div>\n );\n}\n\nfunction ContestInformation() {\n const { contest } = useAdmin();\n\n const totalSchools = useCount<Participation>(\"participations\", {\n constraints: {\n contestId: contest.id,\n disabled: false,\n },\n });\n\n const finalizedSchools = useCount<Participation>(\"participations\", {\n constraints: {\n contestId: contest.id,\n finalized: true,\n disabled: false,\n },\n });\n\n const totalStudents = useCount<Student>(\"students\", {\n constraints: {\n contestId: contest.id,\n disabled: false,\n },\n group: true,\n });\n\n return (\n <div>\n <div>Scuole totali: {totalSchools}</div>\n <div>Scuole finalizzate: {finalizedSchools}</div>\n <div>Studenti totali: {totalStudents}</div>\n </div>\n );\n}\n"],"names":["Dashboard","contest","useAdmin","jsxs","jsx","Card","CardBody","ContestSettings","Announcements","Suspense","Loading","ContestInformation","CardActions","Link","Export","participationConverter","studentConverter","totalSchools","useCount","finalizedSchools","totalStudents"],"mappings":";;;;;;;;;;;AAeA,SAAwBA,IAAY;AAClC,QAAM,EAAE,SAAAC,EAAA,IAAYC,EAAA;AAEpB,SACE,gBAAAC,EAAC,OAAA,EAAI,WAAU,uBACb,UAAA;AAAA,IAAA,gBAAAC,EAACC,GAAA,EACC,4BAACC,GAAA,EAAS,OAAM,qBACd,UAAA,gBAAAF,EAACG,GAAA,CAAA,CAAgB,GACnB,EAAA,CACF;AAAA,IACA,gBAAAH,EAACC,KACC,UAAA,gBAAAD,EAACE,GAAA,EAAS,OAAM,iBACd,UAAA,gBAAAF,EAACI,GAAA,CAAA,CAAc,EAAA,CACjB,EAAA,CACF;AAAA,IACA,gBAAAJ,EAACC,GAAA,EACC,UAAA,gBAAAF,EAACG,GAAA,EAAS,OAAM,UACd,UAAA;AAAA,MAAA,gBAAAF,EAACK,KAAS,UAAU,gBAAAL,EAACM,KAAQ,GAC3B,UAAA,gBAAAN,EAACO,KAAmB,EAAA,CACtB;AAAA,MACA,gBAAAP,EAACQ,KACC,UAAA,gBAAAR,EAACS,GAAA,EAAK,MAAK,YAAW,WAAU,mBAAkB,UAAA,kBAAA,CAElD,EAAA,CACF;AAAA,IAAA,EAAA,CACF,EAAA,CACF;AAAA,sBACCR,GAAA,EACC,UAAA,gBAAAD,EAACE,KAAS,OAAM,gBACd,4BAACM,GAAA,EACC,UAAA;AAAA,MAAA,gBAAAR;AAAA,QAACU;AAAA,QAAA;AAAA,UACC,OAAM;AAAA,UACN,aAAY;AAAA,UACZ,YAAW;AAAA,UACX,WAAWC;AAAA,UACX,SAAS,EAAE,aAAa,EAAE,WAAWd,EAAQ,IAAI,UAAU,GAAA,EAAM;AAAA,QAAE;AAAA,MAAA;AAAA,MAErE,gBAAAG;AAAA,QAACU;AAAA,QAAA;AAAA,UACC,OAAM;AAAA,UACN,aAAY;AAAA,UACZ,YAAW;AAAA,UACX,WAAWE;AAAA,UACX,SAAS;AAAA,YACP,aAAa,EAAE,WAAWf,EAAQ,IAAI,UAAU,GAAA;AAAA,YAChD,OAAO;AAAA,UAAA;AAAA,QACT;AAAA,MAAA;AAAA,IACF,EAAA,CACF,GACF,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;AAEA,SAASU,IAAqB;AAC5B,QAAM,EAAE,SAAAV,EAAA,IAAYC,EAAA,GAEde,IAAeC,EAAwB,kBAAkB;AAAA,IAC7D,aAAa;AAAA,MACX,WAAWjB,EAAQ;AAAA,MACnB,UAAU;AAAA,IAAA;AAAA,EACZ,CACD,GAEKkB,IAAmBD,EAAwB,kBAAkB;AAAA,IACjE,aAAa;AAAA,MACX,WAAWjB,EAAQ;AAAA,MACnB,WAAW;AAAA,MACX,UAAU;AAAA,IAAA;AAAA,EACZ,CACD,GAEKmB,IAAgBF,EAAkB,YAAY;AAAA,IAClD,aAAa;AAAA,MACX,WAAWjB,EAAQ;AAAA,MACnB,UAAU;AAAA,IAAA;AAAA,IAEZ,OAAO;AAAA,EAAA,CACR;AAED,2BACG,OAAA,EACC,UAAA;AAAA,IAAA,gBAAAE,EAAC,OAAA,EAAI,UAAA;AAAA,MAAA;AAAA,MAAgBc;AAAA,IAAA,GAAa;AAAA,sBACjC,OAAA,EAAI,UAAA;AAAA,MAAA;AAAA,MAAqBE;AAAA,IAAA,GAAiB;AAAA,sBAC1C,OAAA,EAAI,UAAA;AAAA,MAAA;AAAA,MAAkBC;AAAA,IAAA,EAAA,CAAc;AAAA,EAAA,GACvC;AAEJ;"}
1
+ {"version":3,"file":"dashboard.js","sources":["../../../../src/web/admin/dashboard.tsx"],"sourcesContent":["import { Suspense, useCallback } from \"react\";\n\nimport { Loading } from \"@olinfo/quizms/components\";\nimport type { Participation, Student } from \"@olinfo/quizms/models\";\nimport { Card, CardActions, CardBody } from \"@olinfo/react-components\";\nimport { Link } from \"wouter\";\n\nimport { participationConverter, studentConverter } from \"~/web/common/converters\";\nimport { useCount } from \"~/web/hooks/count\";\n\nimport Announcements from \"./announcements\";\nimport ContestSettings from \"./contest-settings\";\nimport { useAdmin } from \"./context\";\nimport Export, { scoreboardFormatter, scoreboradHeader } from \"./export\";\n\nexport default function Dashboard() {\n const { contest, variants } = useAdmin();\n const contestScoreboardFormatter = useCallback(\n (student: Student) => scoreboardFormatter(contest, variants, student),\n [contest, variants],\n );\n\n return (\n <div className=\"flex flex-col gap-4\">\n <Card>\n <CardBody title=\"Impostazioni gara\">\n <ContestSettings />\n </CardBody>\n </Card>\n <Card>\n <CardBody title=\"Comunicazioni\">\n <Announcements />\n </CardBody>\n </Card>\n <Card>\n <CardBody title=\"Scuole\">\n <Suspense fallback={<Loading />}>\n <ContestInformation />\n </Suspense>\n <CardActions>\n <Link href=\"/schools\" className=\"btn btn-primary\">\n Gestione scuole\n </Link>\n </CardActions>\n </CardBody>\n </Card>\n <Card>\n <CardBody title=\"Esportazione\">\n <CardActions>\n <Export\n label=\"scuole\"\n description=\"i dati di tutte le scuole\"\n collection=\"participations\"\n converter={participationConverter}\n options={{ constraints: { contestId: contest.id, disabled: false } }}\n formatter={(data: Participation) => JSON.stringify(data)}\n />\n <Export\n label=\"studenti\"\n description=\"i dati di tutti gli studenti\"\n collection=\"students\"\n converter={studentConverter}\n options={{\n constraints: { contestId: contest.id, disabled: false },\n group: true,\n }}\n formatter={(data: Student) => JSON.stringify(data)}\n />\n <Export\n label=\"classifica\"\n description=\"la classifica\"\n collection=\"students\"\n suggestedName={`scoreboard-${contest.id}.csv`}\n converter={studentConverter}\n options={{\n constraints: { contestId: contest.id, disabled: false },\n group: true,\n }}\n formatter={contestScoreboardFormatter}\n header={scoreboradHeader(contest)}\n />\n </CardActions>\n </CardBody>\n </Card>\n </div>\n );\n}\n\nfunction ContestInformation() {\n const { contest } = useAdmin();\n\n const totalSchools = useCount<Participation>(\"participations\", {\n constraints: {\n contestId: contest.id,\n disabled: false,\n },\n });\n\n const finalizedSchools = useCount<Participation>(\"participations\", {\n constraints: {\n contestId: contest.id,\n finalized: true,\n disabled: false,\n },\n });\n\n const totalStudents = useCount<Student>(\"students\", {\n constraints: {\n contestId: contest.id,\n disabled: false,\n },\n group: true,\n });\n\n return (\n <div>\n <div>Scuole totali: {totalSchools}</div>\n <div>Scuole finalizzate: {finalizedSchools}</div>\n <div>Studenti totali: {totalStudents}</div>\n </div>\n );\n}\n"],"names":["Dashboard","contest","variants","useAdmin","contestScoreboardFormatter","useCallback","student","scoreboardFormatter","jsxs","jsx","Card","CardBody","ContestSettings","Announcements","Suspense","Loading","ContestInformation","CardActions","Link","Export","participationConverter","data","studentConverter","scoreboradHeader","totalSchools","useCount","finalizedSchools","totalStudents"],"mappings":"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"}
@@ -1,59 +1,112 @@
1
- import { jsxs as i, Fragment as p, jsx as t } from "react/jsx-runtime";
2
- import { useRef as h, forwardRef as w } from "react";
3
- import { Button as u, Modal as g } from "@olinfo/react-components";
4
- import { getDocs as x } from "firebase/firestore";
5
- import { TriangleAlert as b } from "lucide-react";
6
- import { useDb as y } from "../common/base-login.js";
7
- import E from "../common/query.js";
8
- import { useAdmin as v } from "./context.js";
9
- function $(a) {
10
- const e = h(null);
11
- return /* @__PURE__ */ i(p, { children: [
12
- /* @__PURE__ */ i(u, { className: "btn-primary", onClick: () => e.current?.showModal(), children: [
1
+ import { jsxs as d, Fragment as h, jsx as p } from "react/jsx-runtime";
2
+ import { useRef as g, forwardRef as v } from "react";
3
+ import { formatUserData as x, unshuffleAnswer as $, calcProblemPoints as I } from "@olinfo/quizms/models";
4
+ import { Button as w, Modal as y } from "@olinfo/react-components";
5
+ import { formatISO as E } from "date-fns";
6
+ import { getDocs as S } from "firebase/firestore";
7
+ import { TriangleAlert as P } from "lucide-react";
8
+ import { useDb as j } from "../common/base-login.js";
9
+ import k from "../common/query.js";
10
+ import { useAdmin as A } from "./context.js";
11
+ function Q(s) {
12
+ const o = g(null);
13
+ return /* @__PURE__ */ d(h, { children: [
14
+ /* @__PURE__ */ d(w, { className: "btn-primary", onClick: () => o.current?.showModal(), children: [
13
15
  "Esporta ",
14
- a.label
16
+ s.label
15
17
  ] }),
16
- /* @__PURE__ */ t(N, { ref: e, ...a })
18
+ /* @__PURE__ */ p(D, { ref: o, ...s })
17
19
  ] });
18
20
  }
19
- const N = w(function({ label: e, description: l, collection: c, converter: m, options: n }, r) {
20
- const { contest: s } = v(), d = y(), o = async () => {
21
- await S(d, c, m, n), r && "current" in r && r.current?.close();
21
+ const D = v(function({
22
+ label: o,
23
+ description: e,
24
+ collection: r,
25
+ converter: l,
26
+ options: a,
27
+ formatter: t,
28
+ suggestedName: n,
29
+ header: c
30
+ }, i) {
31
+ const { contest: f } = A(), u = j(), m = async () => {
32
+ await N(u, r, l, a, t, n, c), i && "current" in i && i.current?.close();
22
33
  };
23
- return /* @__PURE__ */ t(g, { ref: r, title: `Esporta ${e}`, children: window.showSaveFilePicker === void 0 ? /* @__PURE__ */ i("div", { className: "flex items-center gap-3", children: [
24
- /* @__PURE__ */ t(b, { size: 20, className: "flex-none text-warning" }),
25
- /* @__PURE__ */ t("span", { children: "Il tuo browser non supporta l'esportazione dei dati. Usa un browser Chrome o Edge." })
26
- ] }) : /* @__PURE__ */ i(p, { children: [
27
- /* @__PURE__ */ i("div", { children: [
28
- "Stai per esportare i dati di ",
29
- l,
34
+ return /* @__PURE__ */ p(y, { ref: i, title: `Esporta ${o}`, children: window.showSaveFilePicker === void 0 ? /* @__PURE__ */ d("div", { className: "flex items-center gap-3", children: [
35
+ /* @__PURE__ */ p(P, { size: 20, className: "flex-none text-warning" }),
36
+ /* @__PURE__ */ p("span", { children: "Il tuo browser non supporta l'esportazione dei dati. Usa un browser Chrome o Edge." })
37
+ ] }) : /* @__PURE__ */ d(h, { children: [
38
+ /* @__PURE__ */ d("div", { children: [
39
+ "Stai per esportare ",
40
+ e,
30
41
  " della gara ",
31
- /* @__PURE__ */ t("b", { children: s.name }),
42
+ /* @__PURE__ */ p("b", { children: f.name }),
32
43
  ". Questa operazione esegue un grande numero di query al database che richiederà del tempo e potrà incidere sulla fatturazione del progetto."
33
44
  ] }),
34
- /* @__PURE__ */ t("div", { className: "mt-4 flex flex-wrap justify-center gap-2", children: /* @__PURE__ */ t(u, { className: "btn-primary", onClick: o, children: "Esporta" }) })
45
+ /* @__PURE__ */ p("div", { className: "mt-4 flex flex-wrap justify-center gap-2", children: /* @__PURE__ */ p(w, { className: "btn-primary", onClick: m, children: "Esporta" }) })
35
46
  ] }) });
36
47
  });
37
- async function S(a, e, l, c) {
38
- const n = await (await window.showSaveFilePicker({
39
- suggestedName: `${e}.jsonl`
40
- })).createWritable(), r = 1e4;
41
- let s;
48
+ function T(s) {
49
+ const o = [];
50
+ for (const e of s.userData)
51
+ o.push(e.label);
52
+ o.push("Scuola", "Variante");
53
+ for (const e of s.problemIds)
54
+ o.push(`${e}A`, `${e}P`);
55
+ return o.push("Punteggio"), o.push("Assente"), `${o.map((e) => `"${e}"`).join(",")}
56
+ `;
57
+ }
58
+ function V(s, o, e) {
59
+ const r = [];
60
+ for (const a of s.userData)
61
+ if (a.type === "date") {
62
+ const t = e.userData?.[a.name];
63
+ t ? r.push(E(t, { representation: "date" })) : r.push("");
64
+ } else
65
+ r.push(x(e, a));
66
+ r.push(e.participationId?.split("-")[0] ?? ""), r.push(e.variant ?? "");
67
+ const l = {};
68
+ if (e.answers !== void 0 && e.variant !== void 0 && o[e.variant] !== void 0) {
69
+ const a = o[e.variant].schema;
70
+ for (const t in a) {
71
+ const n = a[t], c = n.originalId, i = e.answers[t];
72
+ l[c] = [
73
+ $(n, i) ?? "",
74
+ I(n, i)
75
+ ];
76
+ }
77
+ }
78
+ for (const a of s.problemIds)
79
+ if (l[a] !== void 0) {
80
+ const [t, n] = l[a];
81
+ r.push(`${t}`), r.push(`${n}`);
82
+ } else
83
+ r.push(""), r.push("");
84
+ return r.push(`${e.score ?? ""}`), r.push(`${e.absent ? 1 : 0}`), r.map((a) => `"${a}"`).join(",");
85
+ }
86
+ async function N(s, o, e, r, l, a, t) {
87
+ const c = await (await window.showSaveFilePicker({
88
+ suggestedName: a ?? `${o}.jsonl`
89
+ })).createWritable();
90
+ t !== void 0 && await c.write(t);
91
+ const i = 1e4;
92
+ let f;
42
93
  for (; ; ) {
43
- const d = E(a, e, l, {
44
- ...c,
45
- limit: r,
46
- startAfter: s
47
- }), o = await x(d);
48
- if (o.empty) break;
49
- for (const f of o.docs)
50
- await n.write(`${JSON.stringify(f.data())}
94
+ const u = k(s, o, e, {
95
+ ...r,
96
+ limit: i,
97
+ startAfter: f
98
+ }), m = await S(u);
99
+ if (m.empty) break;
100
+ for (const b of m.docs)
101
+ await c.write(`${l(b.data())}
51
102
  `);
52
- s = o.docs.at(-1);
103
+ f = m.docs.at(-1);
53
104
  }
54
- await n.close();
105
+ await c.close();
55
106
  }
56
107
  export {
57
- $ as default
108
+ Q as default,
109
+ V as scoreboardFormatter,
110
+ T as scoreboradHeader
58
111
  };
59
112
  //# sourceMappingURL=export.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"export.js","sources":["../../../../src/web/admin/export.tsx"],"sourcesContent":["import { forwardRef, type Ref, useRef } from \"react\";\n\nimport { Button, Modal } from \"@olinfo/react-components\";\nimport {\n type DocumentSnapshot,\n type Firestore,\n type FirestoreDataConverter,\n getDocs,\n} from \"firebase/firestore\";\nimport { TriangleAlert } from \"lucide-react\";\n\nimport { useDb } from \"~/web/common/base-login\";\nimport query, { type QueryOption } from \"~/web/common/query\";\n\nimport { useAdmin } from \"./context\";\n\ntype Props<T> = {\n label: string;\n description: string;\n collection: string;\n converter: FirestoreDataConverter<T>;\n options: QueryOption<T>;\n};\n\nexport default function Export<T>(props: Props<T>) {\n const ref = useRef<HTMLDialogElement>(null);\n\n return (\n <>\n <Button className=\"btn-primary\" onClick={() => ref.current?.showModal()}>\n Esporta {props.label}\n </Button>\n <ExportModal ref={ref} {...props} />\n </>\n );\n}\n\nconst ExportModal = forwardRef(function StudentExportModal(\n { label, description, collection, converter, options }: Props<any>,\n ref: Ref<HTMLDialogElement>,\n) {\n const { contest } = useAdmin();\n const db = useDb();\n\n const onExport = async () => {\n await saveExport(db, collection, converter, options);\n if (ref && \"current\" in ref) {\n ref.current?.close();\n }\n };\n\n return (\n <Modal ref={ref} title={`Esporta ${label}`}>\n {window.showSaveFilePicker === undefined ? (\n <div className=\"flex items-center gap-3\">\n <TriangleAlert size={20} className=\"flex-none text-warning\" />\n <span>\n Il tuo browser non supporta l&apos;esportazione dei dati. Usa un browser Chrome o Edge.\n </span>\n </div>\n ) : (\n <>\n <div>\n Stai per esportare i dati di {description} della gara <b>{contest.name}</b>. Questa\n operazione esegue un grande numero di query al database che richiederà del tempo e potrà\n incidere sulla fatturazione del progetto.\n </div>\n <div className=\"mt-4 flex flex-wrap justify-center gap-2\">\n <Button className=\"btn-primary\" onClick={onExport}>\n Esporta\n </Button>\n </div>\n </>\n )}\n </Modal>\n );\n});\n\nasync function saveExport<T>(\n db: Firestore,\n collection: string,\n converter: FirestoreDataConverter<T>,\n options: QueryOption<T>,\n) {\n const handle = await window.showSaveFilePicker({\n suggestedName: `${collection}.jsonl`,\n });\n\n const writableStream = await handle.createWritable();\n\n const chunkSize = 10_000;\n\n let last: DocumentSnapshot<T> | undefined;\n for (;;) {\n const q = query(db, collection, converter, {\n ...options,\n limit: chunkSize,\n startAfter: last,\n });\n\n const snapshot = await getDocs(q);\n if (snapshot.empty) break;\n\n for (const doc of snapshot.docs) {\n await writableStream.write(`${JSON.stringify(doc.data())}\\n`);\n }\n\n last = snapshot.docs.at(-1);\n }\n\n await writableStream.close();\n}\n"],"names":["Export","props","ref","useRef","jsxs","Fragment","Button","jsx","ExportModal","forwardRef","label","description","collection","converter","options","contest","useAdmin","db","useDb","onExport","saveExport","Modal","TriangleAlert","writableStream","chunkSize","last","q","query","snapshot","getDocs","doc"],"mappings":";;;;;;;;AAwBA,SAAwBA,EAAUC,GAAiB;AACjD,QAAMC,IAAMC,EAA0B,IAAI;AAE1C,SACE,gBAAAC,EAAAC,GAAA,EACE,UAAA;AAAA,IAAA,gBAAAD,EAACE,GAAA,EAAO,WAAU,eAAc,SAAS,MAAMJ,EAAI,SAAS,aAAa,UAAA;AAAA,MAAA;AAAA,MAC9DD,EAAM;AAAA,IAAA,GACjB;AAAA,IACA,gBAAAM,EAACC,GAAA,EAAY,KAAAN,GAAW,GAAGD,EAAA,CAAO;AAAA,EAAA,GACpC;AAEJ;AAEA,MAAMO,IAAcC,EAAW,SAC7B,EAAE,OAAAC,GAAO,aAAAC,GAAa,YAAAC,GAAY,WAAAC,GAAW,SAAAC,EAAA,GAC7CZ,GACA;AACA,QAAM,EAAE,SAAAa,EAAA,IAAYC,EAAA,GACdC,IAAKC,EAAA,GAELC,IAAW,YAAY;AAC3B,UAAMC,EAAWH,GAAIL,GAAYC,GAAWC,CAAO,GAC/CZ,KAAO,aAAaA,KACtBA,EAAI,SAAS,MAAA;AAAA,EAEjB;AAEA,SACE,gBAAAK,EAACc,GAAA,EAAM,KAAAnB,GAAU,OAAO,WAAWQ,CAAK,IACrC,UAAA,OAAO,uBAAuB,SAC7B,gBAAAN,EAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,IAAA,gBAAAG,EAACe,GAAA,EAAc,MAAM,IAAI,WAAU,0BAAyB;AAAA,IAC5D,gBAAAf,EAAC,UAAK,UAAA,qFAAA,CAEN;AAAA,EAAA,EAAA,CACF,IAEA,gBAAAH,EAAAC,GAAA,EACE,UAAA;AAAA,IAAA,gBAAAD,EAAC,OAAA,EAAI,UAAA;AAAA,MAAA;AAAA,MAC2BO;AAAA,MAAY;AAAA,MAAY,gBAAAJ,EAAC,KAAA,EAAG,UAAAQ,EAAQ,KAAA,CAAK;AAAA,MAAI;AAAA,IAAA,GAG7E;AAAA,IACA,gBAAAR,EAAC,OAAA,EAAI,WAAU,4CACb,UAAA,gBAAAA,EAACD,GAAA,EAAO,WAAU,eAAc,SAASa,GAAU,UAAA,UAAA,CAEnD,EAAA,CACF;AAAA,EAAA,EAAA,CACF,EAAA,CAEJ;AAEJ,CAAC;AAED,eAAeC,EACbH,GACAL,GACAC,GACAC,GACA;AAKA,QAAMS,IAAiB,OAJR,MAAM,OAAO,mBAAmB;AAAA,IAC7C,eAAe,GAAGX,CAAU;AAAA,EAAA,CAC7B,GAEmC,eAAA,GAE9BY,IAAY;AAElB,MAAIC;AACJ,aAAS;AACP,UAAMC,IAAIC,EAAMV,GAAIL,GAAYC,GAAW;AAAA,MACzC,GAAGC;AAAA,MACH,OAAOU;AAAA,MACP,YAAYC;AAAA,IAAA,CACb,GAEKG,IAAW,MAAMC,EAAQH,CAAC;AAChC,QAAIE,EAAS,MAAO;AAEpB,eAAWE,KAAOF,EAAS;AACzB,YAAML,EAAe,MAAM,GAAG,KAAK,UAAUO,EAAI,MAAM,CAAC;AAAA,CAAI;AAG9D,IAAAL,IAAOG,EAAS,KAAK,GAAG,EAAE;AAAA,EAC5B;AAEA,QAAML,EAAe,MAAA;AACvB;"}
1
+ {"version":3,"file":"export.js","sources":["../../../../src/web/admin/export.tsx"],"sourcesContent":["import { forwardRef, type Ref, useRef } from \"react\";\n\nimport {\n type Answer,\n type Contest,\n calcProblemPoints,\n formatUserData,\n type Student,\n unshuffleAnswer,\n type Variant,\n} from \"@olinfo/quizms/models\";\nimport { Button, Modal } from \"@olinfo/react-components\";\nimport { formatISO } from \"date-fns\";\nimport {\n type DocumentSnapshot,\n type Firestore,\n type FirestoreDataConverter,\n getDocs,\n} from \"firebase/firestore\";\nimport { TriangleAlert } from \"lucide-react\";\n\nimport { useDb } from \"~/web/common/base-login\";\nimport query, { type QueryOption } from \"~/web/common/query\";\n\nimport { useAdmin } from \"./context\";\n\ntype Props<T> = {\n label: string;\n description: string;\n collection: string;\n converter: FirestoreDataConverter<T>;\n options: QueryOption<T>;\n suggestedName?: string;\n formatter: (data: T) => string;\n header?: string;\n};\n\nexport default function Export<T>(props: Props<T>) {\n const ref = useRef<HTMLDialogElement>(null);\n\n return (\n <>\n <Button className=\"btn-primary\" onClick={() => ref.current?.showModal()}>\n Esporta {props.label}\n </Button>\n <ExportModal ref={ref} {...props} />\n </>\n );\n}\n\nconst ExportModal = forwardRef(function StudentExportModal(\n {\n label,\n description,\n collection,\n converter,\n options,\n formatter,\n suggestedName,\n header,\n }: Props<any>,\n ref: Ref<HTMLDialogElement>,\n) {\n const { contest } = useAdmin();\n const db = useDb();\n\n const onExport = async () => {\n await saveExport(db, collection, converter, options, formatter, suggestedName, header);\n if (ref && \"current\" in ref) {\n ref.current?.close();\n }\n };\n\n return (\n <Modal ref={ref} title={`Esporta ${label}`}>\n {window.showSaveFilePicker === undefined ? (\n <div className=\"flex items-center gap-3\">\n <TriangleAlert size={20} className=\"flex-none text-warning\" />\n <span>\n Il tuo browser non supporta l&apos;esportazione dei dati. Usa un browser Chrome o Edge.\n </span>\n </div>\n ) : (\n <>\n <div>\n Stai per esportare {description} della gara <b>{contest.name}</b>. Questa operazione\n esegue un grande numero di query al database che richiederà del tempo e potrà incidere\n sulla fatturazione del progetto.\n </div>\n <div className=\"mt-4 flex flex-wrap justify-center gap-2\">\n <Button className=\"btn-primary\" onClick={onExport}>\n Esporta\n </Button>\n </div>\n </>\n )}\n </Modal>\n );\n});\n\nexport function scoreboradHeader(contest: Contest) {\n const row: string[] = [];\n for (const field of contest.userData) {\n row.push(field.label);\n }\n row.push(\"Scuola\", \"Variante\");\n for (const problemId of contest.problemIds) {\n row.push(`${problemId}A`, `${problemId}P`);\n }\n row.push(\"Punteggio\");\n row.push(\"Assente\");\n return `${row.map((s) => `\"${s}\"`).join(\",\")}\\n`;\n}\n\n// TODO: move logic to @olinfo/quizms\nexport function scoreboardFormatter(\n contest: Contest,\n variants: Record<string, Variant>,\n student: Student,\n) {\n const row: string[] = [];\n for (const field of contest.userData) {\n if (field.type === \"date\") {\n const value = student.userData?.[field.name];\n if (value) {\n row.push(formatISO(value, { representation: \"date\" }));\n } else {\n row.push(\"\");\n }\n } else {\n row.push(formatUserData(student, field));\n }\n }\n row.push(student.participationId?.split(\"-\")[0] ?? \"\");\n row.push(student.variant ?? \"\");\n const unshuflledProblems: Record<string, [Answer, number]> = {};\n if (\n student.answers !== undefined &&\n student.variant !== undefined &&\n variants[student.variant] !== undefined\n ) {\n const schema = variants[student.variant].schema;\n for (const problemId in schema) {\n const problem = schema[problemId];\n const originalId = problem.originalId;\n const answer = student.answers[problemId];\n unshuflledProblems[originalId] = [\n unshuffleAnswer(problem, answer) ?? \"\",\n calcProblemPoints(problem, answer),\n ];\n }\n }\n for (const problemId of contest.problemIds) {\n if (unshuflledProblems[problemId] !== undefined) {\n const [answer, points] = unshuflledProblems[problemId];\n row.push(`${answer}`);\n row.push(`${points}`);\n } else {\n row.push(\"\");\n row.push(\"\");\n }\n }\n row.push(`${student.score ?? \"\"}`);\n row.push(`${student.absent ? 1 : 0}`);\n return row.map((s) => `\"${s}\"`).join(\",\");\n}\n\nasync function saveExport<T>(\n db: Firestore,\n collection: string,\n converter: FirestoreDataConverter<T>,\n options: QueryOption<T>,\n formatter: (data: T) => string,\n suggestedName: string | undefined,\n header?: string,\n) {\n const handle = await window.showSaveFilePicker({\n suggestedName: suggestedName ?? `${collection}.jsonl`,\n });\n\n const writableStream = await handle.createWritable();\n\n if (header !== undefined) {\n await writableStream.write(header);\n }\n\n const chunkSize = 10_000;\n\n let last: DocumentSnapshot<T> | undefined;\n for (;;) {\n const q = query(db, collection, converter, {\n ...options,\n limit: chunkSize,\n startAfter: last,\n });\n\n const snapshot = await getDocs(q);\n if (snapshot.empty) break;\n\n for (const doc of snapshot.docs) {\n await writableStream.write(`${formatter(doc.data())}\\n`);\n }\n\n last = snapshot.docs.at(-1);\n }\n\n await writableStream.close();\n}\n"],"names":["Export","props","ref","useRef","jsxs","Fragment","Button","jsx","ExportModal","forwardRef","label","description","collection","converter","options","formatter","suggestedName","header","contest","useAdmin","db","useDb","onExport","saveExport","Modal","TriangleAlert","scoreboradHeader","row","field","problemId","s","scoreboardFormatter","variants","student","value","formatISO","formatUserData","unshuflledProblems","schema","problem","originalId","answer","unshuffleAnswer","calcProblemPoints","points","writableStream","chunkSize","last","q","query","snapshot","getDocs","doc"],"mappings":"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"}
@@ -1,34 +1,38 @@
1
- import { jsxs as o, Fragment as s, jsx as r } from "react/jsx-runtime";
2
- import { Suspense as u } from "react";
3
- import { Title as p, ErrorBoundary as h, Loading as f } from "@olinfo/quizms/components";
4
- import { Navbar as b, NavbarBrand as x, NavbarMenu as N, NavbarSubmenu as v, NavbarMenuItem as w, NavbarContent as g, Dropdown as D, DropdownButton as B, DropdownMenu as I, DropdownItem as L, Button as j } from "@olinfo/react-components";
1
+ import { jsxs as i, Fragment as u, jsx as e } from "react/jsx-runtime";
2
+ import { Suspense as p } from "react";
3
+ import { Title as f, ErrorBoundary as b, Loading as x } from "@olinfo/quizms/components";
4
+ import { Navbar as N, NavbarBrand as v, NavbarMenu as w, NavbarSubmenu as g, NavbarMenuItem as d, NavbarContent as D, Dropdown as $, DropdownButton as B, DropdownMenu as I, DropdownItem as L, Button as j } from "@olinfo/react-components";
5
5
  import k from "clsx";
6
6
  import { LogOut as y } from "lucide-react";
7
- import { useRoute as M, Link as $ } from "wouter";
8
- function T({ name: t, contests: n, logout: l, children: c }) {
9
- const [, a] = M("/:contestId/*?"), i = n.find((e) => e.id === a?.contestId);
10
- return /* @__PURE__ */ o(s, { children: [
11
- /* @__PURE__ */ o(b, { color: "bg-error text-error-content", children: [
12
- /* @__PURE__ */ r(x, { children: /* @__PURE__ */ r("div", { className: "flex items-center h-full font-bold", children: /* @__PURE__ */ r(p, {}) }) }),
13
- i && n.length >= 2 && /* @__PURE__ */ r(N, { children: /* @__PURE__ */ r(v, { title: i.name, children: n.map((e) => {
14
- const d = a?.["*"], m = d ? `/${e.id}/${d}` : `/${e.id}`;
15
- return /* @__PURE__ */ r(w, { children: /* @__PURE__ */ r($, { className: k(i.id === e.id && "active"), href: m, children: e.name }) }, e.id);
16
- }) }) }),
17
- /* @__PURE__ */ r(g, { children: /* @__PURE__ */ r(C, { name: t, logout: l }) })
7
+ import { useRoute as M, Link as l } from "wouter";
8
+ function R({ name: t, contests: n, logout: s, children: m }) {
9
+ const [, a] = M("/:contestId/*?"), o = n.find((r) => r.id === a?.contestId);
10
+ return /* @__PURE__ */ i(u, { children: [
11
+ /* @__PURE__ */ i(N, { color: "bg-error text-error-content", children: [
12
+ /* @__PURE__ */ e(v, { children: /* @__PURE__ */ e("div", { className: "flex items-center h-full font-bold", children: /* @__PURE__ */ e(f, {}) }) }),
13
+ o && n.length >= 2 && /* @__PURE__ */ i(w, { children: [
14
+ /* @__PURE__ */ e(g, { title: o.name, children: n.map((r) => {
15
+ const c = a?.["*"], h = c ? `/${r.id}/${c}` : `/${r.id}`;
16
+ return /* @__PURE__ */ e(d, { children: /* @__PURE__ */ e(l, { className: k(o.id === r.id && "active"), href: h, children: r.name }) }, r.id);
17
+ }) }),
18
+ /* @__PURE__ */ e(d, { children: /* @__PURE__ */ e(l, { href: `/${o.id}`, children: "Gestione gara" }) }),
19
+ /* @__PURE__ */ e(d, { children: /* @__PURE__ */ e(l, { href: `/${o.id}/schools`, children: "Gestione scuole" }) })
20
+ ] }),
21
+ /* @__PURE__ */ e(D, { children: /* @__PURE__ */ e(C, { name: t, logout: s }) })
18
22
  ] }),
19
- /* @__PURE__ */ r("div", { className: "mx-auto flex w-full max-w-screen-xl grow flex-col p-4 pb-8", children: /* @__PURE__ */ r(h, { children: /* @__PURE__ */ r(u, { fallback: /* @__PURE__ */ r(f, {}), children: c }) }) })
23
+ /* @__PURE__ */ e("div", { className: "mx-auto flex w-full max-w-screen-xl grow flex-col p-4 pb-8", children: /* @__PURE__ */ e(b, { children: /* @__PURE__ */ e(p, { fallback: /* @__PURE__ */ e(x, {}), children: m }) }) })
20
24
  ] });
21
25
  }
22
26
  function C({ name: t, logout: n }) {
23
- return /* @__PURE__ */ o(D, { className: "dropdown-end", children: [
24
- /* @__PURE__ */ r(B, { children: /* @__PURE__ */ r("div", { className: "truncate uppercase", children: t }) }),
25
- /* @__PURE__ */ r(I, { children: /* @__PURE__ */ r(L, { children: /* @__PURE__ */ o(j, { className: "flex justify-between gap-4", onClick: n, children: [
27
+ return /* @__PURE__ */ i($, { className: "dropdown-end", children: [
28
+ /* @__PURE__ */ e(B, { children: /* @__PURE__ */ e("div", { className: "truncate uppercase", children: t }) }),
29
+ /* @__PURE__ */ e(I, { children: /* @__PURE__ */ e(L, { children: /* @__PURE__ */ i(j, { className: "flex justify-between gap-4", onClick: n, children: [
26
30
  "Esci ",
27
- /* @__PURE__ */ r(y, { size: 20 })
31
+ /* @__PURE__ */ e(y, { size: 20 })
28
32
  ] }) }) })
29
33
  ] });
30
34
  }
31
35
  export {
32
- T as AdminLayout
36
+ R as AdminLayout
33
37
  };
34
38
  //# sourceMappingURL=layout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout.js","sources":["../../../../src/web/admin/layout.tsx"],"sourcesContent":["import { type ReactNode, Suspense } from \"react\";\n\nimport { ErrorBoundary, Loading, Title } from \"@olinfo/quizms/components\";\nimport type { Contest } from \"@olinfo/quizms/models\";\nimport {\n Button,\n Dropdown,\n DropdownButton,\n DropdownItem,\n DropdownMenu,\n Navbar,\n NavbarBrand,\n NavbarContent,\n NavbarMenu,\n NavbarMenuItem,\n NavbarSubmenu,\n} from \"@olinfo/react-components\";\nimport clsx from \"clsx\";\nimport { LogOut } from \"lucide-react\";\nimport { Link, useRoute } from \"wouter\";\n\ntype Props = {\n name: string;\n contests: Contest[];\n logout: () => Promise<void>;\n children: ReactNode;\n};\n\nexport function AdminLayout({ name, contests, logout, children }: Props) {\n const [, params] = useRoute(\"/:contestId/*?\");\n const contest = contests.find((c) => c.id === params?.contestId);\n\n return (\n <>\n <Navbar color=\"bg-error text-error-content\">\n <NavbarBrand>\n <div className=\"flex items-center h-full font-bold\">\n <Title />\n </div>\n </NavbarBrand>\n {contest && contests.length >= 2 && (\n <NavbarMenu>\n <NavbarSubmenu title={contest.name}>\n {contests.map((c) => {\n const path = params?.[\"*\"];\n const href = path ? `/${c.id}/${path}` : `/${c.id}`;\n return (\n <NavbarMenuItem key={c.id}>\n <Link className={clsx(contest.id === c.id && \"active\")} href={href}>\n {c.name}\n </Link>\n </NavbarMenuItem>\n );\n })}\n </NavbarSubmenu>\n </NavbarMenu>\n )}\n <NavbarContent>\n <UserDropdown name={name} logout={logout} />\n </NavbarContent>\n </Navbar>\n <div className=\"mx-auto flex w-full max-w-screen-xl grow flex-col p-4 pb-8\">\n <ErrorBoundary>\n <Suspense fallback={<Loading />}>{children}</Suspense>\n </ErrorBoundary>\n </div>\n </>\n );\n}\n\nfunction UserDropdown({ name, logout }: { name: string; logout: () => Promise<void> }) {\n return (\n <Dropdown className=\"dropdown-end\">\n <DropdownButton>\n <div className=\"truncate uppercase\">{name}</div>\n </DropdownButton>\n <DropdownMenu>\n <DropdownItem>\n <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":["AdminLayout","name","contests","logout","children","params","useRoute","contest","c","jsxs","Fragment","Navbar","jsx","NavbarBrand","Title","NavbarMenu","NavbarSubmenu","path","href","NavbarMenuItem","Link","clsx","NavbarContent","UserDropdown","ErrorBoundary","Suspense","Loading","Dropdown","DropdownButton","DropdownMenu","DropdownItem","Button","LogOut"],"mappings":";;;;;;;AA4BO,SAASA,EAAY,EAAE,MAAAC,GAAM,UAAAC,GAAU,QAAAC,GAAQ,UAAAC,KAAmB;AACvE,QAAM,GAAGC,CAAM,IAAIC,EAAS,gBAAgB,GACtCC,IAAUL,EAAS,KAAK,CAACM,MAAMA,EAAE,OAAOH,GAAQ,SAAS;AAE/D,SACE,gBAAAI,EAAAC,GAAA,EACE,UAAA;AAAA,IAAA,gBAAAD,EAACE,GAAA,EAAO,OAAM,+BACZ,UAAA;AAAA,MAAA,gBAAAC,EAACC,GAAA,EACC,4BAAC,OAAA,EAAI,WAAU,sCACb,UAAA,gBAAAD,EAACE,GAAA,CAAA,CAAM,GACT,EAAA,CACF;AAAA,MACCP,KAAWL,EAAS,UAAU,uBAC5Ba,GAAA,EACC,UAAA,gBAAAH,EAACI,GAAA,EAAc,OAAOT,EAAQ,MAC3B,UAAAL,EAAS,IAAI,CAACM,MAAM;AACnB,cAAMS,IAAOZ,IAAS,GAAG,GACnBa,IAAOD,IAAO,IAAIT,EAAE,EAAE,IAAIS,CAAI,KAAK,IAAIT,EAAE,EAAE;AACjD,iCACGW,GAAA,EACC,UAAA,gBAAAP,EAACQ,GAAA,EAAK,WAAWC,EAAKd,EAAQ,OAAOC,EAAE,MAAM,QAAQ,GAAG,MAAAU,GACrD,YAAE,MACL,EAAA,GAHmBV,EAAE,EAIvB;AAAA,MAEJ,CAAC,GACH,GACF;AAAA,wBAEDc,GAAA,EACC,UAAA,gBAAAV,EAACW,GAAA,EAAa,MAAAtB,GAAY,QAAAE,GAAgB,EAAA,CAC5C;AAAA,IAAA,GACF;AAAA,IACA,gBAAAS,EAAC,OAAA,EAAI,WAAU,8DACb,4BAACY,GAAA,EACC,UAAA,gBAAAZ,EAACa,GAAA,EAAS,UAAU,gBAAAb,EAACc,GAAA,CAAA,CAAQ,GAAK,UAAAtB,GAAS,GAC7C,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;AAEA,SAASmB,EAAa,EAAE,MAAAtB,GAAM,QAAAE,KAAyD;AACrF,SACE,gBAAAM,EAACkB,GAAA,EAAS,WAAU,gBAClB,UAAA;AAAA,IAAA,gBAAAf,EAACgB,KACC,UAAA,gBAAAhB,EAAC,OAAA,EAAI,WAAU,sBAAsB,aAAK,EAAA,CAC5C;AAAA,IACA,gBAAAA,EAACiB,KACC,UAAA,gBAAAjB,EAACkB,GAAA,EACC,4BAACC,GAAA,EAAO,WAAU,8BAA6B,SAAS5B,GAAQ,UAAA;AAAA,MAAA;AAAA,MACzD,gBAAAS,EAACoB,GAAA,EAAO,MAAM,GAAA,CAAI;AAAA,IAAA,EAAA,CACzB,GACF,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;"}
1
+ {"version":3,"file":"layout.js","sources":["../../../../src/web/admin/layout.tsx"],"sourcesContent":["import { type ReactNode, Suspense } from \"react\";\n\nimport { ErrorBoundary, Loading, Title } from \"@olinfo/quizms/components\";\nimport type { Contest } from \"@olinfo/quizms/models\";\nimport {\n Button,\n Dropdown,\n DropdownButton,\n DropdownItem,\n DropdownMenu,\n Navbar,\n NavbarBrand,\n NavbarContent,\n NavbarMenu,\n NavbarMenuItem,\n NavbarSubmenu,\n} from \"@olinfo/react-components\";\nimport clsx from \"clsx\";\nimport { LogOut } from \"lucide-react\";\nimport { Link, useRoute } from \"wouter\";\n\ntype Props = {\n name: string;\n contests: Contest[];\n logout: () => Promise<void>;\n children: ReactNode;\n};\n\nexport function AdminLayout({ name, contests, logout, children }: Props) {\n const [, params] = useRoute(\"/:contestId/*?\");\n const contest = contests.find((c) => c.id === params?.contestId);\n\n return (\n <>\n <Navbar color=\"bg-error text-error-content\">\n <NavbarBrand>\n <div className=\"flex items-center h-full font-bold\">\n <Title />\n </div>\n </NavbarBrand>\n {contest && contests.length >= 2 && (\n <NavbarMenu>\n <NavbarSubmenu title={contest.name}>\n {contests.map((c) => {\n const path = params?.[\"*\"];\n const href = path ? `/${c.id}/${path}` : `/${c.id}`;\n return (\n <NavbarMenuItem key={c.id}>\n <Link className={clsx(contest.id === c.id && \"active\")} href={href}>\n {c.name}\n </Link>\n </NavbarMenuItem>\n );\n })}\n </NavbarSubmenu>\n <NavbarMenuItem>\n <Link href={`/${contest.id}`}>Gestione gara</Link>\n </NavbarMenuItem>\n <NavbarMenuItem>\n <Link href={`/${contest.id}/schools`}>Gestione scuole</Link>\n </NavbarMenuItem>\n </NavbarMenu>\n )}\n <NavbarContent>\n <UserDropdown name={name} logout={logout} />\n </NavbarContent>\n </Navbar>\n <div className=\"mx-auto flex w-full max-w-screen-xl grow flex-col p-4 pb-8\">\n <ErrorBoundary>\n <Suspense fallback={<Loading />}>{children}</Suspense>\n </ErrorBoundary>\n </div>\n </>\n );\n}\n\nfunction UserDropdown({ name, logout }: { name: string; logout: () => Promise<void> }) {\n return (\n <Dropdown className=\"dropdown-end\">\n <DropdownButton>\n <div className=\"truncate uppercase\">{name}</div>\n </DropdownButton>\n <DropdownMenu>\n <DropdownItem>\n <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":["AdminLayout","name","contests","logout","children","params","useRoute","contest","c","jsxs","Fragment","Navbar","jsx","NavbarBrand","Title","NavbarMenu","NavbarSubmenu","path","href","NavbarMenuItem","Link","clsx","NavbarContent","UserDropdown","ErrorBoundary","Suspense","Loading","Dropdown","DropdownButton","DropdownMenu","DropdownItem","Button","LogOut"],"mappings":";;;;;;;AA4BO,SAASA,EAAY,EAAE,MAAAC,GAAM,UAAAC,GAAU,QAAAC,GAAQ,UAAAC,KAAmB;AACvE,QAAM,GAAGC,CAAM,IAAIC,EAAS,gBAAgB,GACtCC,IAAUL,EAAS,KAAK,CAACM,MAAMA,EAAE,OAAOH,GAAQ,SAAS;AAE/D,SACE,gBAAAI,EAAAC,GAAA,EACE,UAAA;AAAA,IAAA,gBAAAD,EAACE,GAAA,EAAO,OAAM,+BACZ,UAAA;AAAA,MAAA,gBAAAC,EAACC,GAAA,EACC,4BAAC,OAAA,EAAI,WAAU,sCACb,UAAA,gBAAAD,EAACE,GAAA,CAAA,CAAM,GACT,EAAA,CACF;AAAA,MACCP,KAAWL,EAAS,UAAU,uBAC5Ba,GAAA,EACC,UAAA;AAAA,QAAA,gBAAAH,EAACI,KAAc,OAAOT,EAAQ,MAC3B,UAAAL,EAAS,IAAI,CAACM,MAAM;AACnB,gBAAMS,IAAOZ,IAAS,GAAG,GACnBa,IAAOD,IAAO,IAAIT,EAAE,EAAE,IAAIS,CAAI,KAAK,IAAIT,EAAE,EAAE;AACjD,mCACGW,GAAA,EACC,UAAA,gBAAAP,EAACQ,GAAA,EAAK,WAAWC,EAAKd,EAAQ,OAAOC,EAAE,MAAM,QAAQ,GAAG,MAAAU,GACrD,YAAE,MACL,EAAA,GAHmBV,EAAE,EAIvB;AAAA,QAEJ,CAAC,EAAA,CACH;AAAA,QACA,gBAAAI,EAACO,GAAA,EACC,UAAA,gBAAAP,EAACQ,GAAA,EAAK,MAAM,IAAIb,EAAQ,EAAE,IAAI,UAAA,gBAAA,CAAa,GAC7C;AAAA,QACA,gBAAAK,EAACO,GAAA,EACC,UAAA,gBAAAP,EAACQ,GAAA,EAAK,MAAM,IAAIb,EAAQ,EAAE,YAAY,UAAA,kBAAA,CAAe,EAAA,CACvD;AAAA,MAAA,GACF;AAAA,wBAEDe,GAAA,EACC,UAAA,gBAAAV,EAACW,GAAA,EAAa,MAAAtB,GAAY,QAAAE,GAAgB,EAAA,CAC5C;AAAA,IAAA,GACF;AAAA,IACA,gBAAAS,EAAC,OAAA,EAAI,WAAU,8DACb,4BAACY,GAAA,EACC,UAAA,gBAAAZ,EAACa,GAAA,EAAS,UAAU,gBAAAb,EAACc,GAAA,CAAA,CAAQ,GAAK,UAAAtB,GAAS,GAC7C,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;AAEA,SAASmB,EAAa,EAAE,MAAAtB,GAAM,QAAAE,KAAyD;AACrF,SACE,gBAAAM,EAACkB,GAAA,EAAS,WAAU,gBAClB,UAAA;AAAA,IAAA,gBAAAf,EAACgB,KACC,UAAA,gBAAAhB,EAAC,OAAA,EAAI,WAAU,sBAAsB,aAAK,EAAA,CAC5C;AAAA,IACA,gBAAAA,EAACiB,KACC,UAAA,gBAAAjB,EAACkB,GAAA,EACC,4BAACC,GAAA,EAAO,WAAU,8BAA6B,SAAS5B,GAAQ,UAAA;AAAA,MAAA;AAAA,MACzD,gBAAAS,EAACoB,GAAA,EAAO,MAAM,GAAA,CAAI;AAAA,IAAA,EAAA,CACzB,GACF,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;"}
@@ -1,30 +1,50 @@
1
- import { jsxs as o, jsx as r } from "react/jsx-runtime";
2
- import { lazy as c } from "react";
3
- import { Route as a, Redirect as d, Link as m, useParams as h, Switch as f } from "wouter";
4
- import { AdminContext as p } from "./context.js";
5
- import { AdminLayout as s } from "./layout.js";
6
- import { SchoolTable as u } from "./school-table.js";
7
- function P({ name: i, contests: e, setContest: l, logout: n }) {
8
- return /* @__PURE__ */ o(s, { name: i, contests: e, logout: n, children: [
9
- /* @__PURE__ */ o(a, { path: "/", children: [
10
- e.length === 1 && /* @__PURE__ */ r(d, { to: `/${e[0].id}` }),
11
- /* @__PURE__ */ o("div", { className: "flex w-full grow flex-col items-center justify-center gap-4", children: [
1
+ import { jsxs as d, jsx as r } from "react/jsx-runtime";
2
+ import { lazy as s } from "react";
3
+ import { Route as l, Redirect as m, Link as h, useParams as f, Switch as p } from "wouter";
4
+ import { AdminContext as u } from "./context.js";
5
+ import { AdminLayout as x } from "./layout.js";
6
+ import { SchoolTable as v } from "./school-table.js";
7
+ function A({
8
+ name: t,
9
+ contests: e,
10
+ setContest: c,
11
+ logout: a,
12
+ ...n
13
+ }) {
14
+ return /* @__PURE__ */ d(x, { name: t, contests: e, logout: a, children: [
15
+ /* @__PURE__ */ d(l, { path: "/", children: [
16
+ e.length === 1 && /* @__PURE__ */ r(m, { to: `/${e[0].id}` }),
17
+ /* @__PURE__ */ d("div", { className: "flex w-full grow flex-col items-center justify-center gap-4", children: [
12
18
  /* @__PURE__ */ r("p", { className: "text-2xl", children: "Seleziona una gara" }),
13
- /* @__PURE__ */ r("div", { className: "flex flex-wrap justify-center gap-2", children: e.map((t) => /* @__PURE__ */ r(m, { className: "btn btn-primary", href: `/${t.id}`, children: t.name }, t.id)) })
19
+ /* @__PURE__ */ r("div", { className: "flex flex-wrap justify-center gap-2", children: e.map((o) => /* @__PURE__ */ r(h, { className: "btn btn-primary", href: `/${o.id}`, children: o.name }, o.id)) })
14
20
  ] })
15
21
  ] }),
16
- /* @__PURE__ */ r(a, { path: "/:contestId", nest: !0, children: /* @__PURE__ */ r(v, { name: i, contests: e, setContest: l, logout: n }) })
22
+ /* @__PURE__ */ r(l, { path: "/:contestId", nest: !0, children: /* @__PURE__ */ r(
23
+ j,
24
+ {
25
+ name: t,
26
+ contests: e,
27
+ setContest: c,
28
+ logout: a,
29
+ ...n
30
+ }
31
+ ) })
17
32
  ] });
18
33
  }
19
- const x = c(() => import("./dashboard.js"));
20
- function v({ contests: i, ...e }) {
21
- const { contestId: l } = h(), n = i.find((t) => t.id === l);
22
- return n ? /* @__PURE__ */ r(p.Provider, { value: { ...e, contest: n, contests: i }, children: /* @__PURE__ */ o(f, { children: [
23
- /* @__PURE__ */ r(a, { path: "/", children: /* @__PURE__ */ r(x, {}) }),
24
- /* @__PURE__ */ r(a, { path: "/schools", children: /* @__PURE__ */ r(u, {}) })
25
- ] }) }) : /* @__PURE__ */ r(d, { to: "/" });
34
+ const b = s(() => import("./dashboard.js"));
35
+ function j({ contests: t, variants: e, ...c }) {
36
+ const { contestId: a } = f(), n = t.find((i) => i.id === a);
37
+ if (!n)
38
+ return /* @__PURE__ */ r(m, { to: "/" });
39
+ const o = Object.fromEntries(
40
+ e.filter((i) => i.contestId === n?.id).map((i) => [i.id, i])
41
+ );
42
+ return /* @__PURE__ */ r(u.Provider, { value: { ...c, variants: o, contest: n, contests: t }, children: /* @__PURE__ */ d(p, { children: [
43
+ /* @__PURE__ */ r(l, { path: "/", children: /* @__PURE__ */ r(b, {}) }),
44
+ /* @__PURE__ */ r(l, { path: "/schools", children: /* @__PURE__ */ r(v, {}) })
45
+ ] }) });
26
46
  }
27
47
  export {
28
- P as AdminProvider
48
+ A as AdminProvider
29
49
  };
30
50
  //# sourceMappingURL=provider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"provider.js","sources":["../../../../src/web/admin/provider.tsx"],"sourcesContent":["import { lazy } from \"react\";\n\nimport type { Contest } from \"@olinfo/quizms/models\";\nimport { Link, Redirect, Route, Switch, useParams } from \"wouter\";\n\nimport { AdminContext } from \"./context\";\nimport { AdminLayout } from \"./layout\";\nimport { SchoolTable } from \"./school-table\";\n\ntype AdminProviderProps = {\n name: string;\n contests: Contest[];\n setContest: (contest: Contest) => Promise<void>;\n logout: () => Promise<void>;\n};\n\nexport function AdminProvider({ name, contests, setContest, logout }: AdminProviderProps) {\n return (\n <AdminLayout name={name} contests={contests} logout={logout}>\n <Route path=\"/\">\n {contests.length === 1 && <Redirect to={`/${contests[0].id}`} />}\n <div className=\"flex w-full grow flex-col items-center justify-center gap-4\">\n <p className=\"text-2xl\">Seleziona una gara</p>\n <div className=\"flex flex-wrap justify-center gap-2\">\n {contests.map((c) => (\n <Link key={c.id} className=\"btn btn-primary\" href={`/${c.id}`}>\n {c.name}\n </Link>\n ))}\n </div>\n </div>\n </Route>\n <Route path=\"/:contestId\" nest>\n <ProviderInner name={name} contests={contests} setContest={setContest} logout={logout} />\n </Route>\n </AdminLayout>\n );\n}\n\nconst Dashboard = lazy(() => import(\"./dashboard\"));\n\nfunction ProviderInner({ contests, ...props }: AdminProviderProps) {\n const { contestId } = useParams();\n\n const contest = contests.find((c) => c.id === contestId);\n if (!contest) {\n return <Redirect to=\"/\" />;\n }\n\n return (\n <AdminContext.Provider value={{ ...props, contest, contests }}>\n <Switch>\n <Route path=\"/\">\n <Dashboard />\n </Route>\n <Route path=\"/schools\">\n <SchoolTable />\n </Route>\n </Switch>\n </AdminContext.Provider>\n );\n}\n"],"names":["AdminProvider","name","contests","setContest","logout","jsxs","AdminLayout","Route","jsx","Redirect","c","Link","ProviderInner","Dashboard","lazy","props","contestId","useParams","contest","AdminContext","Switch","SchoolTable"],"mappings":";;;;;;AAgBO,SAASA,EAAc,EAAE,MAAAC,GAAM,UAAAC,GAAU,YAAAC,GAAY,QAAAC,KAA8B;AACxF,SACE,gBAAAC,EAACC,GAAA,EAAY,MAAAL,GAAY,UAAAC,GAAoB,QAAAE,GAC3C,UAAA;AAAA,IAAA,gBAAAC,EAACE,GAAA,EAAM,MAAK,KACT,UAAA;AAAA,MAAAL,EAAS,WAAW,KAAK,gBAAAM,EAACC,GAAA,EAAS,IAAI,IAAIP,EAAS,CAAC,EAAE,EAAE,GAAA,CAAI;AAAA,MAC9D,gBAAAG,EAAC,OAAA,EAAI,WAAU,+DACb,UAAA;AAAA,QAAA,gBAAAG,EAAC,KAAA,EAAE,WAAU,YAAW,UAAA,sBAAkB;AAAA,QAC1C,gBAAAA,EAAC,SAAI,WAAU,uCACZ,YAAS,IAAI,CAACE,MACb,gBAAAF,EAACG,GAAA,EAAgB,WAAU,mBAAkB,MAAM,IAAID,EAAE,EAAE,IACxD,YAAE,KAAA,GADMA,EAAE,EAEb,CACD,EAAA,CACH;AAAA,MAAA,EAAA,CACF;AAAA,IAAA,GACF;AAAA,IACA,gBAAAF,EAACD,GAAA,EAAM,MAAK,eAAc,MAAI,IAC5B,UAAA,gBAAAC,EAACI,GAAA,EAAc,MAAAX,GAAY,UAAAC,GAAoB,YAAAC,GAAwB,QAAAC,EAAA,CAAgB,EAAA,CACzF;AAAA,EAAA,GACF;AAEJ;AAEA,MAAMS,IAAYC,EAAK,MAAM,OAAO,gBAAa,CAAC;AAElD,SAASF,EAAc,EAAE,UAAAV,GAAU,GAAGa,KAA6B;AACjE,QAAM,EAAE,WAAAC,EAAA,IAAcC,EAAA,GAEhBC,IAAUhB,EAAS,KAAK,CAACQ,MAAMA,EAAE,OAAOM,CAAS;AACvD,SAAKE,IAKH,gBAAAV,EAACW,EAAa,UAAb,EAAsB,OAAO,EAAE,GAAGJ,GAAO,SAAAG,GAAS,UAAAhB,EAAA,GACjD,UAAA,gBAAAG,EAACe,GAAA,EACC,UAAA;AAAA,IAAA,gBAAAZ,EAACD,GAAA,EAAM,MAAK,KACV,UAAA,gBAAAC,EAACK,KAAU,GACb;AAAA,sBACCN,GAAA,EAAM,MAAK,YACV,UAAA,gBAAAC,EAACa,KAAY,EAAA,CACf;AAAA,EAAA,EAAA,CACF,EAAA,CACF,IAbO,gBAAAb,EAACC,GAAA,EAAS,IAAG,IAAA,CAAI;AAe5B;"}
1
+ {"version":3,"file":"provider.js","sources":["../../../../src/web/admin/provider.tsx"],"sourcesContent":["import { lazy } from \"react\";\n\nimport type { Contest, Variant } from \"@olinfo/quizms/models\";\nimport { Link, Redirect, Route, Switch, useParams } from \"wouter\";\n\nimport { AdminContext } from \"./context\";\nimport { AdminLayout } from \"./layout\";\nimport { SchoolTable } from \"./school-table\";\n\ntype AdminProviderProps = {\n name: string;\n contests: Contest[];\n variants: Variant[];\n setContest: (contest: Contest) => Promise<void>;\n logout: () => Promise<void>;\n};\n\nexport function AdminProvider({\n name,\n contests,\n setContest,\n logout,\n ...props\n}: AdminProviderProps) {\n return (\n <AdminLayout name={name} contests={contests} logout={logout}>\n <Route path=\"/\">\n {contests.length === 1 && <Redirect to={`/${contests[0].id}`} />}\n <div className=\"flex w-full grow flex-col items-center justify-center gap-4\">\n <p className=\"text-2xl\">Seleziona una gara</p>\n <div className=\"flex flex-wrap justify-center gap-2\">\n {contests.map((c) => (\n <Link key={c.id} className=\"btn btn-primary\" href={`/${c.id}`}>\n {c.name}\n </Link>\n ))}\n </div>\n </div>\n </Route>\n <Route path=\"/:contestId\" nest>\n <ProviderInner\n name={name}\n contests={contests}\n setContest={setContest}\n logout={logout}\n {...props}\n />\n </Route>\n </AdminLayout>\n );\n}\n\nconst Dashboard = lazy(() => import(\"./dashboard\"));\n\nfunction ProviderInner({ contests, variants, ...props }: AdminProviderProps) {\n const { contestId } = useParams();\n\n const contest = contests.find((c) => c.id === contestId);\n if (!contest) {\n return <Redirect to=\"/\" />;\n }\n\n const contestVariants = Object.fromEntries(\n variants.filter((v) => v.contestId === contest?.id).map((v) => [v.id, v]),\n );\n\n return (\n <AdminContext.Provider value={{ ...props, variants: contestVariants, contest, contests }}>\n <Switch>\n <Route path=\"/\">\n <Dashboard />\n </Route>\n <Route path=\"/schools\">\n <SchoolTable />\n </Route>\n </Switch>\n </AdminContext.Provider>\n );\n}\n"],"names":["AdminProvider","name","contests","setContest","logout","props","jsxs","AdminLayout","Route","jsx","Redirect","c","Link","ProviderInner","Dashboard","lazy","variants","contestId","useParams","contest","contestVariants","v","AdminContext","Switch","SchoolTable"],"mappings":";;;;;;AAiBO,SAASA,EAAc;AAAA,EAC5B,MAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA,QAAAC;AAAA,EACA,GAAGC;AACL,GAAuB;AACrB,SACE,gBAAAC,EAACC,GAAA,EAAY,MAAAN,GAAY,UAAAC,GAAoB,QAAAE,GAC3C,UAAA;AAAA,IAAA,gBAAAE,EAACE,GAAA,EAAM,MAAK,KACT,UAAA;AAAA,MAAAN,EAAS,WAAW,KAAK,gBAAAO,EAACC,GAAA,EAAS,IAAI,IAAIR,EAAS,CAAC,EAAE,EAAE,GAAA,CAAI;AAAA,MAC9D,gBAAAI,EAAC,OAAA,EAAI,WAAU,+DACb,UAAA;AAAA,QAAA,gBAAAG,EAAC,KAAA,EAAE,WAAU,YAAW,UAAA,sBAAkB;AAAA,QAC1C,gBAAAA,EAAC,SAAI,WAAU,uCACZ,YAAS,IAAI,CAACE,MACb,gBAAAF,EAACG,GAAA,EAAgB,WAAU,mBAAkB,MAAM,IAAID,EAAE,EAAE,IACxD,YAAE,KAAA,GADMA,EAAE,EAEb,CACD,EAAA,CACH;AAAA,MAAA,EAAA,CACF;AAAA,IAAA,GACF;AAAA,IACA,gBAAAF,EAACD,GAAA,EAAM,MAAK,eAAc,MAAI,IAC5B,UAAA,gBAAAC;AAAA,MAACI;AAAA,MAAA;AAAA,QACC,MAAAZ;AAAA,QACA,UAAAC;AAAA,QACA,YAAAC;AAAA,QACA,QAAAC;AAAA,QACC,GAAGC;AAAA,MAAA;AAAA,IAAA,EACN,CACF;AAAA,EAAA,GACF;AAEJ;AAEA,MAAMS,IAAYC,EAAK,MAAM,OAAO,gBAAa,CAAC;AAElD,SAASF,EAAc,EAAE,UAAAX,GAAU,UAAAc,GAAU,GAAGX,KAA6B;AAC3E,QAAM,EAAE,WAAAY,EAAA,IAAcC,EAAA,GAEhBC,IAAUjB,EAAS,KAAK,CAACS,MAAMA,EAAE,OAAOM,CAAS;AACvD,MAAI,CAACE;AACH,WAAO,gBAAAV,EAACC,GAAA,EAAS,IAAG,IAAA,CAAI;AAG1B,QAAMU,IAAkB,OAAO;AAAA,IAC7BJ,EAAS,OAAO,CAACK,MAAMA,EAAE,cAAcF,GAAS,EAAE,EAAE,IAAI,CAACE,MAAM,CAACA,EAAE,IAAIA,CAAC,CAAC;AAAA,EAAA;AAG1E,SACE,gBAAAZ,EAACa,EAAa,UAAb,EAAsB,OAAO,EAAE,GAAGjB,GAAO,UAAUe,GAAiB,SAAAD,GAAS,UAAAjB,KAC5E,4BAACqB,GAAA,EACC,UAAA;AAAA,IAAA,gBAAAd,EAACD,GAAA,EAAM,MAAK,KACV,UAAA,gBAAAC,EAACK,KAAU,GACb;AAAA,sBACCN,GAAA,EAAM,MAAK,YACV,UAAA,gBAAAC,EAACe,KAAY,EAAA,CACf;AAAA,EAAA,EAAA,CACF,EAAA,CACF;AAEJ;"}
@@ -19,22 +19,26 @@ function m(t) {
19
19
  return i.teacherLogin.mutate(t);
20
20
  }
21
21
  function f(t) {
22
+ return i.adminLogin.mutate(t);
23
+ }
24
+ function h(t) {
22
25
  return i.studentLogin.mutate(t);
23
26
  }
24
- async function h(t, n) {
27
+ async function g(t, n) {
25
28
  await i.teacherStartParticipation.mutate({ participationId: n }, { context: { user: t } });
26
29
  }
27
- async function P(t, n) {
30
+ async function L(t, n) {
28
31
  await i.teacherStopParticipation.mutate({ participationId: n }, { context: { user: t } });
29
32
  }
30
- async function g(t, n) {
33
+ async function P(t, n) {
31
34
  await i.teacherFinalizeParticipation.mutate({ participationId: n }, { context: { user: t } });
32
35
  }
33
36
  export {
34
- g as finalizeParticipation,
35
- h as startParticipation,
36
- P as stopParticipation,
37
- f as studentLogin,
37
+ f as adminLogin,
38
+ P as finalizeParticipation,
39
+ g as startParticipation,
40
+ L as stopParticipation,
41
+ h as studentLogin,
38
42
  m as teacherLogin
39
43
  };
40
44
  //# 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 { 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
+ {"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 adminLogin(body: { username: string; password: string }) {\n return trpc.adminLogin.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","adminLogin","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,EAAWD,GAA8C;AACvE,SAAOR,EAAK,WAAW,OAAOQ,CAAI;AACpC;AAEO,SAASE,EAAaF,GAK1B;AACD,SAAOR,EAAK,aAAa,OAAOQ,CAAI;AACtC;AAEA,eAAsBG,EAAmBN,GAAYO,GAAyB;AAC5E,QAAMZ,EAAK,0BAA0B,OAAO,EAAE,iBAAAY,EAAA,GAAmB,EAAE,SAAS,EAAE,MAAAP,EAAA,GAAQ;AACxF;AAEA,eAAsBQ,EAAkBR,GAAYO,GAAyB;AAC3E,QAAMZ,EAAK,yBAAyB,OAAO,EAAE,iBAAAY,EAAA,GAAmB,EAAE,SAAS,EAAE,MAAAP,EAAA,GAAQ;AACvF;AAEA,eAAsBS,EAAsBT,GAAYO,GAAyB;AAC/E,QAAMZ,EAAK,6BAA6B,OAAO,EAAE,iBAAAY,EAAA,GAAmB,EAAE,SAAS,EAAE,MAAAP,EAAA,GAAQ;AAC3F;"}
@@ -1,40 +1,40 @@
1
- import { jsxs as a, Fragment as u, jsx as e } from "react/jsx-runtime";
1
+ import { jsxs as n, 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
- import { Navbar as h, NavbarBrand as b, Form as w, UsernameField as g, CurrentPasswordField as x, SubmitButton as N } from "@olinfo/react-components";
5
- import { getAuth as S, signInWithCustomToken as v } from "firebase/auth";
6
- import { useSearch as A } from "wouter";
7
- import { useAuth as F } from "../hooks/auth.js";
4
+ import { Navbar as h, NavbarBrand as b, Form as w, UsernameField as g, CurrentPasswordField as x, SubmitButton as L } from "@olinfo/react-components";
5
+ import { getAuth as N, signInWithCustomToken as S } from "firebase/auth";
6
+ import { useSearch as v } from "wouter";
7
+ import { useAuth as A } from "../hooks/auth.js";
8
8
  import "firebase/firestore";
9
9
  import "lodash-es";
10
10
  import "react-error-boundary";
11
11
  import "swr";
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());
12
+ import { useDb as F } from "./base-login.js";
13
+ import { adminLogin as P, teacherLogin as k } from "./api.js";
14
+ function z({ allowedRole: t, children: s }) {
15
+ const i = F(), a = new URLSearchParams(v());
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: t.get("username") ?? "",
22
- password: t.get("password") ?? ""
21
+ username: a.get("username") ?? "",
22
+ password: a.get("password") ?? ""
23
23
  }, c = async (r) => {
24
- const l = await P(r), d = S(i.app);
25
- await v(d, l);
26
- }, o = F(s);
27
- return o ? n(o) : /* @__PURE__ */ a(u, { children: [
24
+ const d = t === "admin" ? await P(r) : await k(r), l = N(i.app);
25
+ await S(l, d);
26
+ }, o = A(t);
27
+ return o ? s(o) : /* @__PURE__ */ n(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__ */ a(w, { defaultValue: m, onSubmit: c, className: "p-4 pb-8", children: [
29
+ /* @__PURE__ */ n(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" }),
33
- /* @__PURE__ */ e(N, { children: "Accedi" })
33
+ /* @__PURE__ */ e(L, { children: "Accedi" })
34
34
  ] })
35
35
  ] });
36
36
  }
37
37
  export {
38
- q as default
38
+ z as default
39
39
  };
40
40
  //# sourceMappingURL=token-login.js.map
@@ -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 { 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;"}
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 { adminLogin, 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 =\n allowedRole === \"admin\" ? await adminLogin(credential) : 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","adminLogin","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,IACJX,MAAgB,UAAU,MAAMY,EAAWF,CAAU,IAAI,MAAMG,EAAaH,CAAU,GAElFI,IAAOC,EAAQb,EAAG,GAAG;AAC3B,UAAMc,EAAsBF,GAAMH,CAAK;AAAA,EACzC,GAEMG,IAAOG,EAAQjB,CAAW;AAChC,SAAIc,IACKb,EAASa,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,cAAchB,GAAmB,UAAUC,GAAQ,WAAU,YACjE,UAAA;AAAA,MAAA,gBAAAW,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,25 +1,34 @@
1
1
  import { jsx as t } from "react/jsx-runtime";
2
- import { useCallback as s } from "react";
3
- import { signOut as a, getAuth as p } from "firebase/auth";
4
- import { AdminProvider as c } from "../admin/provider.js";
5
- import { useDb as l } from "../common/base-login.js";
6
- import { contestConverter as u } from "../common/converters.js";
7
- import d from "../common/token-login.js";
2
+ import { useCallback as p } from "react";
3
+ import { signOut as c, getAuth as l } from "firebase/auth";
4
+ import { AdminProvider as u } from "../admin/provider.js";
5
+ import { useDb as d } from "../common/base-login.js";
6
+ import { variantConverter as f, contestConverter as v } from "../common/converters.js";
7
+ import C from "../common/token-login.js";
8
8
  import "lodash-es";
9
9
  import "react-error-boundary";
10
10
  import "swr";
11
- import { useCollection as f } from "../hooks/collection.js";
11
+ import { useCollection as n } from "../hooks/collection.js";
12
12
  import "firebase/firestore";
13
- function I() {
14
- return /* @__PURE__ */ t(d, { allowedRole: "admin", children: (o) => /* @__PURE__ */ t(g, { user: o.user }) });
13
+ function N() {
14
+ return /* @__PURE__ */ t(C, { allowedRole: "admin", children: (o) => /* @__PURE__ */ t(g, { user: o.user }) });
15
15
  }
16
16
  function g({ user: o }) {
17
- const r = l(), n = o.displayName ?? "Utente anonimo", [e, i] = f("contests", u), m = s(async () => {
18
- await a(p(r.app)), window.location.reload();
17
+ const r = d(), e = o.displayName ?? "Utente anonimo", [i, m] = n("contests", v), [s] = n("variants", f), a = p(async () => {
18
+ await c(l(r.app)), window.location.reload();
19
19
  }, [r]);
20
- return /* @__PURE__ */ t(c, { name: n, contests: e, setContest: i, logout: m });
20
+ return /* @__PURE__ */ t(
21
+ u,
22
+ {
23
+ name: e,
24
+ contests: i,
25
+ setContest: m,
26
+ logout: a,
27
+ variants: s
28
+ }
29
+ );
21
30
  }
22
31
  export {
23
- I as default
32
+ N as default
24
33
  };
25
34
  //# sourceMappingURL=admin.js.map
@@ -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\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;"}
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, variantConverter } 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 [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 <AdminProvider\n name={name}\n contests={contests}\n setContest={setContest}\n logout={logout}\n variants={variants}\n />\n );\n}\n"],"names":["AdminEntry","jsx","TokenLogin","auth","AdminInner","user","db","useDb","name","contests","setContest","useCollection","contestConverter","variants","variantConverter","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,GAEnE,CAACC,CAAQ,IAAIF,EAAc,YAAYG,CAAgB,GAEvDC,IAASC,EAAY,YAAY;AACrC,UAAMC,EAAQC,EAAQZ,EAAG,GAAG,CAAC,GAC7B,OAAO,SAAS,OAAA;AAAA,EAClB,GAAG,CAACA,CAAE,CAAC;AAEP,SACE,gBAAAL;AAAA,IAACkB;AAAA,IAAA;AAAA,MACC,MAAAX;AAAA,MACA,UAAAC;AAAA,MACA,YAAAC;AAAA,MACA,QAAAK;AAAA,MACA,UAAAF;AAAA,IAAA;AAAA,EAAA;AAGN;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olinfo/quizms-firebase",
3
- "version": "4.1.2",
3
+ "version": "4.1.3",
4
4
  "type": "module",
5
5
  "author": "Alessandro Bortolin <bortolin.alessandro@outlook.it>",
6
6
  "license": "MIT",
@@ -59,7 +59,7 @@
59
59
  "vite-plugin-dts": "^4.5.4",
60
60
  "wouter": "^3.9.0",
61
61
  "zod": "^4.3.6",
62
- "@olinfo/quizms": "^4.1.2"
62
+ "@olinfo/quizms": "^4.1.3"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "react": "~19.1.4"