@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.
Files changed (283) hide show
  1. package/assets/css/primitives.css +1113 -0
  2. package/assets/css/screens.css +1097 -0
  3. package/assets/css/shell.css +258 -0
  4. package/assets/css/tokens.css +236 -0
  5. package/components/AcceptDialog.vue +17 -18
  6. package/components/AccessCard/AvailableStats.vue +24 -16
  7. package/components/AccessCardAddForm.vue +42 -42
  8. package/components/AccessCardAssignToUnitForm.vue +13 -21
  9. package/components/AccessCardDeleteDialog.vue +41 -39
  10. package/components/AccessCardDetailsDialog.vue +125 -79
  11. package/components/AccessCardPreviewDialog.vue +58 -84
  12. package/components/AccessCardQrTagging.vue +60 -63
  13. package/components/AccessCardReplaceForm.vue +27 -23
  14. package/components/AccessManagement.vue +86 -85
  15. package/components/AddEqupmentForm.vue +18 -22
  16. package/components/AddPassKeyToVisitor.vue +95 -73
  17. package/components/AppButton.vue +62 -0
  18. package/components/AppCard.vue +20 -0
  19. package/components/AppDateField.vue +49 -0
  20. package/components/AppField.vue +76 -0
  21. package/components/AppKpiTile.vue +65 -0
  22. package/components/AppSegmented.vue +38 -0
  23. package/components/AppSelect.vue +247 -0
  24. package/components/AreaChecklistHistoryLogs.vue +51 -44
  25. package/components/AreaChecklistHistoryMain.vue +14 -7
  26. package/components/AreaFormDialog.vue +71 -59
  27. package/components/AreaMain.vue +73 -107
  28. package/components/AttachmentsViewer.vue +84 -22
  29. package/components/AttendanceCheckInOutDialog.vue +85 -88
  30. package/components/AttendanceDetailsDialog.vue +57 -63
  31. package/components/AttendanceMain.vue +4 -3
  32. package/components/AttendanceMapSearchDialog.vue +42 -44
  33. package/components/AttendanceSettingsDialog.vue +79 -63
  34. package/components/Avatar/Main.vue +54 -2
  35. package/components/BillingMain.vue +26 -30
  36. package/components/BtnUploadFile.vue +53 -11
  37. package/components/BuildingForm.vue +34 -27
  38. package/components/BuildingManagement/buildings.vue +39 -39
  39. package/components/BuildingManagement/units.vue +26 -42
  40. package/components/BuildingUnitFormAdd.vue +20 -23
  41. package/components/BuildingUnitFormEdit.vue +8 -4
  42. package/components/BulletinBoardForm.vue +19 -24
  43. package/components/BulletinBoardManagement.vue +49 -57
  44. package/components/BulletinBoardView.vue +9 -6
  45. package/components/BulletinExpirationChip.vue +13 -4
  46. package/components/Button/Close.vue +2 -2
  47. package/components/CameraForm.vue +21 -22
  48. package/components/CameraMain.vue +50 -49
  49. package/components/Card/DeleteConfirmation.vue +24 -22
  50. package/components/Card/MemberInfoSummary.vue +28 -3
  51. package/components/Card/Toggle.vue +15 -3
  52. package/components/CardToolbar.vue +37 -0
  53. package/components/Carousel.vue +56 -36
  54. package/components/Chat/Bubbles.vue +43 -14
  55. package/components/Chat/Information.vue +49 -32
  56. package/components/Chat/ListCard.vue +38 -9
  57. package/components/Chat/Message.vue +41 -15
  58. package/components/Chat/Navigation.vue +27 -15
  59. package/components/Chat/SkeletonLoader.vue +9 -2
  60. package/components/ChecklistItemRow.vue +25 -1
  61. package/components/CleaningScheduleMain.vue +44 -49
  62. package/components/ClientDetailForm.vue +81 -56
  63. package/components/ClientMain.vue +92 -82
  64. package/components/ConfirmDialog.vue +14 -8
  65. package/components/Container/Standard.vue +9 -7
  66. package/components/CreateSubsciptionPlan.vue +148 -89
  67. package/components/DashboardEmptyState.vue +48 -3
  68. package/components/DashboardMain.vue +924 -676
  69. package/components/DashboardPanel.vue +84 -21
  70. package/components/DeliveryCompany.vue +34 -20
  71. package/components/Dialog/DeleteConfirmation.vue +20 -21
  72. package/components/Dialog/ReplaceAutofillPrompt.vue +19 -26
  73. package/components/Dialog/ReusablePrompt.vue +30 -29
  74. package/components/Dialog/UpdateMoreAction.vue +62 -59
  75. package/components/DocumentForm.vue +14 -21
  76. package/components/DocumentManagement.vue +162 -108
  77. package/components/DocumentViewer.vue +29 -12
  78. package/components/DrawImage.vue +29 -21
  79. package/components/EntryPass/QrTemplatePreview.vue +27 -16
  80. package/components/EntryPassInformation.vue +40 -10
  81. package/components/EntryPassMain.vue +49 -27
  82. package/components/EquipmentItemMain.vue +84 -97
  83. package/components/EquipmentManagementMain.vue +81 -103
  84. package/components/Facility/BookingSetup.vue +43 -52
  85. package/components/Feedback/Form.vue +26 -12
  86. package/components/FeedbackMain.vue +35 -49
  87. package/components/FileInput.vue +75 -58
  88. package/components/FileInputWithList.vue +59 -12
  89. package/components/FormDialog.vue +60 -21
  90. package/components/HidAccessLogDashboard.vue +512 -490
  91. package/components/HidCameraCaptureDialog.vue +216 -0
  92. package/components/HidEnabledGate.vue +67 -9
  93. package/components/HidIntercomManagement.vue +1322 -653
  94. package/components/HidQrCodeConfiguration.vue +1030 -258
  95. package/components/HidReaderForm.vue +142 -59
  96. package/components/HidReaderManagement.vue +211 -323
  97. package/components/HidServiceSettingsPanel.vue +44 -13
  98. package/components/HidUserEnrollment.vue +1185 -412
  99. package/components/HygieneUpdateMoreAction.vue +44 -44
  100. package/components/IncidentReport/Authorities.vue +30 -18
  101. package/components/IncidentReport/IncidentInformation.vue +10 -10
  102. package/components/IncidentReport/IncidentInformationDownload.vue +3 -4
  103. package/components/IncidentReport/affectedEntities.vue +41 -20
  104. package/components/Input/Date.vue +3 -0
  105. package/components/Input/DatePicker.vue +1 -2
  106. package/components/Input/DateTimePicker.vue +1 -1
  107. package/components/Input/File.vue +55 -10
  108. package/components/Input/FileV2.vue +39 -6
  109. package/components/Input/InputPhoneNumberV2.vue +2 -1
  110. package/components/Input/ListGroupSelection.vue +56 -8
  111. package/components/Input/NRICNumber.vue +1 -0
  112. package/components/Input/NewDate.vue +1 -0
  113. package/components/Input/Number.vue +1 -0
  114. package/components/Input/Password.vue +1 -0
  115. package/components/Input/PhoneNumber.vue +21 -1
  116. package/components/Input/VehicleNumber.vue +1 -0
  117. package/components/InputLabel.vue +2 -2
  118. package/components/InvitationClientForm.vue +21 -19
  119. package/components/InvitationForm.vue +15 -21
  120. package/components/InvitationMain.vue +46 -61
  121. package/components/Layout/Header.vue +263 -36
  122. package/components/Layout/NavigationDrawer.vue +300 -30
  123. package/components/ListItem.vue +33 -3
  124. package/components/LocalPagination.vue +27 -3
  125. package/components/ManageChecklistMain.vue +136 -86
  126. package/components/MemberInformation.vue +32 -16
  127. package/components/MemberMain.vue +113 -93
  128. package/components/MyAttendanceMain.vue +3 -5
  129. package/components/NavigationItem.vue +83 -3
  130. package/components/Nfc/NFCPatrolReportMain.vue +47 -33
  131. package/components/Nfc/NFCPatrolRouteForm.vue +18 -20
  132. package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
  133. package/components/Nfc/NFCPatrolSettings.vue +14 -4
  134. package/components/Nfc/NFCTagForm.vue +22 -8
  135. package/components/Nfc/NFCTagMain.vue +41 -43
  136. package/components/Nfc/PatrolReport/DailyReportTab.vue +8 -1
  137. package/components/Nfc/PatrolReport/MonthlyReportTab.vue +8 -1
  138. package/components/Nfc/PatrolReport/MonthlyReportTable.vue +5 -3
  139. package/components/Nfc/PatrolReport/PatrolActivityTable.vue +26 -6
  140. package/components/Nfc/PatrolReport/PatrolReportTab.vue +8 -0
  141. package/components/Nfc/PatrolReport/PatrolRouteSummary.vue +41 -9
  142. package/components/Nfc/PatrolReport/RemarksDialog.vue +39 -9
  143. package/components/Nfc/PatrolReport/ReportEmptyState.vue +29 -9
  144. package/components/Nfc/PatrolReport/ReportFilters.vue +19 -10
  145. package/components/Nfc/PatrolReport/ReportLocationHeader.vue +32 -6
  146. package/components/Nfc/PatrolReport/SummaryReportTable.vue +28 -6
  147. package/components/NotificationSettings.vue +373 -0
  148. package/components/NumberSettingField.vue +9 -2
  149. package/components/OnlineFormConfigurationForm.vue +725 -237
  150. package/components/OnlineFormFill.vue +213 -106
  151. package/components/OnlineFormRenderPreview.vue +25 -30
  152. package/components/OnlineFormsConfiguration.vue +113 -109
  153. package/components/OrgViewDialog.vue +34 -16
  154. package/components/OvernightParkingAvailability.vue +49 -26
  155. package/components/OvernightParkingManagement.vue +78 -18
  156. package/components/PageHeader.vue +43 -0
  157. package/components/PassInformation.vue +109 -53
  158. package/components/PasswordConfirmation.vue +44 -26
  159. package/components/PeopleForm.vue +41 -20
  160. package/components/PhotoUpload.vue +110 -48
  161. package/components/PlaceholderComponent.vue +35 -19
  162. package/components/PlanActionsMenu.vue +12 -2
  163. package/components/PlanStatusBadge.vue +7 -8
  164. package/components/PlateNumberDisplay.vue +9 -6
  165. package/components/QrTemplate/PrintDialog.vue +66 -43
  166. package/components/RolePermissionFormCreate.vue +104 -79
  167. package/components/RolePermissionFormPreviewUpdate.vue +111 -109
  168. package/components/RolePermissionMain.vue +183 -81
  169. package/components/ScanVisitorQRCode.vue +99 -35
  170. package/components/ScheduleAreaMain.vue +34 -59
  171. package/components/ScheduleTaskForm.vue +61 -42
  172. package/components/ScheduleTaskMain.vue +56 -69
  173. package/components/SearchVehicleNumberUser.vue +39 -37
  174. package/components/ServiceProviderFormCreate.vue +29 -20
  175. package/components/ServiceProviderInvitationPrompt.vue +155 -0
  176. package/components/ServiceProviderMain.vue +601 -432
  177. package/components/Signature.vue +40 -34
  178. package/components/SignaturePad.vue +5 -29
  179. package/components/SiteSettings.vue +41 -11
  180. package/components/SiteSettingsWorkOrder.vue +61 -30
  181. package/components/SlideCardGroup.vue +35 -11
  182. package/components/Snackbar.vue +36 -3
  183. package/components/SpecificAttr.vue +18 -8
  184. package/components/StatusChip.vue +156 -0
  185. package/components/StockCard.vue +17 -12
  186. package/components/SubscriptionPlanTable.vue +207 -140
  187. package/components/SwitchContext.vue +201 -9
  188. package/components/TableHygiene.vue +36 -46
  189. package/components/TableList.vue +10 -10
  190. package/components/TableListSecondary.vue +105 -54
  191. package/components/TableMain.vue +251 -78
  192. package/components/TableV3.vue +61 -70
  193. package/components/TiptapEditor.vue +33 -40
  194. package/components/TiptapEditorQr.vue +5 -2
  195. package/components/Tooltip/Info.vue +69 -24
  196. package/components/UnitMain.vue +40 -60
  197. package/components/UnitPersonCard.vue +42 -13
  198. package/components/VehicleAddSelection.vue +48 -32
  199. package/components/VehicleForm.vue +112 -129
  200. package/components/VehicleManagement.vue +26 -12
  201. package/components/VehicleUpdateMoreAction.vue +59 -46
  202. package/components/VisitorDataFromScannedQRCodeForm.vue +90 -65
  203. package/components/VisitorForm.vue +774 -210
  204. package/components/VisitorFormSelection.vue +40 -37
  205. package/components/VisitorManagement.vue +239 -82
  206. package/components/VisitorPassKeyQRScanner.vue +65 -18
  207. package/components/VisitorSearchResultSummary.vue +60 -16
  208. package/components/VisitorSocketPopUp.vue +22 -14
  209. package/components/VisitorsReportPreview.vue +44 -29
  210. package/components/WorkOrder/Create.vue +26 -17
  211. package/components/WorkOrder/ListView.vue +38 -7
  212. package/components/WorkOrder/Main.vue +62 -59
  213. package/composables/useFeedback.ts +3 -3
  214. package/composables/useHidAmico.ts +269 -0
  215. package/composables/useHidNavigation.ts +2 -9
  216. package/composables/useMember.ts +3 -3
  217. package/composables/useNFCPatrolDailyReport.ts +13 -0
  218. package/composables/useNFCPatrolMonthlyReport.ts +22 -56
  219. package/composables/useNFCPatrolReport.ts +13 -0
  220. package/composables/useNotificationPreference.ts +87 -0
  221. package/composables/useOnlineFormPages.ts +2 -0
  222. package/composables/useRole.ts +22 -0
  223. package/composables/useServiceProvider.ts +3 -3
  224. package/composables/useServiceProviderInvitation.ts +145 -0
  225. package/composables/useSipWebPhone.ts +336 -0
  226. package/composables/useSubscriptionPlan.ts +62 -0
  227. package/composables/useThemePreference.ts +63 -0
  228. package/composables/useVisitor.ts +6 -1
  229. package/composables/useWebUsb.ts +127 -37
  230. package/composables/useWorkOrder.ts +17 -3
  231. package/error.vue +30 -6
  232. package/nuxt.config.ts +36 -0
  233. package/package.json +25 -4
  234. package/pages/[org]/[site]/access-mgmt/administrator/index.vue +2 -2
  235. package/pages/[org]/[site]/access-mgmt/{identity-mapping → hid-cards}/index.vue +2 -2
  236. package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
  237. package/pages/notification-settings.vue +19 -0
  238. package/pages/payment-method-linked.vue +34 -10
  239. package/pages/require-customer.vue +48 -10
  240. package/pages/require-organization-membership.vue +32 -8
  241. package/pages/unauthorized.vue +29 -4
  242. package/plugins/secure-member.client.ts +21 -56
  243. package/plugins/vuetify.ts +69 -2
  244. package/types/nfc-patrol-report.ts +3 -2
  245. package/types/site.d.ts +71 -0
  246. package/types/subscription.ts +18 -11
  247. package/utils/breadcrumb.test.ts +108 -0
  248. package/utils/breadcrumb.ts +82 -0
  249. package/utils/dashboard.test.ts +118 -0
  250. package/utils/dashboard.ts +53 -0
  251. package/utils/data.test.ts +95 -0
  252. package/utils/data.ts +40 -2
  253. package/utils/hid-discover.test.ts +102 -0
  254. package/utils/list-page.test.ts +60 -0
  255. package/utils/list-page.ts +38 -0
  256. package/utils/materialSymbols.ts +363 -0
  257. package/utils/nav-sections.test.ts +190 -0
  258. package/utils/nav-sections.ts +151 -0
  259. package/utils/nfc-patrol-report.test.ts +130 -0
  260. package/utils/nfc-patrol-report.ts +101 -0
  261. package/utils/role.test.ts +69 -0
  262. package/utils/role.ts +10 -0
  263. package/utils/route-name.test.ts +85 -0
  264. package/utils/route-name.ts +24 -0
  265. package/utils/status.test.ts +110 -0
  266. package/utils/status.ts +135 -0
  267. package/utils/table-card.test.ts +84 -0
  268. package/utils/theme-aa-ledger.ts +148 -0
  269. package/utils/theme.test.ts +485 -7
  270. package/utils/theme.ts +295 -110
  271. package/.changeset/README.md +0 -8
  272. package/.changeset/config.json +0 -11
  273. package/.editorconfig +0 -12
  274. package/.github/workflows/main.yml +0 -17
  275. package/.github/workflows/publish-staging.yml +0 -47
  276. package/.github/workflows/publish.yml +0 -39
  277. package/PUBLISHING.md +0 -269
  278. package/components/HidIdentityMapping.vue +0 -975
  279. package/components/ScheduleTaskAreaFormDialog.vue +0 -144
  280. package/components/ScheduleTaskAreaUpdateMoreAction.vue +0 -109
  281. package/components/ScheduleTastTicketMain.vue +0 -182
  282. package/components/TableWithButton.vue +0 -94
  283. package/test/visitor-socket.test.mjs +0 -36
