@7365admin1/layer-common 3.2.2-staging.106 → 3.2.2-staging.108

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.
@@ -260,6 +260,8 @@
260
260
  :loading="entryPassSettingsPending || siteDataPending" :site-id="prop.site"
261
261
  :unit-id="visitor.unit || null" :visitor-type="prop.type" :hid-qr-code-enabled="hidQrCodePassAllowed"
262
262
  :hid-qr-printer-configured="hasHidQrPrinterConfig"
263
+ :hid-reader-name="hidQrReader?.name || ''"
264
+ :hid-portal-name="hidQrReader?.portalName || ''"
263
265
  @update:available-qr-count="(val) => availableQrCount = val" />
264
266
  </v-col>
265
267
 
@@ -269,6 +271,7 @@
269
271
  :settings="entryPassSettings" :settings-loading="entryPassSettingsPending || siteDataPending"
270
272
  :unit-id="visitor.unit || null" :hid-qr-code-enabled="hidQrCodePassAllowed"
271
273
  :hid-qr-printer-configured="hasHidQrPrinterConfig"
274
+ :hid-reader-name="hidQrReader?.name || ''" :hid-portal-name="hidQrReader?.portalName || ''"
272
275
  :selected-nfc-cards="passType === 'NFC' ? passCards : []" />
273
276
  </v-col>
274
277
 
@@ -533,6 +536,9 @@
533
536
  </v-btn>
534
537
 
535
538
  <div class="text-h6 font-weight-bold">{{ visitor.name }}</div>
539
+ <div v-if="hidQrReader?.portalName" class="text-caption text-medium-emphasis mt-1">
540
+ {{ hidQrReader.name || "HID Reader" }} · {{ hidQrReader.portalName }}
541
+ </div>
536
542
  <div v-if="hidQrPreview.expiresAt" class="text-caption text-medium-emphasis mt-1">
537
543
  Valid until {{ formatHidQrExpiry(hidQrPreview.expiresAt) }}
538
544
  </div>
