@7365admin1/layer-common 3.2.2-staging.124 → 3.2.2-staging.126
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/components/AccessCardDetailsDialog.vue +125 -79
- package/components/AttendanceMapSearchDialog.vue +42 -44
- package/components/AttendanceSettingsDialog.vue +62 -63
- package/components/DashboardMain.vue +151 -283
- package/components/DashboardPanel.vue +84 -21
- package/components/RolePermissionFormCreate.vue +104 -78
- package/components/RolePermissionFormPreviewUpdate.vue +111 -108
- package/components/VisitorForm.vue +402 -218
- package/package.json +1 -1
- package/utils/status.test.ts +2 -2
- package/utils/status.ts +4 -0
|
@@ -1,117 +1,109 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-dialog :model-value="modelValue" width="480" persistent>
|
|
3
|
-
<v-card width="100%">
|
|
4
|
-
<
|
|
5
|
-
<v-
|
|
3
|
+
<v-card width="100%" class="screen-modal card-details">
|
|
4
|
+
<div class="card-details__head">
|
|
5
|
+
<v-card-title>
|
|
6
6
|
Card Details — {{ card?.cardNo ?? "N/A" }}
|
|
7
|
-
</v-
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
</v-toolbar>
|
|
7
|
+
</v-card-title>
|
|
8
|
+
<AppButton variant="icon" icon="mdi-close" aria-label="Close"
|
|
9
|
+
@click="emit('update:modelValue', false)" />
|
|
10
|
+
</div>
|
|
12
11
|
|
|
13
|
-
<
|
|
12
|
+
<div class="screen-modal__body card-details__body">
|
|
14
13
|
<div v-if="pending" class="d-flex justify-center align-center py-8">
|
|
15
|
-
<v-progress-circular indeterminate color="
|
|
14
|
+
<v-progress-circular indeterminate color="accent" />
|
|
16
15
|
</div>
|
|
17
16
|
|
|
18
|
-
<div v-else-if="!details" class="
|
|
17
|
+
<div v-else-if="!details" class="card-details__empty">
|
|
19
18
|
No details available.
|
|
20
19
|
</div>
|
|
21
20
|
|
|
22
21
|
<div v-else class="d-flex flex-column ga-3">
|
|
23
22
|
<div>
|
|
24
|
-
<div class="
|
|
25
|
-
<div class="
|
|
23
|
+
<div class="card-details__label">Card No</div>
|
|
24
|
+
<div class="card-details__value">{{ details.cardNo ?? "N/A" }}</div>
|
|
26
25
|
</div>
|
|
27
26
|
|
|
28
27
|
<div>
|
|
29
|
-
<div class="
|
|
30
|
-
<div class="
|
|
28
|
+
<div class="card-details__label">Type</div>
|
|
29
|
+
<div class="card-details__value">{{ details.type ?? "N/A" }}</div>
|
|
31
30
|
</div>
|
|
32
31
|
|
|
33
32
|
<div v-if="details.status">
|
|
34
|
-
<div class="
|
|
35
|
-
<
|
|
36
|
-
:color="statusColor(details.status)"
|
|
37
|
-
variant="flat"
|
|
38
|
-
size="x-small"
|
|
39
|
-
class="text-capitalize font-weight-medium mt-1"
|
|
40
|
-
>
|
|
41
|
-
{{ details.status ?? "N/A" }}
|
|
42
|
-
</v-chip>
|
|
33
|
+
<div class="card-details__label">Status</div>
|
|
34
|
+
<StatusChip class="mt-1 text-capitalize" :status="details.status" />
|
|
43
35
|
</div>
|
|
44
36
|
|
|
45
37
|
<div>
|
|
46
|
-
<div class="
|
|
47
|
-
<div class="
|
|
38
|
+
<div class="card-details__label">Activated</div>
|
|
39
|
+
<div class="card-details__value">{{ details.isActivated ? "Yes" : "No" }}</div>
|
|
48
40
|
</div>
|
|
49
41
|
|
|
50
42
|
<div v-if="details.site?.name">
|
|
51
|
-
<div class="
|
|
52
|
-
<div class="
|
|
43
|
+
<div class="card-details__label">Site</div>
|
|
44
|
+
<div class="card-details__value">{{ details.site?.name ?? "N/A" }}</div>
|
|
53
45
|
</div>
|
|
54
46
|
|
|
55
47
|
<div>
|
|
56
|
-
<div class="
|
|
57
|
-
<div class="
|
|
48
|
+
<div class="card-details__label">User</div>
|
|
49
|
+
<div class="card-details__value">
|
|
58
50
|
{{ details.userCred?.name ?? details.user?.name ?? "N/A" }}
|
|
59
51
|
<template v-if="details.user?.email">({{ details.user.email }})</template>
|
|
60
52
|
</div>
|
|
61
53
|
</div>
|
|
62
54
|
|
|
63
55
|
<div v-if="details.userType">
|
|
64
|
-
<div class="
|
|
65
|
-
<div class="
|
|
56
|
+
<div class="card-details__label">User Type</div>
|
|
57
|
+
<div class="card-details__value">{{ details.userType }}</div>
|
|
66
58
|
</div>
|
|
67
59
|
|
|
68
60
|
<!-- Visitor / Resident Credentials -->
|
|
69
61
|
<template v-if="details.userCred">
|
|
70
62
|
<v-divider />
|
|
71
|
-
<div class="
|
|
63
|
+
<div class="card-details__section">
|
|
72
64
|
{{ details.userType ?? "Visitor Information" }}
|
|
73
65
|
</div>
|
|
74
66
|
|
|
75
67
|
<div v-if="details.userCred.email">
|
|
76
|
-
<div class="
|
|
77
|
-
<div class="
|
|
68
|
+
<div class="card-details__label">Email</div>
|
|
69
|
+
<div class="card-details__value">{{ details.userCred.email }}</div>
|
|
78
70
|
</div>
|
|
79
71
|
|
|
80
72
|
<div v-if="details.userCred.nric">
|
|
81
|
-
<div class="
|
|
82
|
-
<div class="
|
|
73
|
+
<div class="card-details__label">NRIC</div>
|
|
74
|
+
<div class="card-details__value">{{ details.userCred.nric }}</div>
|
|
83
75
|
</div>
|
|
84
76
|
|
|
85
77
|
<div v-if="details.userCred.contact">
|
|
86
|
-
<div class="
|
|
87
|
-
<div class="
|
|
78
|
+
<div class="card-details__label">Contact</div>
|
|
79
|
+
<div class="card-details__value">{{ details.userCred.contact }}</div>
|
|
88
80
|
</div>
|
|
89
81
|
|
|
90
82
|
<div v-if="details.userCred.company">
|
|
91
|
-
<div class="
|
|
92
|
-
<div class="
|
|
83
|
+
<div class="card-details__label">Company</div>
|
|
84
|
+
<div class="card-details__value">{{ details.userCred.company }}</div>
|
|
93
85
|
</div>
|
|
94
86
|
|
|
95
87
|
<div v-if="details.userCred.plateNumber">
|
|
96
|
-
<div class="
|
|
97
|
-
<div class="
|
|
88
|
+
<div class="card-details__label">Plate Number</div>
|
|
89
|
+
<div class="card-details__value">{{ details.userCred.plateNumber }}</div>
|
|
98
90
|
</div>
|
|
99
91
|
<v-divider />
|
|
100
92
|
</template>
|
|
101
93
|
|
|
102
94
|
<div>
|
|
103
|
-
<div class="
|
|
104
|
-
<div class="
|
|
95
|
+
<div class="card-details__label">Created At</div>
|
|
96
|
+
<div class="card-details__value">{{ formatDate(details.createdAt) }}</div>
|
|
105
97
|
</div>
|
|
106
98
|
|
|
107
99
|
<div>
|
|
108
|
-
<div class="
|
|
109
|
-
<div class="
|
|
100
|
+
<div class="card-details__label">Updated At</div>
|
|
101
|
+
<div class="card-details__value">{{ formatDate(details.updatedAt) }}</div>
|
|
110
102
|
</div>
|
|
111
103
|
|
|
112
104
|
<div v-if="details.remarks">
|
|
113
|
-
<div class="
|
|
114
|
-
<div class="
|
|
105
|
+
<div class="card-details__label">Remarks</div>
|
|
106
|
+
<div class="card-details__value">{{ details.remarks }}</div>
|
|
115
107
|
</div>
|
|
116
108
|
|
|
117
109
|
<!-- History -->
|
|
@@ -121,7 +113,7 @@
|
|
|
121
113
|
class="d-flex align-center justify-space-between cursor-pointer"
|
|
122
114
|
@click="historyOpen = !historyOpen"
|
|
123
115
|
>
|
|
124
|
-
<div class="
|
|
116
|
+
<div class="card-details__section">
|
|
125
117
|
History ({{ details.history.length }})
|
|
126
118
|
</div>
|
|
127
119
|
<v-icon size="16" :icon="historyOpen ? 'mdi-chevron-up' : 'mdi-chevron-down'" />
|
|
@@ -137,37 +129,36 @@
|
|
|
137
129
|
<div class="d-flex flex-column align-center" style="width: 20px; flex-shrink: 0;">
|
|
138
130
|
<div
|
|
139
131
|
class="timeline-dot rounded-circle mt-1"
|
|
140
|
-
:class="(entry
|
|
132
|
+
:class="isAssignment(entry) ? 'timeline-dot--assigned' : 'timeline-dot--returned'"
|
|
141
133
|
/>
|
|
142
134
|
<div
|
|
143
135
|
v-if="idx < sortedHistory.length - 1"
|
|
144
|
-
class="timeline-line
|
|
136
|
+
class="timeline-line mt-1"
|
|
145
137
|
/>
|
|
146
138
|
</div>
|
|
147
139
|
|
|
148
140
|
<!-- Entry content -->
|
|
149
141
|
<div class="d-flex flex-column ga-1 pb-4 flex-grow-1">
|
|
150
142
|
<div class="d-flex align-center justify-space-between">
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
<span class="text-caption text-grey">{{ formatDate(entry.createdAt) }}</span>
|
|
143
|
+
<StatusChip :status="isAssignment(entry) ? 'Assigned' : 'Returned'">
|
|
144
|
+
<v-icon
|
|
145
|
+
size="13"
|
|
146
|
+
class="mr-1"
|
|
147
|
+
:icon="isAssignment(entry) ? 'mdi-account-arrow-right' : 'mdi-arrow-u-left-top'"
|
|
148
|
+
/>{{ isAssignment(entry) ? "Assigned" : "Returned" }}
|
|
149
|
+
</StatusChip>
|
|
150
|
+
<span class="card-details__label">{{ formatDate(entry.createdAt) }}</span>
|
|
160
151
|
</div>
|
|
161
152
|
|
|
162
153
|
<!-- Assignment entry -->
|
|
163
154
|
<template v-if="entry.type === 'assigned' || entry.visitorId">
|
|
164
|
-
<div class="
|
|
165
|
-
<div class="
|
|
155
|
+
<div class="card-details__value">{{ entry.name ?? "N/A" }}</div>
|
|
156
|
+
<div class="card-details__sub">
|
|
166
157
|
<span v-if="entry.nric">NRIC: {{ entry.nric }}</span>
|
|
167
158
|
<span v-if="entry.contact" class="ml-2">· {{ entry.contact }}</span>
|
|
168
159
|
<span v-if="entry.email && !entry.nric && !entry.contact">{{ entry.email }}</span>
|
|
169
160
|
</div>
|
|
170
|
-
<div class="
|
|
161
|
+
<div class="card-details__sub">
|
|
171
162
|
<span v-if="entry.company">{{ entry.company }}</span>
|
|
172
163
|
<span v-if="entry.plateNumber" class="ml-2">· {{ entry.plateNumber }}</span>
|
|
173
164
|
</div>
|
|
@@ -176,12 +167,10 @@
|
|
|
176
167
|
<!-- Return/checkout entry -->
|
|
177
168
|
<template v-else>
|
|
178
169
|
<div class="d-flex align-center ga-2">
|
|
179
|
-
<
|
|
180
|
-
|
|
181
|
-
</v-chip>
|
|
182
|
-
<span v-if="entry.damage" class="text-caption text-error">Damaged</span>
|
|
170
|
+
<StatusChip :status="entry.status" :label="entry.status ?? 'N/A'" />
|
|
171
|
+
<span v-if="entry.damage" class="card-details__damage">Damaged</span>
|
|
183
172
|
</div>
|
|
184
|
-
<div v-if="entry.remarks" class="
|
|
173
|
+
<div v-if="entry.remarks" class="card-details__sub">
|
|
185
174
|
Remarks: {{ entry.remarks }}
|
|
186
175
|
</div>
|
|
187
176
|
</template>
|
|
@@ -190,7 +179,7 @@
|
|
|
190
179
|
</template>
|
|
191
180
|
</template>
|
|
192
181
|
</div>
|
|
193
|
-
</
|
|
182
|
+
</div>
|
|
194
183
|
</v-card>
|
|
195
184
|
</v-dialog>
|
|
196
185
|
</template>
|
|
@@ -245,15 +234,12 @@ watch(
|
|
|
245
234
|
}
|
|
246
235
|
);
|
|
247
236
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
available: "grey",
|
|
255
|
-
};
|
|
256
|
-
return map[status] ?? "grey";
|
|
237
|
+
/**
|
|
238
|
+
* A history entry is an ASSIGNMENT if it says so or carries a visitor; the
|
|
239
|
+
* alternative is a return. Was written out three times inline.
|
|
240
|
+
*/
|
|
241
|
+
function isAssignment(entry: Record<string, any>) {
|
|
242
|
+
return entry.type === "assigned" || Boolean(entry.visitorId);
|
|
257
243
|
}
|
|
258
244
|
|
|
259
245
|
function formatDate(date: string) {
|
|
@@ -270,14 +256,74 @@ function formatDate(date: string) {
|
|
|
270
256
|
</script>
|
|
271
257
|
|
|
272
258
|
<style scoped>
|
|
259
|
+
.card-details__head {
|
|
260
|
+
display: flex;
|
|
261
|
+
align-items: center;
|
|
262
|
+
justify-content: space-between;
|
|
263
|
+
padding-right: 12px;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.card-details__body {
|
|
267
|
+
padding: 0 20px 20px;
|
|
268
|
+
max-height: 70vh;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.card-details__label {
|
|
272
|
+
font-size: var(--fs-breadcrumb);
|
|
273
|
+
color: var(--text2);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.card-details__value {
|
|
277
|
+
font-size: var(--fs-cell);
|
|
278
|
+
font-weight: var(--fw-cell);
|
|
279
|
+
color: var(--text);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.card-details__section {
|
|
283
|
+
font-size: var(--fs-section-label);
|
|
284
|
+
font-weight: var(--fw-section-label);
|
|
285
|
+
letter-spacing: var(--ls-section-label);
|
|
286
|
+
text-transform: uppercase;
|
|
287
|
+
color: var(--text2);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.card-details__sub {
|
|
291
|
+
font-size: var(--fs-breadcrumb);
|
|
292
|
+
color: var(--text2);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.card-details__empty {
|
|
296
|
+
padding: 32px 0;
|
|
297
|
+
text-align: center;
|
|
298
|
+
font-size: var(--fs-cell);
|
|
299
|
+
color: var(--muted);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.card-details__damage {
|
|
303
|
+
font-size: var(--fs-breadcrumb);
|
|
304
|
+
color: var(--err);
|
|
305
|
+
}
|
|
306
|
+
|
|
273
307
|
.timeline-dot {
|
|
274
308
|
width: 10px;
|
|
275
309
|
height: 10px;
|
|
276
310
|
flex-shrink: 0;
|
|
277
311
|
}
|
|
312
|
+
|
|
313
|
+
/* Were `bg-blue` / `bg-orange` / `bg-grey-lighten-2` - three fixed Vuetify hues
|
|
314
|
+
that do not follow the theme. Same meanings, as tokens. */
|
|
315
|
+
.timeline-dot--assigned {
|
|
316
|
+
background: var(--info);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.timeline-dot--returned {
|
|
320
|
+
background: var(--warn);
|
|
321
|
+
}
|
|
322
|
+
|
|
278
323
|
.timeline-line {
|
|
279
324
|
width: 2px;
|
|
280
325
|
flex-grow: 1;
|
|
281
326
|
min-height: 16px;
|
|
327
|
+
background: var(--border);
|
|
282
328
|
}
|
|
283
329
|
</style>
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-dialog v-model="showDialog" max-width="500" persistent>
|
|
3
|
-
<v-card>
|
|
4
|
-
<
|
|
5
|
-
<v-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
<v-card-text class="pa-6">
|
|
3
|
+
<v-card class="screen-modal map-search">
|
|
4
|
+
<div class="map-search__head">
|
|
5
|
+
<v-card-title>Search Location</v-card-title>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div class="screen-modal__body map-search__body filter-field">
|
|
11
9
|
<v-form ref="formRef" @submit.prevent="handleSearch">
|
|
12
10
|
<v-row dense>
|
|
13
11
|
<v-col cols="12" md="6">
|
|
@@ -64,47 +62,23 @@
|
|
|
64
62
|
</v-col>
|
|
65
63
|
</v-row>
|
|
66
64
|
</v-form>
|
|
67
|
-
</
|
|
68
|
-
|
|
69
|
-
<
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
@click="handleClose"
|
|
80
|
-
>
|
|
81
|
-
Cancel
|
|
82
|
-
</v-btn>
|
|
83
|
-
</v-col>
|
|
84
|
-
<v-col cols="6" class="pa-0">
|
|
85
|
-
<v-btn
|
|
86
|
-
block
|
|
87
|
-
variant="flat"
|
|
88
|
-
color="black"
|
|
89
|
-
class="text-none font-weight-bold rounded-0"
|
|
90
|
-
height="56"
|
|
91
|
-
size="large"
|
|
92
|
-
:loading="searching"
|
|
93
|
-
:disabled="!formData.postalCode"
|
|
94
|
-
@click="handleSearch"
|
|
95
|
-
>
|
|
96
|
-
<v-icon class="mr-2">mdi-magnify</v-icon>
|
|
97
|
-
Search
|
|
98
|
-
</v-btn>
|
|
99
|
-
</v-col>
|
|
100
|
-
</v-row>
|
|
101
|
-
</v-toolbar>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div class="map-search__footer">
|
|
68
|
+
<AppButton variant="ghost" :disabled="searching" @click="handleClose">Cancel</AppButton>
|
|
69
|
+
<AppButton
|
|
70
|
+
icon="mdi-magnify"
|
|
71
|
+
:disabled="searching || !formData.postalCode"
|
|
72
|
+
@click="handleSearch"
|
|
73
|
+
>
|
|
74
|
+
Search
|
|
75
|
+
</AppButton>
|
|
76
|
+
</div>
|
|
102
77
|
</v-card>
|
|
103
78
|
</v-dialog>
|
|
104
79
|
|
|
105
80
|
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
106
81
|
</template>
|
|
107
|
-
That is
|
|
108
82
|
|
|
109
83
|
<script setup lang="ts">
|
|
110
84
|
const showDialog = defineModel({ type: Boolean, default: false });
|
|
@@ -391,3 +365,27 @@ watch(showDialog, (newVal) => {
|
|
|
391
365
|
}
|
|
392
366
|
});
|
|
393
367
|
</script>
|
|
368
|
+
|
|
369
|
+
<style scoped>
|
|
370
|
+
.map-search__head {
|
|
371
|
+
display: flex;
|
|
372
|
+
align-items: center;
|
|
373
|
+
justify-content: space-between;
|
|
374
|
+
padding-right: 12px;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.map-search__body {
|
|
378
|
+
padding: 0 20px;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/* The design's dialog footer - ghost secondary, then one primary, right
|
|
382
|
+
aligned. Deliberately not the shared `.screen-modal__footer`: that class is
|
|
383
|
+
on the batch-D branch (#1150), not on this base, and adding a second copy of
|
|
384
|
+
it to `screens.css` here would collide with that PR. */
|
|
385
|
+
.map-search__footer {
|
|
386
|
+
display: flex;
|
|
387
|
+
justify-content: flex-end;
|
|
388
|
+
gap: 8px;
|
|
389
|
+
padding: 16px 20px 20px;
|
|
390
|
+
}
|
|
391
|
+
</style>
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-dialog v-model="showDialog" max-width="600" persistent>
|
|
3
|
-
<v-card>
|
|
4
|
-
<
|
|
5
|
-
<v-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
<v-card-text class="pa-6">
|
|
3
|
+
<v-card class="screen-modal attendance-settings">
|
|
4
|
+
<div class="attendance-settings__head">
|
|
5
|
+
<v-card-title>Attendance Settings</v-card-title>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div class="screen-modal__body attendance-settings__body filter-field">
|
|
11
9
|
<v-alert
|
|
12
10
|
v-if="error"
|
|
13
11
|
type="error"
|
|
@@ -26,12 +24,13 @@
|
|
|
26
24
|
<v-checkbox
|
|
27
25
|
v-model="isGeofencingEnabled"
|
|
28
26
|
label="Enable Geofencing"
|
|
27
|
+
color="accent"
|
|
29
28
|
density="comfortable"
|
|
30
29
|
hide-details
|
|
31
30
|
/>
|
|
32
31
|
<span
|
|
33
32
|
v-if="isGeofencingEnabled"
|
|
34
|
-
class="
|
|
33
|
+
class="attendance-settings__hint"
|
|
35
34
|
>
|
|
36
35
|
Enabling this setting will require users to take attendance
|
|
37
36
|
within a certain radius via QR code or within the site location.
|
|
@@ -43,44 +42,32 @@
|
|
|
43
42
|
<v-checkbox
|
|
44
43
|
v-model="isQrEnabled"
|
|
45
44
|
label="Enable QR Code"
|
|
45
|
+
color="accent"
|
|
46
46
|
density="comfortable"
|
|
47
47
|
hide-details
|
|
48
48
|
/>
|
|
49
49
|
</v-col>
|
|
50
50
|
|
|
51
51
|
<v-col v-if="isQrEnabled" cols="12">
|
|
52
|
-
<
|
|
53
|
-
variant="flat"
|
|
54
|
-
class="pa-4 d-flex flex-column align-center"
|
|
55
|
-
>
|
|
52
|
+
<div class="attendance-settings__qr">
|
|
56
53
|
<QrcodeVue
|
|
57
54
|
:value="site"
|
|
58
55
|
:level="'M'"
|
|
59
56
|
:render-as="'svg'"
|
|
60
57
|
:size="300"
|
|
61
58
|
/>
|
|
62
|
-
<
|
|
63
|
-
variant="text"
|
|
64
|
-
color="primary"
|
|
65
|
-
class="mt-2"
|
|
66
|
-
@click="downloadQrCode"
|
|
67
|
-
>
|
|
68
|
-
<v-icon class="mr-2">mdi-download</v-icon>
|
|
59
|
+
<AppButton variant="ghost" icon="mdi-download" class="mt-2" @click="downloadQrCode">
|
|
69
60
|
Download QR Code
|
|
70
|
-
</
|
|
71
|
-
</
|
|
61
|
+
</AppButton>
|
|
62
|
+
</div>
|
|
72
63
|
</v-col>
|
|
73
64
|
|
|
74
65
|
<v-col cols="12" class="mt-4">
|
|
75
66
|
<InputLabel title="Site Location" />
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
class="mt-2"
|
|
79
|
-
@click="isMapSearchVisible = true"
|
|
80
|
-
>
|
|
81
|
-
<v-icon class="mr-2">mdi-map-search</v-icon>
|
|
67
|
+
<AppButton variant="ghost" icon="mdi-map-search" class="mt-2"
|
|
68
|
+
@click="isMapSearchVisible = true">
|
|
82
69
|
Search Location
|
|
83
|
-
</
|
|
70
|
+
</AppButton>
|
|
84
71
|
</v-col>
|
|
85
72
|
|
|
86
73
|
<v-col v-if="geolocation" cols="12">
|
|
@@ -125,40 +112,12 @@
|
|
|
125
112
|
</template>
|
|
126
113
|
</v-row>
|
|
127
114
|
</v-form>
|
|
128
|
-
</
|
|
129
|
-
|
|
130
|
-
<
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
block
|
|
135
|
-
variant="text"
|
|
136
|
-
class="text-none"
|
|
137
|
-
size="large"
|
|
138
|
-
@click="close"
|
|
139
|
-
height="56"
|
|
140
|
-
:disabled="submitting"
|
|
141
|
-
>
|
|
142
|
-
Cancel
|
|
143
|
-
</v-btn>
|
|
144
|
-
</v-col>
|
|
145
|
-
<v-col cols="6" class="pa-0">
|
|
146
|
-
<v-btn
|
|
147
|
-
block
|
|
148
|
-
variant="flat"
|
|
149
|
-
color="black"
|
|
150
|
-
class="text-none font-weight-bold rounded-0"
|
|
151
|
-
height="56"
|
|
152
|
-
size="large"
|
|
153
|
-
:loading="submitting"
|
|
154
|
-
@click="onSubmit"
|
|
155
|
-
>
|
|
156
|
-
<v-icon class="mr-2">mdi-check</v-icon>
|
|
157
|
-
Update
|
|
158
|
-
</v-btn>
|
|
159
|
-
</v-col>
|
|
160
|
-
</v-row>
|
|
161
|
-
</v-toolbar>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<div class="attendance-settings__footer">
|
|
118
|
+
<AppButton variant="ghost" :disabled="submitting" @click="close">Cancel</AppButton>
|
|
119
|
+
<AppButton icon="mdi-check" :disabled="submitting" @click="onSubmit">Update</AppButton>
|
|
120
|
+
</div>
|
|
162
121
|
</v-card>
|
|
163
122
|
</v-dialog>
|
|
164
123
|
|
|
@@ -397,3 +356,43 @@ watch(showDialog, (newVal) => {
|
|
|
397
356
|
}
|
|
398
357
|
});
|
|
399
358
|
</script>
|
|
359
|
+
|
|
360
|
+
<style scoped>
|
|
361
|
+
.attendance-settings__head {
|
|
362
|
+
display: flex;
|
|
363
|
+
align-items: center;
|
|
364
|
+
justify-content: space-between;
|
|
365
|
+
padding-right: 12px;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.attendance-settings__body {
|
|
369
|
+
padding: 0 20px;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.attendance-settings__hint {
|
|
373
|
+
font-size: var(--fs-cell);
|
|
374
|
+
color: var(--text2);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* Was a `variant="flat"` v-card, i.e. a second card inside the modal card. It
|
|
378
|
+
is a frame around the QR, so it takes the design's inner surface. */
|
|
379
|
+
.attendance-settings__qr {
|
|
380
|
+
display: flex;
|
|
381
|
+
flex-direction: column;
|
|
382
|
+
align-items: center;
|
|
383
|
+
padding: 16px;
|
|
384
|
+
border: 1px solid var(--border);
|
|
385
|
+
border-radius: var(--r-inner);
|
|
386
|
+
background: var(--thead);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/* The design's dialog footer. Deliberately not the shared
|
|
390
|
+
`.screen-modal__footer` - that class is on the batch-D branch (#1150), not
|
|
391
|
+
on this base, and a second copy in `screens.css` would collide with it. */
|
|
392
|
+
.attendance-settings__footer {
|
|
393
|
+
display: flex;
|
|
394
|
+
justify-content: flex-end;
|
|
395
|
+
gap: 8px;
|
|
396
|
+
padding: 16px 20px 20px;
|
|
397
|
+
}
|
|
398
|
+
</style>
|