placementt-core 1.400.1133 → 1.400.1135

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.
@@ -0,0 +1,78 @@
1
+ import { z } from "zod";
2
+ import { CustomFormSchema } from "../typeDefinitions";
3
+ /**
4
+ * Contract for AI form import
5
+ * (`Placementt-Backend/functions/src/formImport.ts`).
6
+ *
7
+ * Turns a form that already exists somewhere else (a public Microsoft Forms or
8
+ * Google Forms link, or a Word document) into a `CustomFormSchema` the form
9
+ * builder can open. Nothing is saved: the builder opens the result as a draft
10
+ * and the member of staff saves it the usual way.
11
+ *
12
+ * A Word document is read in the browser (mammoth, to HTML) and only its
13
+ * HTML is sent, so the function never handles the file itself.
14
+ */
15
+ export declare const formImportCallables: {
16
+ "formImport-generateFormSchema": {
17
+ request: z.ZodObject<{
18
+ /** The organisation the form is for. Staff access is checked. */
19
+ oId: z.ZodString;
20
+ source: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
21
+ kind: z.ZodLiteral<"url">;
22
+ /** A public Microsoft Forms or Google Forms link. */
23
+ url: z.ZodString;
24
+ }, "strip", z.ZodTypeAny, {
25
+ url: string;
26
+ kind: "url";
27
+ }, {
28
+ url: string;
29
+ kind: "url";
30
+ }>, z.ZodObject<{
31
+ kind: z.ZodLiteral<"document">;
32
+ fileName: z.ZodString;
33
+ /** The document as HTML, as mammoth produces it. */
34
+ html: z.ZodString;
35
+ }, "strip", z.ZodTypeAny, {
36
+ html: string;
37
+ kind: "document";
38
+ fileName: string;
39
+ }, {
40
+ html: string;
41
+ kind: "document";
42
+ fileName: string;
43
+ }>]>;
44
+ }, "strip", z.ZodTypeAny, {
45
+ source: {
46
+ url: string;
47
+ kind: "url";
48
+ } | {
49
+ html: string;
50
+ kind: "document";
51
+ fileName: string;
52
+ };
53
+ oId: string;
54
+ }, {
55
+ source: {
56
+ url: string;
57
+ kind: "url";
58
+ } | {
59
+ html: string;
60
+ kind: "document";
61
+ fileName: string;
62
+ };
63
+ oId: string;
64
+ }>;
65
+ response: z.ZodObject<{
66
+ form: z.ZodType<CustomFormSchema, z.ZodTypeDef, CustomFormSchema>;
67
+ /** Anything that didn't map across cleanly (file uploads,
68
+ * multi-select questions, grids), for the user to review. */
69
+ warnings: z.ZodArray<z.ZodString, "many">;
70
+ }, "strip", z.ZodTypeAny, {
71
+ form: CustomFormSchema;
72
+ warnings: string[];
73
+ }, {
74
+ form: CustomFormSchema;
75
+ warnings: string[];
76
+ }>;
77
+ };
78
+ };
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formImportCallables = void 0;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * Contract for AI form import
7
+ * (`Placementt-Backend/functions/src/formImport.ts`).
8
+ *
9
+ * Turns a form that already exists somewhere else (a public Microsoft Forms or
10
+ * Google Forms link, or a Word document) into a `CustomFormSchema` the form
11
+ * builder can open. Nothing is saved: the builder opens the result as a draft
12
+ * and the member of staff saves it the usual way.
13
+ *
14
+ * A Word document is read in the browser (mammoth, to HTML) and only its
15
+ * HTML is sent, so the function never handles the file itself.
16
+ */
17
+ exports.formImportCallables = {
18
+ "formImport-generateFormSchema": {
19
+ request: zod_1.z.object({
20
+ /** The organisation the form is for. Staff access is checked. */
21
+ oId: zod_1.z.string(),
22
+ source: zod_1.z.discriminatedUnion("kind", [
23
+ zod_1.z.object({
24
+ kind: zod_1.z.literal("url"),
25
+ /** A public Microsoft Forms or Google Forms link. */
26
+ url: zod_1.z.string(),
27
+ }),
28
+ zod_1.z.object({
29
+ kind: zod_1.z.literal("document"),
30
+ fileName: zod_1.z.string(),
31
+ /** The document as HTML, as mammoth produces it. */
32
+ html: zod_1.z.string(),
33
+ }),
34
+ ]),
35
+ }),
36
+ response: zod_1.z.object({
37
+ form: zod_1.z.custom(),
38
+ /** Anything that didn't map across cleanly (file uploads,
39
+ * multi-select questions, grids), for the user to review. */
40
+ warnings: zod_1.z.array(zod_1.z.string()),
41
+ }),
42
+ },
43
+ };
44
+ //# sourceMappingURL=formImport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formImport.js","sourceRoot":"","sources":["../../src/callables/formImport.ts"],"names":[],"mappings":";;;AAAA,6BAAsB;AAItB;;;;;;;;;;;GAWG;AACU,QAAA,mBAAmB,GAAG;IAC/B,+BAA+B,EAAE;QAC7B,OAAO,EAAE,OAAC,CAAC,MAAM,CAAC;YACd,iEAAiE;YACjE,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE;YACf,MAAM,EAAE,OAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;gBACjC,OAAC,CAAC,MAAM,CAAC;oBACL,IAAI,EAAE,OAAC,CAAC,OAAO,CAAC,KAAK,CAAC;oBACtB,qDAAqD;oBACrD,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE;iBAClB,CAAC;gBACF,OAAC,CAAC,MAAM,CAAC;oBACL,IAAI,EAAE,OAAC,CAAC,OAAO,CAAC,UAAU,CAAC;oBAC3B,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE;oBACpB,oDAAoD;oBACpD,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE;iBACnB,CAAC;aACL,CAAC;SACL,CAAC;QACF,QAAQ,EAAE,OAAC,CAAC,MAAM,CAAC;YACf,IAAI,EAAE,OAAC,CAAC,MAAM,EAAoB;YAClC;yEAC6D;YAC7D,QAAQ,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC;SAChC,CAAC;KACL;CACoB,CAAC"}
@@ -14,3 +14,4 @@ export * from "./wonde";
14
14
  export * from "./misSync";