@@ -668,7 +674,7 @@ const {
668
674
  const { getBySiteId: getEntryPassSettingsBySiteId } =
669
675
  useSiteEntryPassSettings();
670
676
  const { createVisitorPass, signQr } = useAccessManagement();
671
- const { issueVisitorQr } = useHidAmico();
677
+ const { getReaders, issueVisitorQr } = useHidAmico();
672
678
  const { testConnection } = useWebUsb();
673
679
  const {
674
680
  findPersonByNRIC,
@@ -877,6 +883,23 @@ const hidQrValidityMinutes = computed(() => {
877
883
  });
878
884
 
879
885
  const hidQrReaderId = computed(() => String(hidQrCodePassConfig.value?.readerId || ""));
886
+ const hidQrReader = ref<Record<string, any> | null>(null);
887
+
888
+ watch(
889
+ [hidQrReaderId, () => prop.site],
890
+ async ([readerId, site]) => {
891
+ hidQrReader.value = null;
892
+ if (!readerId || !site) return;
893
+ try {
894
+ const response = await getReaders({ site, page: 1, limit: 100 });
895
+ const readers = response?.items ?? response?.data?.items ?? response?.data?.readers ?? [];
896
+ hidQrReader.value = readers.find((reader: Record<string, any>) => String(reader._id) === readerId) || null;
897
+ } catch {
898
+ hidQrReader.value = null;
899
+ }
900
+ },
901
+ { immediate: true },
902
+ );
880
903
 
881
904
  const hidQrCodePassAllowed = computed(() => {
882
905
  return (
@@ -2100,6 +2123,12 @@ function validateHidQrReaderConfig() {
2100
2123
  return false;
2101
2124
  }
2102
2125
 
2126
+ if (!hidQrReader.value?.portalId || !hidQrReader.value?.portalName) {
2127
+ errorMessage.value =
2128
+ "The selected HID reader has no portal configured. Reconnect the reader and select a portal before generating a QR code.";
2129
+ return false;
2130
+ }
2131
+
2103
2132
  return true;
2104
2133
  }
2105
2134
 
@@ -6,6 +6,8 @@ type HidReaderPayload = {
6
6
  username?: string;
7
7
  password?: string;
8
8
  deviceId?: string;
9
+ portalId?: number;
10
+ portalName?: string;
9
11
  monitorPath?: string;
10
12
  enabled?: boolean;
11
13
  status?: string;
@@ -34,6 +36,9 @@ type HidVisitorQrData = {
34
36
  hidUserId: number;
35
37
  qrValue: string;
36
38
  qrFormat: "0";
39
+ portalId: number;
40
+ portalName: string;
41
+ readerName: string;
37
42
  issuedAt: string;
38
43
  expiresAt: string;
39
44
  };
@@ -189,6 +194,13 @@ export default function useHidAmico() {
189
194
  );
190
195
  }
191
196
 
197
+ function discoverReader(payload: Pick<HidReaderPayload, "baseUrl" | "username" | "password">) {
198
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/discover`, {
199
+ method: "POST",
200
+ body: payload,
201
+ });
202
+ }
203
+
192
204
  function getUserPinStatus(readerId: string, hidUserId: string | number) {
193
205
  return useNuxtApp().$api<{ data: { pinEnrolled: boolean } }>(
194
206
  `${basePath}/readers/${readerId}/users/${hidUserId}/pin`,
@@ -294,6 +306,7 @@ export default function useHidAmico() {
294
306
 
295
307
  return {
296
308
  getReaders,
309
+ discoverReader,
297
310
  createReader,
298
311
  updateReader,
299
312
  deleteReader,
@@ -0,0 +1,62 @@
1
+ export default function useSubscriptionPlan() {
2
+ function getAll({
3
+ page = 1,
4
+ search = "",
5
+ limit = 10,
6
+ status = "active",
7
+ planType = "",
8
+ billingCycle = "",
9
+ } = {}) {
10
+ return useNuxtApp().$api<Record<string, any>>(`/api/subscription-plans`, {
11
+ method: "GET",
12
+ query: {
13
+ page,
14
+ search,
15
+ limit,
16
+ status,
17
+ planType,
18
+ billingCycle,
19
+ },
20
+ });
21
+ }
22
+ function add(value: any) {
23
+ return useNuxtApp().$api<Record<string, any>>("/api/subscription-plans", {
24
+ method: "POST",
25
+ body: value,
26
+ });
27
+ }
28
+ function getById(id: string) {
29
+ return useNuxtApp().$api<Record<string, any>>(
30
+ `/api/subscription-plans/id/${id}`,
31
+ {
32
+ method: "GET",
33
+ }
34
+ );
35
+ }
36
+ function updateById(id: string, value: any) {
37
+ return useNuxtApp().$api<Record<string, any>>(
38
+ `/api/subscription-plans/${id}`,
39
+ {
40
+ method: "PUT",
41
+ body: value,
42
+ }
43
+ );
44
+ }
45
+ function updateStatusById(id: string, status: "active" | "deactive") {
46
+ return useNuxtApp().$api<Record<string, any>>(
47
+ `/api/subscription-plans/${id}/status`,
48
+ {
49
+ method: "PATCH",
50
+ body: { status },
51
+ }
52
+ );
53
+ }
54
+
55
+ return {
56
+ getAll,
57
+ add,
58
+ getById,
59
+ updateById,
60
+ updateStatusById,
61
+ };
62
+ }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "3.2.2-staging.106",
5
+ "version": "3.2.2-staging.108",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {
@@ -5,10 +5,8 @@ export type PlanType = "free_bundle" | "paid_bundle" | "custom_apps";
5
5
  export type BillingCycle = "monthly" | "annually";
6
6
 
7
7
  export interface AppOption {
8
- id: string;
9
- name: string;
10
- monthlyPrice: number;
11
- yearlyPrice: number;
8
+ title: string;
9
+ value: string;
12
10
  }
13
11
 
14
12
  export interface SubscriptionPlan {
@@ -32,6 +30,7 @@ export interface PlanFormState {
32
30
  bundleApps: string[];
33
31
  bundlePrice: number | null;
34
32
  customApps: string[];
33
+ customAppPrices: Record<string, number>;
35
34
  }
36
35
 
37
36
  export const PLAN_TYPE_LABEL: Record<PlanType, string> = {
@@ -40,12 +39,20 @@ export const PLAN_TYPE_LABEL: Record<PlanType, string> = {
40
39
  custom_apps: "Paid (Custom Apps)",
41
40
  };
42
41
 
43
- // Mock catalogue of applications that can be bundled into a plan.
42
+ // Catalogue of applications that can be bundled into a plan.
44
43
  // Replace with data fetched from your backend.
45
- export const AVAILABLE_APPLICATIONS: AppOption[] = [
46
- { id: "crm", name: "CRM", monthlyPrice: 49, yearlyPrice: 490 },
47
- { id: "hrm", name: "HRM", monthlyPrice: 39, yearlyPrice: 390 },
48
- { id: "accounting", name: "Accounting", monthlyPrice: 59, yearlyPrice: 590 },
49
- { id: "inventory", name: "Inventory", monthlyPrice: 45, yearlyPrice: 450 },
50
- { id: "pos", name: "POS", monthlyPrice: 69, yearlyPrice: 690 },
44
+ export const APP_LIST: AppOption[] = [
45
+ { title: "Organization", value: "organization" },
46
+ { title: "Security Agency", value: "security_agency" },
47
+ { title: "Cleaning Services", value: "cleaning_services" },
48
+ { title: "Property Management Agency", value: "property_management_agency" },
49
+ { title: "Mechanical & Electrical Services", value: "mechanical_electrical_services" },
50
+ { title: "Pest Control Services", value: "pest_control_services" },
51
+ { title: "Landscaping Services", value: "landscaping_services" },
52
+ { title: "Pool Maintenance Services", value: "pool_maintenance_services" },
51
53
  ];
54
+
55
+ // Static placeholder price applied to every app, for now.
56
+ // Replace with real per-app pricing once that's available.
57
+ export const STATIC_APP_MONTHLY_PRICE = 49;
58
+ export const STATIC_APP_YEARLY_PRICE = 490;