mgv-backoffice 1.19.0 → 1.21.0
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/README.md +158 -4
- package/dist/components/BaseGoogleSignInButton.vue.d.ts +22 -0
- package/dist/components/BaseLoginForm.vue.d.ts +51 -0
- package/dist/components/BaseNotificationPanel.vue.d.ts +19 -0
- package/dist/components/BaseSidebar.vue.d.ts +9 -3
- package/dist/components/EarningsCard.vue.d.ts +17 -6
- package/dist/composables/useNotifications.d.ts +29 -0
- package/dist/composables/useSidebarCollapse.d.ts +22 -0
- package/dist/index.d.ts +8 -0
- package/dist/types/auth.d.ts +11 -0
- package/dist/types/notification.d.ts +20 -0
- package/dist/ui-lib.css +1 -1
- package/dist/ui-lib.js +1102 -673
- package/dist/ui-lib.umd.cjs +1 -1
- package/package.json +1 -1
- package/src/components/BaseAppLayout.vue +4 -1
- package/src/components/BaseGoogleSignInButton.vue +83 -0
- package/src/components/BaseLoginForm.test.ts +52 -0
- package/src/components/BaseLoginForm.vue +204 -0
- package/src/components/BaseNotificationPanel.vue +183 -0
- package/src/components/BaseSidebar.vue +140 -31
- package/src/components/EarningsCard.test.ts +57 -0
- package/src/components/EarningsCard.vue +55 -18
- package/src/composables/useNotifications.test.ts +64 -0
- package/src/composables/useNotifications.ts +81 -0
- package/src/composables/useSidebarCollapse.ts +64 -0
- package/src/index.ts +8 -0
- package/src/types/auth.ts +11 -0
- package/src/types/notification.ts +20 -0
package/README.md
CHANGED
|
@@ -414,29 +414,43 @@ import { ColoredSquares, ColorsEnums } from 'mgv-backoffice'
|
|
|
414
414
|
|
|
415
415
|
### EarningsCard
|
|
416
416
|
|
|
417
|
-
Earnings summary card with formatted currency display.
|
|
417
|
+
Earnings summary card with formatted currency display. Supports a signed P&L
|
|
418
|
+
mode that renders a red loss theme (and a downward trend glyph) for negative
|
|
419
|
+
amounts.
|
|
418
420
|
|
|
419
421
|
**Props:**
|
|
420
422
|
|
|
421
423
|
| Prop | Type | Default | Description |
|
|
422
424
|
| ---------- | -------- |-------------------------|----------------------|
|
|
423
425
|
| `title` | `String` | `'TOTAL EARNINGS'` | Card heading |
|
|
424
|
-
| `amount` | `Number` | `0` | Monetary value
|
|
426
|
+
| `amount` | `Number` | `0` | Monetary value (a stringified number is coerced) |
|
|
425
427
|
| `subtitle` | `String` | `'Lifetime commission'` | Subheading text |
|
|
426
428
|
| `badge` | `String` | `''` | Optional badge label |
|
|
427
429
|
| `currency` | `String` | `'$'` | Currency symbol |
|
|
428
430
|
| `decimals` | `Number` | `2` | Fraction digits shown for the amount |
|
|
431
|
+
| `accent` | `'orange' \| 'emerald' \| 'red'` | `'orange'` | Card theme. `emerald` tints it green; `red` is the loss theme. |
|
|
432
|
+
| `signed` | `Boolean` | `false` | Treat `amount` as a signed P&L figure: a negative value automatically switches to the `red` loss theme and flips the trend glyph to point **down**; a non-negative value keeps the chosen `accent` and the upward glyph. |
|
|
429
433
|
|
|
430
434
|
**Example:**
|
|
431
435
|
|
|
432
436
|
```vue
|
|
433
437
|
<template>
|
|
438
|
+
<!-- Always-positive total: original behaviour. -->
|
|
434
439
|
<EarningsCard
|
|
435
440
|
title="Monthly Revenue"
|
|
436
441
|
:amount="12500"
|
|
437
442
|
subtitle="April 2026"
|
|
438
443
|
currency="€"
|
|
439
444
|
/>
|
|
445
|
+
|
|
446
|
+
<!-- Signed P&L: renders red + a down arrow when the amount is negative. -->
|
|
447
|
+
<EarningsCard
|
|
448
|
+
title="TOTAL P&L"
|
|
449
|
+
:amount="-128.4"
|
|
450
|
+
subtitle="Realised + unrealised"
|
|
451
|
+
accent="emerald"
|
|
452
|
+
signed
|
|
453
|
+
/>
|
|
440
454
|
</template>
|
|
441
455
|
|
|
442
456
|
<script setup lang="ts">
|
|
@@ -707,6 +721,8 @@ computePnL({ buyPrice: 100, lastPrice: 110, filledQty: 5 })
|
|
|
707
721
|
### BaseAppLayout
|
|
708
722
|
|
|
709
723
|
Root layout: dark/light page background, skip link, `<main>`-with-inert wrapper.
|
|
724
|
+
The `<main>` content offset tracks the sidebar width automatically —
|
|
725
|
+
`lg:ml-60` when expanded, `lg:ml-16` when collapsed (via `useSidebarCollapse()`).
|
|
710
726
|
|
|
711
727
|
**Props:**
|
|
712
728
|
|
|
@@ -727,8 +743,9 @@ Root layout: dark/light page background, skip link, `<main>`-with-inert wrapper.
|
|
|
727
743
|
### BaseSidebar
|
|
728
744
|
|
|
729
745
|
Responsive sidebar with desktop fixed-positioning and mobile off-canvas
|
|
730
|
-
behavior, focus management, optional theme toggle,
|
|
731
|
-
|
|
746
|
+
behavior, focus management, optional theme toggle, a desktop collapse
|
|
747
|
+
toggle (icon-only rail), an optional notifications bell, and configurable
|
|
748
|
+
nav sections.
|
|
732
749
|
|
|
733
750
|
**Props:**
|
|
734
751
|
|
|
@@ -739,6 +756,18 @@ sections.
|
|
|
739
756
|
| `appName` | `String` | `''` | Optional app name in the footer. |
|
|
740
757
|
| `version` | `String` | `''` | Optional version string in the footer. |
|
|
741
758
|
| `showThemeToggle` | `Boolean` | `true` | Toggle the dark/light switch in the footer. |
|
|
759
|
+
| `collapsible` | `Boolean` | `true` | Show the desktop collapse toggle that shrinks the sidebar to an icon-only rail. |
|
|
760
|
+
| `showNotifications` | `Boolean` | `false` | Show the notifications bell (with unread badge) that toggles `BaseNotificationPanel`. |
|
|
761
|
+
|
|
762
|
+
> **Collapse state** is shared via `useSidebarCollapse()` (and persisted to
|
|
763
|
+
> localStorage) so `BaseAppLayout` can shrink the content offset from
|
|
764
|
+
> `lg:ml-60` to `lg:ml-16` in step with the rail. Collapsing only affects
|
|
765
|
+
> desktop (`lg+`); on mobile the sidebar stays a full off-canvas panel.
|
|
766
|
+
|
|
767
|
+
> **Notifications:** set `:show-notifications="true"` to render the bell,
|
|
768
|
+
> then drop a [`BaseNotificationPanel`](#basenotificationpanel) in your app.
|
|
769
|
+
> Both share state through `useNotifications()`, so the unread badge and the
|
|
770
|
+
> panel stay in sync.
|
|
742
771
|
|
|
743
772
|
**Slots:**
|
|
744
773
|
|
|
@@ -776,6 +805,127 @@ interface NavSection {
|
|
|
776
805
|
|
|
777
806
|
---
|
|
778
807
|
|
|
808
|
+
### BaseNotificationPanel
|
|
809
|
+
|
|
810
|
+
Left-anchored notification drawer (teleported to `<body>`, slides in from
|
|
811
|
+
the left, backdrop + Escape to close). Open/close state and the list live
|
|
812
|
+
in `useNotifications()`, so the sidebar bell and the panel stay in sync.
|
|
813
|
+
|
|
814
|
+
Enable the bell on the sidebar with `:show-notifications="true"`, drop one
|
|
815
|
+
`<BaseNotificationPanel />` anywhere in your app, and feed it data via the
|
|
816
|
+
composable.
|
|
817
|
+
|
|
818
|
+
**Props:**
|
|
819
|
+
|
|
820
|
+
| Prop | Type | Default | Description |
|
|
821
|
+
| ----------------- | --------- | ------------------------------ | ----------- |
|
|
822
|
+
| `title` | `String` | `'Notifications'` | Panel heading. |
|
|
823
|
+
| `emptyText` | `String` | `'You have no notifications.'` | Shown when the list is empty. |
|
|
824
|
+
| `showMarkAllRead` | `Boolean` | `true` | Render the "Mark all as read" action when there are unread items. |
|
|
825
|
+
|
|
826
|
+
**Emits:** `select` (the clicked notification's `id`; the row is also marked read).
|
|
827
|
+
|
|
828
|
+
```vue
|
|
829
|
+
<script setup lang="ts">
|
|
830
|
+
import { BaseNotificationPanel, useNotifications } from 'mgv-backoffice'
|
|
831
|
+
const { setNotifications } = useNotifications()
|
|
832
|
+
setNotifications([
|
|
833
|
+
{ id: 1, title: 'New comment', message: 'Alice replied to your post', time: '2m ago', type: 'info' },
|
|
834
|
+
{ id: 2, title: 'Build passed', time: '1h ago', read: true, type: 'success' },
|
|
835
|
+
])
|
|
836
|
+
</script>
|
|
837
|
+
|
|
838
|
+
<template>
|
|
839
|
+
<BaseSidebar :sections="navSections" :show-notifications="true" />
|
|
840
|
+
<BaseNotificationPanel @select="(id) => goTo(id)" />
|
|
841
|
+
</template>
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
```ts
|
|
845
|
+
import type { NotificationItem } from 'mgv-backoffice'
|
|
846
|
+
|
|
847
|
+
interface NotificationItem {
|
|
848
|
+
id: string | number
|
|
849
|
+
title: string
|
|
850
|
+
message?: string
|
|
851
|
+
time?: string // pre-formatted by you
|
|
852
|
+
read?: boolean
|
|
853
|
+
type?: 'info' | 'success' | 'warning' | 'error' // status dot colour
|
|
854
|
+
}
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
---
|
|
858
|
+
|
|
859
|
+
## Authentication
|
|
860
|
+
|
|
861
|
+
### BaseGoogleSignInButton
|
|
862
|
+
|
|
863
|
+
Google-branded "Sign in with Google" button (official multi-colour "G",
|
|
864
|
+
dark-mode surface swap). Purely presentational — it runs no OAuth itself;
|
|
865
|
+
listen on `click` and start your own Google Identity / Firebase / backend
|
|
866
|
+
flow there.
|
|
867
|
+
|
|
868
|
+
**Props:**
|
|
869
|
+
|
|
870
|
+
| Prop | Type | Default | Description |
|
|
871
|
+
| ---------- | --------- | -------------------------- | ----------- |
|
|
872
|
+
| `label` | `String` | `'Sign in with Google'` | Button text. |
|
|
873
|
+
| `loading` | `Boolean` | `false` | Disables and shows a spinner. |
|
|
874
|
+
| `disabled` | `Boolean` | `false` | Disables without the spinner. |
|
|
875
|
+
| `block` | `Boolean` | `true` | Full-width layout. |
|
|
876
|
+
|
|
877
|
+
**Emits:** `click` (only when not disabled/loading).
|
|
878
|
+
|
|
879
|
+
### BaseLoginForm
|
|
880
|
+
|
|
881
|
+
Presentational sign-in card: email + password (with show/hide), an optional
|
|
882
|
+
"Remember me" checkbox, an error banner, the Google button + "or" divider,
|
|
883
|
+
and `logo` / `forgot` / `footer` slots. Owns its input state and emits
|
|
884
|
+
`submit` / `google-sign-in`; the app handles the actual request and feeds
|
|
885
|
+
back `loading` / `error`.
|
|
886
|
+
|
|
887
|
+
**Props:**
|
|
888
|
+
|
|
889
|
+
| Prop | Type | Default | Description |
|
|
890
|
+
| --------------- | --------- | ----------- | ----------- |
|
|
891
|
+
| `title` | `String` | `'Sign in'` | Card heading. |
|
|
892
|
+
| `subtitle` | `String` | `''` | Muted line under the heading. |
|
|
893
|
+
| `submitLabel` | `String` | `'Sign in'` | Submit button text. |
|
|
894
|
+
| `loading` | `Boolean` | `false` | Disables the form, spinner on submit. |
|
|
895
|
+
| `googleLoading` | `Boolean` | `false` | Disables the form, spinner on the Google button. |
|
|
896
|
+
| `error` | `String` | `''` | Error banner above the form. |
|
|
897
|
+
| `showGoogle` | `Boolean` | `true` | Render the Google button + divider. |
|
|
898
|
+
| `showRemember` | `Boolean` | `false` | Render the "Remember me" checkbox. |
|
|
899
|
+
|
|
900
|
+
**Emits:** `submit` (`LoginCredentials`), `google-sign-in`.
|
|
901
|
+
|
|
902
|
+
**Slots:** `logo`, `forgot` (next to the password label), `footer`.
|
|
903
|
+
|
|
904
|
+
```vue
|
|
905
|
+
<script setup lang="ts">
|
|
906
|
+
import { BaseLoginForm } from 'mgv-backoffice'
|
|
907
|
+
import type { LoginCredentials } from 'mgv-backoffice'
|
|
908
|
+
|
|
909
|
+
async function onSubmit(creds: LoginCredentials) { /* call your API */ }
|
|
910
|
+
function onGoogle() { /* start Google OAuth */ }
|
|
911
|
+
</script>
|
|
912
|
+
|
|
913
|
+
<template>
|
|
914
|
+
<BaseLoginForm
|
|
915
|
+
subtitle="Welcome back"
|
|
916
|
+
:show-remember="true"
|
|
917
|
+
@submit="onSubmit"
|
|
918
|
+
@google-sign-in="onGoogle"
|
|
919
|
+
>
|
|
920
|
+
<template #logo><MyLogo /></template>
|
|
921
|
+
<template #forgot><a href="/forgot" class="text-sm text-emerald-600">Forgot?</a></template>
|
|
922
|
+
<template #footer>No account? <a href="/signup" class="text-emerald-600">Sign up</a></template>
|
|
923
|
+
</BaseLoginForm>
|
|
924
|
+
</template>
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
---
|
|
928
|
+
|
|
779
929
|
## Modals & sections
|
|
780
930
|
|
|
781
931
|
### BaseModalShell
|
|
@@ -1025,6 +1175,8 @@ import {
|
|
|
1025
1175
|
useDebouncedRef,
|
|
1026
1176
|
useToast,
|
|
1027
1177
|
useMobileSidebar,
|
|
1178
|
+
useSidebarCollapse,
|
|
1179
|
+
useNotifications,
|
|
1028
1180
|
} from 'mgv-backoffice'
|
|
1029
1181
|
```
|
|
1030
1182
|
|
|
@@ -1037,6 +1189,8 @@ import {
|
|
|
1037
1189
|
| `useDebouncedRef(source, delay?)` | Debounced mirror of a ref. Timer cleared on scope dispose. |
|
|
1038
1190
|
| `useToast(durationMs?)` | Per-component toast state: `{ showToast, toastMessage, toastType, showToastMessage }`. |
|
|
1039
1191
|
| `useMobileSidebar()` | Singleton state shared between `BaseSidebar` and `BaseAppLayout` for the off-canvas open/closed flag. |
|
|
1192
|
+
| `useSidebarCollapse({ storageKey? })` | Singleton collapsed/expanded state for the desktop sidebar rail, shared between `BaseSidebar` and `BaseAppLayout` and persisted to localStorage (default key `'mgv-sidebar-collapsed'`). |
|
|
1193
|
+
| `useNotifications()` | Singleton notification state shared by the sidebar bell and `BaseNotificationPanel`: `{ notifications, unreadCount, open, openPanel, closePanel, togglePanel, setNotifications, add, remove, markRead, markAllRead, clear }`. |
|
|
1040
1194
|
|
|
1041
1195
|
---
|
|
1042
1196
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Button label. Defaults to "Sign in with Google". */
|
|
3
|
+
label?: string;
|
|
4
|
+
/** Disables the button and shows a busy spinner. */
|
|
5
|
+
loading?: boolean;
|
|
6
|
+
/** Disables the button without the busy spinner. */
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
/** Full-width (block) layout. Defaults to true. */
|
|
9
|
+
block?: boolean;
|
|
10
|
+
}
|
|
11
|
+
declare const __VLS_export: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
12
|
+
click: () => any;
|
|
13
|
+
}, string, import('vue').PublicProps, Readonly<Props> & Readonly<{
|
|
14
|
+
onClick?: (() => any) | undefined;
|
|
15
|
+
}>, {
|
|
16
|
+
label: string;
|
|
17
|
+
disabled: boolean;
|
|
18
|
+
loading: boolean;
|
|
19
|
+
block: boolean;
|
|
20
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
21
|
+
declare const _default: typeof __VLS_export;
|
|
22
|
+
export default _default;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { LoginCredentials } from '../types/auth';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Card heading. Defaults to "Sign in". */
|
|
4
|
+
title?: string;
|
|
5
|
+
/** Optional muted line under the heading. */
|
|
6
|
+
subtitle?: string;
|
|
7
|
+
/** Submit button label. Defaults to "Sign in". */
|
|
8
|
+
submitLabel?: string;
|
|
9
|
+
/** Disables the form and shows a spinner on the submit button. */
|
|
10
|
+
loading?: boolean;
|
|
11
|
+
/** Disables the form and shows a spinner on the Google button. */
|
|
12
|
+
googleLoading?: boolean;
|
|
13
|
+
/** Error message rendered above the form (e.g. "Invalid credentials"). */
|
|
14
|
+
error?: string;
|
|
15
|
+
/** Render the "Sign in with Google" button + divider. Defaults to true. */
|
|
16
|
+
showGoogle?: boolean;
|
|
17
|
+
/** Render the "Remember me" checkbox. Defaults to false. */
|
|
18
|
+
showRemember?: boolean;
|
|
19
|
+
}
|
|
20
|
+
declare var __VLS_1: {}, __VLS_10: {}, __VLS_12: {};
|
|
21
|
+
type __VLS_Slots = {} & {
|
|
22
|
+
logo?: (props: typeof __VLS_1) => any;
|
|
23
|
+
} & {
|
|
24
|
+
forgot?: (props: typeof __VLS_10) => any;
|
|
25
|
+
} & {
|
|
26
|
+
footer?: (props: typeof __VLS_12) => any;
|
|
27
|
+
};
|
|
28
|
+
declare const __VLS_base: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
29
|
+
submit: (credentials: LoginCredentials) => any;
|
|
30
|
+
"google-sign-in": () => any;
|
|
31
|
+
}, string, import('vue').PublicProps, Readonly<Props> & Readonly<{
|
|
32
|
+
onSubmit?: ((credentials: LoginCredentials) => any) | undefined;
|
|
33
|
+
"onGoogle-sign-in"?: (() => any) | undefined;
|
|
34
|
+
}>, {
|
|
35
|
+
title: string;
|
|
36
|
+
error: string;
|
|
37
|
+
subtitle: string;
|
|
38
|
+
loading: boolean;
|
|
39
|
+
submitLabel: string;
|
|
40
|
+
googleLoading: boolean;
|
|
41
|
+
showGoogle: boolean;
|
|
42
|
+
showRemember: boolean;
|
|
43
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
44
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
45
|
+
declare const _default: typeof __VLS_export;
|
|
46
|
+
export default _default;
|
|
47
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
48
|
+
new (): {
|
|
49
|
+
$slots: S;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Panel heading. Defaults to "Notifications". */
|
|
3
|
+
title?: string;
|
|
4
|
+
/** Shown when there are no notifications. */
|
|
5
|
+
emptyText?: string;
|
|
6
|
+
/** Render the "Mark all read" action when there are unread items. */
|
|
7
|
+
showMarkAllRead?: boolean;
|
|
8
|
+
}
|
|
9
|
+
declare const __VLS_export: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
10
|
+
select: (id: string | number) => any;
|
|
11
|
+
}, string, import('vue').PublicProps, Readonly<Props> & Readonly<{
|
|
12
|
+
onSelect?: ((id: string | number) => any) | undefined;
|
|
13
|
+
}>, {
|
|
14
|
+
title: string;
|
|
15
|
+
emptyText: string;
|
|
16
|
+
showMarkAllRead: boolean;
|
|
17
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
18
|
+
declare const _default: typeof __VLS_export;
|
|
19
|
+
export default _default;
|
|
@@ -10,26 +10,32 @@ interface Props {
|
|
|
10
10
|
version?: string;
|
|
11
11
|
/** Whether to render the dark/light toggle in the footer. */
|
|
12
12
|
showThemeToggle?: boolean;
|
|
13
|
+
/** Whether to render the desktop collapse toggle (icon-only rail). */
|
|
14
|
+
collapsible?: boolean;
|
|
15
|
+
/** Whether to render the notifications bell button. */
|
|
16
|
+
showNotifications?: boolean;
|
|
13
17
|
}
|
|
14
18
|
declare var __VLS_1: {
|
|
15
19
|
size: number;
|
|
16
20
|
}, __VLS_13: {
|
|
17
21
|
size: number;
|
|
18
|
-
},
|
|
22
|
+
}, __VLS_40: {}, __VLS_42: {};
|
|
19
23
|
type __VLS_Slots = {} & {
|
|
20
24
|
logo?: (props: typeof __VLS_1) => any;
|
|
21
25
|
} & {
|
|
22
26
|
logo?: (props: typeof __VLS_13) => any;
|
|
23
27
|
} & {
|
|
24
|
-
status?: (props: typeof
|
|
28
|
+
status?: (props: typeof __VLS_40) => any;
|
|
25
29
|
} & {
|
|
26
|
-
footer?: (props: typeof
|
|
30
|
+
footer?: (props: typeof __VLS_42) => any;
|
|
27
31
|
};
|
|
28
32
|
declare const __VLS_base: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<Props> & Readonly<{}>, {
|
|
29
33
|
homeRouteName: string;
|
|
30
34
|
appName: string;
|
|
31
35
|
version: string;
|
|
32
36
|
showThemeToggle: boolean;
|
|
37
|
+
collapsible: boolean;
|
|
38
|
+
showNotifications: boolean;
|
|
33
39
|
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
34
40
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
35
41
|
declare const _default: typeof __VLS_export;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Accent colour of the card. Defaults to `orange` so existing consumers are
|
|
3
|
-
* unaffected; `emerald` lets a host app tint the card to a green-themed brand
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* unaffected; `emerald` lets a host app tint the card to a green-themed brand,
|
|
4
|
+
* and `red` renders a loss/negative theme (also selected automatically by
|
|
5
|
+
* `signed`, see below). Each accent carries the full literal Tailwind class
|
|
6
|
+
* strings (rather than a single interpolated hue) so the classes are statically
|
|
7
|
+
* detectable and the themes can differ in more than hue — e.g. orange renders
|
|
8
|
+
* the amount in neutral grey while emerald/red render it in the accent itself.
|
|
8
9
|
*/
|
|
9
|
-
type Accent = 'orange' | 'emerald';
|
|
10
|
+
type Accent = 'orange' | 'emerald' | 'red';
|
|
10
11
|
interface Props {
|
|
11
12
|
title?: string;
|
|
12
13
|
amount?: number;
|
|
@@ -15,6 +16,15 @@ interface Props {
|
|
|
15
16
|
currency?: string;
|
|
16
17
|
decimals?: number;
|
|
17
18
|
accent?: Accent;
|
|
19
|
+
/**
|
|
20
|
+
* Treat the card as a signed P&L figure. When `true` and the amount is
|
|
21
|
+
* negative, the card renders the loss theme automatically: the `red` accent
|
|
22
|
+
* (border / amount / subtitle / icon-box recoloured) AND the trend glyph
|
|
23
|
+
* flipped to point DOWN. A non-negative amount keeps the chosen `accent` and
|
|
24
|
+
* the upward glyph. Defaults to `false` so existing consumers — which use the
|
|
25
|
+
* card for always-positive totals like lifetime earnings — are unaffected.
|
|
26
|
+
*/
|
|
27
|
+
signed?: boolean;
|
|
18
28
|
}
|
|
19
29
|
declare const __VLS_export: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<Props> & Readonly<{}>, {
|
|
20
30
|
title: string;
|
|
@@ -24,6 +34,7 @@ declare const __VLS_export: import('vue').DefineComponent<Props, {}, {}, {}, {},
|
|
|
24
34
|
currency: string;
|
|
25
35
|
decimals: number;
|
|
26
36
|
accent: Accent;
|
|
37
|
+
signed: boolean;
|
|
27
38
|
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
28
39
|
declare const _default: typeof __VLS_export;
|
|
29
40
|
export default _default;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { NotificationItem } from '../types/notification';
|
|
2
|
+
export declare function useNotifications(): {
|
|
3
|
+
notifications: import('vue').Ref<{
|
|
4
|
+
id: string | number;
|
|
5
|
+
title: string;
|
|
6
|
+
message?: string | undefined;
|
|
7
|
+
time?: string | undefined;
|
|
8
|
+
read?: boolean | undefined;
|
|
9
|
+
type?: "info" | "success" | "warning" | "error" | undefined;
|
|
10
|
+
}[], NotificationItem[] | {
|
|
11
|
+
id: string | number;
|
|
12
|
+
title: string;
|
|
13
|
+
message?: string | undefined;
|
|
14
|
+
time?: string | undefined;
|
|
15
|
+
read?: boolean | undefined;
|
|
16
|
+
type?: "info" | "success" | "warning" | "error" | undefined;
|
|
17
|
+
}[]>;
|
|
18
|
+
unreadCount: import('vue').ComputedRef<number>;
|
|
19
|
+
open: import('vue').Ref<boolean, boolean>;
|
|
20
|
+
openPanel: () => void;
|
|
21
|
+
closePanel: () => void;
|
|
22
|
+
togglePanel: () => void;
|
|
23
|
+
setNotifications: (items: NotificationItem[]) => void;
|
|
24
|
+
add: (item: NotificationItem) => void;
|
|
25
|
+
remove: (id: NotificationItem["id"]) => void;
|
|
26
|
+
markRead: (id: NotificationItem["id"]) => void;
|
|
27
|
+
markAllRead: () => void;
|
|
28
|
+
clear: () => void;
|
|
29
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared collapsed/expanded state for the desktop sidebar.
|
|
3
|
+
*
|
|
4
|
+
* Module-scoped singleton (mirrors useMobileSidebar) so `BaseSidebar` and
|
|
5
|
+
* the page layout (`BaseAppLayout`, which offsets `<main>` by the sidebar
|
|
6
|
+
* width) stay in sync. When collapsed, the sidebar shrinks to an icon-only
|
|
7
|
+
* rail on desktop.
|
|
8
|
+
*
|
|
9
|
+
* The preference is persisted to localStorage. The first consumer can pass
|
|
10
|
+
* an options object to override the key; subsequent callers reuse the same
|
|
11
|
+
* singleton and the option is only consulted on the first call.
|
|
12
|
+
*/
|
|
13
|
+
export interface UseSidebarCollapseOptions {
|
|
14
|
+
/** localStorage key used to persist the preference. */
|
|
15
|
+
storageKey?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function useSidebarCollapse(options?: UseSidebarCollapseOptions): {
|
|
18
|
+
collapsed: import('vue').Ref<boolean, boolean>;
|
|
19
|
+
collapse: () => void;
|
|
20
|
+
expand: () => void;
|
|
21
|
+
toggle: () => void;
|
|
22
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,9 @@ export { default as BasePageHeader } from './components/BasePageHeader.vue';
|
|
|
25
25
|
export { default as BaseToolbarButton } from './components/BaseToolbarButton.vue';
|
|
26
26
|
export { default as BaseActionButton } from './components/BaseActionButton.vue';
|
|
27
27
|
export { default as BaseCopyButton } from './components/BaseCopyButton.vue';
|
|
28
|
+
export { default as BaseGoogleSignInButton } from './components/BaseGoogleSignInButton.vue';
|
|
29
|
+
export { default as BaseLoginForm } from './components/BaseLoginForm.vue';
|
|
30
|
+
export { default as BaseNotificationPanel } from './components/BaseNotificationPanel.vue';
|
|
28
31
|
export { useTheme, initTheme } from './composables/useTheme';
|
|
29
32
|
export type { UseThemeOptions } from './composables/useTheme';
|
|
30
33
|
export { useThemeClasses } from './composables/useThemeClasses';
|
|
@@ -33,6 +36,9 @@ export { useEscapeKey } from './composables/useEscapeKey';
|
|
|
33
36
|
export { useDebouncedRef } from './composables/useDebounce';
|
|
34
37
|
export { useToast } from './composables/useToast';
|
|
35
38
|
export { useMobileSidebar } from './composables/useMobileSidebar';
|
|
39
|
+
export { useSidebarCollapse } from './composables/useSidebarCollapse';
|
|
40
|
+
export type { UseSidebarCollapseOptions } from './composables/useSidebarCollapse';
|
|
41
|
+
export { useNotifications } from './composables/useNotifications';
|
|
36
42
|
export { AlertEnum } from './enums/AlertEnum';
|
|
37
43
|
export { BaseBadgeEnum } from './enums/BaseBadgeEnum';
|
|
38
44
|
export { BaseButtonEnum } from './enums/BaseButtonEnum';
|
|
@@ -46,6 +52,8 @@ export { PositioningEnum } from './enums/PositioningEnum';
|
|
|
46
52
|
export type { BreadCrumb } from './components/BaseBreadcrumb.vue';
|
|
47
53
|
export type { NavItem, NavSection } from './types/sidebar';
|
|
48
54
|
export type { EntityPickerItem } from './types/entityPicker';
|
|
55
|
+
export type { LoginCredentials } from './types/auth';
|
|
56
|
+
export type { NotificationItem } from './types/notification';
|
|
49
57
|
export { getBaseColor, getBaseColorOf } from './utils/util';
|
|
50
58
|
export { methodBadgeSolid, methodBadgeBright, statusBadgeSolid, statusBadgeTinted, } from './utils/httpColors';
|
|
51
59
|
export { sanitizeHtml, isSafeHref } from './utils/sanitizeHtml';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credentials emitted by `BaseLoginForm` on submit.
|
|
3
|
+
*
|
|
4
|
+
* `remember` is only present when the form is rendered with the
|
|
5
|
+
* "remember me" checkbox enabled.
|
|
6
|
+
*/
|
|
7
|
+
export interface LoginCredentials {
|
|
8
|
+
email: string;
|
|
9
|
+
password: string;
|
|
10
|
+
remember?: boolean;
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single entry rendered in `BaseNotificationPanel` and counted by the
|
|
3
|
+
* sidebar's unread badge.
|
|
4
|
+
*
|
|
5
|
+
* • `id` — stable key, used for list rendering and mark/remove ops.
|
|
6
|
+
* • `title` — bold headline line.
|
|
7
|
+
* • `message` — optional secondary line.
|
|
8
|
+
* • `time` — optional pre-formatted timestamp (e.g. "2h ago"). The
|
|
9
|
+
* library does not format dates for you.
|
|
10
|
+
* • `read` — unread entries are highlighted and count toward the badge.
|
|
11
|
+
* • `type` — drives the small status dot colour.
|
|
12
|
+
*/
|
|
13
|
+
export interface NotificationItem {
|
|
14
|
+
id: string | number;
|
|
15
|
+
title: string;
|
|
16
|
+
message?: string;
|
|
17
|
+
time?: string;
|
|
18
|
+
read?: boolean;
|
|
19
|
+
type?: 'info' | 'success' | 'warning' | 'error';
|
|
20
|
+
}
|