@@ -1,12 +1,12 @@
1
1
  <template>
2
2
  <v-dialog v-model="dialogModel" max-width="430" persistent>
3
- <v-card class="hid-reader-form" rounded="lg">
3
+ <v-card class="hid-reader-form screen-modal">
4
4
  <v-card-title class="form-title">
5
5
  {{ mode === "edit" ? "Edit Reader" : "Add Reader" }}
6
6
  </v-card-title>
7
7
 
8
- <v-card-text class="form-body">
9
- <div class="field-label">HID Reader Name <span>*</span></div>
8
+ <v-card-text class="form-body screen-modal__body">
9
+ <InputLabel title="HID Reader Name" required class="mb-1" />
10
10
  <v-text-field
11
11
  v-model="draft.name"
12
12
  aria-label="HID Reader Name"
@@ -17,7 +17,7 @@
17
17
  class="mb-3"
18
18
  />
19
19
 
20
- <div class="field-label">Base URL / IP address <span>*</span></div>
20
+ <InputLabel title="Base URL / IP address" required class="mb-1" />
21
21
  <v-text-field
22
22
  v-model="draft.baseUrl"
23
23
  aria-label="Base URL / IP address"
@@ -28,18 +28,7 @@
28
28
  class="mb-3"
29
29
  />