15
15
  export * from "./userManagement";
16
16
  export * from "./outreach";
17
+ export * from "./formImport";
@@ -30,4 +30,5 @@ __exportStar(require("./wonde"), exports);
30
30
  __exportStar(require("./misSync"), exports);
31
31
  __exportStar(require("./userManagement"), exports);
32
32
  __exportStar(require("./outreach"), exports);
33
+ __exportStar(require("./formImport"), exports);
33
34
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/callables/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,6CAA2B;AAC3B,uDAAqC;AACrC,8CAA4B;AAC5B,2CAAyB;AACzB,6CAA2B;AAC3B,oDAAkC;AAClC,uCAAqB;AACrB,oDAAkC;AAClC,4CAA0B;AAC1B,oDAAkC;AAClC,wCAAsB;AACtB,0CAAwB;AACxB,4CAA0B;AAC1B,mDAAiC;AACjC,6CAA2B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/callables/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,6CAA2B;AAC3B,uDAAqC;AACrC,8CAA4B;AAC5B,2CAAyB;AACzB,6CAA2B;AAC3B,oDAAkC;AAClC,uCAAqB;AACrB,oDAAkC;AAClC,4CAA0B;AAC1B,oDAAkC;AAClC,wCAAsB;AACtB,0CAAwB;AACxB,4CAA0B;AAC1B,mDAAiC;AACjC,6CAA2B;AAC3B,+CAA6B"}
@@ -11,6 +11,63 @@ import { z } from "zod";
11
11
  * equals the sum of the group key counts.
12
12
  */
13
13
  export declare const allCallables: {
14
+ "formImport-generateFormSchema": {
15
+ request: z.ZodObject<{
16
+ oId: z.ZodString;
17
+ source: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
18
+ kind: z.ZodLiteral<"url">;
19
+ url: z.ZodString;
20
+ }, "strip", z.ZodTypeAny, {
21
+ url: string;
22
+ kind: "url";
23
+ }, {
24
+ url: string;
25
+ kind: "url";
26
+ }>, z.ZodObject<{
27
+ kind: z.ZodLiteral<"document">;
28
+ fileName: z.ZodString;
29
+ html: z.ZodString;
30
+ }, "strip", z.ZodTypeAny, {
31
+ html: string;
32
+ kind: "document";
33
+ fileName: string;
34
+ }, {
35
+ html: string;
36
+ kind: "document";
37
+ fileName: string;
38
+ }>]>;
39
+ }, "strip", z.ZodTypeAny, {
40
+ source: {
41
+ url: string;
42
+ kind: "url";
43
+ } | {
44
+ html: string;
45
+ kind: "document";
46
+ fileName: string;
47
+ };
48
+ oId: string;
49
+ }, {
50
+ source: {
51
+ url: string;
52
+ kind: "url";
53
+ } | {
54
+ html: string;
55
+ kind: "document";
56
+ fileName: string;
57
+ };
58
+ oId: string;
59
+ }>;
60
+ response: z.ZodObject<{
61
+ form: z.ZodType<import("..").CustomFormSchema, z.ZodTypeDef, import("..").CustomFormSchema>;
62
+ warnings: z.ZodArray<z.ZodString, "many">;
63
+ }, "strip", z.ZodTypeAny, {
64
+ form: import("..").CustomFormSchema;
65
+ warnings: string[];
66
+ }, {
67
+ form: import("..").CustomFormSchema;
68
+ warnings: string[];
69
+ }>;
70
+ };
14
71
  "outreach-upsertContacts": {
15
72
  request: z.ZodObject<{
16
73
  rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodString>>, "many">;
@@ -3977,4 +4034,63 @@ export declare const callableGroups: {
3977
4034
  }>;
3978
4035
  };
3979
4036
  };
