@7365admin1/layer-common 4.107.0 → 4.108.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 4.108.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6b9abd5: Online forms: an application whose form template was deleted can still be reviewed - new `definitionFromSubmission()` builds a read-only form from the submitted answers (keys turned into readable labels), and `OnlineFormFill` takes a `notice` shown above the form.
8
+
3
9
  ## 4.107.0
4
10
 
5
11
  ### Minor Changes
@@ -9,6 +9,15 @@
9
9
  <slot name="title-actions" />
10
10
  </v-card-title>
11
11
 
12
+ <v-alert
13
+ v-if="notice"
14
+ type="info"
15
+ variant="tonal"
16
+ density="compact"
17
+ class="mx-6 mt-2"
18
+ data-test="online-form-notice"
19
+ >{{ notice }}</v-alert>
20
+
12
21
  <!-- Submission metadata bar (readonly only). Shown for an actual
13
22
  submission (submissionStatus) OR a pure template preview that
14
23
  requires payment — a preview has no submission yet, but should
@@ -481,6 +490,8 @@ const props = defineProps({
481
490
  submissionStatus: { type: String, default: "" },
482
491
  submissionUnit: { type: String, default: "" },
483
492
  approvalMode: { type: Boolean, default: false },
493
+ /** A short note above the form, e.g. that its template was deleted. */
494
+ notice: { type: String, default: "" },
484
495
  initialManagementValues: { type: Object as PropType<Record<string, string>>, default: () => ({}) },
485
496
  formType: { type: String, default: "" },
486
497
  paymentAmount: { type: Number, default: 0 },
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "4.107.0",
5
+ "version": "4.108.0",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "//files": "What a consumer extending this layer actually loads. Without this npm ships the whole working tree - the changesets, the CI workflows, the render harness in tools/ and any scratch directory that happened to exist at publish time. Nuxt resolves a layer by directory, so every runtime directory below has to stay listed; adding a new top-level runtime directory means adding it here too.",
@@ -0,0 +1,48 @@
1
+ /**
2
+ * An application whose form template was deleted (owner 2026-10-06, option A):
3
+ * the review screen still shows what the resident sent, built from the
4
+ * submission alone. The submission stores answers by key only, so each key is
5
+ * turned into a readable label ("unitOwnerName" -> "Unit Owner Name").
6
+ */
7
+
8
+ /** Shown in the meta bar already, or a machine value - not repeated as an answer. */
9
+ const SKIP = new Set(["formDate", "formTo", "block", "level"]);
10
+
11
+ export function readableKey(key: string): string {
12
+ return key
13
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
14
+ .replace(/[_-]+/g, " ")
15
+ .replace(/\s+/g, " ")
16
+ .trim()
17
+ .replace(/\b\w/g, (c) => c.toUpperCase());
18
+ }
19
+
20
+ const isImage = (v: unknown) =>
21
+ typeof v === "string" && (/^data:image\//.test(v) || /^https?:\/\/\S+\.(png|jpe?g|webp)(\?|$)/i.test(v));
22
+
23
+ function asText(v: unknown): string {
24
+ if (v === true) return "Yes";
25
+ if (v === false) return "No";
26
+ if (Array.isArray(v)) return v.map(asText).join(", ");
27
+ if (v && typeof v === "object") return Object.entries(v).map(([k, x]) => `${readableKey(k)}: ${asText(x)}`).join(", ");
28
+ return v == null ? "" : String(v);
29
+ }
30
+
31
+ /** A read-only form definition + display values made from the submitted answers. */
32
+ export function definitionFromSubmission(title: string, fields: Record<string, unknown> = {}) {
33
+ const blocks: Array<Record<string, any>> = [];
34
+ const values: Record<string, unknown> = { ...fields };
35
+ for (const [key, value] of Object.entries(fields)) {
36
+ if (SKIP.has(key) || value === "" || value == null) continue;
37
+ if (isImage(value)) {
38
+ blocks.push({ type: "signature-row", label: readableKey(key), key });
39
+ continue;
40
+ }
41
+ const text = asText(value);
42
+ values[key] = text;
43
+ blocks.push({ type: "field", field: { key, label: readableKey(key), fieldType: text.length > 80 ? "textarea" : "text" } });
44
+ }
45
+ return { definition: { title, blocks }, values };
46
+ }
47
+
48
+ export const DELETED_TEMPLATE_NOTICE = "This form has since been deleted - showing the answers as submitted.";