30
30
 
31
- <div class="field-label">Device ID <span>*</span></div>
32
- <v-text-field
33
- v-model="draft.deviceId"
34
- aria-label="Device ID"
35
- placeholder="Enter device id"
36
- variant="outlined"
37
- density="compact"
38
- hide-details="auto"
39
- class="mb-3"
40
- />
41
-
42
- <div class="field-label">Location <span>*</span></div>
31
+ <InputLabel title="Location" required class="mb-1" />
43
32
  <v-text-field
44
33
  v-model="draft.location"
45
34
  aria-label="Location"
@@ -50,13 +39,13 @@
50
39
  class="mb-3"
51
40
  />
52
41
 
53
- <div class="field-label">Monitor Path <span>*</span></div>
42
+ <InputLabel title="Monitor Path" required class="mb-1" />
54
43
  <v-btn-toggle v-model="monitorEnabled" mandatory divided class="monitor-toggle mb-3">
55
44
  <v-btn :value="true" class="text-none">Enable</v-btn>
56
45
  <v-btn :value="false" class="text-none">Disable</v-btn>
57
46
  </v-btn-toggle>
58
47
 
59
- <div class="field-label">Username <span>*</span></div>
48
+ <InputLabel title="Username" required class="mb-1" />
60
49
  <v-text-field
