@7365admin1/layer-common 3.2.2-staging.122 → 3.2.2-staging.124

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 (37) hide show
  1. package/assets/css/primitives.css +39 -0
  2. package/components/EntryPass/QrTemplatePreview.vue +27 -16
  3. package/components/Feedback/Form.vue +26 -12
  4. package/components/HygieneUpdateMoreAction.vue +44 -44
  5. package/components/IncidentReport/affectedEntities.vue +41 -20
  6. package/components/NumberSettingField.vue +9 -2
  7. package/components/Signature.vue +40 -34
  8. package/components/UnitPersonCard.vue +42 -13
  9. package/nuxt.config.ts +6 -1
  10. package/package.json +22 -1
  11. package/plugins/vuetify.ts +37 -1
  12. package/utils/materialSymbols.ts +363 -0
  13. package/.changeset/README.md +0 -8
  14. package/.changeset/camera-wall-gated-endpoint.md +0 -47
  15. package/.changeset/camera-wall-plain-english.md +0 -60
  16. package/.changeset/camera-wall-selection-and-guidance.md +0 -65
  17. package/.changeset/config.json +0 -11
  18. package/.changeset/dark-primary-contrast.md +0 -39
  19. package/.changeset/dashboard-dark-theme-contrast.md +0 -15
  20. package/.changeset/notification-preferences.md +0 -17
  21. package/.changeset/pm-sidebar-grouping-and-list-scaffold.md +0 -27
  22. package/.changeset/service-provider-invitation-actions.md +0 -28
  23. package/.editorconfig +0 -12
  24. package/.github/workflows/main.yml +0 -17
  25. package/.github/workflows/publish-staging.yml +0 -47
  26. package/.github/workflows/publish.yml +0 -39
  27. package/PUBLISHING.md +0 -269
  28. package/components/ScheduleTaskAreaFormDialog.vue +0 -141
  29. package/components/ScheduleTaskAreaUpdateMoreAction.vue +0 -104
  30. package/components/TableWithButton.vue +0 -94
  31. package/test/visitor-socket.test.mjs +0 -36
  32. package/tools/render-harness/README.md +0 -87
  33. package/tools/render-harness/baselines.json +0 -117
  34. package/tools/render-harness/harness-init.ps1 +0 -127
  35. package/tools/render-harness/probe.mjs +0 -229
  36. package/tools/render-harness/render-check.mjs +0 -306
  37. package/tools/render-harness/render-check.selftest.mjs +0 -129
@@ -92,6 +92,45 @@
92
92
  font-family: var(--font-sans, Roboto, sans-serif);
93
93
  }
94
94
 
95
+ /* ------------------------------------------------------------------ */
96
+ /* The icon face. */
97
+ /* ------------------------------------------------------------------ */
98
+
99
+ /**
100
+ * The handoff draws every icon with Google's `Material Symbols Outlined`
101
+ * variable font, requested in `nuxt.config.ts` alongside Manrope. The icon
102
+ * set in `plugins/vuetify.ts` turns each `mdi-*` name into that font's
103
+ * ligature and puts this class on the element; this rule is what makes the
104
+ * ligature resolve.
105
+ *
106
+ * One class, no `!important`. `.v-icon` already gives us `font-feature-
107
+ * settings: "liga"`, `letter-spacing: normal`, `line-height: 1` and the 1em
108
+ * box, and Vuetify never sets `font-family` on it - the old family arrived
109
+ * through `.mdi:before`, which we no longer emit. Only `text-transform` has
110
+ * to be stated: `.v-btn` uppercases its contents, and an uppercased "close"
111
+ * matches no ligature, so the icon would render as the word.
112
+ */
113
+ .material-symbols-outlined {
114
+ font-family: "Material Symbols Outlined";
115
+ font-style: normal;
116
+ font-variation-settings:
117
+ "FILL" 0,
118
+ "GRAD" 0,
119
+ "opsz" 24;
120
+ text-transform: none;
121
+ white-space: nowrap;
122
+ word-wrap: normal;
123
+ direction: ltr;
124
+ -webkit-font-smoothing: antialiased;
125
+ }
126
+
127
+ /* mdi's spin modifier, kept working now the glyph is not a `:before`.
128
+ `@keyframes mdi-spin` still comes from `@mdi/font`, which stays loaded so
129
+ that any icon name without a mapping keeps drawing its old glyph. */
130
+ .material-symbols-outlined.mdi-spin {
131
+ animation: mdi-spin 2s infinite linear;
132
+ }
133
+
95
134
  /* ------------------------------------------------------------------ */