4037
+ formImportCallables: {
4038
+ "formImport-generateFormSchema": {
4039
+ request: z.ZodObject<{
4040
+ oId: z.ZodString;
4041
+ source: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
4042
+ kind: z.ZodLiteral<"url">;
4043
+ url: z.ZodString;
4044
+ }, "strip", z.ZodTypeAny, {
4045
+ url: string;
4046
+ kind: "url";
4047
+ }, {
4048
+ url: string;
4049
+ kind: "url";
4050
+ }>, z.ZodObject<{
4051
+ kind: z.ZodLiteral<"document">;
4052
+ fileName: z.ZodString;
4053
+ html: z.ZodString;
4054
+ }, "strip", z.ZodTypeAny, {
4055
+ html: string;
4056
+ kind: "document";
4057
+ fileName: string;
4058
+ }, {
4059
+ html: string;
4060
+ kind: "document";
4061
+ fileName: string;
4062
+ }>]>;
4063
+ }, "strip", z.ZodTypeAny, {
4064
+ source: {
4065
+ url: string;
4066
+ kind: "url";
4067
+ } | {
4068
+ html: string;
4069
+ kind: "document";
4070
+ fileName: string;
4071
+ };
4072
+ oId: string;
4073
+ }, {
4074
+ source: {
4075
+ url: string;
4076
+ kind: "url";
4077
+ } | {
4078
+ html: string;
4079
+ kind: "document";
4080
+ fileName: string;
4081
+ };
4082
+ oId: string;
4083
+ }>;
4084
+ response: z.ZodObject<{
4085
+ form: z.ZodType<import("..").CustomFormSchema, z.ZodTypeDef, import("..").CustomFormSchema>;
4086
+ warnings: z.ZodArray<z.ZodString, "many">;
4087
+ }, "strip", z.ZodTypeAny, {
4088
+ form: import("..").CustomFormSchema;
4089
+ warnings: string[];
4090
+ }, {
4091
+ form: import("..").CustomFormSchema;
4092
+ warnings: string[];
4093
+ }>;
4094
+ };
4095
+ };
3980
4096
  };
@@ -18,6 +18,7 @@ const wonde_1 = require("./wonde");
18
18
  const misSync_1 = require("./misSync");
19
19
  const userManagement_1 = require("./userManagement");
20
20
  const outreach_1 = require("./outreach");
21
+ const formImport_1 = require("./formImport");
21
22
  /**
22
23
  * The single source of truth for every typed callable. Add a feature group's
23
24
  * spread here and its callables are automatically covered by:
@@ -47,6 +48,7 @@ exports.allCallables = {
47
48
  ...misSync_1.misSyncCallables,
48
49
  ...userManagement_1.userManagementCallables,
49
50
  ...outreach_1.outreachCallables,
51
+ ...formImport_1.formImportCallables,
50
52
  };
51
53
  /** The per-group maps, exported so tests can assert no key collisions. */