61
50
  v-model="draft.username"
62
51
  aria-label="Username"
@@ -67,7 +56,7 @@
67
56
  class="mb-3"
68
57
  />
69
58
 
70
- <div class="field-label">Confirm Password <span>*</span></div>
59
+ <InputLabel title="Confirm Password" required class="mb-1" />
71
60
  <v-text-field
72
61
  v-model="draft.password"
73
62
  :type="showPassword ? 'text' : 'password'"
@@ -79,22 +68,64 @@
79
68
  :append-inner-icon="showPassword ? 'mdi-eye-off' : 'mdi-eye'"
80
69
  @click:append-inner="showPassword = !showPassword"
81
70
  />
71
+
72
+ <v-btn
73
+ v-if="mode === 'add'"
74
+ block
75
+ variant="tonal"
76
+ color="primary"
77
+ prepend-icon="mdi-lan-connect"
78
+ class="text-none mt-3"
79
+ :loading="discovering"
80
+ :disabled="!canDiscover"
81
+ @click="connectReader"
82
+ >
83
+ Connect &amp; discover reader
84
+ </v-btn>
85
+ <v-alert v-if="discoveryError" type="error" density="compact" variant="tonal" class="mt-3">
86
+ {{ discoveryError }}
87
+ </v-alert>
88
+
89
+ <InputLabel title="Device ID" required class="mt-3 mb-1" />
90
+ <v-text-field
91
+ v-model="draft.deviceId"
92
+ aria-label="Device ID"
93
+ placeholder="Connect to detect device ID"
94
+ variant="outlined"
95
+ density="compact"
96
+ hide-details="auto"
97
+ class="mb-3"
98
+ readonly
99
+ />
100
+
101
+ <InputLabel title="HID Portal" required class="mb-1" />
102
+ <v-select
103
+ v-model="draft.portalId"
104
+ :items="portals"
105
+ item-title="name"
106
+ item-value="id"
107
+ aria-label="HID Portal"
108
+ placeholder="Connect to load portals"
109
+ variant="outlined"
110
+ density="compact"
111
+ hide-details="auto"
112
+ :readonly="mode === 'edit'"
113
+ />
82
114
  </v-card-text>
