@olinfo/quizms-firebase 4.0.0-canary.20251207 → 4.0.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.
@@ -1,4 +1,4 @@
1
- const e = "4.0.0-canary.20251207", n = { "firebase-functions": "^7.0.0" }, s = {
1
+ const e = "4.0.1", n = { "firebase-functions": "^7.0.0" }, s = {
2
2
  version: e,
3
3
  dependencies: n
4
4
  };
@@ -1,41 +1,44 @@
1
- import { existsSync as d } from "node:fs";
2
- import { confirm as h } from "@inquirer/prompts";
3
- import { fatal as m, success as u } from "@olinfo/quizms/utils-node";
4
- import { SingleBar as w } from "cli-progress";
5
- import { partition as x } from "lodash-es";
6
- import b from "picocolors";
7
- async function E(f, a, s, r) {
8
- const t = new w({
1
+ import { existsSync as h } from "node:fs";
2
+ import { confirm as u } from "@inquirer/prompts";
3
+ import { AsyncPool as w } from "@olinfo/quizms/utils";
4
+ import { fatal as m, success as x } from "@olinfo/quizms/utils-node";
5
+ import { SingleBar as b } from "cli-progress";
6
+ import { partition as y } from "lodash-es";
7
+ import $ from "picocolors";
8
+ async function I(f, i, s, r) {
9
+ const t = new b({
9
10
  format: " {bar} {percentage}% | {value}/{total}",
10
11
  barCompleteChar: "█",
11
12
  barIncompleteChar: "▂"
12
13
  }), l = /* @__PURE__ */ new Set();
13
14
  t.start(s.length, 0), await Promise.all(
14
- s.map(async ([i, e]) => {
15
- d(i) || m(
16
- `Cannot find ${a}. Make sure to generate them first, use \`quizms --help\` for usage.`
15
+ s.map(async ([o, e]) => {
16
+ h(o) || m(
17
+ `Cannot find ${i}. Make sure to generate them first, use \`quizms --help\` for usage.`
17
18
  );
18
- const c = f.file(e), [g] = await c.exists().catch(() => [!1]);
19
- g && l.add(e), t.increment();
19
+ const g = f.file(e), [d] = await g.exists().catch(() => [!1]);
20
+ d && l.add(e), t.increment();
20
21
  })
21
22
  ), t.stop();
22
- const [o, p] = x(s, ([i, e]) => l.has(e));
23
- o.length > 0 && !r.skipExisting && !r.force && m(
24
- `Files already exist in \`${a}\`. Use \`--force\` to overwrite or \`--skip-existing\` to ignore.`
23
+ const [a, c] = y(s, ([o, e]) => l.has(e));
24
+ a.length > 0 && !r.skipExisting && !r.force && m(
25
+ `Files already exist in \`${i}\`. Use \`--force\` to overwrite or \`--skip-existing\` to ignore.`
25
26
  );
26
- const n = r.skipExisting ? p : [...o, ...p];
27
- r.force && o.length > 0 && (await h({
28
- message: `You are about to import the ${b.bold(
29
- a
30
- )}. ${o.length} files will be overwritten, the previous data will be lost. Are you really sure?`,
27
+ const n = r.skipExisting ? c : [...a, ...c];
28
+ r.force && a.length > 0 && (await u({
29
+ message: `You are about to import the ${$.bold(
30
+ i
31
+ )}. ${a.length} files will be overwritten, the previous data will be lost. Are you really sure?`,
31
32
  default: !1
32
- }) || m("Command aborted.")), t.start(n.length, 0), await Promise.all(
33
- n.map(async ([i, e]) => {
34
- await f.upload(i, { destination: e }), t.increment();
35
- })
36
- ), t.stop(), u(`${n.length} ${a} imported!`);
33
+ }) || m("Command aborted.")), t.start(n.length, 0);
34
+ const p = new w(8);
35
+ for (const [o, e] of n)
36
+ p.run(async () => {
37
+ await f.upload(o, { destination: e }), t.increment();
38
+ });
39
+ await p.wait(), t.stop(), x(`${n.length} ${i} imported!`);
37
40
  }
38
41
  export {
39
- E as importStorage
42
+ I as importStorage
40
43
  };
41
44
  //# sourceMappingURL=storage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","sources":["../../../../src/cli/utils/storage.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\n\nimport type { Bucket } from \"@google-cloud/storage\";\nimport { confirm } from \"@inquirer/prompts\";\nimport { fatal, success } from \"@olinfo/quizms/utils-node\";\nimport { SingleBar } from \"cli-progress\";\nimport { partition } from \"lodash-es\";\nimport pc from \"picocolors\";\n\ntype ImportOptions = {\n skipExisting?: true;\n force?: true;\n};\n\nexport async function importStorage(\n bucket: Bucket,\n collection: string,\n files: [string, string][],\n options: ImportOptions,\n) {\n const bar = new SingleBar({\n format: \" {bar} {percentage}% | {value}/{total}\",\n barCompleteChar: \"\\u2588\",\n barIncompleteChar: \"\\u2582\",\n });\n\n const existingFiles = new Set<string>();\n\n bar.start(files.length, 0);\n await Promise.all(\n files.map(async ([local, remote]) => {\n if (!existsSync(local)) {\n fatal(\n `Cannot find ${collection}. Make sure to generate them first, use \\`quizms --help\\` for usage.`,\n );\n }\n\n const file = bucket.file(remote);\n const [exists] = await file.exists().catch(() => [false]);\n if (exists) {\n existingFiles.add(remote);\n }\n bar.increment();\n }),\n );\n bar.stop();\n\n const [existing, nonExisting] = partition(files, ([_, remote]) => existingFiles.has(remote));\n if (existing.length > 0 && !options.skipExisting && !options.force) {\n fatal(\n `Files already exist in \\`${collection}\\`. Use \\`--force\\` to overwrite or \\`--skip-existing\\` to ignore.`,\n );\n }\n\n const filesToImport = options.skipExisting ? nonExisting : [...existing, ...nonExisting];\n\n if (options.force && existing.length > 0) {\n const confirmed = await confirm({\n message: `You are about to import the ${pc.bold(\n collection,\n )}. ${existing.length} files will be overwritten, the previous data will be lost. Are you really sure?`,\n default: false,\n });\n if (!confirmed) {\n fatal(\"Command aborted.\");\n }\n }\n\n bar.start(filesToImport.length, 0);\n await Promise.all(\n filesToImport.map(async ([local, remote]) => {\n await bucket.upload(local, { destination: remote });\n bar.increment();\n }),\n );\n bar.stop();\n\n success(`${filesToImport.length} ${collection} imported!`);\n}\n"],"names":["importStorage","bucket","collection","files","options","bar","SingleBar","existingFiles","local","remote","existsSync","fatal","file","exists","existing","nonExisting","partition","_","filesToImport","confirm","pc","success"],"mappings":";;;;;;AAcA,eAAsBA,EACpBC,GACAC,GACAC,GACAC,GACA;AACA,QAAMC,IAAM,IAAIC,EAAU;AAAA,IACxB,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,EAAA,CACpB,GAEKC,wBAAoB,IAAA;AAE1B,EAAAF,EAAI,MAAMF,EAAM,QAAQ,CAAC,GACzB,MAAM,QAAQ;AAAA,IACZA,EAAM,IAAI,OAAO,CAACK,GAAOC,CAAM,MAAM;AACnC,MAAKC,EAAWF,CAAK,KACnBG;AAAA,QACE,eAAeT,CAAU;AAAA,MAAA;AAI7B,YAAMU,IAAOX,EAAO,KAAKQ,CAAM,GACzB,CAACI,CAAM,IAAI,MAAMD,EAAK,OAAA,EAAS,MAAM,MAAM,CAAC,EAAK,CAAC;AACxD,MAAIC,KACFN,EAAc,IAAIE,CAAM,GAE1BJ,EAAI,UAAA;AAAA,IACN,CAAC;AAAA,EAAA,GAEHA,EAAI,KAAA;AAEJ,QAAM,CAACS,GAAUC,CAAW,IAAIC,EAAUb,GAAO,CAAC,CAACc,GAAGR,CAAM,MAAMF,EAAc,IAAIE,CAAM,CAAC;AAC3F,EAAIK,EAAS,SAAS,KAAK,CAACV,EAAQ,gBAAgB,CAACA,EAAQ,SAC3DO;AAAA,IACE,4BAA4BT,CAAU;AAAA,EAAA;AAI1C,QAAMgB,IAAgBd,EAAQ,eAAeW,IAAc,CAAC,GAAGD,GAAU,GAAGC,CAAW;AAEvF,EAAIX,EAAQ,SAASU,EAAS,SAAS,MACnB,MAAMK,EAAQ;AAAA,IAC9B,SAAS,+BAA+BC,EAAG;AAAA,MACzClB;AAAA,IAAA,CACD,KAAKY,EAAS,MAAM;AAAA,IACrB,SAAS;AAAA,EAAA,CACV,KAECH,EAAM,kBAAkB,IAI5BN,EAAI,MAAMa,EAAc,QAAQ,CAAC,GACjC,MAAM,QAAQ;AAAA,IACZA,EAAc,IAAI,OAAO,CAACV,GAAOC,CAAM,MAAM;AAC3C,YAAMR,EAAO,OAAOO,GAAO,EAAE,aAAaC,GAAQ,GAClDJ,EAAI,UAAA;AAAA,IACN,CAAC;AAAA,EAAA,GAEHA,EAAI,KAAA,GAEJgB,EAAQ,GAAGH,EAAc,MAAM,IAAIhB,CAAU,YAAY;AAC3D;"}
1
+ {"version":3,"file":"storage.js","sources":["../../../../src/cli/utils/storage.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\n\nimport type { Bucket } from \"@google-cloud/storage\";\nimport { confirm } from \"@inquirer/prompts\";\nimport { AsyncPool } from \"@olinfo/quizms/utils\";\nimport { fatal, success } from \"@olinfo/quizms/utils-node\";\nimport { SingleBar } from \"cli-progress\";\nimport { partition } from \"lodash-es\";\nimport pc from \"picocolors\";\n\ntype ImportOptions = {\n skipExisting?: true;\n force?: true;\n};\n\nexport async function importStorage(\n bucket: Bucket,\n collection: string,\n files: [string, string][],\n options: ImportOptions,\n) {\n const bar = new SingleBar({\n format: \" {bar} {percentage}% | {value}/{total}\",\n barCompleteChar: \"\\u2588\",\n barIncompleteChar: \"\\u2582\",\n });\n\n const existingFiles = new Set<string>();\n\n bar.start(files.length, 0);\n await Promise.all(\n files.map(async ([local, remote]) => {\n if (!existsSync(local)) {\n fatal(\n `Cannot find ${collection}. Make sure to generate them first, use \\`quizms --help\\` for usage.`,\n );\n }\n\n const file = bucket.file(remote);\n const [exists] = await file.exists().catch(() => [false]);\n if (exists) {\n existingFiles.add(remote);\n }\n bar.increment();\n }),\n );\n bar.stop();\n\n const [existing, nonExisting] = partition(files, ([_, remote]) => existingFiles.has(remote));\n if (existing.length > 0 && !options.skipExisting && !options.force) {\n fatal(\n `Files already exist in \\`${collection}\\`. Use \\`--force\\` to overwrite or \\`--skip-existing\\` to ignore.`,\n );\n }\n\n const filesToImport = options.skipExisting ? nonExisting : [...existing, ...nonExisting];\n\n if (options.force && existing.length > 0) {\n const confirmed = await confirm({\n message: `You are about to import the ${pc.bold(\n collection,\n )}. ${existing.length} files will be overwritten, the previous data will be lost. Are you really sure?`,\n default: false,\n });\n if (!confirmed) {\n fatal(\"Command aborted.\");\n }\n }\n\n bar.start(filesToImport.length, 0);\n\n const pool = new AsyncPool(8);\n for (const [local, remote] of filesToImport) {\n void pool.run(async () => {\n await bucket.upload(local, { destination: remote });\n bar.increment();\n });\n }\n await pool.wait();\n\n bar.stop();\n\n success(`${filesToImport.length} ${collection} imported!`);\n}\n"],"names":["importStorage","bucket","collection","files","options","bar","SingleBar","existingFiles","local","remote","existsSync","fatal","file","exists","existing","nonExisting","partition","_","filesToImport","confirm","pc","pool","AsyncPool","success"],"mappings":";;;;;;;AAeA,eAAsBA,EACpBC,GACAC,GACAC,GACAC,GACA;AACA,QAAMC,IAAM,IAAIC,EAAU;AAAA,IACxB,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,EAAA,CACpB,GAEKC,wBAAoB,IAAA;AAE1B,EAAAF,EAAI,MAAMF,EAAM,QAAQ,CAAC,GACzB,MAAM,QAAQ;AAAA,IACZA,EAAM,IAAI,OAAO,CAACK,GAAOC,CAAM,MAAM;AACnC,MAAKC,EAAWF,CAAK,KACnBG;AAAA,QACE,eAAeT,CAAU;AAAA,MAAA;AAI7B,YAAMU,IAAOX,EAAO,KAAKQ,CAAM,GACzB,CAACI,CAAM,IAAI,MAAMD,EAAK,OAAA,EAAS,MAAM,MAAM,CAAC,EAAK,CAAC;AACxD,MAAIC,KACFN,EAAc,IAAIE,CAAM,GAE1BJ,EAAI,UAAA;AAAA,IACN,CAAC;AAAA,EAAA,GAEHA,EAAI,KAAA;AAEJ,QAAM,CAACS,GAAUC,CAAW,IAAIC,EAAUb,GAAO,CAAC,CAACc,GAAGR,CAAM,MAAMF,EAAc,IAAIE,CAAM,CAAC;AAC3F,EAAIK,EAAS,SAAS,KAAK,CAACV,EAAQ,gBAAgB,CAACA,EAAQ,SAC3DO;AAAA,IACE,4BAA4BT,CAAU;AAAA,EAAA;AAI1C,QAAMgB,IAAgBd,EAAQ,eAAeW,IAAc,CAAC,GAAGD,GAAU,GAAGC,CAAW;AAEvF,EAAIX,EAAQ,SAASU,EAAS,SAAS,MACnB,MAAMK,EAAQ;AAAA,IAC9B,SAAS,+BAA+BC,EAAG;AAAA,MACzClB;AAAA,IAAA,CACD,KAAKY,EAAS,MAAM;AAAA,IACrB,SAAS;AAAA,EAAA,CACV,KAECH,EAAM,kBAAkB,IAI5BN,EAAI,MAAMa,EAAc,QAAQ,CAAC;AAEjC,QAAMG,IAAO,IAAIC,EAAU,CAAC;AAC5B,aAAW,CAACd,GAAOC,CAAM,KAAKS;AAC5B,IAAKG,EAAK,IAAI,YAAY;AACxB,YAAMpB,EAAO,OAAOO,GAAO,EAAE,aAAaC,GAAQ,GAClDJ,EAAI,UAAA;AAAA,IACN,CAAC;AAEH,QAAMgB,EAAK,KAAA,GAEXhB,EAAI,KAAA,GAEJkB,EAAQ,GAAGL,EAAc,MAAM,IAAIhB,CAAU,YAAY;AAC3D;"}
@@ -0,0 +1,13 @@
1
+ import o from "node:crypto";
2
+ import * as s from "firebase-functions/logger";
3
+ import { auth as t } from "../common.js";
4
+ import { getUser as n } from "./queries.js";
5
+ const d = async (a, r) => {
6
+ s.info("Admin login request received", { data: r });
7
+ const e = await n(r.username);
8
+ return e ? e.role !== "admin" ? { success: !1, errorCode: "INVALID_ROLE", error: "Utenza non abilitata" } : e.password !== r.password ? { success: !1, errorCode: "INVALID_PASSWORD", error: "Password non corretta" } : { success: !0, data: { token: await t.createCustomToken(o.randomUUID(), { role: "admin" }) } } : { success: !1, errorCode: "USER_NOT_FOUND", error: "Username non esistente" };
9
+ };
10
+ export {
11
+ d as adminLogin
12
+ };
13
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.js","sources":["../../../../src/functions/api/admin.ts"],"sourcesContent":["import crypto from \"node:crypto\";\n\nimport * as logger from \"firebase-functions/logger\";\n\nimport { auth, type Endpoint } from \"~/functions/common\";\n\nimport { getUser } from \"./queries\";\nimport type { AdminLogin } from \"./schema\";\n\nexport const adminLogin: Endpoint<AdminLogin> = async (_request, data) => {\n logger.info(\"Admin login request received\", { data });\n\n const user = await getUser(data.username);\n if (!user) {\n return { success: false, errorCode: \"USER_NOT_FOUND\", error: \"Username non esistente\" };\n }\n\n if (user.role !== \"admin\") {\n return { success: false, errorCode: \"INVALID_ROLE\", error: \"Utenza non abilitata\" };\n }\n if (user.password !== data.password) {\n return { success: false, errorCode: \"INVALID_PASSWORD\", error: \"Password non corretta\" };\n }\n const token = await auth.createCustomToken(crypto.randomUUID(), { role: \"admin\" });\n return { success: true, data: { token } };\n};\n"],"names":["adminLogin","_request","data","logger","user","getUser","auth","crypto"],"mappings":";;;;AASO,MAAMA,IAAmC,OAAOC,GAAUC,MAAS;AACxE,EAAAC,EAAO,KAAK,gCAAgC,EAAE,MAAAD,EAAA,CAAM;AAEpD,QAAME,IAAO,MAAMC,EAAQH,EAAK,QAAQ;AACxC,SAAKE,IAIDA,EAAK,SAAS,UACT,EAAE,SAAS,IAAO,WAAW,gBAAgB,OAAO,uBAAA,IAEzDA,EAAK,aAAaF,EAAK,WAClB,EAAE,SAAS,IAAO,WAAW,oBAAoB,OAAO,wBAAA,IAG1D,EAAE,SAAS,IAAM,MAAM,EAAE,OADlB,MAAMI,EAAK,kBAAkBC,EAAO,cAAc,EAAE,MAAM,SAAS,IAC3C,IAV7B,EAAE,SAAS,IAAO,WAAW,kBAAkB,OAAO,yBAAA;AAWjE;"}
@@ -1,38 +1,41 @@
1
1
  import { validate as i } from "@olinfo/quizms/utils";
2
2
  import * as t from "firebase-functions/logger";
3
- import { apiRequestSchema as o } from "./schema.js";
4
- import { studentLogin as c } from "./student.js";
5
- import { teacherFinalizeParticipation as n, teacherStopParticipation as s, teacherStartParticipation as p, teacherLogin as d } from "./teacher.js";
6
- async function R(a) {
3
+ import { adminLogin as o } from "./admin.js";
4
+ import { apiRequestSchema as c } from "./schema.js";
5
+ import { studentLogin as n } from "./student.js";
6
+ import { teacherFinalizeParticipation as s, teacherStopParticipation as p, teacherStartParticipation as d, teacherLogin as m } from "./teacher.js";
7
+ async function w(a) {
7
8
  t.info(`Received request for action: ${a.data.action}`);
8
- const r = await h(a);
9
+ const r = await f(a);
9
10
  return r.success || t.error("Request failed", { request: a, result: r }), r;
10
11
  }
11
- async function h(a) {
12
+ async function f(a) {
12
13
  let r;
13
14
  try {
14
- r = i(o, a.data);
15
+ r = i(c, a.data);
15
16
  } catch (e) {
16
17
  return t.error("Invalid request", { error: e }), { success: !1, errorCode: "INVALID_REQUEST", error: "Richiesta non valida" };
17
18
  }
18
19
  try {
19
20
  switch (r.action) {
21
+ case "adminLogin":
22
+ return await o(a, r);
20
23
  case "studentLogin":
21
- return await c(a, r);
24
+ return await n(a, r);
22
25
  case "teacherLogin":
23
- return await d(a, r);
26
+ return await m(a, r);
24
27
  case "teacherStartParticipation":
25
- return await p(a, r);
28
+ return await d(a, r);
26
29
  case "teacherStopParticipation":
27
- return await s(a, r);
30
+ return await p(a, r);
28
31
  case "teacherFinalizeParticipation":
29
- return await n(a, r);
32
+ return await s(a, r);
30
33
  }
31
34
  } catch (e) {
32
35
  return t.error("Error processing request", { error: e }), { success: !1, errorCode: "INTERNAL_ERROR", error: "Errore del server" };
33
36
  }
34
37
  }
35
38
  export {
36
- R as apiHandler
39
+ w as apiHandler
37
40
  };
38
41
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/functions/api/index.ts"],"sourcesContent":["import { validate } from \"@olinfo/quizms/utils\";\nimport type { CallableRequest } from \"firebase-functions/https\";\nimport * as logger from \"firebase-functions/logger\";\n\nimport { type ApiRequest, apiRequestSchema } from \"./schema\";\nimport { studentLogin } from \"./student\";\nimport {\n teacherFinalizeParticipation,\n teacherLogin,\n teacherStartParticipation,\n teacherStopParticipation,\n} from \"./teacher\";\n\nexport async function apiHandler(request: CallableRequest) {\n logger.info(`Received request for action: ${request.data.action}`);\n const result = await dispatch(request);\n if (!result.success) {\n logger.error(\"Request failed\", { request, result });\n }\n return result;\n}\n\nasync function dispatch(request: CallableRequest) {\n let data: ApiRequest;\n try {\n data = validate(apiRequestSchema, request.data);\n } catch (error) {\n logger.error(\"Invalid request\", { error });\n return { success: false, errorCode: \"INVALID_REQUEST\", error: \"Richiesta non valida\" };\n }\n\n try {\n switch (data.action) {\n case \"studentLogin\":\n return await studentLogin(request, data);\n case \"teacherLogin\":\n return await teacherLogin(request, data);\n case \"teacherStartParticipation\":\n return await teacherStartParticipation(request, data);\n case \"teacherStopParticipation\":\n return await teacherStopParticipation(request, data);\n case \"teacherFinalizeParticipation\":\n return await teacherFinalizeParticipation(request, data);\n }\n } catch (error) {\n logger.error(\"Error processing request\", { error });\n return { success: false, errorCode: \"INTERNAL_ERROR\", error: \"Errore del server\" };\n }\n}\n"],"names":["apiHandler","request","logger","result","dispatch","data","validate","apiRequestSchema","error","studentLogin","teacherLogin","teacherStartParticipation","teacherStopParticipation","teacherFinalizeParticipation"],"mappings":";;;;;AAaA,eAAsBA,EAAWC,GAA0B;AACzD,EAAAC,EAAO,KAAK,gCAAgCD,EAAQ,KAAK,MAAM,EAAE;AACjE,QAAME,IAAS,MAAMC,EAASH,CAAO;AACrC,SAAKE,EAAO,WACVD,EAAO,MAAM,kBAAkB,EAAE,SAAAD,GAAS,QAAAE,GAAQ,GAE7CA;AACT;AAEA,eAAeC,EAASH,GAA0B;AAChD,MAAII;AACJ,MAAI;AACF,IAAAA,IAAOC,EAASC,GAAkBN,EAAQ,IAAI;AAAA,EAChD,SAASO,GAAO;AACd,WAAAN,EAAO,MAAM,mBAAmB,EAAE,OAAAM,EAAA,CAAO,GAClC,EAAE,SAAS,IAAO,WAAW,mBAAmB,OAAO,uBAAA;AAAA,EAChE;AAEA,MAAI;AACF,YAAQH,EAAK,QAAA;AAAA,MACX,KAAK;AACH,eAAO,MAAMI,EAAaR,GAASI,CAAI;AAAA,MACzC,KAAK;AACH,eAAO,MAAMK,EAAaT,GAASI,CAAI;AAAA,MACzC,KAAK;AACH,eAAO,MAAMM,EAA0BV,GAASI,CAAI;AAAA,MACtD,KAAK;AACH,eAAO,MAAMO,EAAyBX,GAASI,CAAI;AAAA,MACrD,KAAK;AACH,eAAO,MAAMQ,EAA6BZ,GAASI,CAAI;AAAA,IAAA;AAAA,EAE7D,SAASG,GAAO;AACd,WAAAN,EAAO,MAAM,4BAA4B,EAAE,OAAAM,EAAA,CAAO,GAC3C,EAAE,SAAS,IAAO,WAAW,kBAAkB,OAAO,oBAAA;AAAA,EAC/D;AACF;"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/functions/api/index.ts"],"sourcesContent":["import { validate } from \"@olinfo/quizms/utils\";\nimport type { CallableRequest } from \"firebase-functions/https\";\nimport * as logger from \"firebase-functions/logger\";\n\nimport { adminLogin } from \"./admin\";\nimport { type ApiRequest, apiRequestSchema } from \"./schema\";\nimport { studentLogin } from \"./student\";\nimport {\n teacherFinalizeParticipation,\n teacherLogin,\n teacherStartParticipation,\n teacherStopParticipation,\n} from \"./teacher\";\n\nexport async function apiHandler(request: CallableRequest) {\n logger.info(`Received request for action: ${request.data.action}`);\n const result = await dispatch(request);\n if (!result.success) {\n logger.error(\"Request failed\", { request, result });\n }\n return result;\n}\n\nasync function dispatch(request: CallableRequest) {\n let data: ApiRequest;\n try {\n data = validate(apiRequestSchema, request.data);\n } catch (error) {\n logger.error(\"Invalid request\", { error });\n return { success: false, errorCode: \"INVALID_REQUEST\", error: \"Richiesta non valida\" };\n }\n\n try {\n switch (data.action) {\n case \"adminLogin\":\n return await adminLogin(request, data);\n case \"studentLogin\":\n return await studentLogin(request, data);\n case \"teacherLogin\":\n return await teacherLogin(request, data);\n case \"teacherStartParticipation\":\n return await teacherStartParticipation(request, data);\n case \"teacherStopParticipation\":\n return await teacherStopParticipation(request, data);\n case \"teacherFinalizeParticipation\":\n return await teacherFinalizeParticipation(request, data);\n }\n } catch (error) {\n logger.error(\"Error processing request\", { error });\n return { success: false, errorCode: \"INTERNAL_ERROR\", error: \"Errore del server\" };\n }\n}\n"],"names":["apiHandler","request","logger","result","dispatch","data","validate","apiRequestSchema","error","adminLogin","studentLogin","teacherLogin","teacherStartParticipation","teacherStopParticipation","teacherFinalizeParticipation"],"mappings":";;;;;;AAcA,eAAsBA,EAAWC,GAA0B;AACzD,EAAAC,EAAO,KAAK,gCAAgCD,EAAQ,KAAK,MAAM,EAAE;AACjE,QAAME,IAAS,MAAMC,EAASH,CAAO;AACrC,SAAKE,EAAO,WACVD,EAAO,MAAM,kBAAkB,EAAE,SAAAD,GAAS,QAAAE,GAAQ,GAE7CA;AACT;AAEA,eAAeC,EAASH,GAA0B;AAChD,MAAII;AACJ,MAAI;AACF,IAAAA,IAAOC,EAASC,GAAkBN,EAAQ,IAAI;AAAA,EAChD,SAASO,GAAO;AACd,WAAAN,EAAO,MAAM,mBAAmB,EAAE,OAAAM,EAAA,CAAO,GAClC,EAAE,SAAS,IAAO,WAAW,mBAAmB,OAAO,uBAAA;AAAA,EAChE;AAEA,MAAI;AACF,YAAQH,EAAK,QAAA;AAAA,MACX,KAAK;AACH,eAAO,MAAMI,EAAWR,GAASI,CAAI;AAAA,MACvC,KAAK;AACH,eAAO,MAAMK,EAAaT,GAASI,CAAI;AAAA,MACzC,KAAK;AACH,eAAO,MAAMM,EAAaV,GAASI,CAAI;AAAA,MACzC,KAAK;AACH,eAAO,MAAMO,EAA0BX,GAASI,CAAI;AAAA,MACtD,KAAK;AACH,eAAO,MAAMQ,EAAyBZ,GAASI,CAAI;AAAA,MACrD,KAAK;AACH,eAAO,MAAMS,EAA6Bb,GAASI,CAAI;AAAA,IAAA;AAAA,EAE7D,SAASG,GAAO;AACd,WAAAN,EAAO,MAAM,4BAA4B,EAAE,OAAAM,EAAA,CAAO,GAC3C,EAAE,SAAS,IAAO,WAAW,kBAAkB,OAAO,oBAAA;AAAA,EAC/D;AACF;"}
@@ -5,38 +5,38 @@ const i = t.object({
5
5
  token: t.string(),
6
6
  userData: t.record(t.coerce.string()),
7
7
  extraData: t.record(t.any())
8
- });
9
- t.object({
8
+ }), a = t.object({
10
9
  action: t.literal("adminLogin"),
11
10
  username: t.string(),
12
11
  password: t.string()
13
- });
14
- const a = t.object({
12
+ }), e = t.object({
15
13
  action: t.literal("teacherLogin"),
16
14
  username: t.string(),
17
15
  password: t.string()
18
- }), e = t.object({
16
+ }), n = t.object({
19
17
  action: t.literal("teacherStartParticipation"),
20
18
  participationId: t.string()
21
- }), n = t.object({
19
+ }), c = t.object({
22
20
  action: t.literal("teacherStopParticipation"),
23
21
  participationId: t.string()
24
22
  }), r = t.object({
25
23
  action: t.literal("teacherFinalizeParticipation"),
26
24
  participationId: t.string()
27
- }), o = t.discriminatedUnion("action", [
28
- i,
25
+ }), s = t.discriminatedUnion("action", [
29
26
  a,
27
+ i,
30
28
  e,
31
29
  n,
30
+ c,
32
31
  r
33
32
  ]);
34
33
  export {
35
- o as apiRequestSchema,
34
+ a as adminLoginSchema,
35
+ s as apiRequestSchema,
36
36
  i as studentLoginSchema,
37
37
  r as teacherFinalizeParticipationSchema,
38
- a as teacherLoginSchema,
39
- e as teacherStartParticipationSchema,
40
- n as teacherStopParticipationSchema
38
+ e as teacherLoginSchema,
39
+ n as teacherStartParticipationSchema,
40
+ c as teacherStopParticipationSchema
41
41
  };
42
42
  //# sourceMappingURL=schema.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sources":["../../../../src/functions/api/schema.ts"],"sourcesContent":["import z from \"zod\";\n\nexport const studentLoginSchema = z.object({\n action: z.literal(\"studentLogin\"),\n contestId: z.string(),\n token: z.string(),\n userData: z.record(z.coerce.string()),\n extraData: z.record(z.any()),\n});\nexport type StudentLogin = z.infer<typeof studentLoginSchema>;\n\nexport const adminLoginSchema = z.object({\n action: z.literal(\"adminLogin\"),\n username: z.string(),\n password: z.string(),\n});\nexport type AdminLogin = z.infer<typeof adminLoginSchema>;\n\nexport const teacherLoginSchema = z.object({\n action: z.literal(\"teacherLogin\"),\n username: z.string(),\n password: z.string(),\n});\nexport type TeacherLogin = z.infer<typeof teacherLoginSchema>;\n\nexport const teacherStartParticipationSchema = z.object({\n action: z.literal(\"teacherStartParticipation\"),\n participationId: z.string(),\n});\nexport type TeacherStartParticipation = z.infer<typeof teacherStartParticipationSchema>;\n\nexport const teacherStopParticipationSchema = z.object({\n action: z.literal(\"teacherStopParticipation\"),\n participationId: z.string(),\n});\nexport type TeacherStopParticipation = z.infer<typeof teacherStopParticipationSchema>;\n\nexport const teacherFinalizeParticipationSchema = z.object({\n action: z.literal(\"teacherFinalizeParticipation\"),\n participationId: z.string(),\n});\nexport type TeacherFinalizeParticipation = z.infer<typeof teacherFinalizeParticipationSchema>;\n\nexport const apiRequestSchema = z.discriminatedUnion(\"action\", [\n studentLoginSchema,\n teacherLoginSchema,\n teacherStartParticipationSchema,\n teacherStopParticipationSchema,\n teacherFinalizeParticipationSchema,\n]);\n\nexport type ApiRequest = z.infer<typeof apiRequestSchema>;\n"],"names":["studentLoginSchema","z","teacherLoginSchema","teacherStartParticipationSchema","teacherStopParticipationSchema","teacherFinalizeParticipationSchema","apiRequestSchema"],"mappings":";AAEO,MAAMA,IAAqBC,EAAE,OAAO;AAAA,EACzC,QAAQA,EAAE,QAAQ,cAAc;AAAA,EAChC,WAAWA,EAAE,OAAA;AAAA,EACb,OAAOA,EAAE,OAAA;AAAA,EACT,UAAUA,EAAE,OAAOA,EAAE,OAAO,QAAQ;AAAA,EACpC,WAAWA,EAAE,OAAOA,EAAE,KAAK;AAC7B,CAAC;AAG+BA,EAAE,OAAO;AAAA,EACvC,QAAQA,EAAE,QAAQ,YAAY;AAAA,EAC9B,UAAUA,EAAE,OAAA;AAAA,EACZ,UAAUA,EAAE,OAAA;AACd,CAAC;AAGM,MAAMC,IAAqBD,EAAE,OAAO;AAAA,EACzC,QAAQA,EAAE,QAAQ,cAAc;AAAA,EAChC,UAAUA,EAAE,OAAA;AAAA,EACZ,UAAUA,EAAE,OAAA;AACd,CAAC,GAGYE,IAAkCF,EAAE,OAAO;AAAA,EACtD,QAAQA,EAAE,QAAQ,2BAA2B;AAAA,EAC7C,iBAAiBA,EAAE,OAAA;AACrB,CAAC,GAGYG,IAAiCH,EAAE,OAAO;AAAA,EACrD,QAAQA,EAAE,QAAQ,0BAA0B;AAAA,EAC5C,iBAAiBA,EAAE,OAAA;AACrB,CAAC,GAGYI,IAAqCJ,EAAE,OAAO;AAAA,EACzD,QAAQA,EAAE,QAAQ,8BAA8B;AAAA,EAChD,iBAAiBA,EAAE,OAAA;AACrB,CAAC,GAGYK,IAAmBL,EAAE,mBAAmB,UAAU;AAAA,EAC7DD;AAAA,EACAE;AAAA,EACAC;AAAA,EACAC;AAAA,EACAC;AACF,CAAC;"}
1
+ {"version":3,"file":"schema.js","sources":["../../../../src/functions/api/schema.ts"],"sourcesContent":["import z from \"zod\";\n\nexport const studentLoginSchema = z.object({\n action: z.literal(\"studentLogin\"),\n contestId: z.string(),\n token: z.string(),\n userData: z.record(z.coerce.string()),\n extraData: z.record(z.any()),\n});\nexport type StudentLogin = z.infer<typeof studentLoginSchema>;\n\nexport const adminLoginSchema = z.object({\n action: z.literal(\"adminLogin\"),\n username: z.string(),\n password: z.string(),\n});\nexport type AdminLogin = z.infer<typeof adminLoginSchema>;\n\nexport const teacherLoginSchema = z.object({\n action: z.literal(\"teacherLogin\"),\n username: z.string(),\n password: z.string(),\n});\nexport type TeacherLogin = z.infer<typeof teacherLoginSchema>;\n\nexport const teacherStartParticipationSchema = z.object({\n action: z.literal(\"teacherStartParticipation\"),\n participationId: z.string(),\n});\nexport type TeacherStartParticipation = z.infer<typeof teacherStartParticipationSchema>;\n\nexport const teacherStopParticipationSchema = z.object({\n action: z.literal(\"teacherStopParticipation\"),\n participationId: z.string(),\n});\nexport type TeacherStopParticipation = z.infer<typeof teacherStopParticipationSchema>;\n\nexport const teacherFinalizeParticipationSchema = z.object({\n action: z.literal(\"teacherFinalizeParticipation\"),\n participationId: z.string(),\n});\nexport type TeacherFinalizeParticipation = z.infer<typeof teacherFinalizeParticipationSchema>;\n\nexport const apiRequestSchema = z.discriminatedUnion(\"action\", [\n adminLoginSchema,\n studentLoginSchema,\n teacherLoginSchema,\n teacherStartParticipationSchema,\n teacherStopParticipationSchema,\n teacherFinalizeParticipationSchema,\n]);\n\nexport type ApiRequest = z.infer<typeof apiRequestSchema>;\n"],"names":["studentLoginSchema","z","adminLoginSchema","teacherLoginSchema","teacherStartParticipationSchema","teacherStopParticipationSchema","teacherFinalizeParticipationSchema","apiRequestSchema"],"mappings":";AAEO,MAAMA,IAAqBC,EAAE,OAAO;AAAA,EACzC,QAAQA,EAAE,QAAQ,cAAc;AAAA,EAChC,WAAWA,EAAE,OAAA;AAAA,EACb,OAAOA,EAAE,OAAA;AAAA,EACT,UAAUA,EAAE,OAAOA,EAAE,OAAO,QAAQ;AAAA,EACpC,WAAWA,EAAE,OAAOA,EAAE,KAAK;AAC7B,CAAC,GAGYC,IAAmBD,EAAE,OAAO;AAAA,EACvC,QAAQA,EAAE,QAAQ,YAAY;AAAA,EAC9B,UAAUA,EAAE,OAAA;AAAA,EACZ,UAAUA,EAAE,OAAA;AACd,CAAC,GAGYE,IAAqBF,EAAE,OAAO;AAAA,EACzC,QAAQA,EAAE,QAAQ,cAAc;AAAA,EAChC,UAAUA,EAAE,OAAA;AAAA,EACZ,UAAUA,EAAE,OAAA;AACd,CAAC,GAGYG,IAAkCH,EAAE,OAAO;AAAA,EACtD,QAAQA,EAAE,QAAQ,2BAA2B;AAAA,EAC7C,iBAAiBA,EAAE,OAAA;AACrB,CAAC,GAGYI,IAAiCJ,EAAE,OAAO;AAAA,EACrD,QAAQA,EAAE,QAAQ,0BAA0B;AAAA,EAC5C,iBAAiBA,EAAE,OAAA;AACrB,CAAC,GAGYK,IAAqCL,EAAE,OAAO;AAAA,EACzD,QAAQA,EAAE,QAAQ,8BAA8B;AAAA,EAChD,iBAAiBA,EAAE,OAAA;AACrB,CAAC,GAGYM,IAAmBN,EAAE,mBAAmB,UAAU;AAAA,EAC7DC;AAAA,EACAF;AAAA,EACAG;AAAA,EACAC;AAAA,EACAC;AAAA,EACAC;AACF,CAAC;"}
@@ -1,23 +1,23 @@
1
- import { jsx as t, jsxs as m, Fragment as b } from "react/jsx-runtime";
1
+ import { jsx as t, jsxs as u, Fragment as b } from "react/jsx-runtime";
2
2
  import { useMemo as w, useCallback as p, useEffect as v } from "react";
3
- import { Title as k, StudentFormField as x } from "@olinfo/quizms/components";
3
+ import { Title as x, StudentFormField as F } from "@olinfo/quizms/components";
4
4
  import { StudentProvider as I } from "@olinfo/quizms/student";
5
- import { Navbar as F, NavbarBrand as y, Form as A, SelectField as C, TextField as D, SubmitButton as W } from "@olinfo/react-components";
6
- import { signOut as j, getAuth as g, signInWithCustomToken as H } from "firebase/auth";
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
7
  import { waitForPendingWrites as f } from "firebase/firestore";
8
8
  import { isEqual as N } from "lodash-es";
9
- import { studentLogin as E } from "../common/api.js";
9
+ import { studentLogin as j } from "../common/api.js";
10
10
  import { useDb as S } from "../common/base-login.js";
11
- import { contestConverter as O, studentConverter as M, participationConverter as P } from "../common/converters.js";
12
- import { useWebsite as R } from "../common/website.js";
13
- import { useAuth as T } from "../hooks/auth.js";
11
+ 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
14
  import { useCollection as z } from "../hooks/collection.js";
15
15
  import { useDocument as h } from "../hooks/document.js";
16
16
  import { StudentRestoring as B } from "./student-restoring.js";
17
- import { FirebaseStatement as q } from "./student-statement.js";
17
+ import { FirebaseStatement as O } from "./student-statement.js";
18
18
  import G from "virtual:quizms-firebase-header";
19
- function mt() {
20
- const e = T("student"), o = R(), [a] = z("contests", O, {
19
+ function ut() {
20
+ const e = q("student"), o = T(), [a] = z("contests", M, {
21
21
  constraints: { id: o.contests },
22
22
  subscribe: !0
23
23
  });
@@ -32,13 +32,13 @@ function mt() {
32
32
  }
33
33
  function L({ contests: e }) {
34
34
  const o = S(), a = p(
35
- async ({ contestId: i, token: s, ...n }) => {
36
- const d = await E(o, {
37
- contestId: i,
35
+ async ({ contestId: n, token: s, ...i }) => {
36
+ const d = await j(o, {
37
+ contestId: n,
38
38
  token: s,
39
39
  userData: {
40
- ...n,
41
- birthDate: n.birthDate ? new Date(n.birthDate) : void 0
40
+ ...i,
41
+ birthDate: i.birthDate ? new Date(i.birthDate) : void 0
42
42
  },
43
43
  extraData: {
44
44
  userAgent: navigator.userAgent,
@@ -49,36 +49,36 @@ function L({ contests: e }) {
49
49
  pixelRatio: window.devicePixelRatio,
50
50
  darkMode: window.matchMedia("(prefers-color-scheme: dark)").matches
51
51
  }
52
- }), u = g(o.app);
53
- await H(u, d.token);
52
+ }), m = g(o.app);
53
+ await H(m, d.token), await document.documentElement.requestFullscreen?.();
54
54
  },
55
55
  [o]
56
56
  ), r = w(
57
57
  () => ({
58
- contestId: Object.keys(e).length === 1 ? Object.keys(e)[0] : void 0
58
+ contestId: e.length === 1 ? e[0].id : void 0
59
59
  }),
60
60
  [e]
61
61
  );
62
- return /* @__PURE__ */ m(b, { children: [
63
- /* @__PURE__ */ t(F, { color: "bg-base-300 text-base-content", children: /* @__PURE__ */ t(y, { children: /* @__PURE__ */ t("div", { className: "flex items-center h-full font-bold", children: /* @__PURE__ */ t(k, {}) }) }) }),
64
- /* @__PURE__ */ m(A, { defaultValue: r, onSubmit: a, className: "p-4 pb-8", children: [
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: [
65
65
  /* @__PURE__ */ t("h1", { className: "mb-2 text-xl font-bold", children: "Accedi alla gara" }),
66
66
  /* @__PURE__ */ t(
67
- C,
67
+ D,
68
68
  {
69
69
  field: "contestId",
70
70
  label: "Gara",
71
- options: Object.fromEntries(e.map((i) => [i.id, i.name])),
71
+ options: Object.fromEntries(e.map((n) => [n.id, n.name])),
72
72
  placeholder: "Seleziona una gara"
73
73
  }
74
74
  ),
75
- ({ contestId: i }) => {
76
- const s = e.find((n) => n.id === i);
75
+ ({ contestId: n }) => {
76
+ const s = e.find((i) => i.id === n);
77
77
  if (s)
78
- return /* @__PURE__ */ m(b, { children: [
79
- s.userData.map((n) => /* @__PURE__ */ t(x, { field: n }, n.name)),
80
- /* @__PURE__ */ t(D, { field: "token", label: "Codice prova", placeholder: "aaaaa-bbbbb-ccccc" }),
81
- /* @__PURE__ */ t(W, { children: "Inizia la prova" })
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" })
82
82
  ] });
83
83
  }
84
84
  ] })
@@ -89,42 +89,43 @@ function V({
89
89
  participationId: o,
90
90
  studentId: a
91
91
  }) {
92
- const r = S(), [i, s] = h(
92
+ const r = S(), [n, s] = h(
93
93
  `participations/${o}/students`,
94
94
  a,
95
- M,
95
+ P,
96
96
  { subscribe: !0 }
97
- ), [n] = h("participations", o, P, {
97
+ ), [i] = h("participations", o, R, {
98
98
  subscribe: !0
99
99
  }), d = w(
100
- () => e.find((c) => c.id === i.contestId),
101
- [e, i.contestId]
102
- ), u = p(async () => {
100
+ () => e.find((c) => c.id === n.contestId),
101
+ [e, n.contestId]
102
+ ), m = p(async () => {
103
103
  await f(r);
104
104
  }, [r]), l = p(async () => {
105
- await f(r), await j(g(r.app)), window.location.reload();
105
+ await f(r), await E(g(r.app)), window.location.reload();
106
106
  }, [r]);
107
107
  return v(() => {
108
- n.token !== i.token && l();
109
- }, [n.token, i.token, l]), /* @__PURE__ */ m(
108
+ i.token !== n.token && l();
109
+ }, [i.token, n.token, l]), /* @__PURE__ */ u(
110
110
  I,
111
111
  {
112
112
  contest: d,
113
- participation: n,
114
- student: i,
113
+ participation: i,
114
+ student: n,
115
115
  setStudent: async (c) => {
116
- N(i, c) || await s({ ...c });
116
+ N(n, c) || await s({ ...c });
117
117
  },
118
118
  logout: l,
119
- onSubmit: u,
119
+ onSubmit: m,
120
+ enforceFullscreen: !0,
120
121
  children: [
121
122
  /* @__PURE__ */ t(G, { contestId: d.id }),
122
- /* @__PURE__ */ t(q, {})
123
+ /* @__PURE__ */ t(O, {})
123
124
  ]
124
125
  }
125
126
  );
126
127
  }
127
128
  export {
128
- mt as default
129
+ ut as default
129
130
  };
130
131
  //# 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 },\n [db],\n );\n\n const defaultValue = useMemo(\n () => ({\n contestId: Object.keys(contests).length === 1 ? Object.keys(contests)[0] : 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 <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 {\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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olinfo/quizms-firebase",
3
- "version": "4.0.0-canary.20251207",
3
+ "version": "4.0.1",
4
4
  "type": "module",
5
5
  "author": "Alessandro Bortolin <bortolin.alessandro@outlook.it>",
6
6
  "license": "MIT",
@@ -56,7 +56,7 @@
56
56
  "vite-tsconfig-paths": "^5.1.4",
57
57
  "wouter": "^3.8.1",
58
58
  "zod": "^3.25.76",
59
- "@olinfo/quizms": "^4.0.0-canary.20251207"
59
+ "@olinfo/quizms": "^4.0.0"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "react": "~19.1"