@7365admin1/layer-common 3.2.7 → 3.2.8-staging.201
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/assets/css/primitives.css +1113 -0
- package/assets/css/screens.css +1097 -0
- package/assets/css/shell.css +258 -0
- package/assets/css/tokens.css +236 -0
- package/components/AcceptDialog.vue +17 -18
- package/components/AccessCard/AvailableStats.vue +24 -16
- package/components/AccessCardAddForm.vue +42 -42
- package/components/AccessCardAssignToUnitForm.vue +13 -21
- package/components/AccessCardDeleteDialog.vue +41 -39
- package/components/AccessCardDetailsDialog.vue +125 -79
- package/components/AccessCardPreviewDialog.vue +58 -84
- package/components/AccessCardQrTagging.vue +60 -63
- package/components/AccessCardReplaceForm.vue +27 -23
- package/components/AccessManagement.vue +86 -85
- package/components/AddEqupmentForm.vue +18 -22
- package/components/AddPassKeyToVisitor.vue +95 -73
- package/components/AppButton.vue +62 -0
- package/components/AppCard.vue +20 -0
- package/components/AppDateField.vue +49 -0
- package/components/AppField.vue +76 -0
- package/components/AppKpiTile.vue +65 -0
- package/components/AppSegmented.vue +38 -0
- package/components/AppSelect.vue +247 -0
- package/components/AreaChecklistHistoryLogs.vue +51 -44
- package/components/AreaChecklistHistoryMain.vue +14 -7
- package/components/AreaFormDialog.vue +71 -59
- package/components/AreaMain.vue +73 -107
- package/components/AttachmentsViewer.vue +84 -22
- package/components/AttendanceCheckInOutDialog.vue +85 -88
- package/components/AttendanceDetailsDialog.vue +57 -63
- package/components/AttendanceMain.vue +4 -3
- package/components/AttendanceMapSearchDialog.vue +42 -44
- package/components/AttendanceSettingsDialog.vue +79 -63
- package/components/Avatar/Main.vue +54 -2
- package/components/BillingMain.vue +26 -30
- package/components/BtnUploadFile.vue +53 -11
- package/components/BuildingForm.vue +34 -27
- package/components/BuildingManagement/buildings.vue +39 -39
- package/components/BuildingManagement/units.vue +26 -42
- package/components/BuildingUnitFormAdd.vue +20 -23
- package/components/BuildingUnitFormEdit.vue +8 -4
- package/components/BulletinBoardForm.vue +19 -24
- package/components/BulletinBoardManagement.vue +49 -57
- package/components/BulletinBoardView.vue +9 -6
- package/components/BulletinExpirationChip.vue +13 -4
- package/components/Button/Close.vue +2 -2
- package/components/CameraForm.vue +21 -22
- package/components/CameraMain.vue +50 -49
- package/components/Card/DeleteConfirmation.vue +24 -22
- package/components/Card/MemberInfoSummary.vue +28 -3
- package/components/Card/Toggle.vue +15 -3
- package/components/CardToolbar.vue +37 -0
- package/components/Carousel.vue +56 -36
- package/components/Chat/Bubbles.vue +43 -14
- package/components/Chat/Information.vue +49 -32
- package/components/Chat/ListCard.vue +38 -9
- package/components/Chat/Message.vue +41 -15
- package/components/Chat/Navigation.vue +27 -15
- package/components/Chat/SkeletonLoader.vue +9 -2
- package/components/ChecklistItemRow.vue +25 -1
- package/components/CleaningScheduleMain.vue +44 -49
- package/components/ClientDetailForm.vue +81 -56
- package/components/ClientMain.vue +92 -82
- package/components/ConfirmDialog.vue +14 -8
- package/components/Container/Standard.vue +9 -7
- package/components/CreateSubsciptionPlan.vue +148 -89
- package/components/DashboardEmptyState.vue +48 -3
- package/components/DashboardMain.vue +924 -676
- package/components/DashboardPanel.vue +84 -21
- package/components/DeliveryCompany.vue +34 -20
- package/components/Dialog/DeleteConfirmation.vue +20 -21
- package/components/Dialog/ReplaceAutofillPrompt.vue +19 -26
- package/components/Dialog/ReusablePrompt.vue +30 -29
- package/components/Dialog/UpdateMoreAction.vue +62 -59
- package/components/DocumentForm.vue +14 -21
- package/components/DocumentManagement.vue +162 -108
- package/components/DocumentViewer.vue +29 -12
- package/components/DrawImage.vue +29 -21
- package/components/EntryPass/QrTemplatePreview.vue +27 -16
- package/components/EntryPassInformation.vue +40 -10
- package/components/EntryPassMain.vue +49 -27
- package/components/EquipmentItemMain.vue +84 -97
- package/components/EquipmentManagementMain.vue +81 -103
- package/components/Facility/BookingSetup.vue +43 -52
- package/components/Feedback/Form.vue +26 -12
- package/components/FeedbackMain.vue +35 -49
- package/components/FileInput.vue +75 -58
- package/components/FileInputWithList.vue +59 -12
- package/components/FormDialog.vue +60 -21
- package/components/HidAccessLogDashboard.vue +512 -490
- package/components/HidCameraCaptureDialog.vue +216 -0
- package/components/HidEnabledGate.vue +67 -9
- package/components/HidIntercomManagement.vue +1322 -653
- package/components/HidQrCodeConfiguration.vue +1030 -258
- package/components/HidReaderForm.vue +142 -59
- package/components/HidReaderManagement.vue +211 -323
- package/components/HidServiceSettingsPanel.vue +44 -13
- package/components/HidUserEnrollment.vue +1185 -412
- package/components/HygieneUpdateMoreAction.vue +44 -44
- package/components/IncidentReport/Authorities.vue +30 -18
- package/components/IncidentReport/IncidentInformation.vue +10 -10
- package/components/IncidentReport/IncidentInformationDownload.vue +3 -4
- package/components/IncidentReport/affectedEntities.vue +41 -20
- package/components/Input/Date.vue +3 -0
- package/components/Input/DatePicker.vue +1 -2
- package/components/Input/DateTimePicker.vue +1 -1
- package/components/Input/File.vue +55 -10
- package/components/Input/FileV2.vue +39 -6
- package/components/Input/InputPhoneNumberV2.vue +2 -1
- package/components/Input/ListGroupSelection.vue +56 -8
- package/components/Input/NRICNumber.vue +1 -0
- package/components/Input/NewDate.vue +1 -0
- package/components/Input/Number.vue +1 -0
- package/components/Input/Password.vue +1 -0
- package/components/Input/PhoneNumber.vue +21 -1
- package/components/Input/VehicleNumber.vue +1 -0
- package/components/InputLabel.vue +2 -2
- package/components/InvitationClientForm.vue +21 -19
- package/components/InvitationForm.vue +15 -21
- package/components/InvitationMain.vue +46 -61
- package/components/Layout/Header.vue +263 -36
- package/components/Layout/NavigationDrawer.vue +300 -30
- package/components/ListItem.vue +33 -3
- package/components/LocalPagination.vue +27 -3
- package/components/ManageChecklistMain.vue +136 -86
- package/components/MemberInformation.vue +32 -16
- package/components/MemberMain.vue +113 -93
- package/components/MyAttendanceMain.vue +3 -5
- package/components/NavigationItem.vue +83 -3
- package/components/Nfc/NFCPatrolReportMain.vue +47 -33
- package/components/Nfc/NFCPatrolRouteForm.vue +18 -20
- package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
- package/components/Nfc/NFCPatrolSettings.vue +14 -4
- package/components/Nfc/NFCTagForm.vue +22 -8
- package/components/Nfc/NFCTagMain.vue +41 -43
- package/components/Nfc/PatrolReport/DailyReportTab.vue +8 -1
- package/components/Nfc/PatrolReport/MonthlyReportTab.vue +8 -1
- package/components/Nfc/PatrolReport/MonthlyReportTable.vue +5 -3
- package/components/Nfc/PatrolReport/PatrolActivityTable.vue +26 -6
- package/components/Nfc/PatrolReport/PatrolReportTab.vue +8 -0
- package/components/Nfc/PatrolReport/PatrolRouteSummary.vue +41 -9
- package/components/Nfc/PatrolReport/RemarksDialog.vue +39 -9
- package/components/Nfc/PatrolReport/ReportEmptyState.vue +29 -9
- package/components/Nfc/PatrolReport/ReportFilters.vue +19 -10
- package/components/Nfc/PatrolReport/ReportLocationHeader.vue +32 -6
- package/components/Nfc/PatrolReport/SummaryReportTable.vue +28 -6
- package/components/NotificationSettings.vue +373 -0
- package/components/NumberSettingField.vue +9 -2
- package/components/OnlineFormConfigurationForm.vue +725 -237
- package/components/OnlineFormFill.vue +213 -106
- package/components/OnlineFormRenderPreview.vue +25 -30
- package/components/OnlineFormsConfiguration.vue +113 -109
- package/components/OrgViewDialog.vue +34 -16
- package/components/OvernightParkingAvailability.vue +49 -26
- package/components/OvernightParkingManagement.vue +78 -18
- package/components/PageHeader.vue +43 -0
- package/components/PassInformation.vue +109 -53
- package/components/PasswordConfirmation.vue +44 -26
- package/components/PeopleForm.vue +41 -20
- package/components/PhotoUpload.vue +110 -48
- package/components/PlaceholderComponent.vue +35 -19
- package/components/PlanActionsMenu.vue +12 -2
- package/components/PlanStatusBadge.vue +7 -8
- package/components/PlateNumberDisplay.vue +9 -6
- package/components/QrTemplate/PrintDialog.vue +66 -43
- package/components/RolePermissionFormCreate.vue +104 -79
- package/components/RolePermissionFormPreviewUpdate.vue +111 -109
- package/components/RolePermissionMain.vue +183 -81
- package/components/ScanVisitorQRCode.vue +99 -35
- package/components/ScheduleAreaMain.vue +34 -59
- package/components/ScheduleTaskForm.vue +61 -42
- package/components/ScheduleTaskMain.vue +56 -69
- package/components/SearchVehicleNumberUser.vue +39 -37
- package/components/ServiceProviderFormCreate.vue +29 -20
- package/components/ServiceProviderInvitationPrompt.vue +155 -0
- package/components/ServiceProviderMain.vue +601 -432
- package/components/Signature.vue +40 -34
- package/components/SignaturePad.vue +5 -29
- package/components/SiteSettings.vue +41 -11
- package/components/SiteSettingsWorkOrder.vue +61 -30
- package/components/SlideCardGroup.vue +35 -11
- package/components/Snackbar.vue +36 -3
- package/components/SpecificAttr.vue +18 -8
- package/components/StatusChip.vue +156 -0
- package/components/StockCard.vue +17 -12
- package/components/SubscriptionPlanTable.vue +207 -140
- package/components/SwitchContext.vue +201 -9
- package/components/TableHygiene.vue +36 -46
- package/components/TableList.vue +10 -10
- package/components/TableListSecondary.vue +105 -54
- package/components/TableMain.vue +251 -78
- package/components/TableV3.vue +61 -70
- package/components/TiptapEditor.vue +33 -40
- package/components/TiptapEditorQr.vue +5 -2
- package/components/Tooltip/Info.vue +69 -24
- package/components/UnitMain.vue +40 -60
- package/components/UnitPersonCard.vue +42 -13
- package/components/VehicleAddSelection.vue +48 -32
- package/components/VehicleForm.vue +112 -129
- package/components/VehicleManagement.vue +26 -12
- package/components/VehicleUpdateMoreAction.vue +59 -46
- package/components/VisitorDataFromScannedQRCodeForm.vue +90 -65
- package/components/VisitorForm.vue +774 -210
- package/components/VisitorFormSelection.vue +40 -37
- package/components/VisitorManagement.vue +239 -82
- package/components/VisitorPassKeyQRScanner.vue +65 -18
- package/components/VisitorSearchResultSummary.vue +60 -16
- package/components/VisitorSocketPopUp.vue +22 -14
- package/components/VisitorsReportPreview.vue +44 -29
- package/components/WorkOrder/Create.vue +26 -17
- package/components/WorkOrder/ListView.vue +38 -7
- package/components/WorkOrder/Main.vue +62 -59
- package/composables/useFeedback.ts +3 -3
- package/composables/useHidAmico.ts +269 -0
- package/composables/useHidNavigation.ts +2 -9
- package/composables/useMember.ts +3 -3
- package/composables/useNFCPatrolDailyReport.ts +13 -0
- package/composables/useNFCPatrolMonthlyReport.ts +22 -56
- package/composables/useNFCPatrolReport.ts +13 -0
- package/composables/useNotificationPreference.ts +87 -0
- package/composables/useOnlineFormPages.ts +2 -0
- package/composables/useRole.ts +22 -0
- package/composables/useServiceProvider.ts +3 -3
- package/composables/useServiceProviderInvitation.ts +145 -0
- package/composables/useSipWebPhone.ts +336 -0
- package/composables/useSubscriptionPlan.ts +62 -0
- package/composables/useThemePreference.ts +63 -0
- package/composables/useVisitor.ts +6 -1
- package/composables/useWebUsb.ts +127 -37
- package/composables/useWorkOrder.ts +17 -3
- package/error.vue +30 -6
- package/nuxt.config.ts +36 -0
- package/package.json +25 -4
- package/pages/[org]/[site]/access-mgmt/administrator/index.vue +2 -2
- package/pages/[org]/[site]/access-mgmt/{identity-mapping → hid-cards}/index.vue +2 -2
- package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
- package/pages/notification-settings.vue +19 -0
- package/pages/payment-method-linked.vue +34 -10
- package/pages/require-customer.vue +48 -10
- package/pages/require-organization-membership.vue +32 -8
- package/pages/unauthorized.vue +29 -4
- package/plugins/secure-member.client.ts +21 -56
- package/plugins/vuetify.ts +69 -2
- package/types/nfc-patrol-report.ts +3 -2
- package/types/site.d.ts +71 -0
- package/types/subscription.ts +18 -11
- package/utils/breadcrumb.test.ts +108 -0
- package/utils/breadcrumb.ts +82 -0
- package/utils/dashboard.test.ts +118 -0
- package/utils/dashboard.ts +53 -0
- package/utils/data.test.ts +95 -0
- package/utils/data.ts +40 -2
- package/utils/hid-discover.test.ts +102 -0
- package/utils/list-page.test.ts +60 -0
- package/utils/list-page.ts +38 -0
- package/utils/materialSymbols.ts +363 -0
- package/utils/nav-sections.test.ts +190 -0
- package/utils/nav-sections.ts +151 -0
- package/utils/nfc-patrol-report.test.ts +130 -0
- package/utils/nfc-patrol-report.ts +101 -0
- package/utils/role.test.ts +69 -0
- package/utils/role.ts +10 -0
- package/utils/route-name.test.ts +85 -0
- package/utils/route-name.ts +24 -0
- package/utils/status.test.ts +110 -0
- package/utils/status.ts +135 -0
- package/utils/table-card.test.ts +84 -0
- package/utils/theme-aa-ledger.ts +148 -0
- package/utils/theme.test.ts +485 -7
- package/utils/theme.ts +295 -110
- package/.changeset/README.md +0 -8
- package/.changeset/config.json +0 -11
- package/.editorconfig +0 -12
- package/.github/workflows/main.yml +0 -17
- package/.github/workflows/publish-staging.yml +0 -47
- package/.github/workflows/publish.yml +0 -39
- package/PUBLISHING.md +0 -269
- package/components/HidIdentityMapping.vue +0 -975
- package/components/ScheduleTaskAreaFormDialog.vue +0 -144
- package/components/ScheduleTaskAreaUpdateMoreAction.vue +0 -109
- package/components/ScheduleTastTicketMain.vue +0 -182
- package/components/TableWithButton.vue +0 -94
- package/test/visitor-socket.test.mjs +0 -36
package/composables/useWebUsb.ts
CHANGED
|
@@ -11,6 +11,10 @@ interface USBDevice {
|
|
|
11
11
|
close(): Promise<void>;
|
|
12
12
|
selectConfiguration(configurationValue: number): Promise<void>;
|
|
13
13
|
claimInterface(interfaceNumber: number): Promise<void>;
|
|
14
|
+
selectAlternateInterface(
|
|
15
|
+
interfaceNumber: number,
|
|
16
|
+
alternateSetting: number,
|
|
17
|
+
): Promise<void>;
|
|
14
18
|
releaseInterface(interfaceNumber: number): Promise<void>;
|
|
15
19
|
transferOut(
|
|
16
20
|
endpointNumber: number,
|
|
@@ -19,14 +23,27 @@ interface USBDevice {
|
|
|
19
23
|
configuration: {
|
|
20
24
|
interfaces: Array<{
|
|
21
25
|
interfaceNumber: number;
|
|
26
|
+
alternate: USBAlternateInterface;
|
|
22
27
|
alternates: Array<{
|
|
28
|
+
alternateSetting: number;
|
|
29
|
+
interfaceClass: number;
|
|
23
30
|
endpoints: Array<{
|
|
24
31
|
endpointNumber: number;
|
|
25
32
|
direction: "in" | "out";
|
|
26
33
|
}>;
|
|
27
34
|
}>;
|
|
28
35
|
}>;
|
|
29
|
-
};
|
|
36
|
+
} | null;
|
|
37
|
+
configurations: Array<{ configurationValue: number }>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface USBAlternateInterface {
|
|
41
|
+
alternateSetting: number;
|
|
42
|
+
interfaceClass: number;
|
|
43
|
+
endpoints: Array<{
|
|
44
|
+
endpointNumber: number;
|
|
45
|
+
direction: "in" | "out";
|
|
46
|
+
}>;
|
|
30
47
|
}
|
|
31
48
|
|
|
32
49
|
interface USBOutTransferResult {
|
|
@@ -39,13 +56,86 @@ declare global {
|
|
|
39
56
|
usb: {
|
|
40
57
|
getDevices(): Promise<USBDevice[]>;
|
|
41
58
|
requestDevice(options: {
|
|
42
|
-
filters: Array<{
|
|
59
|
+
filters: Array<{
|
|
60
|
+
vendorId?: number;
|
|
61
|
+
productId?: number;
|
|
62
|
+
classCode?: number;
|
|
63
|
+
}>;
|
|
43
64
|
}): Promise<USBDevice>;
|
|
44
65
|
};
|
|
45
66
|
}
|
|
46
67
|
}
|
|
47
68
|
|
|
48
69
|
export default function useWebUsb() {
|
|
70
|
+
const printerConnections = new WeakMap<
|
|
71
|
+
USBDevice,
|
|
72
|
+
{ interfaceNumber: number; endpointNumber: number }
|
|
73
|
+
>();
|
|
74
|
+
|
|
75
|
+
const protectedInterfaceClasses = new Set([0x01, 0x03, 0x08, 0x0b, 0x0e, 0x10, 0xe0]);
|
|
76
|
+
|
|
77
|
+
function getErrorMessage(error: unknown, fallback: string) {
|
|
78
|
+
return error instanceof Error && error.message ? error.message : fallback;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function findPrinterInterface(device: USBDevice) {
|
|
82
|
+
const candidates = (device.configuration?.interfaces || []).flatMap((usbInterface) =>
|
|
83
|
+
usbInterface.alternates.flatMap((alternate) => {
|
|
84
|
+
const outputEndpoint = alternate.endpoints.find((endpoint) => endpoint.direction === "out");
|
|
85
|
+
if (!outputEndpoint || protectedInterfaceClasses.has(alternate.interfaceClass)) return [];
|
|
86
|
+
|
|
87
|
+
return [{
|
|
88
|
+
interfaceNumber: usbInterface.interfaceNumber,
|
|
89
|
+
alternateSetting: alternate.alternateSetting,
|
|
90
|
+
activeAlternateSetting: usbInterface.alternate.alternateSetting,
|
|
91
|
+
interfaceClass: alternate.interfaceClass,
|
|
92
|
+
endpointNumber: outputEndpoint.endpointNumber,
|
|
93
|
+
}];
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
candidates.sort((left, right) => {
|
|
98
|
+
const priority = (interfaceClass: number) => interfaceClass === 0x07 ? 0 : interfaceClass === 0xff ? 1 : 2;
|
|
99
|
+
return priority(left.interfaceClass) - priority(right.interfaceClass);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return candidates[0] || null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function preparePrinterDevice(device: USBDevice) {
|
|
106
|
+
await device.open();
|
|
107
|
+
if (!device.configuration) {
|
|
108
|
+
await device.selectConfiguration(device.configurations[0]?.configurationValue || 1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const printerInterface = findPrinterInterface(device);
|
|
112
|
+
if (!printerInterface) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
"The selected USB device is not a compatible printer. Select the receipt printer instead of a Bluetooth, camera, keyboard, or other system device.",
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
await device.claimInterface(printerInterface.interfaceNumber);
|
|
119
|
+
if (printerInterface.alternateSetting !== printerInterface.activeAlternateSetting) {
|
|
120
|
+
await device.selectAlternateInterface(
|
|
121
|
+
printerInterface.interfaceNumber,
|
|
122
|
+
printerInterface.alternateSetting,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
printerConnections.set(device, {
|
|
127
|
+
interfaceNumber: printerInterface.interfaceNumber,
|
|
128
|
+
endpointNumber: printerInterface.endpointNumber,
|
|
129
|
+
});
|
|
130
|
+
return device;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function getPrinterConnection(device: USBDevice) {
|
|
134
|
+
const connection = printerConnections.get(device);
|
|
135
|
+
if (!connection) throw new Error("Printer interface is not connected");
|
|
136
|
+
return connection;
|
|
137
|
+
}
|
|
138
|
+
|
|
49
139
|
const isWebUsbSupported = computed(() => {
|
|
50
140
|
return typeof navigator !== "undefined" && "usb" in navigator;
|
|
51
141
|
});
|
|
@@ -75,7 +165,9 @@ export default function useWebUsb() {
|
|
|
75
165
|
throw new Error("Web USB is not supported in this browser");
|
|
76
166
|
}
|
|
77
167
|
try {
|
|
78
|
-
const device = await navigator.usb.requestDevice({
|
|
168
|
+
const device = await navigator.usb.requestDevice({
|
|
169
|
+
filters: [{ classCode: 0x07 }, { classCode: 0xff }],
|
|
170
|
+
});
|
|
79
171
|
return {
|
|
80
172
|
vendorId: device.vendorId,
|
|
81
173
|
productId: device.productId,
|
|
@@ -84,12 +176,12 @@ export default function useWebUsb() {
|
|
|
84
176
|
serialNumber: device.serialNumber || "Unknown",
|
|
85
177
|
deviceId: `${device.vendorId}:${device.productId}`,
|
|
86
178
|
};
|
|
87
|
-
} catch (error:
|
|
88
|
-
if (error.name === "NotFoundError") {
|
|
179
|
+
} catch (error: unknown) {
|
|
180
|
+
if (error instanceof DOMException && error.name === "NotFoundError") {
|
|
89
181
|
throw new Error("No device selected");
|
|
90
182
|
}
|
|
91
183
|
console.error("Error requesting USB device:", error);
|
|
92
|
-
throw new Error("Failed to access USB
|
|
184
|
+
throw new Error(getErrorMessage(error, "Failed to access USB printer"));
|
|
93
185
|
}
|
|
94
186
|
};
|
|
95
187
|
|
|
@@ -110,14 +202,17 @@ export default function useWebUsb() {
|
|
|
110
202
|
});
|
|
111
203
|
}
|
|
112
204
|
console.log("[WebUSB] Opening device:", device.vendorId, device.productId);
|
|
113
|
-
await device
|
|
114
|
-
await device.selectConfiguration(1);
|
|
115
|
-
await device.claimInterface(0);
|
|
205
|
+
await preparePrinterDevice(device);
|
|
116
206
|
console.log("[WebUSB] Device opened and interface claimed.");
|
|
117
207
|
return device;
|
|
118
|
-
} catch (error) {
|
|
208
|
+
} catch (error: unknown) {
|
|
119
209
|
console.error("Error connecting to USB device:", error);
|
|
120
|
-
|
|
210
|
+
try {
|
|
211
|
+
await device?.close();
|
|
212
|
+
} catch (closeError) {
|
|
213
|
+
console.error("Error closing rejected USB device:", closeError);
|
|
214
|
+
}
|
|
215
|
+
throw new Error(getErrorMessage(error, "Failed to connect to USB printer"));
|
|
121
216
|
}
|
|
122
217
|
};
|
|
123
218
|
|
|
@@ -173,15 +268,20 @@ export default function useWebUsb() {
|
|
|
173
268
|
? "Connection and print test successful"
|
|
174
269
|
: "Connection successful but print test failed",
|
|
175
270
|
};
|
|
176
|
-
} catch (error:
|
|
271
|
+
} catch (error: unknown) {
|
|
177
272
|
return {
|
|
178
273
|
success: false,
|
|
179
|
-
error: error
|
|
274
|
+
error: getErrorMessage(error, "Failed to connect to USB printer"),
|
|
180
275
|
message: "Connection failed",
|
|
181
276
|
};
|
|
182
277
|
} finally {
|
|
183
278
|
if (device) {
|
|
184
279
|
try {
|
|
280
|
+
const connection = printerConnections.get(device);
|
|
281
|
+
if (connection) {
|
|
282
|
+
await device.releaseInterface(connection.interfaceNumber);
|
|
283
|
+
printerConnections.delete(device);
|
|
284
|
+
}
|
|
185
285
|
await device.close();
|
|
186
286
|
} catch (closeError) {
|
|
187
287
|
console.error("Error closing device:", closeError);
|
|
@@ -205,21 +305,16 @@ export default function useWebUsb() {
|
|
|
205
305
|
];
|
|
206
306
|
|
|
207
307
|
const testData = new Uint8Array(testCommands);
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
const outputEndpoint = alternate.endpoints.find(
|
|
211
|
-
(ep: any) => ep.direction === "out",
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
if (!outputEndpoint) {
|
|
215
|
-
throw new Error("No output endpoint found");
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
await device.transferOut(outputEndpoint.endpointNumber, testData);
|
|
308
|
+
const { endpointNumber } = getPrinterConnection(device);
|
|
309
|
+
await device.transferOut(endpointNumber, testData);
|
|
219
310
|
return { success: true, message: "Test print sent successfully" };
|
|
220
|
-
} catch (error:
|
|
311
|
+
} catch (error: unknown) {
|
|
221
312
|
console.error("Test print error:", error);
|
|
222
|
-
return {
|
|
313
|
+
return {
|
|
314
|
+
success: false,
|
|
315
|
+
error: getErrorMessage(error, "Test print failed"),
|
|
316
|
+
message: "Test print failed",
|
|
317
|
+
};
|
|
223
318
|
}
|
|
224
319
|
};
|
|
225
320
|
|
|
@@ -354,13 +449,8 @@ export default function useWebUsb() {
|
|
|
354
449
|
) => {
|
|
355
450
|
try {
|
|
356
451
|
console.log("[WebUSB] printQrCode start — doorLevel:", doorLevel, "liftLevel:", liftLevel, "company:", companyName, "address:", address);
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
const outputEndpoint = alternate.endpoints.find(
|
|
360
|
-
(ep: any) => ep.direction === "out",
|
|
361
|
-
);
|
|
362
|
-
if (!outputEndpoint) throw new Error("No output endpoint found");
|
|
363
|
-
console.log("[WebUSB] Using endpoint:", outputEndpoint.endpointNumber);
|
|
452
|
+
const { endpointNumber } = getPrinterConnection(device);
|
|
453
|
+
console.log("[WebUSB] Using endpoint:", endpointNumber);
|
|
364
454
|
|
|
365
455
|
const canvas = await createReceiptLayout(
|
|
366
456
|
urlImage,
|
|
@@ -375,17 +465,17 @@ export default function useWebUsb() {
|
|
|
375
465
|
const rasterData = canvasToRaster(canvas);
|
|
376
466
|
console.log("[WebUSB] Raster data size:", rasterData.byteLength, "bytes");
|
|
377
467
|
|
|
378
|
-
await device.transferOut(
|
|
468
|
+
await device.transferOut(endpointNumber, rasterData.buffer);
|
|
379
469
|
console.log("[WebUSB] Raster data sent.");
|
|
380
470
|
|
|
381
471
|
const CUT = new Uint8Array([0x1d, 0x56, 0x41, 0x10]);
|
|
382
|
-
await device.transferOut(
|
|
472
|
+
await device.transferOut(endpointNumber, CUT.buffer);
|
|
383
473
|
console.log("[WebUSB] Cut command sent.");
|
|
384
474
|
|
|
385
475
|
return { success: true, message: "QR print sent successfully" };
|
|
386
|
-
} catch (error:
|
|
476
|
+
} catch (error: unknown) {
|
|
387
477
|
console.error("[WebUSB] printQrCode error:", error);
|
|
388
|
-
return { success: false, error: error
|
|
478
|
+
return { success: false, error: getErrorMessage(error, "Unable to print QR code") };
|
|
389
479
|
}
|
|
390
480
|
};
|
|
391
481
|
|
|
@@ -57,9 +57,23 @@ export default function useWorkOrder() {
|
|
|
57
57
|
|
|
58
58
|
const workOrder = useState("workOrder", () => new MWorkOrder());
|
|
59
59
|
const workOrders = useState<Array<TWorkOrder>>("workOrders", () => []);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
/*
|
|
61
|
+
* Namespaced, like every other composable in this folder (`facilityPage`,
|
|
62
|
+
* `commentPage`, `unit-billPage`). These three were registered on the bare
|
|
63
|
+
* keys "page" / "pages" / "pageRange", which `useState` treats as ONE
|
|
64
|
+
* app-global cell per key - so `useFeedback`, `useMember` and
|
|
65
|
+
* `useServiceProvider`, which register the same bare keys, were handed the
|
|
66
|
+
* very same refs. Two of those screens open at once and they share one page
|
|
67
|
+
* counter.
|
|
68
|
+
*
|
|
69
|
+
* Nothing depended on the collision: these three are never assigned anywhere
|
|
70
|
+
* in this composable, and `WorkOrder/ListView.vue` is the only file in the
|
|
71
|
+
* layer or in any of the 11 web apps that destructures them - every real
|
|
72
|
+
* work-order page keeps its own local `ref(1)`.
|
|
73
|
+
*/
|
|
74
|
+
const page = useState("workOrderPage", () => 1);
|
|
75
|
+
const pages = useState("workOrderPages", () => 0);
|
|
76
|
+
const pageRange = useState("workOrderPageRange", () => "-- - -- of --");
|
|
63
77
|
|
|
64
78
|
function setWorkOrder(data?: TWorkOrder) {
|
|
65
79
|
workOrder.value = data || new MWorkOrder();
|
package/error.vue
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
THE ERROR PAGE - what a user sees at their worst moment.
|
|
3
|
+
|
|
4
|
+
It was Vuetify's own chrome end to end: `text-h2` (a 60px Roboto display
|
|
5
|
+
size the design's scale does not contain), and a `variant="tonal"
|
|
6
|
+
rounded="xl" size="large"` button - a 24px-cornered 44px pill in the theme's
|
|
7
|
+
tinted wash, where every other action in the product is a 40px, 10px-cornered
|
|
8
|
+
`.screen-btn-primary`.
|
|
9
|
+
|
|
10
|
+
Nothing about the behaviour changes: same `v-app`/`v-main` wrapper (this page
|
|
11
|
+
renders OUTSIDE the app layout, so it has to bring its own), same message,
|
|
12
|
+
same route home.
|
|
13
|
+
-->
|
|
1
14
|
<script setup lang="ts">
|
|
2
15
|
import type { NuxtError } from "#app";
|
|
3
16
|
|
|
@@ -17,16 +30,14 @@ const props = defineProps({
|
|
|
17
30
|
>
|
|
18
31
|
<v-col cols="9">
|
|
19
32
|
<v-row>
|
|
20
|
-
<v-col cols="12" class="text-center
|
|
21
|
-
<
|
|
33
|
+
<v-col cols="12" class="text-center">
|
|
34
|
+
<p class="error-page__message">{{ props.error?.message }}</p>
|
|
22
35
|
</v-col>
|
|
23
36
|
<v-col cols="12">
|
|
24
37
|
<v-row no-gutters justify="center">
|
|
25
38
|
<v-btn
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class="text-none font-weight-bold"
|
|
29
|
-
variant="tonal"
|
|
39
|
+
class="screen-btn-primary"
|
|
40
|
+
variant="flat"
|
|
30
41
|
:to="{ name: 'index' }"
|
|
31
42
|
>
|
|
32
43
|
Go back
|
|
@@ -39,3 +50,16 @@ const props = defineProps({
|
|
|
39
50
|
</v-main>
|
|
40
51
|
</v-app>
|
|
41
52
|
</template>
|
|
53
|
+
|
|
54
|
+
<style scoped>
|
|
55
|
+
/* The design's page title, one step up: an error message is the only thing on
|
|
56
|
+
the screen, so it keeps display weight without borrowing Roboto's 60px. */
|
|
57
|
+
.error-page__message {
|
|
58
|
+
font-family: var(--font-sans);
|
|
59
|
+
font-size: calc(var(--fs-h1) * 1.6);
|
|
60
|
+
font-weight: var(--fw-h1);
|
|
61
|
+
letter-spacing: var(--ls-h1);
|
|
62
|
+
color: var(--text);
|
|
63
|
+
margin-bottom: 24px;
|
|
64
|
+
}
|
|
65
|
+
</style>
|
package/nuxt.config.ts
CHANGED
|
@@ -7,6 +7,42 @@ export default defineNuxtConfig({
|
|
|
7
7
|
transpile: ["vuetify"],
|
|
8
8
|
},
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* THE REDESIGN'S TYPEFACE - LOADED, DELIBERATELY NOT APPLIED YET.
|
|
12
|
+
*
|
|
13
|
+
* `--font-sans` in `assets/css/tokens.css` names Manrope, and a token that
|
|
14
|
+
* names a font nobody loaded is a trap: the next phase writes
|
|
15
|
+
* `font-family: var(--font-sans)`, silently falls back to Arial, and looks
|
|
16
|
+
* fine to whoever wrote it. So the stylesheet is requested here.
|
|
17
|
+
*
|
|
18
|
+
* Nothing sets `font-family` on the page in Phase 1. Switching eleven
|
|
19
|
+
* applications to a new typeface before one screen has been rebuilt would
|
|
20
|
+
* leave the whole product looking half-migrated; each phase adopts the token
|
|
21
|
+
* as it rebuilds its own surfaces. Browsers do not download a webfont no
|
|
22
|
+
* element uses, so until then this costs one small CSS request.
|
|
23
|
+
*
|
|
24
|
+
* The second family is the redesign's icon set. Its axis ranges are the
|
|
25
|
+
* handoff's, character for character (`opsz,wght,FILL,GRAD@20..48,300..700,
|
|
26
|
+
* 0..1,0`) - do not widen or narrow them without a design change to point
|
|
27
|
+
* at. `plugins/vuetify.ts` and `assets/css/primitives.css` do the applying.
|
|
28
|
+
*/
|
|
29
|
+
app: {
|
|
30
|
+
head: {
|
|
31
|
+
link: [
|
|
32
|
+
{ rel: "preconnect", href: "https://fonts.googleapis.com" },
|
|
33
|
+
{
|
|
34
|
+
rel: "preconnect",
|
|
35
|
+
href: "https://fonts.gstatic.com",
|
|
36
|
+
crossorigin: "",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
rel: "stylesheet",
|
|
40
|
+
href: "https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,300..700,0..1,0&display=swap",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
|
|
10
46
|
runtimeConfig: {
|
|
11
47
|
public: {
|
|
12
48
|
cookieConfig: {
|
package/package.json
CHANGED
|
@@ -2,9 +2,30 @@
|
|
|
2
2
|
"name": "@7365admin1/layer-common",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "3.2.
|
|
5
|
+
"version": "3.2.8-staging.201",
|
|
6
6
|
"author": "7365admin1",
|
|
7
7
|
"main": "./nuxt.config.ts",
|
|
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.",
|
|
9
|
+
"files": [
|
|
10
|
+
"CHANGELOG.md",
|
|
11
|
+
"app.vue",
|
|
12
|
+
"error.vue",
|
|
13
|
+
"nuxt.config.ts",
|
|
14
|
+
"tsconfig.json",
|
|
15
|
+
".nuxtrc",
|
|
16
|
+
"assets",
|
|
17
|
+
"components",
|
|
18
|
+
"composables",
|
|
19
|
+
"constants",
|
|
20
|
+
"layouts",
|
|
21
|
+
"middleware",
|
|
22
|
+
"pages",
|
|
23
|
+
"plugins",
|
|
24
|
+
"public",
|
|
25
|
+
"services",
|
|
26
|
+
"types",
|
|
27
|
+
"utils"
|
|
28
|
+
],
|
|
8
29
|
"publishConfig": {
|
|
9
30
|
"access": "public"
|
|
10
31
|
},
|
|
@@ -14,9 +35,8 @@
|
|
|
14
35
|
"build": "nuxt build .playground",
|
|
15
36
|
"generate": "nuxt generate .playground",
|
|
16
37
|
"preview": "nuxt preview .playground",
|
|
17
|
-
"test": "esbuild composables/useVisitorSocket.ts --format=esm --outfile=test/.build/useVisitorSocket.mjs --log-level=error && node --test \"test/*.test.mjs\"",
|
|
18
|
-
"test:
|
|
19
|
-
"test:camera-wall": "node --test \"utils/camera-wall.test.ts\"",
|
|
38
|
+
"test": "esbuild composables/useVisitorSocket.ts --format=esm --outfile=test/.build/useVisitorSocket.mjs --log-level=error && node --test \"test/*.test.mjs\" && yarn test:units",
|
|
39
|
+
"test:units": "node --experimental-strip-types --test \"utils/*.test.ts\"",
|
|
20
40
|
"release": "yarn run build && changeset publish"
|
|
21
41
|
},
|
|
22
42
|
"devDependencies": {
|
|
@@ -43,6 +63,7 @@
|
|
|
43
63
|
"qrcode": "^1.5.4",
|
|
44
64
|
"qrcode.vue": "^3.4.1",
|
|
45
65
|
"sass": "^1.80.6",
|
|
66
|
+
"sip.js": "0.21.2",
|
|
46
67
|
"socket.io-client": "^4.8.3",
|
|
47
68
|
"vue-draggable-next": "^2.3.0",
|
|
48
69
|
"vue3-signature": "^0.2.4",
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
<HidEnabledGate
|
|
4
4
|
:site="siteId"
|
|
5
5
|
:org="orgId"
|
|
6
|
-
message="Enable HID as a service for this site before managing HID
|
|
6
|
+
message="Enable HID as a service for this site before managing HID users."
|
|
7
7
|
>
|
|
8
|
-
<HidUserEnrollment :site="siteId"
|
|
8
|
+
<HidUserEnrollment :site="siteId" />
|
|
9
9
|
</HidEnabledGate>
|
|
10
10
|
</v-container>
|
|
11
11
|
</template>
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
<HidEnabledGate
|
|
4
4
|
:site="siteId"
|
|
5
5
|
:org="orgId"
|
|
6
|
-
message="Enable HID as a service for this site before
|
|
6
|
+
message="Enable HID as a service for this site before managing physical HID cards."
|
|
7
7
|
>
|
|
8
|
-
<
|
|
8
|
+
<HidUserEnrollment :site="siteId" card-management />
|
|
9
9
|
</HidEnabledGate>
|
|
10
10
|
</v-container>
|
|
11
11
|
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<NotificationSettings />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
/**
|
|
7
|
+
* Notification settings, reachable from Profile.
|
|
8
|
+
*
|
|
9
|
+
* The page lives in the shared layer so every web app has the route and there
|
|
10
|
+
* is only one screen to keep correct; the link to it sits in Profile, in the
|
|
11
|
+
* account app, which is where "Manage Account" already sends everybody.
|
|
12
|
+
*/
|
|
13
|
+
definePageMeta({
|
|
14
|
+
middleware: ["01-auth"],
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const { authenticate } = useLocalAuth();
|
|
18
|
+
authenticate();
|
|
19
|
+
</script>
|
|
@@ -1,20 +1,44 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
The "payment method linked" confirmation the payment window returns to.
|
|
3
|
+
|
|
4
|
+
THE DEFECT IS THE CLOSE BUTTON, NOT THE CARD. Measured before assuming:
|
|
5
|
+
`color="success lighten-1"` is Vuetify **2** syntax, but Vuetify 3 takes the
|
|
6
|
+
first token and applies it, so the card IS green - `rgb(15,138,98)` light,
|
|
7
|
+
`rgb(65,199,149)` dark. Only `lighten-1` is dead, and it changes nothing.
|
|
8
|
+
The green card stays: a success page is green on purpose, and swapping it for
|
|
9
|
+
a neutral surface would be a design decision, not a conversion.
|
|
10
|
+
|
|
11
|
+
What was wrong is `color="white" variant="tonal"` on Close. `tonal` paints
|
|
12
|
+
its tint on an OVERLAY, so the button's own background measured
|
|
13
|
+
`rgba(0,0,0,0)` and all that reached the glass was white text - on the green
|
|
14
|
+
card. Light: white on `rgb(15,138,98)` = 4.35:1. **Dark: white on
|
|
15
|
+
`rgb(65,199,149)` = 2.12:1**, which is the worst reading on this page and the
|
|
16
|
+
reason it needed touching at all. `.screen-btn-ghost` is the design's
|
|
17
|
+
secondary button - the card surface with `--text2` ink and a `--border` - so
|
|
18
|
+
it reads on green in both themes, and it is the 40px/10px button the rest of
|
|
19
|
+
the product uses rather than a 44px 24px-cornered pill.
|
|
20
|
+
|
|
21
|
+
The title had the same defect in the other direction: a literal `text-white`
|
|
22
|
+
on a fill that changes with the theme. See the comment on it below.
|
|
23
|
+
|
|
24
|
+
Behaviour is unchanged: same `window.close()`.
|
|
25
|
+
-->
|
|
1
26
|
<template>
|
|
2
27
|
<v-row no-gutters class="fill-height" justify="center" align-content="center">
|
|
3
|
-
<v-card class="pa-4" elevation="4" color="success
|
|
4
|
-
|
|
28
|
+
<v-card class="pa-4" elevation="4" color="success">
|
|
29
|
+
<!-- `text-white` was a literal on a THEMED fill, and the fill changes with
|
|
30
|
+
the theme: white on dark's `rgb(65,199,149)` measures **2.14:1**.
|
|
31
|
+
Dropping it lets the card use its own paired ink - Vuetify sets
|
|
32
|
+
`on-success` from `color="success"` - which follows the fill. -->
|
|
33
|
+
<v-card-title class="d-flex align-center">
|
|
5
34
|
<v-icon class="mr-2">mdi-check-circle</v-icon>
|
|
6
35
|
Successfully Linked Payment Method
|
|
7
36
|
</v-card-title>
|
|
8
37
|
|
|
9
38
|
<v-card-actions class="d-flex justify-center">
|
|
10
|
-
<v-btn
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
rounded="xl"
|
|
14
|
-
size="large"
|
|
15
|
-
@click="closeWindow"
|
|
16
|
-
>Close</v-btn
|
|
17
|
-
>
|
|
39
|
+
<v-btn class="screen-btn-ghost" variant="flat" @click="closeWindow">
|
|
40
|
+
Close
|
|
41
|
+
</v-btn>
|
|
18
42
|
</v-card-actions>
|
|
19
43
|
</v-card>
|
|
20
44
|
</v-row>
|
|
@@ -1,23 +1,32 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
The "you must have a customer" gate.
|
|
3
|
+
|
|
4
|
+
Was Vuetify's chrome: `text-subtitle-1` for the sentence, `text-subtitle-2`
|
|
5
|
+
on the buttons, and `variant="tonal" rounded="xl"` - a 24px-cornered pill in
|
|
6
|
+
the theme's tinted wash, where the product's action is a 40px, 10px-cornered
|
|
7
|
+
button. The sentence is now `--text2` (9.62:1 on a card) at the design's
|
|
8
|
+
reading size, and the actions are the design's primary and ghost buttons.
|
|
9
|
+
|
|
10
|
+
No behaviour change: same handlers, same destinations, same `plain` layout.
|
|
11
|
+
-->
|
|
1
12
|
<template>
|
|
2
13
|
<v-row no-gutters class="fill-height" align="center" justify="center">
|
|
3
|
-
<v-col cols="12" lg="6" md="6" sm="6" class="text-center
|
|
14
|
+
<v-col cols="12" lg="6" md="6" sm="6" class="text-center gate-page__message">
|
|
4
15
|
You must have a customer to view this page. Please join or contact your
|
|
5
16
|
administrator for access.
|
|
6
17
|
<v-row no-gutters justify="center" align="center" class="mt-2">
|
|
7
18
|
<v-btn
|
|
19
|
+
class="screen-btn-ghost"
|
|
20
|
+
variant="flat"
|
|
8
21
|
@click="goToAccount"
|
|
9
|
-
rounded="xl"
|
|
10
|
-
variant="tonal"
|
|
11
|
-
class="text-none text-subtitle-2"
|
|
12
22
|
>
|
|
13
23
|
Go to Organization Page
|
|
14
24
|
</v-btn>
|
|
15
|
-
<span class="mx-2">or</span>
|
|
25
|
+
<span class="mx-2 gate-page__or">or</span>
|
|
16
26
|
<v-btn
|
|
27
|
+
class="screen-btn-primary"
|
|
28
|
+
variant="flat"
|
|
17
29
|
@click="createCustomer"
|
|
18
|
-
rounded="xl"
|
|
19
|
-
variant="tonal"
|
|
20
|
-
class="text-none text-subtitle-2"
|
|
21
30
|
>
|
|
22
31
|
Create an Customer
|
|
23
32
|
</v-btn>
|
|
@@ -27,6 +36,10 @@
|
|
|
27
36
|
</template>
|
|
28
37
|
|
|
29
38
|
<script setup lang="ts">
|
|
39
|
+
// Relative import, never Nuxt auto-import: this layer's `utils/` are auto-imported
|
|
40
|
+
// into the consuming apps but not reliably into the layer's own components.
|
|
41
|
+
import { pickRouteName } from "../utils/route-name";
|
|
42
|
+
|
|
30
43
|
definePageMeta({
|
|
31
44
|
layout: "plain",
|
|
32
45
|
});
|
|
@@ -44,9 +57,19 @@ authenticate();
|
|
|
44
57
|
const { currentUser } = useLocalAuth();
|
|
45
58
|
|
|
46
59
|
function createCustomer() {
|
|
47
|
-
|
|
60
|
+
// The org app's page is `pages/org/[organization]/customers/add.vue`, whose
|
|
61
|
+
// route name is `org-organization-customers-add` - singular. The plural spelt
|
|
62
|
+
// here matched nothing, so the only way off this dead-end page threw. The
|
|
63
|
+
// `hasRoute` check also keeps a mis-set APP_NAME from taking this branch in an
|
|
64
|
+
// app that has no such page; that app falls through to the cross-app URL.
|
|
65
|
+
const customerAdd = pickRouteName(
|
|
66
|
+
useRouter(),
|
|
67
|
+
"org-organization-customers-add",
|
|
68
|
+
"organizations-customers-add"
|
|
69
|
+
);
|
|
70
|
+
if (APP_NAME.toLowerCase() === "org" && customerAdd) {
|
|
48
71
|
navigateTo({
|
|
49
|
-
name:
|
|
72
|
+
name: customerAdd,
|
|
50
73
|
params: { organization: currentUser.value?.defaultOrg },
|
|
51
74
|
});
|
|
52
75
|
} else {
|
|
@@ -54,3 +77,18 @@ function createCustomer() {
|
|
|
54
77
|
}
|
|
55
78
|
}
|
|
56
79
|
</script>
|
|
80
|
+
|
|
81
|
+
<style scoped>
|
|
82
|
+
.gate-page__message {
|
|
83
|
+
font-family: var(--font-sans);
|
|
84
|
+
font-size: var(--fs-cell);
|
|
85
|
+
color: var(--text2);
|
|
86
|
+
line-height: 1.6;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.gate-page__or {
|
|
90
|
+
font-family: var(--font-sans);
|
|
91
|
+
font-size: var(--fs-cell);
|
|
92
|
+
color: var(--muted);
|
|
93
|
+
}
|
|
94
|
+
</style>
|