@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
|
@@ -1,29 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-card class="
|
|
2
|
+
<v-card class="screen-modal">
|
|
3
3
|
<!-- Header -->
|
|
4
|
-
<v-card-title
|
|
5
|
-
|
|
6
|
-
{{ isEditing ? "Edit Plan" : "Create New Plan" }}
|
|
7
|
-
</span>
|
|
8
|
-
|
|
9
|
-
<v-btn
|
|
10
|
-
icon="mdi-close"
|
|
11
|
-
variant="text"
|
|
12
|
-
density="comfortable"
|
|
13
|
-
aria-label="Close"
|
|
14
|
-
@click="$emit('cancel')"
|
|
15
|
-
/>
|
|
4
|
+
<v-card-title>
|
|
5
|
+
{{ isEditing ? "Edit Plan" : "Create New Plan" }}
|
|
16
6
|
</v-card-title>
|
|
17
7
|
|
|
18
|
-
<v-divider />
|
|
19
|
-
|
|
20
8
|
<!-- Form Content -->
|
|
21
|
-
<v-form ref="formRef"
|
|
22
|
-
<v-card-text class="
|
|
9
|
+
<v-form ref="formRef" @submit.prevent="submit">
|
|
10
|
+
<v-card-text class="screen-modal__body px-6 pt-4">
|
|
23
11
|
<!-- Plan Details Section -->
|
|
24
|
-
<div class="
|
|
25
|
-
Plan Details
|
|
26
|
-
</div>
|
|
12
|
+
<div class="plan-form__section">Plan Details</div>
|
|
27
13
|
|
|
28
14
|
<!-- Plan Name -->
|
|
29
15
|
<v-text-field
|
|
@@ -32,8 +18,8 @@
|
|
|
32
18
|
placeholder="e.g., Business"
|
|
33
19
|
variant="outlined"
|
|
34
20
|
density="compact"
|
|
21
|
+
class="filter-field mb-3"
|
|
35
22
|
:error-messages="errors.name"
|
|
36
|
-
class="mb-3"
|
|
37
23
|
required
|
|
38
24
|
>
|
|
39
25
|
<template #label>
|
|
@@ -48,9 +34,9 @@
|
|
|
48
34
|
placeholder="Enter description"
|
|
49
35
|
variant="outlined"
|
|
50
36
|
density="compact"
|
|
37
|
+
class="filter-field mb-3"
|
|
51
38
|
rows="2"
|
|
52
39
|
auto-grow
|
|
53
|
-
class="mb-3"
|
|
54
40
|
/>
|
|
55
41
|
|
|
56
42
|
<!-- Max Seats -->
|
|
@@ -60,7 +46,7 @@
|
|
|
60
46
|
min="0"
|
|
61
47
|
variant="outlined"
|
|
62
48
|
density="compact"
|
|
63
|
-
class="mb-4"
|
|
49
|
+
class="filter-field mb-4"
|
|
64
50
|
required
|
|
65
51
|
>
|
|
66
52
|
<template #label>
|
|
@@ -68,12 +54,8 @@
|
|
|
68
54
|
</template>
|
|
69
55
|
</v-text-field>
|
|
70
56
|
|
|
71
|
-
<v-divider class="my-4" />
|
|
72
|
-
|
|
73
57
|
<!-- Pricing Section -->
|
|
74
|
-
<div class="
|
|
75
|
-
Pricing
|
|
76
|
-
</div>
|
|
58
|
+
<div class="plan-form__section">Pricing</div>
|
|
77
59
|
|
|
78
60
|
<!-- Plan Type -->
|
|
79
61
|
<v-select
|
|
@@ -81,7 +63,7 @@
|
|
|
81
63
|
:items="planTypeOptions"
|
|
82
64
|
variant="outlined"
|
|
83
65
|
density="compact"
|
|
84
|
-
class="mb-3"
|
|
66
|
+
class="filter-field mb-3"
|
|
85
67
|
>
|
|
86
68
|
<template #label>
|
|
87
69
|
Subscription plan type <span class="text-error">*</span>
|
|
@@ -96,7 +78,7 @@
|
|
|
96
78
|
placeholder="Select billing cycle"
|
|
97
79
|
variant="outlined"
|
|
98
80
|
density="compact"
|
|
99
|
-
class="mb-3"
|
|
81
|
+
class="filter-field mb-3"
|
|
100
82
|
>
|
|
101
83
|
<template #label>
|
|
102
84
|
Billing cycle <span class="text-error">*</span>
|
|
@@ -105,25 +87,23 @@
|
|
|
105
87
|
|
|
106
88
|
<!-- Bundle Applications (Chip Toggle) -->
|
|
107
89
|
<div v-if="form.planType !== 'custom_apps'" class="mb-4">
|
|
108
|
-
<div class="
|
|
109
|
-
Included Applications
|
|
110
|
-
</div>
|
|
90
|
+
<div class="plan-form__label mb-2">Included Applications</div>
|
|
111
91
|
|
|
112
92
|
<v-chip-group
|
|
113
93
|
v-model="form.bundleApps"
|
|
114
94
|
column
|
|
115
95
|
multiple
|
|
116
|
-
selected-class="
|
|
96
|
+
selected-class="plan-form__chip--on"
|
|
117
97
|
>
|
|
118
98
|
<v-chip
|
|
119
|
-
v-for="app in
|
|
120
|
-
:key="app.
|
|
121
|
-
:value="app.
|
|
99
|
+
v-for="app in APP_LIST"
|
|
100
|
+
:key="app.value"
|
|
101
|
+
:value="app.value"
|
|
122
102
|
filter
|
|
123
103
|
variant="outlined"
|
|
124
104
|
size="small"
|
|
125
105
|
>
|
|
126
|
-
{{ app.
|
|
106
|
+
{{ app.title }}
|
|
127
107
|
</v-chip>
|
|
128
108
|
</v-chip-group>
|
|
129
109
|
</div>
|
|
@@ -139,7 +119,7 @@
|
|
|
139
119
|
:suffix="form.billingCycle === 'annually' ? '/yr' : '/mo'"
|
|
140
120
|
variant="outlined"
|
|
141
121
|
density="compact"
|
|
142
|
-
class="mb-3"
|
|
122
|
+
class="filter-field mb-3"
|
|
143
123
|
>
|
|
144
124
|
<template #label>
|
|
145
125
|
Price <span class="text-error">*</span>
|
|
@@ -148,53 +128,60 @@
|
|
|
148
128
|
|
|
149
129
|
<!-- Custom Applications (List Checkbox) -->
|
|
150
130
|
<div v-if="form.planType === 'custom_apps'" class="mb-4">
|
|
151
|
-
<div class="
|
|
131
|
+
<div class="plan-form__label mb-1">
|
|
152
132
|
Included Applications <span class="text-error">*</span>
|
|
153
133
|
</div>
|
|
154
134
|
|
|
155
135
|
<v-card variant="outlined" class="pa-1 mb-3">
|
|
156
136
|
<v-list density="compact" class="py-0">
|
|
157
137
|
<v-list-item
|
|
158
|
-
v-for="app in
|
|
159
|
-
:key="app.
|
|
138
|
+
v-for="app in APP_LIST"
|
|
139
|
+
:key="app.value"
|
|
160
140
|
class="px-2"
|
|
161
141
|
>
|
|
162
142
|
<template #prepend>
|
|
163
143
|
<v-checkbox-btn
|
|
164
|
-
:model-value="form.customApps.includes(app.
|
|
144
|
+
:model-value="form.customApps.includes(app.value)"
|
|
165
145
|
density="compact"
|
|
166
|
-
@update:model-value="toggleApp(form.customApps, app.
|
|
146
|
+
@update:model-value="toggleApp(form.customApps, app.value)"
|
|
167
147
|
/>
|
|
168
148
|
</template>
|
|
169
149
|
|
|
170
150
|
<v-list-item-title class="text-body-2">
|
|
171
|
-
{{ app.
|
|
151
|
+
{{ app.title }}
|
|
172
152
|
</v-list-item-title>
|
|
173
153
|
|
|
174
154
|
<template #append>
|
|
175
|
-
<
|
|
176
|
-
|
|
177
|
-
|
|
155
|
+
<div class="d-flex align-center" style="gap: 4px">
|
|
156
|
+
<span class="plan-form__unit">$</span>
|
|
157
|
+
<v-text-field
|
|
158
|
+
v-model.number="form.customAppPrices[app.value]"
|
|
159
|
+
type="number"
|
|
160
|
+
min="0"
|
|
161
|
+
density="compact"
|
|
162
|
+
variant="outlined"
|
|
163
|
+
hide-details
|
|
164
|
+
style="width: 84px"
|
|
165
|
+
:disabled="!form.customApps.includes(app.value)"
|
|
166
|
+
/>
|
|
167
|
+
<span class="plan-form__unit">/{{ form.billingCycle === 'annually' ? 'yr' : 'mo' }}</span>
|
|
168
|
+
</div>
|
|
178
169
|
</template>
|
|
179
170
|
</v-list-item>
|
|
180
171
|
</v-list>
|
|
181
172
|
</v-card>
|
|
182
173
|
|
|
183
174
|
<!-- Custom Total Price Display -->
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
rounded
|
|
187
|
-
class="pa-3 d-flex align-center justify-space-between mb-1"
|
|
188
|
-
>
|
|
189
|
-
<span class="text-caption text-grey-darken-2">
|
|
175
|
+
<div class="plan-form__total mb-1">
|
|
176
|
+
<span class="plan-form__label">
|
|
190
177
|
Total price {{ form.billingCycle === 'annually' ? '/ year' : '/ month' }}
|
|
191
178
|
</span>
|
|
192
|
-
<span class="
|
|
179
|
+
<span class="plan-form__total-value">
|
|
193
180
|
${{ customTotalPrice.toLocaleString() }}
|
|
194
181
|
</span>
|
|
195
|
-
</
|
|
182
|
+
</div>
|
|
196
183
|
|
|
197
|
-
<p class="
|
|
184
|
+
<p class="plan-form__unit">
|
|
198
185
|
Calculated automatically from the selected applications.
|
|
199
186
|
</p>
|
|
200
187
|
</div>
|
|
@@ -211,27 +198,20 @@
|
|
|
211
198
|
</v-alert>
|
|
212
199
|
</v-card-text>
|
|
213
200
|
|
|
214
|
-
<v-divider />
|
|
215
|
-
|
|
216
201
|
<!-- Footer Buttons -->
|
|
217
|
-
<v-card-actions class="
|
|
218
|
-
<v-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
variant="flat"
|
|
231
|
-
class="flex-1 text-none"
|
|
232
|
-
>
|
|
233
|
-
{{ isEditing ? "Save Changes" : "Create Plan" }}
|
|
234
|
-
</v-btn>
|
|
202
|
+
<v-card-actions class="screen-modal__footer">
|
|
203
|
+
<v-row no-gutters>
|
|
204
|
+
<v-col cols="6" class="pr-2">
|
|
205
|
+
<v-btn block variant="outlined" class="text-none screen-btn-ghost" @click="$emit('cancel')">
|
|
206
|
+
Cancel
|
|
207
|
+
</v-btn>
|
|
208
|
+
</v-col>
|
|
209
|
+
<v-col cols="6" class="pl-2">
|
|
210
|
+
<v-btn block type="submit" variant="flat" class="text-none screen-btn-primary">
|
|
211
|
+
{{ isEditing ? "Save Changes" : "Create Plan" }}
|
|
212
|
+
</v-btn>
|
|
213
|
+
</v-col>
|
|
214
|
+
</v-row>
|
|
235
215
|
</v-card-actions>
|
|
236
216
|
</v-form>
|
|
237
217
|
</v-card>
|
|
@@ -241,7 +221,9 @@
|
|
|
241
221
|
import { reactive, computed, watch } from "vue";
|
|
242
222
|
|
|
243
223
|
import {
|
|
244
|
-
|
|
224
|
+
APP_LIST,
|
|
225
|
+
STATIC_APP_MONTHLY_PRICE,
|
|
226
|
+
STATIC_APP_YEARLY_PRICE,
|
|
245
227
|
type PlanFormState,
|
|
246
228
|
type SubscriptionPlan,
|
|
247
229
|
} from "../types/subscription";
|
|
@@ -284,6 +266,14 @@ const form = reactive<PlanFormState>({
|
|
|
284
266
|
props.initialPlan?.planType === "custom_apps"
|
|
285
267
|
? [...(props.initialPlan?.applicationIds ?? [])]
|
|
286
268
|
: [],
|
|
269
|
+
customAppPrices: Object.fromEntries(
|
|
270
|
+
APP_LIST.map((app) => [
|
|
271
|
+
app.value,
|
|
272
|
+
props.initialPlan?.billingCycle === "annually"
|
|
273
|
+
? STATIC_APP_YEARLY_PRICE
|
|
274
|
+
: STATIC_APP_MONTHLY_PRICE,
|
|
275
|
+
])
|
|
276
|
+
),
|
|
287
277
|
});
|
|
288
278
|
|
|
289
279
|
const errors = reactive({
|
|
@@ -304,6 +294,29 @@ watch(
|
|
|
304
294
|
}
|
|
305
295
|
);
|
|
306
296
|
|
|
297
|
+
function staticPriceForCycle(cycle: typeof form.billingCycle) {
|
|
298
|
+
return cycle === "annually" ? STATIC_APP_YEARLY_PRICE : STATIC_APP_MONTHLY_PRICE;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/*
|
|
302
|
+
* Keep custom app prices in sync with the billing cycle, but only for
|
|
303
|
+
* apps still sitting at the previous cycle's default (leave manual edits alone)
|
|
304
|
+
*/
|
|
305
|
+
watch(
|
|
306
|
+
() => form.billingCycle,
|
|
307
|
+
(newCycle, oldCycle) => {
|
|
308
|
+
const oldDefault = staticPriceForCycle(oldCycle);
|
|
309
|
+
const newDefault = staticPriceForCycle(newCycle);
|
|
310
|
+
if (oldDefault === newDefault) return;
|
|
311
|
+
|
|
312
|
+
for (const app of APP_LIST) {
|
|
313
|
+
if (form.customAppPrices[app.value] === oldDefault) {
|
|
314
|
+
form.customAppPrices[app.value] = newDefault;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
);
|
|
319
|
+
|
|
307
320
|
/*
|
|
308
321
|
* Toggle application selection helper
|
|
309
322
|
*/
|
|
@@ -320,15 +333,10 @@ function toggleApp(list: string[], id: string) {
|
|
|
320
333
|
* Custom app total price calculation
|
|
321
334
|
*/
|
|
322
335
|
const customTotalPrice = computed(() => {
|
|
323
|
-
return form.customApps.reduce(
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
return (
|
|
328
|
-
total +
|
|
329
|
-
(form.billingCycle === "annually" ? app.yearlyPrice : app.monthlyPrice)
|
|
330
|
-
);
|
|
331
|
-
}, 0);
|
|
336
|
+
return form.customApps.reduce(
|
|
337
|
+
(total, value) => total + (form.customAppPrices[value] ?? 0),
|
|
338
|
+
0
|
|
339
|
+
);
|
|
332
340
|
});
|
|
333
341
|
|
|
334
342
|
/*
|
|
@@ -340,6 +348,11 @@ function validate(): boolean {
|
|
|
340
348
|
|
|
341
349
|
if (form.planType !== "free_bundle" && !form.billingCycle) {
|
|
342
350
|
errors.pricing = "Please select a billing cycle.";
|
|
351
|
+
} else if (
|
|
352
|
+
(form.planType === "free_bundle" || form.planType === "paid_bundle") &&
|
|
353
|
+
form.bundleApps.length === 0
|
|
354
|
+
) {
|
|
355
|
+
errors.pricing = "Select at least one application.";
|
|
343
356
|
} else if (
|
|
344
357
|
form.planType === "paid_bundle" &&
|
|
345
358
|
(!form.bundlePrice || form.bundlePrice <= 0)
|
|
@@ -370,7 +383,53 @@ function submit() {
|
|
|
370
383
|
</script>
|
|
371
384
|
|
|
372
385
|
<style scoped>
|
|
373
|
-
|
|
374
|
-
|
|
386
|
+
/* The design's section label: the same all-caps rule the rest of the estate
|
|
387
|
+
uses, instead of Vuetify's `text-subtitle-2 text-grey-darken-3`, which is a
|
|
388
|
+
fixed grey that does not follow the theme. */
|
|
389
|
+
.plan-form__section {
|
|
390
|
+
font-size: var(--fs-section-label);
|
|
391
|
+
font-weight: var(--fw-section-label);
|
|
392
|
+
letter-spacing: var(--ls-section-label);
|
|
393
|
+
text-transform: uppercase;
|
|
394
|
+
color: var(--muted);
|
|
395
|
+
margin-bottom: 12px;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.plan-form__label {
|
|
399
|
+
font-size: var(--fs-cell-dense);
|
|
400
|
+
font-weight: 600;
|
|
401
|
+
color: var(--text2);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.plan-form__unit {
|
|
405
|
+
font-size: var(--fs-chip-lg);
|
|
406
|
+
font-weight: 600;
|
|
407
|
+
color: var(--muted);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/* Was `<v-sheet color="grey-lighten-4">`, a light-theme-only grey that stayed
|
|
411
|
+
light in dark mode. */
|
|
412
|
+
.plan-form__total {
|
|
413
|
+
display: flex;
|
|
414
|
+
align-items: center;
|
|
415
|
+
justify-content: space-between;
|
|
416
|
+
padding: 12px;
|
|
417
|
+
border-radius: var(--r-inner);
|
|
418
|
+
background: var(--hover);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.plan-form__total-value {
|
|
422
|
+
font-size: var(--fs-card-title);
|
|
423
|
+
font-weight: var(--fw-card-title);
|
|
424
|
+
color: var(--text);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/* The selected application chip. The handoff draws no chip GROUP, so this is
|
|
428
|
+
the estate's soft-accent selected tone rather than an invented one - it
|
|
429
|
+
replaces `bg-grey-darken-3 text-white`, which was a fixed dark grey. */
|
|
430
|
+
.plan-form__chip--on {
|
|
431
|
+
background: var(--accent-soft) !important;
|
|
432
|
+
color: var(--accent-text) !important;
|
|
433
|
+
border-color: var(--accent) !important;
|
|
375
434
|
}
|
|
376
435
|
</style>
|
|
@@ -1,10 +1,28 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
"No data to display" and "we could not fetch it" are not the same sentence.
|
|
3
|
+
|
|
4
|
+
This component drew the first one for both, because every dashboard request
|
|
5
|
+
defaulted a rejection to an empty list. `error` splits them: a genuine zero
|
|
6
|
+
keeps the neutral wording, a failed request says so in the error tone and
|
|
7
|
+
tells the reader the figure is missing rather than nil.
|
|
8
|
+
-->
|
|
1
9
|
<template>
|
|
2
10
|
<div
|
|
3
|
-
class="d-flex flex-column align-center justify-center text-center
|
|
11
|
+
class="d-flex flex-column align-center justify-center text-center dashboard-empty"
|
|
12
|
+
:class="{ 'dashboard-empty--error': error }"
|
|
4
13
|
:style="{ minHeight: compact ? '72px' : '140px', gap: '8px' }"
|
|
14
|
+
role="status"
|
|
5
15
|
>
|
|
6
|
-
<v-icon
|
|
7
|
-
|
|
16
|
+
<v-icon
|
|
17
|
+
:icon="error ? 'mdi-cloud-alert-outline' : 'mdi-database-search-outline'"
|
|
18
|
+
size="28"
|
|
19
|
+
/>
|
|
20
|
+
<span class="text-caption font-weight-bold">
|
|
21
|
+
{{ error ? "Couldn't load this data" : "No data to display" }}
|
|
22
|
+
</span>
|
|
23
|
+
<span v-if="error" class="text-caption dashboard-empty__hint">
|
|
24
|
+
The request failed — this is not a zero. Refresh to try again.
|
|
25
|
+
</span>
|
|
8
26
|
</div>
|
|
9
27
|
</template>
|
|
10
28
|
|
|
@@ -14,5 +32,32 @@ defineProps({
|
|
|
14
32
|
type: Boolean,
|
|
15
33
|
default: false,
|
|
16
34
|
},
|
|
35
|
+
/** The request behind this panel failed - say so instead of showing "none". */
|
|
36
|
+
error: {
|
|
37
|
+
type: Boolean,
|
|
38
|
+
default: false,
|
|
39
|
+
},
|
|
17
40
|
});
|
|
18
41
|
</script>
|
|
42
|
+
|
|
43
|
+
<style scoped>
|
|
44
|
+
/* Was `text-blue-grey-lighten-1`, a fixed Vuetify grey that measured 2.4:1 in
|
|
45
|
+
LIGHT mode - the empty state was the hardest thing on the page to read. The
|
|
46
|
+
muted token is the design's own and follows the theme. */
|
|
47
|
+
.dashboard-empty {
|
|
48
|
+
font-family: var(--font-sans);
|
|
49
|
+
color: var(--muted);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* `--err` is the design's own error tone and is AA in both themes; the hint
|
|
53
|
+
line stays on `--text2` so the explanation is never the palest thing here
|
|
54
|
+
(a dimmed explanation is the one line that must stay readable). */
|
|
55
|
+
.dashboard-empty--error {
|
|
56
|
+
color: var(--err);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.dashboard-empty__hint {
|
|
60
|
+
color: var(--text2);
|
|
61
|
+
max-width: 34ch;
|
|
62
|
+
}
|
|
63
|
+
</style>
|