@7365admin1/layer-common 4.64.4 → 4.65.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.
- package/CHANGELOG.md +50 -4
- package/assets/css/primitives.css +13 -0
- package/components/AccessCardAddForm.vue +5 -4
- package/components/AccessCardAssignToUnitForm.vue +3 -2
- package/components/AccessCardPreviewDialog.vue +4 -3
- package/components/AreaMain.vue +33 -30
- package/components/BuildingManagement/units.vue +30 -21
- package/components/CameraBulkImportDialog.vue +41 -12
- package/components/DashboardMain.vue +14 -7
- package/components/EntryPass/QrTemplatePreview.vue +21 -25
- package/components/EntryPassInformation.vue +119 -24
- package/components/ImportExcelDialog.vue +136 -0
- package/components/InventoryImportDialog.vue +33 -104
- package/components/InventoryItemDetail.vue +3 -2
- package/components/InventoryItemDialog.vue +62 -7
- package/components/InventoryReportsTab.vue +2 -1
- package/components/InventorySettingsTab.vue +2 -5
- package/components/InventoryStockTab.vue +13 -4
- package/components/NotificationBell.vue +1 -1
- package/components/NotificationSettings.vue +1 -1
- package/components/Robot/MiniMap.vue +1 -1
- package/components/UnitMain.vue +30 -28
- package/components/VehicleBulkAddForm.vue +30 -16
- package/composables/useAccessManagement.ts +5 -0
- package/composables/useImportTemplates.ts +24 -0
- package/composables/useInventory.ts +12 -5
- package/composables/useSiteEntryPassSettings.ts +26 -0
- package/composables/useWebUsb.ts +14 -11
- package/package.json +1 -1
- package/types/inventory.d.ts +2 -0
- package/utils/camera-csv.ts +2 -0
- package/utils/dashboard.ts +11 -0
- package/utils/import-result.ts +62 -0
- package/utils/inventory-store-label.ts +27 -0
- package/utils/vehicle-bulk-sheet.ts +59 -3
|
@@ -97,6 +97,8 @@ export type UploadedSheet = {
|
|
|
97
97
|
matchesTemplate: boolean;
|
|
98
98
|
/** The heading line as the file spells it - for telling a person what it has. */
|
|
99
99
|
header: string[];
|
|
100
|
+
/** A template example row was left in and was not read. */
|
|
101
|
+
exampleRowIgnored?: boolean;
|
|
100
102
|
};
|
|
101
103
|
|
|
102
104
|
/**
|
|
@@ -138,14 +140,16 @@ export function mapUploadedGrid(grid: string[][]): UploadedSheet {
|
|
|
138
140
|
// Every line under the header is somebody's car. Nothing is dropped for
|
|
139
141
|
// looking like something else - two people in one household can have the
|
|
140
142
|
// same details but for the plate, and a repeated row is the person's to fix.
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
+
const body = lines.slice(1);
|
|
144
|
+
// The example row of a template the server made is marked beside it; it is nobody's car.
|
|
145
|
+
const rows = body
|
|
146
|
+
.filter((line) => !isExampleLine(line))
|
|
143
147
|
.map((line) =>
|
|
144
148
|
BULK_SHEET_HEADINGS.map((_, column) => String(line[column] ?? "").trim()),
|
|
145
149
|
)
|
|
146
150
|
.filter((line) => !isEmptyLine(line));
|
|
147
151
|
|
|
148
|
-
return { rows, matchesTemplate: true, header };
|
|
152
|
+
return { rows, matchesTemplate: true, header, exampleRowIgnored: body.some(isExampleLine) };
|
|
149
153
|
}
|
|
150
154
|
|
|
151
155
|
/**
|
|
@@ -385,3 +389,55 @@ export async function xlsxToGrid(data: ArrayBuffer | Uint8Array): Promise<string
|
|
|
385
389
|
export function isWorkbookName(name: string): boolean {
|
|
386
390
|
return /\.(xlsx|xlsm)$/i.test(String(name ?? ""));
|
|
387
391
|
}
|
|
392
|
+
|
|
393
|
+
/* ------------------------------ live templates ----------------------------- */
|
|
394
|
+
|
|
395
|
+
/** Beside the example row of every template the server makes. */
|
|
396
|
+
export const TEMPLATE_EXAMPLE_MARK = "Example – delete this row";
|
|
397
|
+
|
|
398
|
+
/** Does this line carry the template's example-row mark? */
|
|
399
|
+
export function isExampleLine(cells: string[]): boolean {
|
|
400
|
+
return (cells ?? []).some((cell) => String(cell ?? "").includes(TEMPLATE_EXAMPLE_MARK));
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* The hidden "_meta" sheet of a template the server made: which kind of
|
|
405
|
+
* template it is and the property it was made for (`kind`, `site`,
|
|
406
|
+
* `siteName`). Empty for any other workbook.
|
|
407
|
+
*/
|
|
408
|
+
export async function xlsxMeta(data: ArrayBuffer | Uint8Array): Promise<Record<string, string>> {
|
|
409
|
+
const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
410
|
+
const entries = readZipEntries(bytes);
|
|
411
|
+
const book = entries.get("xl/workbook.xml");
|
|
412
|
+
const rels = entries.get("xl/_rels/workbook.xml.rels");
|
|
413
|
+
if (!book || !rels) return {};
|
|
414
|
+
const sheet = (await readPart(bytes, book)).match(/<sheet\b[^>]*\bname="_meta"[^>]*>/)?.[0];
|
|
415
|
+
const rid = sheet?.match(/\br:id="([^"]+)"/)?.[1];
|
|
416
|
+
if (!rid) return {};
|
|
417
|
+
const relation = ((await readPart(bytes, rels)).match(/<Relationship\b[^>]*>/g) ?? []).find((tag) => tag.includes(` Id="${rid}"`));
|
|
418
|
+
const target = relation?.match(/\bTarget="([^"]+)"/)?.[1];
|
|
419
|
+
const part = target ? entries.get(target.startsWith("/") ? target.slice(1) : `xl/${target}`) : undefined;
|
|
420
|
+
if (!part) return {};
|
|
421
|
+
const sharedEntry = entries.get("xl/sharedStrings.xml");
|
|
422
|
+
const strings = sharedEntry ? sharedStringsOf(await readPart(bytes, sharedEntry)) : [];
|
|
423
|
+
const meta: Record<string, string> = {};
|
|
424
|
+
for (const line of sheetToGrid(await readPart(bytes, part), strings).slice(1)) {
|
|
425
|
+
if (line[0]) meta[String(line[0])] = String(line[1] ?? "");
|
|
426
|
+
}
|
|
427
|
+
return meta;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Why a template may not be used here, or "" when it may: a template made for
|
|
432
|
+
* another property, or a different kind of template. A workbook without the
|
|
433
|
+
* hidden sheet (an older template, a person's own file) is allowed.
|
|
434
|
+
*/
|
|
435
|
+
export function templateRefusal(meta: Record<string, string>, expected: { kind: string; site: string }): string {
|
|
436
|
+
if (meta.kind && meta.kind !== expected.kind) {
|
|
437
|
+
return "This is a different kind of template. Download the template on this screen and copy your rows into it.";
|
|
438
|
+
}
|
|
439
|
+
if (meta.site && expected.site && meta.site !== expected.site) {
|
|
440
|
+
return `This file was made for ${meta.siteName ? `"${meta.siteName}"` : "another property"}. Download the template on this screen and copy your rows into it.`;
|
|
441
|
+
}
|
|
442
|
+
return "";
|
|
443
|
+
}
|