96
135
  /* PageHeader - the row every screen opens with. */
97
136
  /* ------------------------------------------------------------------ */
@@ -6,24 +6,28 @@
6
6
  persistent
7
7
  @update:model-value="emit('update:modelValue', $event)"
8
8
  >
9
- <v-card>
10
- <v-toolbar
11
- style="height: 12px"
12
- :color="isDark ? 'grey-darken-4' : 'white'"
13
- >
14
- <v-spacer></v-spacer>
9
+ <v-card class="screen-modal">
10
+ <!-- Was a 12px-tall `v-toolbar` themed by hand (`white` / `grey-darken-4`)
11
+ carrying a `grey-darken-1` icon button. The design's modal close is a
12
+ borderless 32px square on `--muted`, so the whole bar goes and the
13
+ close sits on the card's own surface. -->
14
+ <div class="d-flex justify-end pa-2">
15
15
  <v-btn
16
- color="grey-darken-1"
16
+ variant="text"
17
17
  icon="mdi-close"
18
+ class="screen-modal__close"
19
+ aria-label="Close preview"
18
20
  @click="emit('update:modelValue', false)"
19
21
  ></v-btn>
20
- </v-toolbar>
22
+ </div>
21
23
 
22
- <v-card-text style="padding: 0 !important">
24
+ <v-card-text class="screen-modal__body pa-0">
23
25
  <v-container>
24
26
  <v-row no-gutters class="text-center">
25
27
  <v-col cols="12">
26
- <p class="font-weight-bold text-h5 mb-3">{{ header }}</p>
28
+ <!-- `text-h5` is Vuetify's 24px/400 Roboto scale. The design's
29
+ modal title token is `--fs-card-title` / `--fw-card-title`. -->
30
+ <p class="qr-preview__title mb-3">{{ header }}</p>
27
31
  <p>{{ subText }}</p>
28
32
  </v-col>
29
33
 
@@ -53,8 +57,8 @@
53
57
  </v-col>
54
58
 
55
59
  <v-col cols="12" class="mt-5">
56
- <p class="font-weight-bold text-h5 mb-2">Company Name</p>
57
- <p class="font-weight-bold text-h5 mb-3">BL 01/03/24</p>
60
+ <p class="qr-preview__title mb-2">Company Name</p>
61
+ <p class="qr-preview__title mb-3">BL 01/03/24</p>
58
62
  <p class="mb-1">{{ `Date: ${dateStr} ${timeStr}` }}</p>
59
63
  <p class="mb-1">Access: Door</p>
60
64
  <p>Scan and press level 1. One time use.</p>
@@ -67,7 +71,6 @@
67
71
  </template>
68
72
 
69
73
  <script lang="ts" setup>
70
- import { useTheme } from "vuetify";
71
74
  import QRCode from "qrcode";
72
75
 
