@7365admin1/layer-common 3.2.6 → 3.2.8-staging.200

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 (284) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/assets/css/primitives.css +1113 -0
  3. package/assets/css/screens.css +1097 -0
  4. package/assets/css/shell.css +258 -0
  5. package/assets/css/tokens.css +236 -0
  6. package/components/AcceptDialog.vue +17 -18
  7. package/components/AccessCard/AvailableStats.vue +24 -16
  8. package/components/AccessCardAddForm.vue +42 -42
  9. package/components/AccessCardAssignToUnitForm.vue +13 -21
  10. package/components/AccessCardDeleteDialog.vue +41 -39
  11. package/components/AccessCardDetailsDialog.vue +125 -79
  12. package/components/AccessCardPreviewDialog.vue +58 -84
  13. package/components/AccessCardQrTagging.vue +60 -63
  14. package/components/AccessCardReplaceForm.vue +27 -23
  15. package/components/AccessManagement.vue +86 -85
  16. package/components/AddEqupmentForm.vue +18 -22
  17. package/components/AddPassKeyToVisitor.vue +95 -73
  18. package/components/AppButton.vue +62 -0
  19. package/components/AppCard.vue +20 -0
  20. package/components/AppDateField.vue +49 -0
  21. package/components/AppField.vue +76 -0
  22. package/components/AppKpiTile.vue +65 -0
  23. package/components/AppSegmented.vue +38 -0
  24. package/components/AppSelect.vue +247 -0
  25. package/components/AreaChecklistHistoryLogs.vue +51 -44
  26. package/components/AreaChecklistHistoryMain.vue +14 -7
  27. package/components/AreaFormDialog.vue +71 -59
  28. package/components/AreaMain.vue +73 -107
  29. package/components/AttachmentsViewer.vue +84 -22
  30. package/components/AttendanceCheckInOutDialog.vue +85 -88
  31. package/components/AttendanceDetailsDialog.vue +57 -63
  32. package/components/AttendanceMain.vue +4 -3
  33. package/components/AttendanceMapSearchDialog.vue +42 -44
  34. package/components/AttendanceSettingsDialog.vue +79 -63
  35. package/components/Avatar/Main.vue +54 -2
  36. package/components/BillingMain.vue +26 -30
  37. package/components/BtnUploadFile.vue +53 -11
  38. package/components/BuildingForm.vue +34 -27
  39. package/components/BuildingManagement/buildings.vue +39 -39
  40. package/components/BuildingManagement/units.vue +26 -42
  41. package/components/BuildingUnitFormAdd.vue +20 -23
  42. package/components/BuildingUnitFormEdit.vue +8 -4
  43. package/components/BulletinBoardForm.vue +19 -24
  44. package/components/BulletinBoardManagement.vue +49 -57
  45. package/components/BulletinBoardView.vue +9 -6
  46. package/components/BulletinExpirationChip.vue +13 -4
  47. package/components/Button/Close.vue +2 -2
  48. package/components/CameraForm.vue +21 -22
  49. package/components/CameraMain.vue +50 -49
  50. package/components/Card/DeleteConfirmation.vue +24 -22
  51. package/components/Card/MemberInfoSummary.vue +28 -3
  52. package/components/Card/Toggle.vue +15 -3
  53. package/components/CardToolbar.vue +37 -0
  54. package/components/Carousel.vue +56 -36
  55. package/components/Chat/Bubbles.vue +43 -14
  56. package/components/Chat/Information.vue +49 -32
  57. package/components/Chat/ListCard.vue +38 -9
  58. package/components/Chat/Message.vue +41 -15
  59. package/components/Chat/Navigation.vue +27 -15
  60. package/components/Chat/SkeletonLoader.vue +9 -2
  61. package/components/ChecklistItemRow.vue +25 -1
  62. package/components/CleaningScheduleMain.vue +44 -49
  63. package/components/ClientDetailForm.vue +81 -56
  64. package/components/ClientMain.vue +92 -82
  65. package/components/ConfirmDialog.vue +14 -8
  66. package/components/Container/Standard.vue +9 -7
  67. package/components/CreateSubsciptionPlan.vue +148 -89
  68. package/components/DashboardEmptyState.vue +48 -3
  69. package/components/DashboardMain.vue +924 -676
  70. package/components/DashboardPanel.vue +84 -21
  71. package/components/DeliveryCompany.vue +34 -20
  72. package/components/Dialog/DeleteConfirmation.vue +20 -21
  73. package/components/Dialog/ReplaceAutofillPrompt.vue +19 -26
  74. package/components/Dialog/ReusablePrompt.vue +30 -29
  75. package/components/Dialog/UpdateMoreAction.vue +62 -59
  76. package/components/DocumentForm.vue +14 -21
  77. package/components/DocumentManagement.vue +162 -108
  78. package/components/DocumentViewer.vue +29 -12
  79. package/components/DrawImage.vue +29 -21
  80. package/components/EntryPass/QrTemplatePreview.vue +27 -16
  81. package/components/EntryPassInformation.vue +40 -10
  82. package/components/EntryPassMain.vue +49 -27
  83. package/components/EquipmentItemMain.vue +84 -97
  84. package/components/EquipmentManagementMain.vue +81 -103
  85. package/components/Facility/BookingSetup.vue +43 -52
  86. package/components/Feedback/Form.vue +26 -12
  87. package/components/FeedbackMain.vue +35 -49
  88. package/components/FileInput.vue +75 -58
  89. package/components/FileInputWithList.vue +59 -12
  90. package/components/FormDialog.vue +60 -21
  91. package/components/HidAccessLogDashboard.vue +512 -490
  92. package/components/HidCameraCaptureDialog.vue +216 -0
  93. package/components/HidEnabledGate.vue +67 -9
  94. package/components/HidIntercomManagement.vue +1322 -653
  95. package/components/HidQrCodeConfiguration.vue +1030 -258
  96. package/components/HidReaderForm.vue +142 -59
  97. package/components/HidReaderManagement.vue +211 -323
  98. package/components/HidServiceSettingsPanel.vue +44 -13
  99. package/components/HidUserEnrollment.vue +1185 -412
  100. package/components/HygieneUpdateMoreAction.vue +44 -44
  101. package/components/IncidentReport/Authorities.vue +30 -18
  102. package/components/IncidentReport/IncidentInformation.vue +10 -10
  103. package/components/IncidentReport/IncidentInformationDownload.vue +3 -4
  104. package/components/IncidentReport/affectedEntities.vue +41 -20
  105. package/components/Input/Date.vue +3 -0
  106. package/components/Input/DatePicker.vue +1 -2
  107. package/components/Input/DateTimePicker.vue +1 -1
  108. package/components/Input/File.vue +55 -10
  109. package/components/Input/FileV2.vue +39 -6
  110. package/components/Input/InputPhoneNumberV2.vue +2 -1
  111. package/components/Input/ListGroupSelection.vue +56 -8
  112. package/components/Input/NRICNumber.vue +1 -0
  113. package/components/Input/NewDate.vue +1 -0
  114. package/components/Input/Number.vue +1 -0
  115. package/components/Input/Password.vue +1 -0
  116. package/components/Input/PhoneNumber.vue +21 -1
  117. package/components/Input/VehicleNumber.vue +1 -0
  118. package/components/InputLabel.vue +2 -2
  119. package/components/InvitationClientForm.vue +21 -19
  120. package/components/InvitationForm.vue +15 -21
  121. package/components/InvitationMain.vue +46 -61
  122. package/components/Layout/Header.vue +263 -36
  123. package/components/Layout/NavigationDrawer.vue +300 -30
  124. package/components/ListItem.vue +33 -3
  125. package/components/LocalPagination.vue +27 -3
  126. package/components/ManageChecklistMain.vue +136 -86
  127. package/components/MemberInformation.vue +32 -16
  128. package/components/MemberMain.vue +113 -93
  129. package/components/MyAttendanceMain.vue +3 -5
  130. package/components/NavigationItem.vue +83 -3
  131. package/components/Nfc/NFCPatrolReportMain.vue +47 -33
  132. package/components/Nfc/NFCPatrolRouteForm.vue +18 -20
  133. package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
  134. package/components/Nfc/NFCPatrolSettings.vue +14 -4
  135. package/components/Nfc/NFCTagForm.vue +22 -8
  136. package/components/Nfc/NFCTagMain.vue +41 -43
  137. package/components/Nfc/PatrolReport/DailyReportTab.vue +8 -1
  138. package/components/Nfc/PatrolReport/MonthlyReportTab.vue +8 -1
  139. package/components/Nfc/PatrolReport/MonthlyReportTable.vue +5 -3
  140. package/components/Nfc/PatrolReport/PatrolActivityTable.vue +26 -6
  141. package/components/Nfc/PatrolReport/PatrolReportTab.vue +8 -0
  142. package/components/Nfc/PatrolReport/PatrolRouteSummary.vue +41 -9
  143. package/components/Nfc/PatrolReport/RemarksDialog.vue +39 -9
  144. package/components/Nfc/PatrolReport/ReportEmptyState.vue +29 -9
  145. package/components/Nfc/PatrolReport/ReportFilters.vue +19 -10
  146. package/components/Nfc/PatrolReport/ReportLocationHeader.vue +32 -6
  147. package/components/Nfc/PatrolReport/SummaryReportTable.vue +28 -6
  148. package/components/NotificationSettings.vue +373 -0
  149. package/components/NumberSettingField.vue +9 -2
  150. package/components/OnlineFormConfigurationForm.vue +725 -237
  151. package/components/OnlineFormFill.vue +213 -106
  152. package/components/OnlineFormRenderPreview.vue +25 -30
  153. package/components/OnlineFormsConfiguration.vue +113 -109
  154. package/components/OrgViewDialog.vue +34 -16
  155. package/components/OvernightParkingAvailability.vue +49 -26
  156. package/components/OvernightParkingManagement.vue +78 -18
  157. package/components/PageHeader.vue +43 -0
  158. package/components/PassInformation.vue +109 -53
  159. package/components/PasswordConfirmation.vue +44 -26
  160. package/components/PeopleForm.vue +41 -20
  161. package/components/PhotoUpload.vue +110 -48
  162. package/components/PlaceholderComponent.vue +35 -19
  163. package/components/PlanActionsMenu.vue +12 -2
  164. package/components/PlanStatusBadge.vue +7 -8
  165. package/components/PlateNumberDisplay.vue +9 -6
  166. package/components/QrTemplate/PrintDialog.vue +66 -43
  167. package/components/RolePermissionFormCreate.vue +104 -79
  168. package/components/RolePermissionFormPreviewUpdate.vue +111 -109
  169. package/components/RolePermissionMain.vue +183 -81
  170. package/components/ScanVisitorQRCode.vue +99 -35
  171. package/components/ScheduleAreaMain.vue +34 -59
  172. package/components/ScheduleTaskForm.vue +61 -42
  173. package/components/ScheduleTaskMain.vue +56 -69
  174. package/components/SearchVehicleNumberUser.vue +39 -37
  175. package/components/ServiceProviderFormCreate.vue +29 -20
  176. package/components/ServiceProviderInvitationPrompt.vue +155 -0
  177. package/components/ServiceProviderMain.vue +601 -432
  178. package/components/Signature.vue +40 -34
  179. package/components/SignaturePad.vue +5 -29
  180. package/components/SiteSettings.vue +41 -11
  181. package/components/SiteSettingsWorkOrder.vue +61 -30
  182. package/components/SlideCardGroup.vue +35 -11
  183. package/components/Snackbar.vue +36 -3
  184. package/components/SpecificAttr.vue +18 -8
  185. package/components/StatusChip.vue +156 -0
  186. package/components/StockCard.vue +17 -12
  187. package/components/SubscriptionPlanTable.vue +207 -140
  188. package/components/SwitchContext.vue +201 -9
  189. package/components/TableHygiene.vue +36 -46
  190. package/components/TableList.vue +10 -10
  191. package/components/TableListSecondary.vue +105 -54
  192. package/components/TableMain.vue +241 -79
  193. package/components/TableV3.vue +61 -70
  194. package/components/TiptapEditor.vue +33 -40
  195. package/components/TiptapEditorQr.vue +5 -2
  196. package/components/Tooltip/Info.vue +69 -24
  197. package/components/UnitMain.vue +40 -60
  198. package/components/UnitPersonCard.vue +42 -13
  199. package/components/VehicleAddSelection.vue +48 -32
  200. package/components/VehicleForm.vue +112 -129
  201. package/components/VehicleManagement.vue +26 -12
  202. package/components/VehicleUpdateMoreAction.vue +59 -46
  203. package/components/VisitorDataFromScannedQRCodeForm.vue +90 -65
  204. package/components/VisitorForm.vue +774 -210
  205. package/components/VisitorFormSelection.vue +40 -37
  206. package/components/VisitorManagement.vue +239 -82
  207. package/components/VisitorPassKeyQRScanner.vue +65 -18
  208. package/components/VisitorSearchResultSummary.vue +60 -16
  209. package/components/VisitorSocketPopUp.vue +22 -14
  210. package/components/VisitorsReportPreview.vue +44 -29
  211. package/components/WorkOrder/Create.vue +26 -17
  212. package/components/WorkOrder/ListView.vue +38 -7
  213. package/components/WorkOrder/Main.vue +62 -59
  214. package/composables/useFeedback.ts +3 -3
  215. package/composables/useHidAmico.ts +269 -0
  216. package/composables/useHidNavigation.ts +2 -9
  217. package/composables/useMember.ts +3 -3
  218. package/composables/useNFCPatrolDailyReport.ts +13 -0
  219. package/composables/useNFCPatrolMonthlyReport.ts +22 -56
  220. package/composables/useNFCPatrolReport.ts +13 -0
  221. package/composables/useNotificationPreference.ts +87 -0
  222. package/composables/useOnlineFormPages.ts +2 -0
  223. package/composables/useRole.ts +22 -0
  224. package/composables/useServiceProvider.ts +3 -3
  225. package/composables/useServiceProviderInvitation.ts +145 -0
  226. package/composables/useSipWebPhone.ts +336 -0
  227. package/composables/useSubscriptionPlan.ts +62 -0
  228. package/composables/useThemePreference.ts +63 -0
  229. package/composables/useVisitor.ts +6 -1
  230. package/composables/useWebUsb.ts +127 -37
  231. package/composables/useWorkOrder.ts +17 -3
  232. package/error.vue +30 -6
  233. package/nuxt.config.ts +36 -0
  234. package/package.json +25 -4
  235. package/pages/[org]/[site]/access-mgmt/administrator/index.vue +2 -2
  236. package/pages/[org]/[site]/access-mgmt/{identity-mapping → hid-cards}/index.vue +2 -2
  237. package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
  238. package/pages/notification-settings.vue +19 -0
  239. package/pages/payment-method-linked.vue +34 -10
  240. package/pages/require-customer.vue +48 -10
  241. package/pages/require-organization-membership.vue +32 -8
  242. package/pages/unauthorized.vue +29 -4
  243. package/plugins/secure-member.client.ts +21 -56
  244. package/plugins/vuetify.ts +69 -2
  245. package/types/nfc-patrol-report.ts +3 -2
  246. package/types/site.d.ts +71 -0
  247. package/types/subscription.ts +18 -11
  248. package/utils/breadcrumb.test.ts +108 -0
  249. package/utils/breadcrumb.ts +82 -0
  250. package/utils/dashboard.test.ts +118 -0
  251. package/utils/dashboard.ts +53 -0
  252. package/utils/data.test.ts +95 -0
  253. package/utils/data.ts +40 -2
  254. package/utils/hid-discover.test.ts +102 -0
  255. package/utils/list-page.test.ts +60 -0
  256. package/utils/list-page.ts +38 -0
  257. package/utils/materialSymbols.ts +363 -0
  258. package/utils/nav-sections.test.ts +190 -0
  259. package/utils/nav-sections.ts +151 -0
  260. package/utils/nfc-patrol-report.test.ts +130 -0
  261. package/utils/nfc-patrol-report.ts +101 -0
  262. package/utils/role.test.ts +69 -0
  263. package/utils/role.ts +10 -0
  264. package/utils/route-name.test.ts +85 -0
  265. package/utils/route-name.ts +24 -0
  266. package/utils/status.test.ts +110 -0
  267. package/utils/status.ts +135 -0
  268. package/utils/table-card.test.ts +84 -0
  269. package/utils/theme-aa-ledger.ts +148 -0
  270. package/utils/theme.test.ts +485 -7
  271. package/utils/theme.ts +295 -110
  272. package/.changeset/README.md +0 -8
  273. package/.changeset/config.json +0 -11
  274. package/.editorconfig +0 -12
  275. package/.github/workflows/main.yml +0 -17
  276. package/.github/workflows/publish-staging.yml +0 -47
  277. package/.github/workflows/publish.yml +0 -39
  278. package/PUBLISHING.md +0 -269
  279. package/components/HidIdentityMapping.vue +0 -975
  280. package/components/ScheduleTaskAreaFormDialog.vue +0 -144
  281. package/components/ScheduleTaskAreaUpdateMoreAction.vue +0 -109
  282. package/components/ScheduleTastTicketMain.vue +0 -182
  283. package/components/TableWithButton.vue +0 -94
  284. 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;