@7365admin1/layer-common 3.2.1 → 3.2.2-staging.100
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/.changeset/camera-wall-gated-endpoint.md +47 -0
- package/.changeset/camera-wall-plain-english.md +60 -0
- package/.changeset/camera-wall-selection-and-guidance.md +65 -0
- package/.changeset/dark-primary-contrast.md +39 -0
- package/.changeset/dashboard-dark-theme-contrast.md +15 -0
- package/.changeset/notification-preferences.md +17 -0
- package/.changeset/pm-sidebar-grouping-and-list-scaffold.md +27 -0
- package/.changeset/service-provider-invitation-actions.md +28 -0
- package/assets/css/primitives.css +680 -0
- package/assets/css/screens.css +541 -0
- package/assets/css/shell.css +258 -0
- package/assets/css/tokens.css +220 -0
- package/components/AccessCardQrTagging.vue +1 -1
- package/components/AccessManagement.vue +53 -72
- package/components/AppButton.vue +52 -0
- package/components/AppCard.vue +20 -0
- package/components/AppDateField.vue +35 -0
- package/components/AppField.vue +46 -0
- package/components/AppKpiTile.vue +65 -0
- package/components/AppSegmented.vue +38 -0
- package/components/AppSelect.vue +203 -0
- package/components/AreaChecklistHistoryMain.vue +6 -2
- package/components/AreaMain.vue +9 -17
- package/components/AttendanceMain.vue +4 -3
- package/components/Avatar/Main.vue +54 -2
- package/components/BillingMain.vue +26 -30
- package/components/BuildingManagement/buildings.vue +27 -38
- package/components/BuildingManagement/units.vue +26 -42
- package/components/BulletinBoardManagement.vue +23 -10
- package/components/CameraWall.vue +1034 -0
- package/components/CameraWallTile.vue +818 -0
- package/components/CardToolbar.vue +37 -0
- package/components/CleaningScheduleMain.vue +29 -46
- package/components/ClientMain.vue +90 -78
- package/components/DashboardEmptyState.vue +11 -1
- package/components/DashboardMain.vue +349 -275
- package/components/DashboardPanel.vue +1 -1
- package/components/DocumentManagement.vue +129 -93
- package/components/EntryPassInformation.vue +4 -4
- package/components/EntryPassMain.vue +49 -27
- package/components/EquipmentItemMain.vue +4 -6
- package/components/EquipmentManagementMain.vue +9 -7
- package/components/FeedbackMain.vue +42 -56
- package/components/FormDialog.vue +60 -21
- package/components/HidAccessLogDashboard.vue +119 -44
- package/components/HidCameraCaptureDialog.vue +199 -0
- package/components/HidIntercomManagement.vue +825 -202
- package/components/HidQrCodeConfiguration.vue +927 -93
- package/components/HidServiceSettingsPanel.vue +20 -1
- package/components/HidUserEnrollment.vue +454 -106
- package/components/InvitationMain.vue +46 -61
- package/components/Layout/Header.vue +289 -37
- package/components/Layout/NavigationDrawer.vue +340 -30
- package/components/ManageChecklistMain.vue +17 -17
- package/components/MemberMain.vue +79 -60
- package/components/MyAttendanceMain.vue +3 -5
- package/components/NavigationItem.vue +9 -0
- package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
- package/components/NotificationSettings.vue +361 -0
- package/components/OnlineFormConfigurationForm.vue +620 -224
- package/components/OnlineFormFill.vue +36 -8
- package/components/OnlineFormsConfiguration.vue +7 -3
- package/components/PageHeader.vue +43 -0
- package/components/PassInformation.vue +104 -45
- package/components/QrTemplate/CreditCardLandscape.vue +19 -12
- package/components/QrTemplate/PrintDialog.vue +209 -196
- package/components/RolePermissionMain.vue +184 -91
- package/components/ScanVisitorQRCode.vue +35 -7
- package/components/ScheduleAreaMain.vue +15 -49
- package/components/ScheduleTaskMain.vue +23 -26
- package/components/ScheduleTastTicketMain.vue +18 -43
- package/components/ServiceProviderInvitationPrompt.vue +150 -0
- package/components/ServiceProviderMain.vue +346 -157
- package/components/SiteSettings.vue +25 -10
- package/components/SpecificAttr.vue +10 -2
- package/components/StatusChip.vue +93 -0
- package/components/SwitchContext.vue +33 -1
- package/components/TableHygiene.vue +29 -46
- package/components/TableList.vue +1 -1
- package/components/TableListSecondary.vue +1 -1
- package/components/TableMain.vue +161 -77
- package/components/TableV3.vue +61 -66
- package/components/UnitMain.vue +9 -17
- package/components/VehicleManagement.vue +20 -9
- package/components/VisitorForm.vue +294 -78
- package/components/VisitorManagement.vue +221 -71
- package/components/VisitorSocketPopUp.vue +56 -2
- package/components/WorkOrder/Main.vue +42 -51
- package/composables/useComment.ts +3 -3
- package/composables/useHidAmico.ts +151 -0
- package/composables/useHidNavigation.ts +0 -7
- package/composables/useLocalAuth.ts +9 -36
- package/composables/useNotificationPreference.ts +87 -0
- package/composables/useOnlineFormPages.ts +2 -0
- package/composables/useOrg.ts +1 -1
- package/composables/useRole.ts +22 -0
- package/composables/useServiceProviderInvitation.ts +145 -0
- package/composables/useSipWebPhone.ts +336 -0
- package/composables/useSiteSettings.ts +50 -0
- package/composables/useThemePreference.ts +63 -0
- package/composables/useVisitor.ts +6 -1
- package/composables/useVisitorSocket.ts +26 -0
- package/composables/useWebUsb.ts +127 -37
- package/nuxt.config.ts +31 -0
- package/package.json +4 -1
- package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
- package/pages/notification-settings.vue +19 -0
- package/plugins/secure-member.client.ts +21 -56
- package/plugins/vuetify.ts +68 -9
- package/test/visitor-socket.test.mjs +36 -0
- package/types/org.d.ts +2 -0
- package/types/site.d.ts +71 -0
- package/utils/camera-wall.test.ts +571 -0
- package/utils/camera-wall.ts +926 -0
- package/utils/nav-sections.test.ts +190 -0
- package/utils/nav-sections.ts +151 -0
- package/utils/status.test.ts +77 -0
- package/utils/status.ts +90 -0
- package/utils/theme.test.ts +534 -0
- package/utils/theme.ts +294 -0
- package/components/HidIdentityMapping.vue +0 -975
- package/middleware/member.ts +0 -4
- package/pages/[org]/[site]/access-mgmt/identity-mapping/index.vue +0 -23
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
THE IN-CARD TOP ROW.
|
|
3
|
+
|
|
4
|
+
The design does NOT put a screen's tabs above its card - they are the card's
|
|
5
|
+
own first row, with the pagination on the opposite end of that same line, and
|
|
6
|
+
the tab underline meets the card's rule. Today those tabs sit in a
|
|
7
|
+
`v-toolbar`'s extension slot, which stacks them BELOW a separate bar and
|
|
8
|
+
costs a whole row of height.
|
|
9
|
+
|
|
10
|
+
Two shapes, both from the handoff: with tabs (`0 20px`, the tabs supply the
|
|
11
|
+
vertical padding) and without (`10px 14px`, a refresh button on the left).
|
|
12
|
+
This component only places things - the tabs, the count and the pager are
|
|
13
|
+
passed in and keep their own behaviour.
|
|
14
|
+
-->
|
|
15
|
+
<template>
|
|
16
|
+
<div class="app-toolbar" :class="{ 'app-toolbar--plain': !$slots.tabs }">
|
|
17
|
+
<div v-if="$slots.tabs" class="app-tabs" role="tablist">
|
|
18
|
+
<slot name="tabs" />
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<slot name="left" />
|
|
22
|
+
|
|
23
|
+
<div class="app-toolbar__spacer" />
|
|
24
|
+
|
|
25
|
+
<div class="app-toolbar__right">
|
|
26
|
+
<span v-if="count" class="app-toolbar__count">{{ count }}</span>
|
|
27
|
+
<slot name="right" />
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<script setup lang="ts">
|
|
33
|
+
defineProps({
|
|
34
|
+
/** The design's `1-1 of 1` range, printed as the screen already computes it. */
|
|
35
|
+
count: { type: String, default: "" },
|
|
36
|
+
});
|
|
37
|
+
</script>
|
|
@@ -17,11 +17,7 @@
|
|
|
17
17
|
>
|
|
18
18
|
<template #extension>
|
|
19
19
|
<v-row no-gutters class="w-100 d-flex flex-column">
|
|
20
|
-
<
|
|
21
|
-
class="w-100 px-3 d-flex align-center ga-5 py-2"
|
|
22
|
-
flat
|
|
23
|
-
:height="60"
|
|
24
|
-
>
|
|
20
|
+
<div class="filter-bar">
|
|
25
21
|
<InputDateTimePicker
|
|
26
22
|
v-model:utc="startDate"
|
|
27
23
|
density="compact"
|
|
@@ -47,7 +43,7 @@
|
|
|
47
43
|
item-value="value"
|
|
48
44
|
label="Status"
|
|
49
45
|
/>
|
|
50
|
-
</
|
|
46
|
+
</div>
|
|
51
47
|
</v-row>
|
|
52
48
|
</template>
|
|
53
49
|
|
|
@@ -60,25 +56,23 @@
|
|
|
60
56
|
: ""
|
|
61
57
|
}}
|
|
62
58
|
</template>
|
|
59
|
+
<!--
|
|
60
|
+
A countdown, not a status word, so it passes the tone the screen has
|
|
61
|
+
already worked out from the time left rather than a word to map.
|
|
62
|
+
-->
|
|
63
63
|
<template #item.closeIn="{ item }">
|
|
64
|
-
<
|
|
64
|
+
<StatusChip
|
|
65
65
|
class="text-capitalize"
|
|
66
|
-
|
|
67
|
-
:
|
|
68
|
-
|
|
69
|
-
>
|
|
70
|
-
{{ remainingTime[item._id] || "00h 00m" }}
|
|
71
|
-
</v-chip>
|
|
66
|
+
:tone="getCloseInTone(item._id)"
|
|
67
|
+
:label="remainingTime[item._id] || '00h 00m'"
|
|
68
|
+
/>
|
|
72
69
|
</template>
|
|
73
70
|
<template #item.status="{ value }">
|
|
74
|
-
<
|
|
71
|
+
<StatusChip
|
|
75
72
|
class="text-capitalize"
|
|
76
|
-
:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
>
|
|
80
|
-
{{ value || "No Status" }}
|
|
81
|
-
</v-chip>
|
|
73
|
+
:status="value"
|
|
74
|
+
:label="value || 'No Status'"
|
|
75
|
+
/>
|
|
82
76
|
</template>
|
|
83
77
|
<template #item.completedAt="{ value }">
|
|
84
78
|
{{
|
|
@@ -159,33 +153,22 @@ const downloadingId = ref<string | null>(null);
|
|
|
159
153
|
const { getCleaningSchedules, downloadChecklistPdf } = useCleaningSchedules();
|
|
160
154
|
const { formatDate } = useUtils();
|
|
161
155
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
case "accepted":
|
|
174
|
-
return "success";
|
|
175
|
-
case "closed":
|
|
176
|
-
case "rejected":
|
|
177
|
-
return "error";
|
|
178
|
-
default:
|
|
179
|
-
return "secondary";
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
const getCloseInColor = (id: string): string => {
|
|
156
|
+
/**
|
|
157
|
+
* The status word now picks its own colour, from the one shared mapping in
|
|
158
|
+
* `utils/status.ts` - which carries every word this screen used to switch on,
|
|
159
|
+
* with the tone it used to give it. The screen no longer keeps a private
|
|
160
|
+
* palette that only it agrees with.
|
|
161
|
+
*
|
|
162
|
+
* The countdown is not a status word, so the thresholds stay here; only the
|
|
163
|
+
* NAMES change, from Vuetify colours to the design's tones. Same three bands,
|
|
164
|
+
* same boundaries.
|
|
165
|
+
*/
|
|
166
|
+
const getCloseInTone = (id: string): "info" | "warn" | "err" => {
|
|
184
167
|
const secs = remainingSeconds.value[id];
|
|
185
|
-
if (secs === undefined) return "
|
|
186
|
-
if (secs <= 0) return "
|
|
187
|
-
if (secs < 3 * 3600) return "
|
|
188
|
-
return "
|
|
168
|
+
if (secs === undefined) return "info";
|
|
169
|
+
if (secs <= 0) return "err";
|
|
170
|
+
if (secs < 3 * 3600) return "warn";
|
|
171
|
+
return "info";
|
|
189
172
|
};
|
|
190
173
|
|
|
191
174
|
const canDownloadSchedule = computed(() => props.canDownloadSchedule);
|
|
@@ -632,7 +632,7 @@ onUnmounted(() => {
|
|
|
632
632
|
.page-wrap {
|
|
633
633
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
634
634
|
font-size: 14px;
|
|
635
|
-
color:
|
|
635
|
+
color: var(--text);
|
|
636
636
|
background: transparent;
|
|
637
637
|
}
|
|
638
638
|
|
|
@@ -643,17 +643,21 @@ onUnmounted(() => {
|
|
|
643
643
|
gap: 12px;
|
|
644
644
|
padding: 12px 0;
|
|
645
645
|
background: transparent;
|
|
646
|
+
/* The title, the 260px search and the invite button do not fit a phone on
|
|
647
|
+
one line, and without this the whole page scrolled sideways. */
|
|
648
|
+
flex-wrap: wrap;
|
|
646
649
|
}
|
|
647
650
|
.topbar-right {
|
|
648
651
|
margin-left: auto;
|
|
649
652
|
display: flex;
|
|
650
653
|
align-items: center;
|
|
651
654
|
gap: 12px;
|
|
655
|
+
flex-wrap: wrap;
|
|
652
656
|
}
|
|
653
657
|
.btn-invite {
|
|
654
|
-
background:
|
|
655
|
-
color:
|
|
656
|
-
border: 1px solid
|
|
658
|
+
background: var(--card);
|
|
659
|
+
color: var(--text);
|
|
660
|
+
border: 1px solid var(--border);
|
|
657
661
|
border-radius: 24px;
|
|
658
662
|
padding: 10px 22px;
|
|
659
663
|
font-size: 14px;
|
|
@@ -662,26 +666,27 @@ onUnmounted(() => {
|
|
|
662
666
|
white-space: nowrap;
|
|
663
667
|
transition: background 0.15s;
|
|
664
668
|
}
|
|
665
|
-
.btn-invite:hover { background:
|
|
669
|
+
.btn-invite:hover { background: var(--hover); }
|
|
666
670
|
|
|
667
671
|
.search-wrap {
|
|
668
672
|
position: relative;
|
|
669
673
|
width: 260px;
|
|
674
|
+
max-width: 100%;
|
|
670
675
|
flex-shrink: 0;
|
|
671
676
|
}
|
|
672
677
|
.search-input {
|
|
673
678
|
width: 100%;
|
|
674
679
|
height: 36px;
|
|
675
|
-
border: 1px solid
|
|
680
|
+
border: 1px solid var(--border);
|
|
676
681
|
border-radius: 8px;
|
|
677
682
|
padding: 0 34px 0 12px;
|
|
678
683
|
font-size: 13px;
|
|
679
|
-
color:
|
|
680
|
-
background:
|
|
684
|
+
color: var(--text);
|
|
685
|
+
background: var(--card);
|
|
681
686
|
outline: none;
|
|
682
687
|
}
|
|
683
|
-
.search-input:focus { border-color:
|
|
684
|
-
.search-input::placeholder { color:
|
|
688
|
+
.search-input:focus { border-color: var(--muted); }
|
|
689
|
+
.search-input::placeholder { color: var(--muted); }
|
|
685
690
|
.search-icon {
|
|
686
691
|
position: absolute;
|
|
687
692
|
right: 12px;
|
|
@@ -698,18 +703,25 @@ onUnmounted(() => {
|
|
|
698
703
|
transform: translateY(-50%);
|
|
699
704
|
border: none;
|
|
700
705
|
background: transparent;
|
|
701
|
-
color:
|
|
706
|
+
color: var(--muted);
|
|
702
707
|
font-size: 16px;
|
|
703
708
|
cursor: pointer;
|
|
704
709
|
line-height: 1;
|
|
705
710
|
padding: 0;
|
|
706
711
|
}
|
|
707
|
-
.clear-btn:hover { color:
|
|
712
|
+
.clear-btn:hover { color: var(--text2); }
|
|
708
713
|
|
|
709
714
|
.filter-wrap {
|
|
710
715
|
display: flex;
|
|
711
716
|
gap: 10px;
|
|
712
|
-
flex-shrink: 0
|
|
717
|
+
/* Three filters do not fit a phone beside the search box. `flex-shrink: 0`
|
|
718
|
+
kept this row at its 463px content width, so it never wrapped and the
|
|
719
|
+
whole page scrolled sideways at 360. */
|
|
720
|
+
flex-wrap: wrap;
|
|
721
|
+
min-width: 0;
|
|
722
|
+
}
|
|
723
|
+
.filter-wrap > * {
|
|
724
|
+
max-width: 100%;
|
|
713
725
|
}
|
|
714
726
|
.filter-field {
|
|
715
727
|
position: relative;
|
|
@@ -721,8 +733,8 @@ onUnmounted(() => {
|
|
|
721
733
|
top: -8px;
|
|
722
734
|
left: 9px;
|
|
723
735
|
font-size: 10px;
|
|
724
|
-
color:
|
|
725
|
-
background:
|
|
736
|
+
color: var(--muted);
|
|
737
|
+
background: var(--card);
|
|
726
738
|
padding: 0 3px;
|
|
727
739
|
pointer-events: none;
|
|
728
740
|
z-index: 1;
|
|
@@ -730,18 +742,18 @@ onUnmounted(() => {
|
|
|
730
742
|
}
|
|
731
743
|
.filter-field select {
|
|
732
744
|
height: 36px;
|
|
733
|
-
border: 1px solid
|
|
745
|
+
border: 1px solid var(--border);
|
|
734
746
|
border-radius: 8px;
|
|
735
747
|
padding: 0 28px 0 10px;
|
|
736
748
|
font-size: 13px;
|
|
737
|
-
color:
|
|
738
|
-
background:
|
|
749
|
+
color: var(--text);
|
|
750
|
+
background: var(--card);
|
|
739
751
|
appearance: none;
|
|
740
752
|
cursor: pointer;
|
|
741
753
|
outline: none;
|
|
742
754
|
min-width: 110px;
|
|
743
755
|
}
|
|
744
|
-
.filter-field select:focus { border-color:
|
|
756
|
+
.filter-field select:focus { border-color: var(--muted); }
|
|
745
757
|
.chevron {
|
|
746
758
|
position: absolute;
|
|
747
759
|
right: 9px;
|
|
@@ -755,8 +767,8 @@ onUnmounted(() => {
|
|
|
755
767
|
/* ===== TABLE CARD ===== */
|
|
756
768
|
.table-card {
|
|
757
769
|
margin: 12px 0 0 0;
|
|
758
|
-
background:
|
|
759
|
-
border: 1px solid
|
|
770
|
+
background: var(--card);
|
|
771
|
+
border: 1px solid var(--border);
|
|
760
772
|
border-radius: 10px;
|
|
761
773
|
overflow: hidden;
|
|
762
774
|
}
|
|
@@ -767,8 +779,8 @@ onUnmounted(() => {
|
|
|
767
779
|
align-items: center;
|
|
768
780
|
justify-content: space-between;
|
|
769
781
|
padding: 6px 12px;
|
|
770
|
-
background:
|
|
771
|
-
border-bottom: 1px solid
|
|
782
|
+
background: var(--card);
|
|
783
|
+
border-bottom: 1px solid var(--hover);
|
|
772
784
|
}
|
|
773
785
|
.toolbar-left { display: flex; align-items: center; }
|
|
774
786
|
.toolbar-right { display: flex; align-items: center; gap: 6px; }
|
|
@@ -777,7 +789,7 @@ onUnmounted(() => {
|
|
|
777
789
|
background: none;
|
|
778
790
|
border: none;
|
|
779
791
|
cursor: pointer;
|
|
780
|
-
color:
|
|
792
|
+
color: var(--muted);
|
|
781
793
|
width: 28px;
|
|
782
794
|
height: 28px;
|
|
783
795
|
border-radius: 6px;
|
|
@@ -786,14 +798,14 @@ onUnmounted(() => {
|
|
|
786
798
|
justify-content: center;
|
|
787
799
|
transition: background 0.15s;
|
|
788
800
|
}
|
|
789
|
-
.refresh-btn:hover { background:
|
|
801
|
+
.refresh-btn:hover { background: var(--hover); }
|
|
790
802
|
.refresh-btn.spinning svg { animation: spin 0.8s linear infinite; }
|
|
791
803
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
792
804
|
|
|
793
|
-
.page-range-text { font-size: 12px; color:
|
|
805
|
+
.page-range-text { font-size: 12px; color: var(--muted); }
|
|
794
806
|
.page-btn {
|
|
795
807
|
background: none;
|
|
796
|
-
border: 1px solid
|
|
808
|
+
border: 1px solid var(--border);
|
|
797
809
|
border-radius: 5px;
|
|
798
810
|
width: 24px;
|
|
799
811
|
height: 24px;
|
|
@@ -801,22 +813,22 @@ onUnmounted(() => {
|
|
|
801
813
|
align-items: center;
|
|
802
814
|
justify-content: center;
|
|
803
815
|
cursor: pointer;
|
|
804
|
-
color:
|
|
816
|
+
color: var(--text2);
|
|
805
817
|
transition: background 0.1s;
|
|
806
818
|
}
|
|
807
|
-
.page-btn:hover:not(:disabled) { background:
|
|
819
|
+
.page-btn:hover:not(:disabled) { background: var(--hover); }
|
|
808
820
|
.page-btn:disabled { opacity: 0.35; cursor: not-allowed; }
|
|
809
821
|
|
|
810
822
|
/* ===== TABS ===== */
|
|
811
823
|
.tabs-row {
|
|
812
824
|
display: flex;
|
|
813
|
-
border-bottom: 1px solid
|
|
825
|
+
border-bottom: 1px solid var(--border);
|
|
814
826
|
padding: 0 16px;
|
|
815
827
|
}
|
|
816
828
|
.tab {
|
|
817
829
|
padding: 10px 14px;
|
|
818
830
|
font-size: 13px;
|
|
819
|
-
color:
|
|
831
|
+
color: var(--muted);
|
|
820
832
|
background: none;
|
|
821
833
|
border: none;
|
|
822
834
|
border-bottom: 2px solid transparent;
|
|
@@ -824,15 +836,15 @@ onUnmounted(() => {
|
|
|
824
836
|
margin-bottom: -1px;
|
|
825
837
|
transition: color 0.15s, border-color 0.15s;
|
|
826
838
|
}
|
|
827
|
-
.tab:hover { color:
|
|
828
|
-
.tab.active { color:
|
|
839
|
+
.tab:hover { color: var(--text2); }
|
|
840
|
+
.tab.active { color: var(--text); border-bottom-color: var(--text); font-weight: 500; }
|
|
829
841
|
|
|
830
842
|
/* ===== LOADING BAR ===== */
|
|
831
|
-
.loading-bar { height: 2px; background:
|
|
843
|
+
.loading-bar { height: 2px; background: var(--hover); overflow: hidden; }
|
|
832
844
|
.loading-bar-fill {
|
|
833
845
|
height: 100%;
|
|
834
846
|
width: 35%;
|
|
835
|
-
background:
|
|
847
|
+
background: var(--muted);
|
|
836
848
|
animation: slide 1.1s ease-in-out infinite;
|
|
837
849
|
}
|
|
838
850
|
@keyframes slide {
|
|
@@ -848,34 +860,34 @@ thead th {
|
|
|
848
860
|
text-align: left;
|
|
849
861
|
font-weight: 600;
|
|
850
862
|
font-size: 13px;
|
|
851
|
-
color:
|
|
852
|
-
border-bottom: 1px solid
|
|
863
|
+
color: var(--text2);
|
|
864
|
+
border-bottom: 1px solid var(--border);
|
|
853
865
|
white-space: nowrap;
|
|
854
|
-
background:
|
|
866
|
+
background: var(--card);
|
|
855
867
|
}
|
|
856
|
-
tbody tr { border-bottom: 1px solid
|
|
868
|
+
tbody tr { border-bottom: 1px solid var(--hover); transition: background 0.1s; }
|
|
857
869
|
tbody tr:last-child { border-bottom: none; }
|
|
858
|
-
tbody tr:hover { background:
|
|
870
|
+
tbody tr:hover { background: var(--thead); }
|
|
859
871
|
tbody td { padding: 14px 16px; vertical-align: middle; }
|
|
860
872
|
|
|
861
|
-
.empty { text-align: center; color:
|
|
862
|
-
.td-name { font-weight: 500; color:
|
|
863
|
-
.td-muted { color:
|
|
873
|
+
.empty { text-align: center; color: var(--muted); padding: 48px 0 !important; }
|
|
874
|
+
.td-name { font-weight: 500; color: var(--text); }
|
|
875
|
+
.td-muted { color: var(--muted); font-size: 13px; }
|
|
864
876
|
|
|
865
877
|
.sites-badge {
|
|
866
878
|
display: inline-block;
|
|
867
|
-
background:
|
|
879
|
+
background: var(--hover);
|
|
868
880
|
border-radius: 20px;
|
|
869
881
|
padding: 3px 12px;
|
|
870
882
|
font-size: 12px;
|
|
871
|
-
color:
|
|
883
|
+
color: var(--text2);
|
|
872
884
|
}
|
|
873
885
|
.date-cell {
|
|
874
886
|
display: inline-flex;
|
|
875
887
|
align-items: center;
|
|
876
888
|
gap: 5px;
|
|
877
889
|
font-size: 13px;
|
|
878
|
-
color:
|
|
890
|
+
color: var(--text2);
|
|
879
891
|
}
|
|
880
892
|
|
|
881
893
|
/* ===== STATUS CHIPS ===== */
|
|
@@ -889,12 +901,12 @@ tbody td { padding: 14px 16px; vertical-align: middle; }
|
|
|
889
901
|
font-weight: 500;
|
|
890
902
|
}
|
|
891
903
|
.status-chip::before { content: ''; width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
|
|
892
|
-
.status-chip.active { background:
|
|
893
|
-
.status-chip.active::before { background:
|
|
894
|
-
.status-chip.suspended { background:
|
|
895
|
-
.status-chip.suspended::before { background:
|
|
896
|
-
.status-chip.pending { background:
|
|
897
|
-
.status-chip.pending::before { background:
|
|
904
|
+
.status-chip.active { background: var(--ok-bg); color: var(--ok); }
|
|
905
|
+
.status-chip.active::before { background: currentColor; }
|
|
906
|
+
.status-chip.suspended { background: var(--err-bg); color: var(--err); }
|
|
907
|
+
.status-chip.suspended::before { background: currentColor; }
|
|
908
|
+
.status-chip.pending { background: var(--warn-bg); color: var(--warn); }
|
|
909
|
+
.status-chip.pending::before { background: currentColor; }
|
|
898
910
|
|
|
899
911
|
/* ===== ACTION MENU ===== */
|
|
900
912
|
.action-wrap { position: relative; }
|
|
@@ -907,16 +919,16 @@ tbody td { padding: 14px 16px; vertical-align: middle; }
|
|
|
907
919
|
border-radius: 6px;
|
|
908
920
|
padding: 6px 8px;
|
|
909
921
|
cursor: pointer;
|
|
910
|
-
color:
|
|
922
|
+
color: var(--muted);
|
|
911
923
|
transition: background 0.15s;
|
|
912
924
|
}
|
|
913
|
-
.dots-btn:hover { background:
|
|
925
|
+
.dots-btn:hover { background: var(--hover); color: var(--text2); }
|
|
914
926
|
.dots-btn span { display: block; width: 4px; height: 4px; border-radius: 50%; background: currentColor; }
|
|
915
927
|
|
|
916
928
|
.dropdown {
|
|
917
929
|
position: fixed;
|
|
918
|
-
background:
|
|
919
|
-
border: 1px solid
|
|
930
|
+
background: var(--card);
|
|
931
|
+
border: 1px solid var(--border);
|
|
920
932
|
border-radius: 8px;
|
|
921
933
|
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
|
922
934
|
min-width: 130px;
|
|
@@ -932,12 +944,12 @@ tbody td { padding: 14px 16px; vertical-align: middle; }
|
|
|
932
944
|
background: none;
|
|
933
945
|
border: none;
|
|
934
946
|
cursor: pointer;
|
|
935
|
-
color:
|
|
947
|
+
color: var(--text2);
|
|
936
948
|
transition: background 0.1s;
|
|
937
949
|
}
|
|
938
|
-
.dd-item:hover { background:
|
|
939
|
-
.dd-item.danger { color:
|
|
940
|
-
.dd-item.danger:hover { background:
|
|
950
|
+
.dd-item:hover { background: var(--hover); }
|
|
951
|
+
.dd-item.danger { color: var(--err); }
|
|
952
|
+
.dd-item.danger:hover { background: var(--err-bg); }
|
|
941
953
|
|
|
942
954
|
.cancel-btn {
|
|
943
955
|
display: inline-flex;
|
|
@@ -945,27 +957,27 @@ tbody td { padding: 14px 16px; vertical-align: middle; }
|
|
|
945
957
|
gap: 6px;
|
|
946
958
|
padding: 5px 12px;
|
|
947
959
|
font-size: 12px;
|
|
948
|
-
color:
|
|
949
|
-
background:
|
|
950
|
-
border: 1px solid
|
|
960
|
+
color: var(--err);
|
|
961
|
+
background: var(--err-bg);
|
|
962
|
+
border: 1px solid var(--err-bg);
|
|
951
963
|
border-radius: 6px;
|
|
952
964
|
cursor: pointer;
|
|
953
965
|
transition: background 0.15s;
|
|
954
966
|
}
|
|
955
|
-
.cancel-btn:hover { background:
|
|
967
|
+
.cancel-btn:hover { background: var(--err-bg); }
|
|
956
968
|
.cancel-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
957
969
|
.spinner {
|
|
958
970
|
display: inline-block;
|
|
959
971
|
width: 11px;
|
|
960
972
|
height: 11px;
|
|
961
|
-
border: 1.5px solid
|
|
962
|
-
border-top-color:
|
|
973
|
+
border: 1.5px solid var(--err-bg);
|
|
974
|
+
border-top-color: var(--err);
|
|
963
975
|
border-radius: 50%;
|
|
964
976
|
animation: spin 0.7s linear infinite;
|
|
965
977
|
}
|
|
966
978
|
.spinner-light {
|
|
967
979
|
border: 1.5px solid rgba(255,255,255,0.5);
|
|
968
|
-
border-top-color:
|
|
980
|
+
border-top-color: var(--card);
|
|
969
981
|
}
|
|
970
982
|
|
|
971
983
|
.client-detail {
|
|
@@ -982,7 +994,7 @@ tbody td { padding: 14px 16px; vertical-align: middle; }
|
|
|
982
994
|
z-index: 1000;
|
|
983
995
|
}
|
|
984
996
|
.dialog-box {
|
|
985
|
-
background:
|
|
997
|
+
background: var(--card);
|
|
986
998
|
border-radius: 12px;
|
|
987
999
|
max-width: 500px;
|
|
988
1000
|
width: 90%;
|
|
@@ -992,7 +1004,7 @@ tbody td { padding: 14px 16px; vertical-align: middle; }
|
|
|
992
1004
|
|
|
993
1005
|
/* ===== SUSPEND CONFIRMATION DIALOG ===== */
|
|
994
1006
|
.confirm-box {
|
|
995
|
-
background:
|
|
1007
|
+
background: var(--card);
|
|
996
1008
|
border-radius: 12px;
|
|
997
1009
|
max-width: 380px;
|
|
998
1010
|
width: 90%;
|
|
@@ -1002,12 +1014,12 @@ tbody td { padding: 14px 16px; vertical-align: middle; }
|
|
|
1002
1014
|
.confirm-title {
|
|
1003
1015
|
font-size: 16px;
|
|
1004
1016
|
font-weight: 600;
|
|
1005
|
-
color:
|
|
1017
|
+
color: var(--text);
|
|
1006
1018
|
margin-bottom: 8px;
|
|
1007
1019
|
}
|
|
1008
1020
|
.confirm-text {
|
|
1009
1021
|
font-size: 13.5px;
|
|
1010
|
-
color:
|
|
1022
|
+
color: var(--text2);
|
|
1011
1023
|
line-height: 1.5;
|
|
1012
1024
|
margin-bottom: 20px;
|
|
1013
1025
|
}
|
|
@@ -1020,14 +1032,14 @@ tbody td { padding: 14px 16px; vertical-align: middle; }
|
|
|
1020
1032
|
padding: 8px 16px;
|
|
1021
1033
|
font-size: 13px;
|
|
1022
1034
|
font-weight: 500;
|
|
1023
|
-
border: 1px solid
|
|
1035
|
+
border: 1px solid var(--border);
|
|
1024
1036
|
border-radius: 8px;
|
|
1025
|
-
background:
|
|
1026
|
-
color:
|
|
1037
|
+
background: var(--card);
|
|
1038
|
+
color: var(--text2);
|
|
1027
1039
|
cursor: pointer;
|
|
1028
1040
|
transition: background 0.15s;
|
|
1029
1041
|
}
|
|
1030
|
-
.btn-cancel:hover { background:
|
|
1042
|
+
.btn-cancel:hover { background: var(--hover); }
|
|
1031
1043
|
.btn-confirm-danger {
|
|
1032
1044
|
display: inline-flex;
|
|
1033
1045
|
align-items: center;
|
|
@@ -1039,11 +1051,11 @@ tbody td { padding: 14px 16px; vertical-align: middle; }
|
|
|
1039
1051
|
font-weight: 500;
|
|
1040
1052
|
border: none;
|
|
1041
1053
|
border-radius: 8px;
|
|
1042
|
-
background:
|
|
1043
|
-
color:
|
|
1054
|
+
background: var(--err);
|
|
1055
|
+
color: var(--card);
|
|
1044
1056
|
cursor: pointer;
|
|
1045
1057
|
transition: background 0.15s;
|
|
1046
1058
|
}
|
|
1047
|
-
.btn-confirm-danger:hover:not(:disabled) { background:
|
|
1059
|
+
.btn-confirm-danger:hover:not(:disabled) { background: var(--err); }
|
|
1048
1060
|
.btn-confirm-danger:disabled { opacity: 0.7; cursor: not-allowed; }
|
|
1049
1061
|
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
class="d-flex flex-column align-center justify-center text-center
|
|
3
|
+
class="d-flex flex-column align-center justify-center text-center dashboard-empty"
|
|
4
4
|
:style="{ minHeight: compact ? '72px' : '140px', gap: '8px' }"
|
|
5
5
|
>
|
|
6
6
|
<v-icon icon="mdi-database-search-outline" size="28" />
|
|
@@ -16,3 +16,13 @@ defineProps({
|
|
|
16
16
|
},
|
|
17
17
|
});
|
|
18
18
|
</script>
|
|
19
|
+
|
|
20
|
+
<style scoped>
|
|
21
|
+
/* Was `text-blue-grey-lighten-1`, a fixed Vuetify grey that measured 2.4:1 in
|
|
22
|
+
LIGHT mode - the empty state was the hardest thing on the page to read. The
|
|
23
|
+
muted token is the design's own and follows the theme. */
|
|
24
|
+
.dashboard-empty {
|
|
25
|
+
font-family: var(--font-sans);
|
|
26
|
+
color: var(--muted);
|
|
27
|
+
}
|
|
28
|
+
</style>
|