83
115
 
84
- <v-card-actions class="form-actions pa-0">
116
+ <v-card-actions class="screen-modal__footer">
85
117
  <v-btn
86
- class="text-none action-cancel"
118
+ class="text-none screen-btn-ghost"
87
119
  variant="flat"
88
- height="48"
89
120
  @click="close"
90
121
  >
91
122
  Cancel
92
123
  </v-btn>
93
124
  <v-btn
94
- class="text-none action-submit"
125
+ class="text-none screen-btn-primary"
95
126
  variant="flat"
96
- height="48"
97
127
  :loading="loading"
128
+ :disabled="mode === 'add' && (!draft.deviceId || !draft.portalId)"
98
129
  @click="submit"
99
130
  >
100
131
  Submit
@@ -122,6 +153,13 @@ const props = defineProps({
122
153
  type: Boolean,
123
154
  default: false,
124
155
  },
156
+ // The site this reader belongs to. Passed down from HidReaderManagement,
157
+ // which already has it as a prop from the page's route. Reader discovery is
158
+ // authorized against it server-side.
159
+ site: {
160
+ type: String,
161
+ default: "",
162
+ },
125
163
  });
126
164
 
127
165
  const emit = defineEmits<{
@@ -135,11 +173,18 @@ const dialogModel = computed({
135
173
  });
136
174
 
137
175
  const showPassword = ref(false);
176
+ const discovering = ref(false);
177
+ const discoveryError = ref("");
178
+ const portals = ref<Array<{ id: number; name: string }>>([]);
179
+ const discoveredConnection = ref("");
180
+ const { discoverReader } = useHidAmico();
138
181
  const monitorEnabled = ref(true);
139
182
  const draft = reactive({
140
183
  name: "",
141
184
  baseUrl: "",
142
185
  deviceId: "",
186
+ portalId: null as number | null,
187
+ portalName: "",
143
188
  location: "",
144
189
  username: "",
145
190
  password: "",
@@ -153,6 +198,13 @@ watch(
153
198
  draft.name = props.reader?.name ?? "";
154
199
  draft.baseUrl = props.reader?.baseUrl ?? "";
155
200
  draft.deviceId = props.reader?.deviceId ?? "";
201
+ draft.portalId = props.reader?.portalId ?? null;
202
+ draft.portalName = props.reader?.portalName ?? "";
203
+ portals.value = draft.portalId
204
+ ? [{ id: draft.portalId, name: draft.portalName || `Portal ${draft.portalId}` }]
205
+ : [];
206
+ discoveryError.value = "";
207
+ discoveredConnection.value = "";
156
208
  draft.location = props.reader?.location ?? "";
157
209
  draft.username = props.reader?.username ?? "";
158
210
  draft.password = props.reader?.password ?? "";
@@ -165,9 +217,71 @@ function close() {
165
217
  emit("update:modelValue", false);
166
218
  }
167
219
 
220
+ const canDiscover = computed(() => Boolean(
221
+ draft.baseUrl.trim() && draft.username.trim() && draft.password,
222
+ ));
223
+
224
+ watch(
225
+ () => [draft.baseUrl, draft.username, draft.password],
226
+ () => {
227
+ if (props.mode !== "add" || !discoveredConnection.value) return;
228
+ const current = [draft.baseUrl.trim(), draft.username.trim(), draft.password].join("\n");
229
+ if (current !== discoveredConnection.value) {
230
+ draft.deviceId = "";
231
+ draft.portalId = null;
232
+ portals.value = [];
233
+ discoveredConnection.value = "";
234
+ }
235
+ },
236
+ );
237
+
238
+ async function connectReader() {
239
+ discovering.value = true;
240
+ discoveryError.value = "";
241
+ try {
242
+ const response = await discoverReader({
243
+ baseUrl: draft.baseUrl.trim(),
244
+ username: draft.username.trim(),
245
+ password: draft.password,
246
+ site: props.site,
247
+ });
248
+ const nestedData = response?.data;
249
+ const data = typeof nestedData === "object" && nestedData !== null
250
+ ? nestedData as Record<string, unknown>
251
+ : response;
252
+ draft.deviceId = String(data?.deviceId || "");
253
+ portals.value = Array.isArray(data?.portals)
254
+ ? data.portals.flatMap((portal: unknown) => {
255
+ if (typeof portal !== "object" || portal === null) return [];
256
+ const item = portal as Record<string, unknown>;
257
+ const id = Number(item.id);
258
+ const name = String(item.name || "").trim();
259
+ return Number.isSafeInteger(id) && id > 0 && name ? [{ id, name }] : [];
260
+ })
261
+ : [];
262
+ draft.portalId = portals.value.length === 1 ? portals.value[0].id : null;
263
+ discoveredConnection.value = [draft.baseUrl.trim(), draft.username.trim(), draft.password].join("\n");
264
+ } catch (error: unknown) {
265
+ draft.deviceId = "";
266
+ draft.portalId = null;
267
+ portals.value = [];
268
+ const record = typeof error === "object" && error !== null ? error as Record<string, unknown> : {};
269
+ const errorData = typeof record.data === "object" && record.data !== null
270
+ ? record.data as Record<string, unknown>
271
+ : {};
272
+ discoveryError.value = String(
273
+ errorData.message || record.message || "Unable to connect to the HID reader.",
274
+ );
275
+ } finally {
276
+ discovering.value = false;
277
+ }
278
+ }
279
+
168
280
  function submit() {
281
+ const portal = portals.value.find((item) => item.id === Number(draft.portalId));
169
282
  const payload: Record<string, any> = {
170
283
  ...draft,
284
+ portalName: portal?.name || draft.portalName,
171
285
  monitorPath: monitorEnabled.value ? "api/notifications" : "",
172
286
  enabled: monitorEnabled.value,
173
287
  status: monitorEnabled.value ? "active" : "inactive",
@@ -182,28 +296,18 @@ function submit() {
182
296
  overflow: hidden;
183
297
  }
184
298
 
299
+ /* Type comes from `.screen-modal .v-card-title`; only the header strip's own
300
+ padding and rule stay here. `#ececec` was a fixed near-white line, i.e.
301
+ invisible on a light card and a bright scratch across a dark one. */
185
302
  .form-title {
186
- font-size: 14px;
187
- font-weight: 700;
188
303
  padding: 12px 16px;
189
- border-bottom: 1px solid #ececec;
304
+ border-bottom: 1px solid var(--border);
190
305
  }
191
306
 
192
307
  .form-body {
193
308
  padding: 18px 16px 20px;
194
309
  }
195
310
 
196
- .field-label {
197
- color: #4f5a66;
198
- font-size: 12px;
199
- font-weight: 600;
200
- margin-bottom: 6px;
201
- }
202
-
203
- .field-label span {
204
- color: #e53935;
205
- }
206
-
207
311
  .monitor-toggle {
208
312
  display: grid;
209
313
  grid-template-columns: 1fr 1fr;
@@ -214,32 +318,11 @@ function submit() {
214
318
  height: 36px;
215
319
  }
216
320
 
217
- .form-actions {
218
- display: grid;
219
- grid-template-columns: 1fr 1fr;
220
- }
221
-
222
- .action-cancel {
223
- background: #ffffff;
224
- color: #111827;
225
- border-radius: 0;
226
- }
227
-
228
- .action-submit {
229
- background: #062d42;
230
- color: #ffffff;
231
- border-radius: 0;
232
- }
233
-
234
321
  @media (max-width: 600px) {
235
322
  .form-body {
236
323
  padding: 14px 12px 16px;
237
324
  }
238
325
 
239
- .form-actions {
240
- grid-template-columns: 1fr;
241
- }
242
-
243
326
  :global(.v-dialog > .v-overlay__content) {
244
327
  max-width: calc(100vw - 24px) !important;
245
328
  margin: 12px !important;