@7365admin1/layer-common 3.2.7 → 3.2.8-staging.200

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 (283) hide show
  1. package/assets/css/primitives.css +1113 -0
  2. package/assets/css/screens.css +1097 -0
  3. package/assets/css/shell.css +258 -0
  4. package/assets/css/tokens.css +236 -0
  5. package/components/AcceptDialog.vue +17 -18
  6. package/components/AccessCard/AvailableStats.vue +24 -16
  7. package/components/AccessCardAddForm.vue +42 -42
  8. package/components/AccessCardAssignToUnitForm.vue +13 -21
  9. package/components/AccessCardDeleteDialog.vue +41 -39
  10. package/components/AccessCardDetailsDialog.vue +125 -79
  11. package/components/AccessCardPreviewDialog.vue +58 -84
  12. package/components/AccessCardQrTagging.vue +60 -63
  13. package/components/AccessCardReplaceForm.vue +27 -23
  14. package/components/AccessManagement.vue +86 -85
  15. package/components/AddEqupmentForm.vue +18 -22
  16. package/components/AddPassKeyToVisitor.vue +95 -73
  17. package/components/AppButton.vue +62 -0
  18. package/components/AppCard.vue +20 -0
  19. package/components/AppDateField.vue +49 -0
  20. package/components/AppField.vue +76 -0
  21. package/components/AppKpiTile.vue +65 -0
  22. package/components/AppSegmented.vue +38 -0
  23. package/components/AppSelect.vue +247 -0
  24. package/components/AreaChecklistHistoryLogs.vue +51 -44
  25. package/components/AreaChecklistHistoryMain.vue +14 -7
  26. package/components/AreaFormDialog.vue +71 -59
  27. package/components/AreaMain.vue +73 -107
  28. package/components/AttachmentsViewer.vue +84 -22
  29. package/components/AttendanceCheckInOutDialog.vue +85 -88
  30. package/components/AttendanceDetailsDialog.vue +57 -63
  31. package/components/AttendanceMain.vue +4 -3
  32. package/components/AttendanceMapSearchDialog.vue +42 -44
  33. package/components/AttendanceSettingsDialog.vue +79 -63
  34. package/components/Avatar/Main.vue +54 -2
  35. package/components/BillingMain.vue +26 -30
  36. package/components/BtnUploadFile.vue +53 -11
  37. package/components/BuildingForm.vue +34 -27
  38. package/components/BuildingManagement/buildings.vue +39 -39
  39. package/components/BuildingManagement/units.vue +26 -42
  40. package/components/BuildingUnitFormAdd.vue +20 -23
  41. package/components/BuildingUnitFormEdit.vue +8 -4
  42. package/components/BulletinBoardForm.vue +19 -24
  43. package/components/BulletinBoardManagement.vue +49 -57
  44. package/components/BulletinBoardView.vue +9 -6
  45. package/components/BulletinExpirationChip.vue +13 -4
  46. package/components/Button/Close.vue +2 -2
  47. package/components/CameraForm.vue +21 -22
  48. package/components/CameraMain.vue +50 -49
  49. package/components/Card/DeleteConfirmation.vue +24 -22
  50. package/components/Card/MemberInfoSummary.vue +28 -3
  51. package/components/Card/Toggle.vue +15 -3
  52. package/components/CardToolbar.vue +37 -0
  53. package/components/Carousel.vue +56 -36
  54. package/components/Chat/Bubbles.vue +43 -14
  55. package/components/Chat/Information.vue +49 -32
  56. package/components/Chat/ListCard.vue +38 -9
  57. package/components/Chat/Message.vue +41 -15
  58. package/components/Chat/Navigation.vue +27 -15
  59. package/components/Chat/SkeletonLoader.vue +9 -2
  60. package/components/ChecklistItemRow.vue +25 -1
  61. package/components/CleaningScheduleMain.vue +44 -49
  62. package/components/ClientDetailForm.vue +81 -56
  63. package/components/ClientMain.vue +92 -82
  64. package/components/ConfirmDialog.vue +14 -8
  65. package/components/Container/Standard.vue +9 -7
  66. package/components/CreateSubsciptionPlan.vue +148 -89
  67. package/components/DashboardEmptyState.vue +48 -3
  68. package/components/DashboardMain.vue +924 -676
  69. package/components/DashboardPanel.vue +84 -21
  70. package/components/DeliveryCompany.vue +34 -20
  71. package/components/Dialog/DeleteConfirmation.vue +20 -21
  72. package/components/Dialog/ReplaceAutofillPrompt.vue +19 -26
  73. package/components/Dialog/ReusablePrompt.vue +30 -29
  74. package/components/Dialog/UpdateMoreAction.vue +62 -59
  75. package/components/DocumentForm.vue +14 -21
  76. package/components/DocumentManagement.vue +162 -108
  77. package/components/DocumentViewer.vue +29 -12
  78. package/components/DrawImage.vue +29 -21
  79. package/components/EntryPass/QrTemplatePreview.vue +27 -16
  80. package/components/EntryPassInformation.vue +40 -10
  81. package/components/EntryPassMain.vue +49 -27
  82. package/components/EquipmentItemMain.vue +84 -97
  83. package/components/EquipmentManagementMain.vue +81 -103
  84. package/components/Facility/BookingSetup.vue +43 -52
  85. package/components/Feedback/Form.vue +26 -12
  86. package/components/FeedbackMain.vue +35 -49
  87. package/components/FileInput.vue +75 -58
  88. package/components/FileInputWithList.vue +59 -12
  89. package/components/FormDialog.vue +60 -21
  90. package/components/HidAccessLogDashboard.vue +512 -490
  91. package/components/HidCameraCaptureDialog.vue +216 -0
  92. package/components/HidEnabledGate.vue +67 -9
  93. package/components/HidIntercomManagement.vue +1322 -653
  94. package/components/HidQrCodeConfiguration.vue +1030 -258
  95. package/components/HidReaderForm.vue +142 -59
  96. package/components/HidReaderManagement.vue +211 -323
  97. package/components/HidServiceSettingsPanel.vue +44 -13
  98. package/components/HidUserEnrollment.vue +1185 -412
  99. package/components/HygieneUpdateMoreAction.vue +44 -44
  100. package/components/IncidentReport/Authorities.vue +30 -18
  101. package/components/IncidentReport/IncidentInformation.vue +10 -10
  102. package/components/IncidentReport/IncidentInformationDownload.vue +3 -4
  103. package/components/IncidentReport/affectedEntities.vue +41 -20
  104. package/components/Input/Date.vue +3 -0
  105. package/components/Input/DatePicker.vue +1 -2
  106. package/components/Input/DateTimePicker.vue +1 -1
  107. package/components/Input/File.vue +55 -10
  108. package/components/Input/FileV2.vue +39 -6
  109. package/components/Input/InputPhoneNumberV2.vue +2 -1
  110. package/components/Input/ListGroupSelection.vue +56 -8
  111. package/components/Input/NRICNumber.vue +1 -0
  112. package/components/Input/NewDate.vue +1 -0
  113. package/components/Input/Number.vue +1 -0
  114. package/components/Input/Password.vue +1 -0
  115. package/components/Input/PhoneNumber.vue +21 -1
  116. package/components/Input/VehicleNumber.vue +1 -0
  117. package/components/InputLabel.vue +2 -2
  118. package/components/InvitationClientForm.vue +21 -19
  119. package/components/InvitationForm.vue +15 -21
  120. package/components/InvitationMain.vue +46 -61
  121. package/components/Layout/Header.vue +263 -36
  122. package/components/Layout/NavigationDrawer.vue +300 -30
  123. package/components/ListItem.vue +33 -3
  124. package/components/LocalPagination.vue +27 -3
  125. package/components/ManageChecklistMain.vue +136 -86
  126. package/components/MemberInformation.vue +32 -16
  127. package/components/MemberMain.vue +113 -93
  128. package/components/MyAttendanceMain.vue +3 -5
  129. package/components/NavigationItem.vue +83 -3
  130. package/components/Nfc/NFCPatrolReportMain.vue +47 -33
  131. package/components/Nfc/NFCPatrolRouteForm.vue +18 -20
  132. package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
  133. package/components/Nfc/NFCPatrolSettings.vue +14 -4
  134. package/components/Nfc/NFCTagForm.vue +22 -8
  135. package/components/Nfc/NFCTagMain.vue +41 -43
  136. package/components/Nfc/PatrolReport/DailyReportTab.vue +8 -1
  137. package/components/Nfc/PatrolReport/MonthlyReportTab.vue +8 -1
  138. package/components/Nfc/PatrolReport/MonthlyReportTable.vue +5 -3
  139. package/components/Nfc/PatrolReport/PatrolActivityTable.vue +26 -6
  140. package/components/Nfc/PatrolReport/PatrolReportTab.vue +8 -0
  141. package/components/Nfc/PatrolReport/PatrolRouteSummary.vue +41 -9
  142. package/components/Nfc/PatrolReport/RemarksDialog.vue +39 -9
  143. package/components/Nfc/PatrolReport/ReportEmptyState.vue +29 -9
  144. package/components/Nfc/PatrolReport/ReportFilters.vue +19 -10
  145. package/components/Nfc/PatrolReport/ReportLocationHeader.vue +32 -6
  146. package/components/Nfc/PatrolReport/SummaryReportTable.vue +28 -6
  147. package/components/NotificationSettings.vue +373 -0
  148. package/components/NumberSettingField.vue +9 -2
  149. package/components/OnlineFormConfigurationForm.vue +725 -237
  150. package/components/OnlineFormFill.vue +213 -106
  151. package/components/OnlineFormRenderPreview.vue +25 -30
  152. package/components/OnlineFormsConfiguration.vue +113 -109
  153. package/components/OrgViewDialog.vue +34 -16
  154. package/components/OvernightParkingAvailability.vue +49 -26
  155. package/components/OvernightParkingManagement.vue +78 -18
  156. package/components/PageHeader.vue +43 -0
  157. package/components/PassInformation.vue +109 -53
  158. package/components/PasswordConfirmation.vue +44 -26
  159. package/components/PeopleForm.vue +41 -20
  160. package/components/PhotoUpload.vue +110 -48
  161. package/components/PlaceholderComponent.vue +35 -19
  162. package/components/PlanActionsMenu.vue +12 -2
  163. package/components/PlanStatusBadge.vue +7 -8
  164. package/components/PlateNumberDisplay.vue +9 -6
  165. package/components/QrTemplate/PrintDialog.vue +66 -43
  166. package/components/RolePermissionFormCreate.vue +104 -79
  167. package/components/RolePermissionFormPreviewUpdate.vue +111 -109
  168. package/components/RolePermissionMain.vue +183 -81
  169. package/components/ScanVisitorQRCode.vue +99 -35
  170. package/components/ScheduleAreaMain.vue +34 -59
  171. package/components/ScheduleTaskForm.vue +61 -42
  172. package/components/ScheduleTaskMain.vue +56 -69
  173. package/components/SearchVehicleNumberUser.vue +39 -37
  174. package/components/ServiceProviderFormCreate.vue +29 -20
  175. package/components/ServiceProviderInvitationPrompt.vue +155 -0
  176. package/components/ServiceProviderMain.vue +601 -432
  177. package/components/Signature.vue +40 -34
  178. package/components/SignaturePad.vue +5 -29
  179. package/components/SiteSettings.vue +41 -11
  180. package/components/SiteSettingsWorkOrder.vue +61 -30
  181. package/components/SlideCardGroup.vue +35 -11
  182. package/components/Snackbar.vue +36 -3
  183. package/components/SpecificAttr.vue +18 -8
  184. package/components/StatusChip.vue +156 -0
  185. package/components/StockCard.vue +17 -12
  186. package/components/SubscriptionPlanTable.vue +207 -140
  187. package/components/SwitchContext.vue +201 -9
  188. package/components/TableHygiene.vue +36 -46
  189. package/components/TableList.vue +10 -10
  190. package/components/TableListSecondary.vue +105 -54
  191. package/components/TableMain.vue +241 -79
  192. package/components/TableV3.vue +61 -70
  193. package/components/TiptapEditor.vue +33 -40
  194. package/components/TiptapEditorQr.vue +5 -2
  195. package/components/Tooltip/Info.vue +69 -24
  196. package/components/UnitMain.vue +40 -60
  197. package/components/UnitPersonCard.vue +42 -13
  198. package/components/VehicleAddSelection.vue +48 -32
  199. package/components/VehicleForm.vue +112 -129
  200. package/components/VehicleManagement.vue +26 -12
  201. package/components/VehicleUpdateMoreAction.vue +59 -46
  202. package/components/VisitorDataFromScannedQRCodeForm.vue +90 -65
  203. package/components/VisitorForm.vue +774 -210
  204. package/components/VisitorFormSelection.vue +40 -37
  205. package/components/VisitorManagement.vue +239 -82
  206. package/components/VisitorPassKeyQRScanner.vue +65 -18
  207. package/components/VisitorSearchResultSummary.vue +60 -16
  208. package/components/VisitorSocketPopUp.vue +22 -14
  209. package/components/VisitorsReportPreview.vue +44 -29
  210. package/components/WorkOrder/Create.vue +26 -17
  211. package/components/WorkOrder/ListView.vue +38 -7
  212. package/components/WorkOrder/Main.vue +62 -59
  213. package/composables/useFeedback.ts +3 -3
  214. package/composables/useHidAmico.ts +269 -0
  215. package/composables/useHidNavigation.ts +2 -9
  216. package/composables/useMember.ts +3 -3
  217. package/composables/useNFCPatrolDailyReport.ts +13 -0
  218. package/composables/useNFCPatrolMonthlyReport.ts +22 -56
  219. package/composables/useNFCPatrolReport.ts +13 -0
  220. package/composables/useNotificationPreference.ts +87 -0
  221. package/composables/useOnlineFormPages.ts +2 -0
  222. package/composables/useRole.ts +22 -0
  223. package/composables/useServiceProvider.ts +3 -3
  224. package/composables/useServiceProviderInvitation.ts +145 -0
  225. package/composables/useSipWebPhone.ts +336 -0
  226. package/composables/useSubscriptionPlan.ts +62 -0
  227. package/composables/useThemePreference.ts +63 -0
  228. package/composables/useVisitor.ts +6 -1
  229. package/composables/useWebUsb.ts +127 -37
  230. package/composables/useWorkOrder.ts +17 -3
  231. package/error.vue +30 -6
  232. package/nuxt.config.ts +36 -0
  233. package/package.json +25 -4
  234. package/pages/[org]/[site]/access-mgmt/administrator/index.vue +2 -2
  235. package/pages/[org]/[site]/access-mgmt/{identity-mapping → hid-cards}/index.vue +2 -2
  236. package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
  237. package/pages/notification-settings.vue +19 -0
  238. package/pages/payment-method-linked.vue +34 -10
  239. package/pages/require-customer.vue +48 -10
  240. package/pages/require-organization-membership.vue +32 -8
  241. package/pages/unauthorized.vue +29 -4
  242. package/plugins/secure-member.client.ts +21 -56
  243. package/plugins/vuetify.ts +69 -2
  244. package/types/nfc-patrol-report.ts +3 -2
  245. package/types/site.d.ts +71 -0
  246. package/types/subscription.ts +18 -11
  247. package/utils/breadcrumb.test.ts +108 -0
  248. package/utils/breadcrumb.ts +82 -0
  249. package/utils/dashboard.test.ts +118 -0
  250. package/utils/dashboard.ts +53 -0
  251. package/utils/data.test.ts +95 -0
  252. package/utils/data.ts +40 -2
  253. package/utils/hid-discover.test.ts +102 -0
  254. package/utils/list-page.test.ts +60 -0
  255. package/utils/list-page.ts +38 -0
  256. package/utils/materialSymbols.ts +363 -0
  257. package/utils/nav-sections.test.ts +190 -0
  258. package/utils/nav-sections.ts +151 -0
  259. package/utils/nfc-patrol-report.test.ts +130 -0
  260. package/utils/nfc-patrol-report.ts +101 -0
  261. package/utils/role.test.ts +69 -0
  262. package/utils/role.ts +10 -0
  263. package/utils/route-name.test.ts +85 -0
  264. package/utils/route-name.ts +24 -0
  265. package/utils/status.test.ts +110 -0
  266. package/utils/status.ts +135 -0
  267. package/utils/table-card.test.ts +84 -0
  268. package/utils/theme-aa-ledger.ts +148 -0
  269. package/utils/theme.test.ts +485 -7
  270. package/utils/theme.ts +295 -110
  271. package/.changeset/README.md +0 -8
  272. package/.changeset/config.json +0 -11
  273. package/.editorconfig +0 -12
  274. package/.github/workflows/main.yml +0 -17
  275. package/.github/workflows/publish-staging.yml +0 -47
  276. package/.github/workflows/publish.yml +0 -39
  277. package/PUBLISHING.md +0 -269
  278. package/components/HidIdentityMapping.vue +0 -975
  279. package/components/ScheduleTaskAreaFormDialog.vue +0 -144
  280. package/components/ScheduleTaskAreaUpdateMoreAction.vue +0 -109
  281. package/components/ScheduleTastTicketMain.vue +0 -182
  282. package/components/TableWithButton.vue +0 -94
  283. package/test/visitor-socket.test.mjs +0 -36