52
54
  exports.callableGroups = {
@@ -67,5 +69,6 @@ exports.callableGroups = {
67
69
  misSyncCallables: misSync_1.misSyncCallables,
68
70
  userManagementCallables: userManagement_1.userManagementCallables,
69
71
  outreachCallables: outreach_1.outreachCallables,
72
+ formImportCallables: formImport_1.formImportCallables,
70
73
  };
71
74
  //# sourceMappingURL=registry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/callables/registry.ts"],"names":[],"mappings":";;;AAEA,6DAAiE;AACjE,2CAA+C;AAC/C,qCAAyC;AACzC,uCAA0C;AAC1C,qCAAyC;AACzC,6CAAiD;AACjD,uDAA2D;AAC3D,6BAAiC;AACjC,uDAAyD;AACzD,uCAA2C;AAC3C,yCAA6C;AAC7C,uDAA2D;AAC3D,+BAAmC;AACnC,mCAAuC;AACvC,uCAA2C;AAC3C,qDAAyD;AACzD,yCAA6C;AAE7C;;;;;;;;;;GAUG;AACU,QAAA,YAAY,GAAG;IACxB,GAAG,gDAA2B;IAC9B,GAAG,8BAAkB;IACrB,GAAG,wBAAe;IAClB,GAAG,yBAAe;IAClB,GAAG,wBAAe;IAClB,GAAG,gCAAmB;IACtB,GAAG,0CAAwB;IAC3B,GAAG,gBAAW;IACd,GAAG,wCAAsB;IACzB,GAAG,0BAAgB;IACnB,GAAG,4BAAiB;IACpB,GAAG,0CAAwB;IAC3B,GAAG,kBAAY;IACf,GAAG,sBAAc;IACjB,GAAG,0BAAgB;IACnB,GAAG,wCAAuB;IAC1B,GAAG,4BAAiB;CACC,CAAC;AAiB1B,0EAA0E;AAC7D,QAAA,cAAc,GAAG;IAC1B,2BAA2B,EAA3B,gDAA2B;IAC3B,kBAAkB,EAAlB,8BAAkB;IAClB,eAAe,EAAf,wBAAe;IACf,eAAe,EAAf,yBAAe;IACf,eAAe,EAAf,wBAAe;IACf,mBAAmB,EAAnB,gCAAmB;IACnB,wBAAwB,EAAxB,0CAAwB;IACxB,WAAW,EAAX,gBAAW;IACX,sBAAsB,EAAtB,wCAAsB;IACtB,gBAAgB,EAAhB,0BAAgB;IAChB,iBAAiB,EAAjB,4BAAiB;IACjB,wBAAwB,EAAxB,0CAAwB;IACxB,YAAY,EAAZ,kBAAY;IACZ,cAAc,EAAd,sBAAc;IACd,gBAAgB,EAAhB,0BAAgB;IAChB,uBAAuB,EAAvB,wCAAuB;IACvB,iBAAiB,EAAjB,4BAAiB;CACpB,CAAC"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/callables/registry.ts"],"names":[],"mappings":";;;AAEA,6DAAiE;AACjE,2CAA+C;AAC/C,qCAAyC;AACzC,uCAA0C;AAC1C,qCAAyC;AACzC,6CAAiD;AACjD,uDAA2D;AAC3D,6BAAiC;AACjC,uDAAyD;AACzD,uCAA2C;AAC3C,yCAA6C;AAC7C,uDAA2D;AAC3D,+BAAmC;AACnC,mCAAuC;AACvC,uCAA2C;AAC3C,qDAAyD;AACzD,yCAA6C;AAC7C,6CAAiD;AAEjD;;;;;;;;;;GAUG;AACU,QAAA,YAAY,GAAG;IACxB,GAAG,gDAA2B;IAC9B,GAAG,8BAAkB;IACrB,GAAG,wBAAe;IAClB,GAAG,yBAAe;IAClB,GAAG,wBAAe;IAClB,GAAG,gCAAmB;IACtB,GAAG,0CAAwB;IAC3B,GAAG,gBAAW;IACd,GAAG,wCAAsB;IACzB,GAAG,0BAAgB;IACnB,GAAG,4BAAiB;IACpB,GAAG,0CAAwB;IAC3B,GAAG,kBAAY;IACf,GAAG,sBAAc;IACjB,GAAG,0BAAgB;IACnB,GAAG,wCAAuB;IAC1B,GAAG,4BAAiB;IACpB,GAAG,gCAAmB;CACD,CAAC;AAiB1B,0EAA0E;AAC7D,QAAA,cAAc,GAAG;IAC1B,2BAA2B,EAA3B,gDAA2B;IAC3B,kBAAkB,EAAlB,8BAAkB;IAClB,eAAe,EAAf,wBAAe;IACf,eAAe,EAAf,yBAAe;IACf,eAAe,EAAf,wBAAe;IACf,mBAAmB,EAAnB,gCAAmB;IACnB,wBAAwB,EAAxB,0CAAwB;IACxB,WAAW,EAAX,gBAAW;IACX,sBAAsB,EAAtB,wCAAsB;IACtB,gBAAgB,EAAhB,0BAAgB;IAChB,iBAAiB,EAAjB,4BAAiB;IACjB,wBAAwB,EAAxB,0CAAwB;IACxB,YAAY,EAAZ,kBAAY;IACZ,cAAc,EAAd,sBAAc;IACd,gBAAgB,EAAhB,0BAAgB;IAChB,uBAAuB,EAAvB,wCAAuB;IACvB,iBAAiB,EAAjB,4BAAiB;IACjB,mBAAmB,EAAnB,gCAAmB;CACtB,CAAC"}
package/lib/config.js CHANGED
@@ -13,8 +13,10 @@ const contactsSlice_1 = __importDefault(require("./features/contacts/contactsSli
13
13
  const activePlacement_1 = __importDefault(require("./features/placements/studentPlacements/activePlacement"));
14
14
  const logsSlice_1 = __importDefault(require("./features/logs/logsSlice"));
15
15
  const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
16
+ const firebaseConfig_1 = require("./firebase/firebaseConfig");
16
17
  const persistConfig = {
17
- key: "root",
18
+ // The admin popout is signed in as someone else, so keep its cached state apart.
19
+ key: firebaseConfig_1.isAdminPopoutWindow ? "adminPopoutRoot" : "root",
18
20
  version: 1,
19
21
  storage: async_storage_1.default,
20
22
  };
package/lib/config.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;;;;AAAA,8CAAgE;AAChE,iDAA4C;AAC5C,0EAAmD;AACnD,4IAAqH;AACrH,8IAAuH;AACvH,sFAA+D;AAC/D,8GAA4F;AAC5F,0EAAmD;AACnD,8FAAoE;AAGpE,MAAM,aAAa,GAAG;IAClB,GAAG,EAAE,MAAM;IACX,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,uBAAY;CACtB,CAAA;AAEH,MAAM,QAAQ,GAAG,IAAA,yBAAe,EAAC;IAC/B,yBAAyB,EAAE,wCAAgC;IAC3D,0BAA0B,EAAE,yCAAiC;IAC7D,QAAQ,EAAE,uBAAe;IACzB,eAAe,EAAE,yBAAsB;IACvC,IAAI,EAAE,mBAAW;IACjB,IAAI,EAAE,mBAAW;CAClB,CAAC,CAAC;AAGH,MAAM,iBAAiB,GAAG,IAAA,8BAAc,EAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;AAGrD,QAAA,KAAK,GAAG,IAAA,wBAAc,EAAC;IAClC,OAAO,EAAE,iBAAiB;IAC1B,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;IAC/C,UAAU,EAAE,CAAC,oBAAoB,EAAE,EAAE,CACrC,oBAAoB,CAAC;QACnB,cAAc,EAAE,KAAK;QACrB,iBAAiB,EAAE,KAAK;KACzB,CAAC;CAEH,CAAC,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;;;;AAAA,8CAAgE;AAChE,iDAA4C;AAC5C,0EAAmD;AACnD,4IAAqH;AACrH,8IAAuH;AACvH,sFAA+D;AAC/D,8GAA4F;AAC5F,0EAAmD;AACnD,8FAAoE;AACpE,8DAA6D;AAG7D,MAAM,aAAa,GAAG;IAClB,iFAAiF;IACjF,GAAG,EAAE,oCAAmB,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM;IACrD,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,uBAAY;CACtB,CAAA;AAEH,MAAM,QAAQ,GAAG,IAAA,yBAAe,EAAC;IAC/B,yBAAyB,EAAE,wCAAgC;IAC3D,0BAA0B,EAAE,yCAAiC;IAC7D,QAAQ,EAAE,uBAAe;IACzB,eAAe,EAAE,yBAAsB;IACvC,IAAI,EAAE,mBAAW;IACjB,IAAI,EAAE,mBAAW;CAClB,CAAC,CAAC;AAGH,MAAM,iBAAiB,GAAG,IAAA,8BAAc,EAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;AAGrD,QAAA,KAAK,GAAG,IAAA,wBAAc,EAAC;IAClC,OAAO,EAAE,iBAAiB;IAC1B,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;IAC/C,UAAU,EAAE,CAAC,oBAAoB,EAAE,EAAE,CACrC,oBAAoB,CAAC;QACnB,cAAc,EAAE,KAAK;QACrB,iBAAiB,EAAE,KAAK;KACzB,CAAC;CAEH,CAAC,CAAC"}
@@ -16,7 +16,11 @@ export declare const credentialsProd: {
16
16
  appId: string;
17
17
  measurementId: string;
18
18
  };
19
+ export declare const ADMIN_POPOUT_PATH = "/admin/institute-popup";
20
+ export declare const isAdminPopoutWindow: boolean;
19
21
  export declare const auth: import("@firebase/auth").Auth;
20
22
  export declare const db: import("@firebase/firestore").Firestore;
21
23
  export declare const functions: import("@firebase/functions").Functions;
22
24
  export declare const storage: import("@firebase/storage").FirebaseStorage;
25
+ export declare const stashAdminSessionForPopout: () => Promise<void>;
26
+ export declare const clearAdminPopoutSession: () => Promise<void>;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.storage = exports.functions = exports.db = exports.auth = exports.credentialsProd = exports.credentialsDev = void 0;
6
+ exports.clearAdminPopoutSession = exports.stashAdminSessionForPopout = exports.storage = exports.functions = exports.db = exports.auth = exports.isAdminPopoutWindow = exports.ADMIN_POPOUT_PATH = exports.credentialsProd = exports.credentialsDev = void 0;
7
7
  /* eslint-disable no-multiple-empty-lines */
8
8
  const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
9
9
  const app_1 = require("firebase/app");
@@ -31,8 +31,20 @@ exports.credentialsProd = {
31
31
  measurementId: "G-NW5YMC6JN8",
32
32
  };
33
33
  const firebaseConfig = !(0, app_1.getApps)().length ? (0, app_1.initializeApp)(exports.credentialsDev) : (0, app_1.getApp)();
34
+ // The admin panel's institute popout (opened while an admin is signed in as a
35
+ // staff user) keeps its own auth session under prefixed keys, so it stays
36
+ // signed in as the admin without touching the impersonated session every
37
+ // other window shares.
38
+ exports.ADMIN_POPOUT_PATH = "/admin/institute-popup";
39
+ const ADMIN_POPOUT_PREFIX = "adminPopout:";
40
+ exports.isAdminPopoutWindow = typeof window !== "undefined" && window.location?.pathname === exports.ADMIN_POPOUT_PATH;
41
+ const prefixedStorage = (prefix) => ({
42
+ setItem: (key, value) => async_storage_1.default.setItem(prefix + key, value),
43
+ getItem: (key) => async_storage_1.default.getItem(prefix + key),
44
+ removeItem: (key) => async_storage_1.default.removeItem(prefix + key),
45
+ });
34
46
  (0, auth_1.initializeAuth)(firebaseConfig, {
35
- persistence: (0, persistence_1.getReactNativePersistence)(async_storage_1.default),
47
+ persistence: (0, persistence_1.getReactNativePersistence)(exports.isAdminPopoutWindow ? prefixedStorage(ADMIN_POPOUT_PREFIX) : async_storage_1.default),
36
48
  });
37
49
  (0, firestore_1.initializeFirestore)(firebaseConfig, {
38
50
  ignoreUndefinedProperties: true,
@@ -42,5 +54,17 @@ exports.auth = (0, auth_1.getAuth)(firebaseConfig);
42
54
  exports.db = (0, firestore_1.getFirestore)(firebaseConfig);
43
55
  exports.functions = (0, functions_1.getFunctions)(firebaseConfig, "europe-west2");
44
56
  exports.storage = (0, storage_1.getStorage)(firebaseConfig);
57
+ // Firebase auth's own persistence key for the signed-in user.
58
+ const authUserKey = `firebase:authUser:${firebaseConfig.options.apiKey}:${firebaseConfig.name}`;
59
+ // Copies the current (admin) session to the popout window's keys. Call it
60
+ // before signing in as another user, while the stored session is still the admin's.
61
+ const stashAdminSessionForPopout = async () => {
62
+ const session = await async_storage_1.default.getItem(authUserKey);
63
+ if (session)
64
+ await async_storage_1.default.setItem(ADMIN_POPOUT_PREFIX + authUserKey, session);
65
+ };
66
+ exports.stashAdminSessionForPopout = stashAdminSessionForPopout;
67
+ const clearAdminPopoutSession = () => async_storage_1.default.removeItem(ADMIN_POPOUT_PREFIX + authUserKey);
68
+ exports.clearAdminPopoutSession = clearAdminPopoutSession;
45
69
  // connectFunctionsEmulator(functions, "localhost", 5001);
46
70
  //# sourceMappingURL=firebaseConfig.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"firebaseConfig.js","sourceRoot":"","sources":["../../src/firebase/firebaseConfig.tsx"],"names":[],"mappings":";;;;;;AAAA,4CAA4C;AAC5C,8FAAqE;AACrE,sCAA4D;AAC5D,wCAAsD;AACtD,kDAAqE;AACrE,kDAAgD;AAChD,8CAA4C;AAC5C,+CAAwD;AAE3C,QAAA,cAAc,GAAG;IAC1B,MAAM,EAAE,yCAAyC;IACjD,UAAU,EAAE,gCAAgC;IAC5C,SAAS,EAAE,gBAAgB;IAC3B,aAAa,EAAE,4BAA4B;IAC3C,iBAAiB,EAAE,cAAc;IACjC,KAAK,EAAE,2CAA2C;IAClD,aAAa,EAAE,cAAc;CAChC,CAAC;AAEW,QAAA,eAAe,GAAG;IAC3B,MAAM,EAAE,yCAAyC;IACjD,UAAU,EAAE,kCAAkC;IAC9C,SAAS,EAAE,kBAAkB;IAC7B,aAAa,EAAE,8BAA8B;IAC7C,iBAAiB,EAAE,cAAc;IACjC,KAAK,EAAE,2CAA2C;IAClD,aAAa,EAAE,cAAc;CAChC,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,IAAA,aAAO,GAAE,CAAC,MAAM,CAAC,CAAC,CAAC,IAAA,mBAAa,EAAC,sBAAc,CAAC,CAAC,CAAC,CAAC,IAAA,YAAM,GAAE,CAAC;AAEpF,IAAA,qBAAc,EAAC,cAAc,EAAE;IAC3B,WAAW,EAAE,IAAA,uCAAyB,EAAC,uBAAY,CAAC;CACvD,CAAC,CAAC;AAEH,IAAA,+BAAmB,EAAC,cAAc,EAAE;IAChC,yBAAyB,EAAE,IAAI;IAC/B,iCAAiC,EAAE,IAAI;CAC1C,CAAC,CAAC;AAEU,QAAA,IAAI,GAAG,IAAA,cAAO,EAAC,cAAc,CAAC,CAAC;AAC/B,QAAA,EAAE,GAAG,IAAA,wBAAY,EAAC,cAAc,CAAC,CAAC;AAClC,QAAA,SAAS,GAAG,IAAA,wBAAY,EAAC,cAAc,EAAE,cAAc,CAAC,CAAC;AACzD,QAAA,OAAO,GAAG,IAAA,oBAAU,EAAC,cAAc,CAAC,CAAC;AAElD,0DAA0D"}
1
+ {"version":3,"file":"firebaseConfig.js","sourceRoot":"","sources":["../../src/firebase/firebaseConfig.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "private": false,
3
3
  "name": "placementt-core",
4
4
  "author": "Placementt",
5
- "version": "1.400.1133",
5
+ "version": "1.400.1135",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
8
8
  "scripts": {
@@ -0,0 +1,43 @@
1
+ import {z} from "zod";
2
+ import {CallableGroup} from "./types";
3
+ import {CustomFormSchema} from "../typeDefinitions";
4
+
5
+ /**
6
+ * Contract for AI form import
7
+ * (`Placementt-Backend/functions/src/formImport.ts`).
8
+ *
9
+ * Turns a form that already exists somewhere else (a public Microsoft Forms or
10
+ * Google Forms link, or a Word document) into a `CustomFormSchema` the form
11
+ * builder can open. Nothing is saved: the builder opens the result as a draft
12
+ * and the member of staff saves it the usual way.
13
+ *
14
+ * A Word document is read in the browser (mammoth, to HTML) and only its
15
+ * HTML is sent, so the function never handles the file itself.
16
+ */
17
+ export const formImportCallables = {
18
+ "formImport-generateFormSchema": {
19
+ request: z.object({
20
+ /** The organisation the form is for. Staff access is checked. */
21
+ oId: z.string(),
22
+ source: z.discriminatedUnion("kind", [
23
+ z.object({
24
+ kind: z.literal("url"),
25
+ /** A public Microsoft Forms or Google Forms link. */
26
+ url: z.string(),
27
+ }),
28
+ z.object({
29
+ kind: z.literal("document"),
30
+ fileName: z.string(),
31
+ /** The document as HTML, as mammoth produces it. */
32
+ html: z.string(),
33
+ }),
34
+ ]),
35
+ }),
36
+ response: z.object({
37
+ form: z.custom<CustomFormSchema>(),
38
+ /** Anything that didn't map across cleanly (file uploads,
39
+ * multi-select questions, grids), for the user to review. */
40
+ warnings: z.array(z.string()),
41
+ }),
42
+ },
43
+ } satisfies CallableGroup;
@@ -14,3 +14,4 @@ export * from "./wonde";
14
14
  export * from "./misSync";
15
15
  export * from "./userManagement";
16
16
  export * from "./outreach";
17
+ export * from "./formImport";
@@ -17,6 +17,7 @@ import {wondeCallables} from "./wonde";
17
17
  import {misSyncCallables} from "./misSync";
18
18
  import {userManagementCallables} from "./userManagement";
19
19
  import {outreachCallables} from "./outreach";
20
+ import {formImportCallables} from "./formImport";
20
21
 
21
22
  /**
22
23
  * The single source of truth for every typed callable. Add a feature group's
@@ -47,6 +48,7 @@ export const allCallables = {
47
48
  ...misSyncCallables,
48
49
  ...userManagementCallables,
49
50
  ...outreachCallables,
51
+ ...formImportCallables,
50
52
  } satisfies CallableGroup;
51
53
 
52
54
  /** Every registered callable name. */
@@ -83,4 +85,5 @@ export const callableGroups = {
83
85
  misSyncCallables,
84
86
  userManagementCallables,
85
87
  outreachCallables,
88
+ formImportCallables,
86
89
  };
package/src/config.ts CHANGED
@@ -7,10 +7,12 @@ import contactsReducer from "./features/contacts/contactsSlice"
7
7
  import activePlacementReducer from "./features/placements/studentPlacements/activePlacement"
8
8
  import logsReducer from "./features/logs/logsSlice"
9
9
  import AsyncStorage from '@react-native-async-storage/async-storage'
10
+ import {isAdminPopoutWindow} from "./firebase/firebaseConfig"
10
11
 
11
12
 
12
13
  const persistConfig = {
13
- key: "root",
14
+ // The admin popout is signed in as someone else, so keep its cached state apart.
15
+ key: isAdminPopoutWindow ? "adminPopoutRoot" : "root",
14
16
  version: 1,
15
17
  storage: AsyncStorage,
16
18
  }
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable no-multiple-empty-lines */
2
2
  import AsyncStorage from "@react-native-async-storage/async-storage";
3
3
  import {getApp, getApps, initializeApp} from "firebase/app";
4
- import {getAuth, initializeAuth} from "firebase/auth";
4
+ import {ReactNativeAsyncStorage, getAuth, initializeAuth} from "firebase/auth";
5
5
  import {getFirestore, initializeFirestore} from "firebase/firestore";
6
6
  import {getFunctions} from "firebase/functions";
7
7
  import {getStorage} from "firebase/storage";
@@ -29,8 +29,22 @@ export const credentialsProd = {
29
29
 
30
30
  const firebaseConfig = !getApps().length ? initializeApp(credentialsDev) : getApp();
31
31
 
32
+ // The admin panel's institute popout (opened while an admin is signed in as a
33
+ // staff user) keeps its own auth session under prefixed keys, so it stays
34
+ // signed in as the admin without touching the impersonated session every
35
+ // other window shares.
36
+ export const ADMIN_POPOUT_PATH = "/admin/institute-popup";
37
+ const ADMIN_POPOUT_PREFIX = "adminPopout:";
38
+ export const isAdminPopoutWindow = typeof window !== "undefined" && window.location?.pathname === ADMIN_POPOUT_PATH;
39
+
40
+ const prefixedStorage = (prefix: string): ReactNativeAsyncStorage => ({
41
+ setItem: (key, value) => AsyncStorage.setItem(prefix + key, value),
42
+ getItem: (key) => AsyncStorage.getItem(prefix + key),
43
+ removeItem: (key) => AsyncStorage.removeItem(prefix + key),
44
+ });
45
+
32
46
  initializeAuth(firebaseConfig, {
33
- persistence: getReactNativePersistence(AsyncStorage),
47
+ persistence: getReactNativePersistence(isAdminPopoutWindow ? prefixedStorage(ADMIN_POPOUT_PREFIX) : AsyncStorage),
34
48
  });
35
49
 
36
50
  initializeFirestore(firebaseConfig, {
@@ -43,6 +57,18 @@ export const db = getFirestore(firebaseConfig);
43
57
  export const functions = getFunctions(firebaseConfig, "europe-west2");
44
58
  export const storage = getStorage(firebaseConfig);
45
59
 
60
+ // Firebase auth's own persistence key for the signed-in user.
61
+ const authUserKey = `firebase:authUser:${firebaseConfig.options.apiKey}:${firebaseConfig.name}`;
62
+
63
+ // Copies the current (admin) session to the popout window's keys. Call it
64
+ // before signing in as another user, while the stored session is still the admin's.
65
+ export const stashAdminSessionForPopout = async () => {
66
+ const session = await AsyncStorage.getItem(authUserKey);
67
+ if (session) await AsyncStorage.setItem(ADMIN_POPOUT_PREFIX + authUserKey, session);
68
+ };
69
+
70
+ export const clearAdminPopoutSession = () => AsyncStorage.removeItem(ADMIN_POPOUT_PREFIX + authUserKey);
71
+
46
72
  // connectFunctionsEmulator(functions, "localhost", 5001);
47
73
 
48
74