73
76
  defineProps({
@@ -87,9 +90,6 @@ defineProps({
87
90
 
88
91
  const emit = defineEmits(["update:modelValue"]);
89
92
 
90
- const { global: themeGlobal } = useTheme();
91
- const isDark = computed(() => themeGlobal.current.value.dark);
92
-
93
93
  const qrSmall = ref<string>("");
94
94
  const qrLarge = ref<string>("");
95
95
 
@@ -102,3 +102,14 @@ const now = new Date();
102
102
  const dateStr = now.toLocaleDateString();
103
103
  const timeStr = now.toLocaleTimeString();
104
104
  </script>
105
+
106
+ <style scoped>
107
+ /* The pass body is a printed artefact, so its own sizes stay. Only the two
108
+ heading lines move off Vuetify's Roboto display scale onto the design's
109
+ modal-title token. */
110
+ .qr-preview__title {
111
+ font-size: var(--fs-card-title);
112
+ font-weight: var(--fw-card-title);
113
+ color: var(--text);
114
+ }
115
+ </style>
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <FormDialog v-model="dialog" @close="closeDialog">
3
3
  <template #title>
4
- <span class="text-h6 font-weight-medium pt-1 text-capitalize">
4
+ <span class="screen-card-title pt-1 text-capitalize">
5
5
  {{ isEditMode ? "Edit Feedback" : "Create Feedback" }}
6
6
  </span>
7
7
  </template>
@@ -22,13 +22,13 @@
22
22
  item-value="value"
23
23
  item-props
24
24
  variant="outlined"
25
- density="compact"
26
- class="mb-2"
25
+ density="comfortable"
26
+ class="mb-2 filter-field"
27
27
  clearable
28
28
  :custom-filter="customFilter"
29
29
  :rules="[requiredRule]"
30
30
  >
31
- <template #label> Subject <span class="text-red">*</span> </template>
31
+ <template #label> Subject <span class="feedback-form__required">*</span> </template>
32
32
  </v-autocomplete>
33
33
 
34
34
  <!-- <v-autocomplete
@@ -44,37 +44,43 @@
44
44
  clearable
45
45
  :rules="[requiredRule]"
46
46
  >
47
- <template #label> Category <span class="text-red">*</span> </template>
47
+ <template #label> Category <span class="feedback-form__required">*</span> </template>
48
48
  </v-autocomplete> -->
49
49
 
50
50
  <v-text-field
51
51
  label="Location"
52
52
  variant="outlined"
53
- density="compact"
53
+ density="comfortable"
54
+ class="mb-2 filter-field"
54
55
  v-model="localFeedback.location"
55
56
  :rules="[requiredRule]"
56
57
  >
57
- <template #label> Location <span class="text-red">*</span> </template>
58
+ <template #label> Location <span class="feedback-form__required">*</span> </template>
58
59
  </v-text-field>
59
60
 
60
61
  <v-textarea
61
62
  label="Description"
62
63
  variant="outlined"
63
- density="compact"
64
+ density="comfortable"
65
+ class="filter-field"
64
66
  auto-grow
65
67
  rows="4"
66
68
  v-model="localFeedback.description"
67
69
  :rules="[requiredRule]"
68
70
  >
69
- <template #label> Description <span class="text-red">*</span> </template>
71
+ <template #label> Description <span class="feedback-form__required">*</span> </template>
70
72
  </v-textarea>
71
73
 
72
74
  <template #footer>
75
+ <!-- `color="primary-button"` was already the right token (the FILL, not
76
+ the `primary` foreground) and `height="40"` already the right number,
77
+ but both were stated by hand. `.screen-btn-primary` is where the
78
+ layer says that once, and it wins over Vuetify's `--v-btn-height`
79
+ without an `!important`. -->
73
80
  <v-btn
74
- color="primary-button"
75
- class="text-capitalize mb-4"
81
+ class="text-capitalize mb-4 screen-btn-primary"
82
+ variant="flat"
76
83
  block
77
- height="40"
78
84
  :loading="loading"
79
85
  :disabled="loading || !isFormValid"
80
86
  @click="submitFeedback"
@@ -171,3 +177,11 @@ const customFilter = (value: string, query: string, item: any) => {
171
177
  return title.includes(search) || subtitle.includes(search);
172
178
  };
173
179
  </script>
180
+
181
+ <style scoped>
182
+ /* Was `text-red`, a fixed Vuetify red that is the same red on both themes.
183
+ `--err` is the theme's error ink and follows it. */
184
+ .feedback-form__required {
185
+ color: var(--err);
186
+ }
187
+ </style>
@@ -1,55 +1,46 @@
1
1
  <template>
2
- <v-card width="100%">
3
- <v-toolbar>
4
- <template v-if="$slots.header">
5
- <slot name="header" :title="title" :onClose="onClose" />
6
- </template>
7
- <template v-else>
8
- <v-row no-gutters class="fill-height px-6" align="center">
9
- <span class="font-weight-bold text-h5 text-capitalize">{{
10
- title
11
- }}</span>
12
- </v-row>
13
- </template>
14
- </v-toolbar>
15
-
16
- <v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
2
+ <!-- The hygiene twin of `Dialog/UpdateMoreAction`, converted to the same
3
+ shape on staging. Its title row and its footer were both `v-toolbar`s -
4
+ fixed grey slabs that stay grey on the dark theme - and the footer's
5
+ action was a hardcoded `color="black"` fill, i.e. the same black on the
6
+ dark theme as on the light one. -->
7
+ <v-card class="screen-modal" width="100%">
8
+ <template v-if="$slots.header">
9
+ <slot name="header" :title="title" :onClose="onClose" />
10
+ </template>
11
+ <v-card-title v-else class="text-capitalize">{{ title }}</v-card-title>
12
+
13
+ <v-card-text class="screen-modal__body pb-0">
17
14
  <slot name="content" />
18
15
  </v-card-text>
19
16
 
20
- <v-toolbar class="pa-0" density="compact">
21
- <v-row no-gutters>
22
- <v-col :cols="showMoreActions ? 6 : 12" class="pa-0">
23
- <v-btn
24
- block
25
- variant="text"
26
- class="text-none"
27
- size="large"
28
- @click="emit('close')"
29
- height="48"
30
- >
31
- Close
32
- </v-btn>
33
- </v-col>
34
-
35
- <v-col v-if="showMoreActions" cols="6" class="pa-0">
36
- <v-menu>
17
+ <!-- The design's footer strip: equal-width buttons, ghost then primary. -->
18
+ <div class="screen-modal__footer">
19
+ <v-btn
20
+ variant="outlined"
21
+ class="text-none screen-btn-ghost"
22
+ @click="emit('close')"
23
+ >
24
+ Close
25
+ </v-btn>
26
+
27
+ <v-menu v-if="showMoreActions" contained>
37
28
  <template #activator="{ props }">
38
29
  <v-btn
39
- block
40
30
  variant="flat"
41
- color="black"
42
- class="text-none"
43
- height="48"
31
+ class="text-none screen-btn-primary"
44
32
  :disabled="disabled"
45
33
  v-bind="props"
46
- tile
47
34
  >
48
35
  More actions
49
36
  </v-btn>
50
37
  </template>
51
38
 
52
- <v-list class="pa-0 rounded-0">
39
+ <!-- The dropdown itself is left as Vuetify draws it, exactly as the
40
+ converted twin left it: the handoff gives a per-row menu no
41
+ shape, and this menu opens on a click the harness cannot drive,
42
+ so it could not be render-verified either. -->
43
+ <v-list class="pa-0 hyg-more__list">
53
44
  <v-list-item
54
45
  v-if="showQrButton && qrButtonLabel"
55
46
  @click="emit('showqr')"
@@ -110,20 +101,22 @@
110
101
  </v-list-item-title>
111
102
  </v-list-item>
112
103
 
104
+ <!-- `text-red` was Vuetify's fixed #f44336, the same red on both
105
+ themes. `text-error` is Vuetify's own class over
106
+ `--v-theme-error`, which IS the design's `--err` - the same
107
+ substitution the converted twin made. -->
113
108
  <v-list-item
114
109
  v-if="canDelete"
115
110
  @click="emit('delete')"
116
- class="text-red"
111
+ class="text-error"
117
112
  >
118
113
  <v-list-item-title class="text-subtitle-2">
119
114
  {{ deleteButtonLabel }}
120
115
  </v-list-item-title>
121
116
  </v-list-item>
122
117
  </v-list>
123
- </v-menu>
124
- </v-col>
125
- </v-row>
126
- </v-toolbar>
118
+ </v-menu>
119
+ </div>
127
120
  </v-card>
128
121
  </template>
129
122
 
@@ -240,4 +233,11 @@ const {
240
233
  } = prop;
241
234
  </script>
242
235
 
243
- <style scoped></style>
236
+ <style scoped>
237
+ /* `text-subtitle-2` is Vuetify's 0.875rem/500; the handoff names no type for a
238
+ menu row, so it takes the established cell type - same rule as the twin. */
239
+ .hyg-more__list :deep(.v-list-item-title) {
240
+ font-size: var(--fs-cell);
241
+ font-weight: var(--fw-cell);
242
+ }
243
+ </style>
@@ -2,18 +2,22 @@
2
2
  <v-row no-gutters class="px-5 pt-4">
3
3
  <!-- Anyone affected/injured -->
4
4
  <v-col cols="12" class="border-b pb-5 mb-5 mt-1">
5
- <p class="mb-2" style="font-size: 17px; font-weight: 600">
5
+ <!-- Same shape as this report's `Authorities.vue` sibling, already on
6
+ staging: a `.screen-card-title` question, a `.report-answer` line
7
+ when the answer is "no", and the table inside an `AppCard` instead of
8
+ a `border` class Vuetify draws from its own fixed grey. -->
9
+ <p class="screen-card-title mb-2">
6
10
  Anyone affected/injured?
7
11
  </p>
8
12
  <p
9
13
  v-if="affectedEntities?.anyoneAffectedValue == 'no'"
10
- class="my-1 text-h6 text-capitalize"
14
+ class="report-answer mt-2 text-capitalize"
11
15
  >
12
16
  {{ affectedEntities?.anyoneAffectedValue }}
13
17
  </p>
14
18
 
19
+ <AppCard v-else class="table-card mt-5">
15
20
  <v-data-table
16
- v-else
17
21
  :headers="injuredTableHeader"
18
22
  :items="affectedEntities?.affectedInjured"
19
23
  fixed-header
@@ -21,16 +25,17 @@
21
25
  :hide-default-header="false"
22
26
  items-per-page="100"
23
27
  style="max-height: calc(100vh - (200px))"
24
- class="border mt-5"
25
28
  >
26
29
  <template #header.nric>
27
30
  <div class="d-flex align-center">
28
31
  NRIC
32
+ <!-- `color="blue"` is a fixed Vuetify blue, the same on both
33
+ themes. `--accent-text` is the design's interactive ink. -->
29
34
  <v-icon
30
35
  v-if="isShowEyeIcon"
31
- class="cursor-pointer ml-1"
36
+ class="cursor-pointer ml-1 affected__eye"
32
37
  size="18"
33
- color="blue"
38
+ :aria-label="isShowNRIC ? 'Hide NRIC' : 'Show NRIC'"
34
39
  @click="isShowNRIC = !isShowNRIC"
35
40
  >
36
41
  {{ isShowNRIC ? "mdi-eye-off-outline" : "mdi-eye-outline" }}
@@ -45,36 +50,52 @@
45
50
  </tr>
46
51
  </template>
47
52
  </v-data-table>
53
+ </AppCard>
48
54
  </v-col>
49
55
 
50
56
  <!-- Anyone damage to property -->
51
57
  <v-col cols="12" class="pb-5 mb-5 mt-1">
52
- <p class="mb-2" style="font-size: 17px; font-weight: 600">
58
+ <p class="screen-card-title mb-2">
53
59
  Anyone damage to property?
54
60
  </p>
55
61
  <p
56
62
  v-if="affectedEntities?.anyUnitAffectedValue == 'no'"
57
- class="my-1 text-h6 text-capitalize"
63
+ class="report-answer mt-2 text-capitalize"
58
64
  >
59
65
  {{ affectedEntities?.anyUnitAffectedValue }}
60
66
  </p>
61
67
 
62
- <v-data-table
63
- v-else
64
- :headers="damagePropertyTableHeader"
65
- :items="affectedEntities?.anyoneDamageToProperty"
66
- fixed-header
67
- hide-default-footer
68
- :hide-default-header="false"
69
- items-per-page="100"
70
- style="max-height: calc(100vh - (200px))"
71
- class="border mt-5"
72
- >
73
- </v-data-table>
68
+ <AppCard v-else class="table-card mt-5">
69
+ <v-data-table
70
+ :headers="damagePropertyTableHeader"
71
+ :items="affectedEntities?.anyoneDamageToProperty"
72
+ fixed-header
73
+ hide-default-footer
74
+ :hide-default-header="false"
75
+ items-per-page="100"
76
+ style="max-height: calc(100vh - (200px))"
77
+ >
78
+ </v-data-table>
79
+ </AppCard>
74
80
  </v-col>
75
81
  </v-row>
76
82
  </template>
77
83
 
84
+ <style scoped>
85
+ /* Same two rules as this report's `Authorities.vue` sibling: the prototype
86
+ states no size for a plain answer line, so it takes the body row's type
87
+ rather than Vuetify's `text-h6`. */
88
+ .report-answer {
89
+ font-size: var(--fs-cell);
90
+ font-weight: var(--fw-cell);
91
+ color: var(--text);
92
+ }
93
+
94
+ .affected__eye {
95
+ color: var(--accent-text);
96
+ }
97
+ </style>
98
+
78
99
  <script lang="ts" setup>
79
100
  definePageMeta({
80
101
  middleware: ["01-auth", "02-org"],
@@ -4,11 +4,18 @@
4
4
  <v-row>
5
5
  <v-col cols="6">
6
6
  <InputLabel class="text-capitalize font-weight-bold" :title="title" :required="required" />
7
- <v-text-field v-model.number="count" type="number" density="comfortable" :rules="rules" :readonly="readOnly" />
7
+ <v-text-field v-model.number="count" type="number" class="filter-field" density="comfortable" :rules="rules" :readonly="readOnly" />
8
8
  </v-col>
9
9
 
10
10
  <v-col cols="6">
11
- <v-btn v-if="!readOnly" color="primary" class="text-none mt-6" size="large" variant="flat"
11
+ <!-- Was `color="primary" size="large"`. On a `v-btn`,
12
+ `primary` is this theme's FOREGROUND token (links,
13
+ icons, text buttons) - the fill is `primary-button`, so
14
+ a flat button asking for `primary` painted itself with
15
+ an ink colour. `.screen-btn-primary` is the design's
16
+ 40px primary and carries the correct fill, so the
17
+ colour prop and the Vuetify size both go. -->
18
+ <v-btn v-if="!readOnly" class="text-none mt-6 screen-btn-primary" variant="flat"
12
19
  :disabled="!valid || disabled" :loading="loading || updating" text="Save" @click="handleSave" />
13
20
  </v-col>
14
21
  </v-row>
@@ -1,16 +1,28 @@
1
1
  <template>
2
2
  <v-dialog v-model="dialogModel" max-width="700" persistent>
3
- <v-card>
4
- <v-toolbar>
5
- <v-toolbar-title>Signature</v-toolbar-title>
3
+ <v-card class="screen-modal">
4
+ <!-- Was a bare `v-toolbar`, which Vuetify paints from its own `surface`:
5
+ a grey slab across the top of the card, visibly lighter than the card
6
+ on the dark theme. A `v-card-title` is the same row on the card's own
7
+ surface, and the close is the design's borderless 32px square. -->
8
+ <v-card-title class="d-flex align-center">
9
+ Signature
6
10
  <v-spacer />
7
- <v-btn icon="mdi-close" @click="hideModal" />
8
- </v-toolbar>
9
-
10
- <v-card-text>
11
+ <v-btn
12
+ variant="text"
13
+ icon="mdi-close"
14
+ class="screen-modal__close"
15
+ aria-label="Close signature"
16
+ @click="hideModal"
17
+ />
18
+ </v-card-title>
19
+
20
+ <v-card-text class="screen-modal__body">
11
21
  <v-row no-gutters>
12
22
  <v-col cols="12">
13
- <div class="text-subtitle-1 text-medium-emphasis ml-2">
23
+ <!-- `text-medium-emphasis` is Vuetify's 60%-alpha ink; `--muted` is
24
+ the design's sub-line and follows the theme. -->
25
+ <div class="screen-hint ml-2 mb-2">
14
26
  Your signature here
15
27
  </div>
16
28
 
@@ -23,34 +35,28 @@
23
35
  class="border"
24
36
  />
25
37
  </v-col>
26
-
27
- <v-col cols="12" class="text-center">
28
- <v-row class="d-flex">
29
- <v-col class="w-50 px-3">
30
- <v-btn
31
- text="clear"
32
- color="warning"
33
- class="my-4 w-100 rounded-lg"
34
- height="40px"
35
- @click="clear"
36
- />
37
- </v-col>
38
-
39
- <v-col class="w-50 px-3">
40
- <v-btn
41
- text="submit"
42
- color="#1867C0"
43
- class="my-4 w-100 rounded-lg"
44
- height="40px"
45
- :loading="loading"
46
- :disabled="loading"
47
- @click="submit"
48
- />
49
- </v-col>
50
- </v-row>
51
- </v-col>
52
38
  </v-row>
53
39
  </v-card-text>
40
+
41
+ <!-- The design's modal footer: two equal-width buttons on a bordered
42
+ strip, ghost then primary. Was a hand-built `v-row` of two 50%
43
+ columns, one `color="warning"` and one a LITERAL `#1867C0` - a fixed
44
+ hex that is the same blue on both themes and is not the theme's
45
+ accent either. Both colours go; the shape is the shared one. -->
46
+ <div class="screen-modal__footer">
47
+ <v-btn class="text-none screen-btn-ghost" variant="outlined" @click="clear">
48
+ Clear
49
+ </v-btn>
50
+ <v-btn
51
+ class="text-none screen-btn-primary"
52
+ variant="flat"
53
+ :loading="loading"
54
+ :disabled="loading"
55
+ @click="submit"
56
+ >
57
+ Submit
58
+ </v-btn>
59
+ </div>
54
60
  </v-card>
55
61
  </v-dialog>
56
62
  </template>