@@ -1,43 +1,87 @@
1
1
  <template>
2
- <v-app-bar scroll-behavior="elevate" scroll-threshold="200">
2
+ <!--
3
+ PHASE 2 - THE 58px TOP BAR.
4
+
5
+ Same bar, same hamburger, same theme toggle, same profile menu with the
6
+ same two actions. What changes is its height (the design's 58px), the
7
+ breadcrumb it now carries, and the shape of the menu it opens.
8
+
9
+ The breadcrumb READS state that is already in the page - the site the
10
+ application has loaded and the route the router is on. It fetches nothing
11
+ and navigates nowhere; it is a label.
12
+ -->
13
+ <v-app-bar
14
+ :height="58"
15
+ flat
16
+ scroll-behavior="elevate"
17
+ scroll-threshold="200"
18
+ class="app-topbar-shell"
19
+ >
3
20
  <v-app-bar-nav-icon v-if="!props.hideNavIcon" @click="drawer = !drawer"></v-app-bar-nav-icon>
4
21
 
22
+ <div class="app-topbar-shell__crumb">
23
+ <span v-if="siteName" class="app-topbar-shell__site">{{ siteName }}</span>
24
+ <span v-if="siteName && moduleName" class="app-topbar-shell__sep">/</span>
25
+ <span class="app-topbar-shell__module">{{ moduleName }}</span>
26
+ </div>
27
+
5
28
  <template #append>
6
- <v-btn icon="mdi-theme-light-dark" variant="text" class="mx-2" @click="toggleTheme" />
29
+ <!--
30
+ The icon now says which theme you would GET, and the choice is
31
+ remembered - see `composables/useThemePreference.ts`. Same button, same
32
+ place, same one click.
33
+ -->
34
+ <!-- The design's 36px bordered icon button. Same one click, same icon. -->
35
+ <v-btn
36
+ :icon="current === 'dark' ? 'mdi-weather-sunny' : 'mdi-weather-night'"
37
+ :aria-label="current === 'dark' ? 'Switch to light theme' : 'Switch to dark theme'"
38
+ variant="text"
39
+ size="36"
40
+ class="app-topbar-shell__icon-btn ms-2"
41
+ @click="toggleTheme"
42
+ />
7
43
 
8
44
  <v-menu offset="10px" :close-on-content-click="false">
9
45
  <template #activator="{ props }">
10
- <v-btn fab variant="text" icon v-bind="props" class="mx-2">
46
+ <v-btn fab variant="text" icon v-bind="props" class="ms-2 me-1">
11
47
  <AvatarMain :image-src="currentUser?.profile" :name="name" />
12
48
  </v-btn>
13
49
  </template>
14
50
 
15
- <v-card width="350" max-height="600px" elevation="2" rounded="xl" class="pa-4">
16
- <v-row no-gutters>
17
- <v-col cols="12">
18
- <v-row no-gutters justify="center">
19
- <AvatarMain :image-src="currentUser?.profile" :name="name" />
20
- </v-row>
21
- </v-col>
22
-
23
- <v-col cols="12" class="text-center mt-2 mb-4">
24
- {{ name }}
25
- </v-col>
26
-
27
- <v-col cols="12" class="mb-3">
28
- <v-btn v-if="APP_NAME.toLowerCase() !== 'account'" block rounded="xl" variant="tonal" size="x-large"
29
- class="text-none text-subtitle-1 font-weight-regular" @click="redirect(APP_ACCOUNT, 'home')">
30
- Manage Account
31
- </v-btn>
32
- </v-col>
33
-
34
- <v-col cols="12">
35
- <v-btn block rounded="xl" variant="tonal" size="x-large"
36
- class="text-none text-subtitle-1 font-weight-regular" @click="logout()">
37
- Logout
38
- </v-btn>
39
- </v-col>
40
- </v-row>
51
+ <!--
52
+ 240px, 14px radius and the design's menu shadow. The two actions and
53
+ the condition on the first are exactly as they were.
54
+ -->
55
+ <v-card :width="240" elevation="0" class="app-profile-menu">
56
+ <div class="app-profile-menu__head">
57
+ <AvatarMain :image-src="currentUser?.profile" :name="name" />
58
+
59
+ <div class="app-profile-menu__who">
60
+ <div class="app-profile-menu__name">{{ name }}</div>
61
+ <div v-if="siteName" class="app-profile-menu__site">{{ siteName }}</div>
62
+ </div>
63
+ </div>
64
+
65
+ <div class="app-profile-menu__rows">
66
+ <button
67
+ v-if="APP_NAME.toLowerCase() !== 'account'"
68
+ type="button"
69
+ class="app-profile-menu__row"
70
+ @click="redirect(APP_ACCOUNT, 'home')"
71
+ >
72
+ <v-icon icon="mdi-account-cog-outline" size="18" />
73
+ Manage Account
74
+ </button>
75
+
76
+ <button
77
+ type="button"
78
+ class="app-profile-menu__row app-profile-menu__row--danger"
79
+ @click="logout()"
80
+ >
81
+ <v-icon icon="mdi-logout" size="18" />
82
+ Logout
83
+ </button>
84
+ </div>
41
85
  </v-card>
42
86
  </v-menu>
43
87
  </template>
@@ -45,7 +89,7 @@
45
89
  </template>
46
90
 
47
91
  <script setup lang="ts">
48
- import { useTheme } from "vuetify";
92
+ import { moduleNameFromRoute } from "../../utils/breadcrumb";
49
93
 
50
94
  const props = defineProps({
51
95
  hideNavIcon: {
@@ -64,14 +108,49 @@ const { redirect } = useUtils();
64
108
 
65
109
  const { APP_ACCOUNT, APP_MAIN, APP_NAME } = useRuntimeConfig().public;
66
110
 
67
- const theme = useTheme();
68
-
69
- function toggleTheme() {
70
- theme.global.name.value = theme.global.current.value.dark ? "light" : "dark";
71
- }
111
+ /**
112
+ * This used to be `theme.global.name.value = ...` and nothing else, so the
113
+ * choice was gone on the next reload - and, because these are eleven separate
114
+ * applications, gone again on every hop between them. The composable writes the
115
+ * layer's shared `theme` cookie, and `plugins/vuetify.ts` boots on it.
116
+ */
117
+ const { current, toggleTheme } = useThemePreference();
72
118
 
73
119
  const { currentUser } = useLocalAuth();
74
120
 
121
+ /**
122
+ * THE BREADCRUMB: `Site name / Current module`.
123
+ *
124
+ * Both halves are read from what the page already holds - the shared `site`
125
+ * state the applications populate, and the router's own current route. NO
126
+ * request is made from this component; if the site has not been loaded on a
127
+ * given screen the site half is simply omitted and the module stands alone.
128
+ */
129
+ const { site } = useSite();
130
+
131
+ /**
132
+ * The shared `site` state is only populated by the screens that load a site
133
+ * document, so on most screens - the dashboard included - the bar had no name
134
+ * to print. `SwitchContext` mirrors the label it is ALREADY showing in the
135
+ * rail into this state, so the bar falls back to the very name the user can
136
+ * see two inches to the left. Still no request from this component.
137
+ */
138
+ const shellSiteName = useState<string>("shell-context-site-name", () => "");
139
+
140
+ const siteName = computed(() =>
141
+ String(site.value?.name || shellSiteName.value || "").trim()
142
+ );
143
+
144
+ const route = useRoute();
145
+
146
+ /**
147
+ * The page's name comes off the route name, which the applications build from
148
+ * their own folder structure: `org-site-work-orders` is Work Orders. The rule
149
+ * itself lives in `utils/breadcrumb.ts`, where it is unit-tested against every
150
+ * route shape the eleven applications actually have.
151
+ */
152
+ const moduleName = computed(() => moduleNameFromRoute(route));
153
+
75
154
  const profile = computed(() => {
76
155
  return `/api/public/${currentUser.value?.profile}`;
77
156
  });
@@ -95,8 +174,11 @@ const name = computed(() => {
95
174
 
96
175
  if(full) return full;
97
176
 
177
+ /* A member invited by email has no name stored yet, which left the avatar's
178
+ initials empty and the circle blank. The address is what the product shows
179
+ that member everywhere else, so it stands in here too. */
98
180
  const alternative = user?.name?.trim();
99
- return alternative || ""
181
+ return alternative || user?.email?.trim() || ""
100
182
 
101
183
  })
102
184
 
@@ -105,6 +187,151 @@ const { getNameInitials } = useUtils();
105
187
  </script>
106
188
 
107
189
  <style scoped>
190
+ /* ---------------------------------------------------------------- */
191
+ /* The top bar. */
192
+ /* ---------------------------------------------------------------- */
193
+
194
+ /**
195
+ * The design's bar is 58px INCLUDING its rule. A real `border-bottom` is added
196
+ * to the 58px Vuetify lays out, so the bar measures 59 and every page below it
197
+ * starts a pixel low; an inset shadow draws the same 1px line inside the 58.
198
+ */
199
+ .app-topbar-shell,
200
+ .app-profile-menu {
201
+ /* The shell is the first surface to wear the design's typeface - see the
202
+ note in NavigationDrawer.vue. */
203
+ font-family: var(--font-sans);
204
+ }
205
+
206
+ .app-topbar-shell {
207
+ box-shadow: inset 0 -1px 0 var(--border);
208
+ }
209
+
210
+ .app-topbar-shell__crumb {
211
+ flex: 1 1 auto;
212
+ min-width: 0;
213
+ display: flex;
214
+ align-items: center;
215
+ gap: 6px;
216
+ font-size: var(--fs-breadcrumb);
217
+ /* Truncates rather than wraps: two lines would break a 58px bar, and the
218
+ site name is the half worth losing first. */
219
+ overflow: hidden;
220
+ white-space: nowrap;
221
+ }
222
+
223
+ .app-topbar-shell__site,
224
+ .app-topbar-shell__sep {
225
+ color: var(--muted);
226
+ overflow: hidden;
227
+ text-overflow: ellipsis;
228
+ }
229
+
230
+ .app-topbar-shell__module {
231
+ color: var(--text);
232
+ font-weight: 700;
233
+ flex: 0 0 auto;
234
+ }
235
+
236
+ /**
237
+ * THE TOP-BAR AVATAR is the design's accent-soft circle with accent-text
238
+ * initials, and only this one. `AvatarMain` gives every person a colour of
239
+ * their own from their name, which is right in a list of people - it is how
240
+ * you tell two rows apart - and wrong for the one avatar that is always the
241
+ * same person. `!important` because that per-person colour arrives as an
242
+ * inline style, and the rule is confined to the bar and its own menu.
243
+ */
244
+ .app-topbar-shell :deep(.v-avatar),
245
+ .app-profile-menu :deep(.v-avatar) {
246
+ background-color: var(--accent-soft) !important;
247
+ /* The initials' colour arrives inline as well, and in dark mode it is the
248
+ per-person colour ON a blue wash: 1.26:1, invisible. */
249
+ color: var(--accent-text) !important;
250
+ font-weight: 800;
251
+ }
252
+
253
+ /* 36px, 1px border, 10px radius - the design's icon button. */
254
+ .app-topbar-shell__icon-btn {
255
+ border: 1px solid var(--border);
256
+ border-radius: var(--r-inner);
257
+ }
258
+
259
+ /* ---------------------------------------------------------------- */
260
+ /* The 240px profile menu. */
261
+ /* ---------------------------------------------------------------- */
262
+
263
+ .app-profile-menu {
264
+ border-radius: var(--r-menu);
265
+ box-shadow: var(--shadow-menu);
266
+ border: 1px solid var(--border);
267
+ overflow: hidden;
268
+ }
269
+
270
+ .app-profile-menu__head {
271
+ display: flex;
272
+ align-items: center;
273
+ gap: 10px;
274
+ padding: 14px;
275
+ border-bottom: 1px solid var(--border);
276
+ }
277
+
278
+ .app-profile-menu__who {
279
+ min-width: 0;
280
+ }
281
+
282
+ .app-profile-menu__name {
283
+ font-size: 13.5px;
284
+ font-weight: 800;
285
+ line-height: 1.3;
286
+ color: var(--text);
287
+ overflow: hidden;
288
+ white-space: nowrap;
289
+ text-overflow: ellipsis;
290
+ }
291
+
292
+ .app-profile-menu__site {
293
+ font-size: 11.5px;
294
+ font-weight: 600;
295
+ line-height: 1.3;
296
+ color: var(--muted);
297
+ overflow: hidden;
298
+ white-space: nowrap;
299
+ text-overflow: ellipsis;
300
+ }
301
+
302
+ .app-profile-menu__rows {
303
+ padding: 6px;
304
+ }
305
+
306
+ .app-profile-menu__row {
307
+ width: 100%;
308
+ display: flex;
309
+ align-items: center;
310
+ gap: 10px;
311
+ padding: 9px 10px;
312
+ border-radius: var(--r-inner-sm);
313
+ font-size: var(--fs-btn);
314
+ font-weight: var(--fw-btn);
315
+ color: var(--text2);
316
+ text-align: start;
317
+ cursor: pointer;
318
+ background: transparent;
319
+ }
320
+
321
+ .app-profile-menu__row:hover {
322
+ background: var(--hover);
323
+ }
324
+
325
+ /* Logout is the design's red row: the colour lives on the label, and the
326
+ soft error background is what the hover paints. */
327
+ .app-profile-menu__row--danger {
328
+ color: var(--err);
329
+ }
330
+
331
+ .app-profile-menu__row--danger:hover {
332
+ background: var(--err-bg);
333
+ }
334
+
108
335
  .APP_NAME {
109
336
  display: inline-block;
110
337
  word-wrap: break-word;