@7365admin1/layer-common 4.97.1-staging.591 → 4.97.1-staging.593

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.
@@ -68,6 +68,8 @@
68
68
  -->
69
69
  <!-- "Admin · St. Regis Residences": names are unique per site (2026-10-01) -->
70
70
  <template #item.name="{ item }">{{ roleLabel(item) }}</template>
71
+ <!-- "App" column: plain app name, not the stored code (security_agency) -->
72
+ <template #item.type="{ item }">{{ roleAppLabel(item.type) }}</template>
71
73
  <template #item.scope="{ item }">
72
74
  <StatusChip
73
75
  :dot="false"
@@ -214,7 +216,7 @@
214
216
 
215
217
  <script setup lang="ts">
216
218
  // Relative import: this layer's `utils/` is not auto-imported into consumers.
217
- import { roleLabel, safeReturnTo, showSiteField, sitePickerRoleLabel } from "../utils/role";
219
+ import { roleAppLabel, roleLabel, safeReturnTo, showSiteField, sitePickerRoleLabel } from "../utils/role";
218
220
  import { holdsPermission } from "../utils/permission-spellings";
219
221
  import { countRealPermissions } from "../utils/security-catalogue";
220
222
  import {
@@ -2,7 +2,7 @@
2
2
  ADD / EDIT AN EXTRA CHECK - four short steps: What, Where, When, Who is told.
3
3
 
4
4
  The last step shows one sentence saying exactly what will be created
5
- ("Adds 1 extra check to 3 areas (24 units) at 08:00 every weekday ... next:
5
+ ("Adds 1 extra check to each of 3 areas (24 units) at 08:00 every weekday ... next:
6
6
  Wed 21 Oct"). The form checks what the server checks (utils/schedule-task.ts)
7
7
  so a schedule that would never run is caught before saving; whatever the
8
8
  server still refuses is shown in its own words.
@@ -85,8 +85,10 @@
85
85
  <v-stepper-window-item :value="2">
86
86
  <div class="st-step">
87
87
  <p class="st-step__help">
88
- Pick the areas to check. Every unit in a picked area gets
89
- one more tick box on the Daily Checklist for this check.
88
+ Pick the areas to check. Each time this check is due, every
89
+ picked area gets exactly one extra round on the Daily
90
+ Checklist (one more tick box per unit) - one round, however
91
+ many normal visits the area has each day.
90
92
  </p>
91
93
  <InputLabel for="selected-areas" title="Areas" required />
92
94
  <v-select
@@ -16,9 +16,11 @@
16
16
  that.
17
17
  </li>
18
18
  <li>
19
- An <strong>extra check</strong> adds one more round of ticks to
20
- that checklist, in the areas you pick, at the time you set - once,
21
- or repeating (every day, weekdays, chosen days, or monthly).
19
+ An <strong>extra check</strong> adds exactly one more round of
20
+ ticks to that checklist each time it is due, in the areas you pick,
21
+ at the time you set - once, or repeating (every day, weekdays,
22
+ chosen days, or monthly). One extra check is one round per area,
23
+ however many normal visits that area has each day.
22
24
  </li>
23
25
  <li>
24
26
  At that time, staff get a phone notification and see the extra
@@ -163,7 +163,11 @@
163
163
  "
164
164
  value="toggle-status"
165
165
  class="text-body-2"
166
- @click="togglePlanStatus(item)"
166
+ @click="
167
+ item.status === 'active'
168
+ ? (planToDeactivate = item)
169
+ : togglePlanStatus(item)
170
+ "
167
171
  />
168
172
  </v-list>
169
173
  </v-card>
@@ -197,6 +201,32 @@
197
201
  />
198
202
  </v-dialog>
199
203
 
204
+ <ConfirmDialog
205
+ :model-value="!!planToDeactivate"
206
+ @update:model-value="(v: boolean) => { if (!v) planToDeactivate = null; }"
207
+ >
208
+ <template #title>Deactivate plan</template>
209
+ <template #description>
210
+ Deactivate {{ planToDeactivate?.name || "this plan" }}? You can activate
211
+ it again from the Deactivated tab.
212
+ </template>
213
+ <template #footer>
214
+ <v-spacer />
215
+ <v-btn
216
+ variant="outlined"
217
+ :disabled="isDeactivating"
218
+ @click="planToDeactivate = null"
219
+ >Cancel</v-btn
220
+ >
221
+ <v-btn
222
+ color="error"
223
+ variant="flat"
224
+ :loading="isDeactivating"
225
+ @click="confirmDeactivate"
226
+ >Deactivate</v-btn
227
+ >
228
+ </template>
229
+ </ConfirmDialog>
200
230
  <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
201
231
  </v-container>
202
232
  </template>
@@ -417,6 +447,20 @@ async function handleSubmit(payload: PlanFormState) {
417
447
  }
418
448
  }
419
449
 
450
+ // Deactivate asks first; Activate stays one tap.
451
+ const planToDeactivate = ref<SubscriptionPlan | null>(null);
452
+ const isDeactivating = ref(false);
453
+ async function confirmDeactivate() {
454
+ if (!planToDeactivate.value) return;
455
+ isDeactivating.value = true;
456
+ try {
457
+ await togglePlanStatus(planToDeactivate.value);
458
+ } finally {
459
+ isDeactivating.value = false;
460
+ planToDeactivate.value = null;
461
+ }
462
+ }
463
+
420
464
  async function togglePlanStatus(plan: SubscriptionPlan) {
421
465
  const nextStatus = plan.status === "active" ? "deactive" : "active";
422
466
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "4.97.1-staging.591",
5
+ "version": "4.97.1-staging.593",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "//files": "What a consumer extending this layer actually loads. Without this npm ships the whole working tree - the changesets, the CI workflows, the render harness in tools/ and any scratch directory that happened to exist at publish time. Nuxt resolves a layer by directory, so every runtime directory below has to stay listed; adding a new top-level runtime directory means adding it here too.",
package/utils/role.ts CHANGED
@@ -158,3 +158,27 @@ export function siteAccessLabel(item: { limitedTo?: unknown } | null | undefined
158
158
  const names = limitedSites(item).map((l) => l.siteName || "a site");
159
159
  return names.length ? `Limited to: ${names.join(", ")}` : "All sites";
160
160
  }
161
+
162
+ /**
163
+ * A role's `type` is the app it belongs to, stored as a code
164
+ * (`security_agency`). The roles table shows the app's plain name instead.
165
+ * An unknown code is shown with its underscores as spaces, never dropped.
166
+ */
167
+ const ROLE_APP_LABELS: Readonly<Record<string, string>> = Object.freeze({
168
+ admin: "Super Admin",
169
+ organization: "Organization",
170
+ property_management_agency: "Property Management",
171
+ security_agency: "Security",
172
+ cleaning_services: "Cleaning",
173
+ mechanical_electrical_services: "Mechanical & Electrical",
174
+ pest_control_services: "Pest Control",
175
+ pool_maintenance_services: "Pool Maintenance",
176
+ landscaping_services: "Landscape",
177
+ resident: "Resident",
178
+ });
179
+
180
+ export function roleAppLabel(type: unknown): string {
181
+ const code = String(type ?? "").trim();
182
+ if (!code) return "";
183
+ return ROLE_APP_LABELS[code] ?? code.replace(/_/g, " ").replace(/^./, (c) => c.toUpperCase());
184
+ }
@@ -268,7 +268,8 @@ export function buildSummary(
268
268
  now: Date = new Date(),
269
269
  ): string {
270
270
  const year = sgtParts(now).date.slice(0, 4);
271
- const areas = `${f.areaCount} area${f.areaCount === 1 ? "" : "s"}`;
271
+ // One extra check = exactly one more round per picked area each time it runs (not one per normal visit).
272
+ const areas = f.areaCount === 1 ? "1 area" : `each of ${f.areaCount} areas`;
272
273
  const units = `${unitCount} unit${unitCount === 1 ? "" : "s"}`;
273
274
  const next = nextRunDate(f, now);
274
275
  const nextText = next ? `${DAYS[weekdayOf(next)]} ${fmtDate(next, year)}` : "never";