mgv-backoffice 1.20.0 → 1.22.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 +145 -2
- package/dist/components/BaseInput.vue.d.ts +21 -0
- package/dist/components/BaseSegmentedControl.vue.d.ts +25 -0
- package/dist/components/BaseSelect.vue.d.ts +28 -0
- package/dist/components/BaseSpecFields.vue.d.ts +23 -0
- package/dist/components/BaseTable.vue.d.ts +26 -0
- package/dist/components/EarningsCard.vue.d.ts +17 -6
- package/dist/index.d.ts +8 -0
- package/dist/types/segmented.d.ts +11 -0
- package/dist/types/specField.d.ts +23 -0
- package/dist/types/table.d.ts +8 -0
- package/dist/ui-lib.css +1 -1
- package/dist/ui-lib.js +442 -270
- package/dist/ui-lib.umd.cjs +1 -1
- package/package.json +1 -1
- package/src/components/BaseInput.test.ts +40 -0
- package/src/components/BaseInput.vue +46 -0
- package/src/components/BaseSegmentedControl.test.ts +78 -0
- package/src/components/BaseSegmentedControl.vue +98 -0
- package/src/components/BaseSelect.test.ts +37 -0
- package/src/components/BaseSelect.vue +46 -0
- package/src/components/BaseSpecFields.test.ts +78 -0
- package/src/components/BaseSpecFields.vue +83 -0
- package/src/components/BaseTable.test.ts +55 -0
- package/src/components/BaseTable.vue +50 -0
- package/src/components/EarningsCard.test.ts +57 -0
- package/src/components/EarningsCard.vue +55 -18
- package/src/index.ts +10 -0
- package/src/types/segmented.ts +11 -0
- package/src/types/specField.ts +25 -0
- package/src/types/table.ts +8 -0
|
@@ -3,13 +3,14 @@ import { computed } from 'vue'
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Accent colour of the card. Defaults to `orange` so existing consumers are
|
|
6
|
-
* unaffected; `emerald` lets a host app tint the card to a green-themed brand
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* unaffected; `emerald` lets a host app tint the card to a green-themed brand,
|
|
7
|
+
* and `red` renders a loss/negative theme (also selected automatically by
|
|
8
|
+
* `signed`, see below). Each accent carries the full literal Tailwind class
|
|
9
|
+
* strings (rather than a single interpolated hue) so the classes are statically
|
|
10
|
+
* detectable and the themes can differ in more than hue — e.g. orange renders
|
|
11
|
+
* the amount in neutral grey while emerald/red render it in the accent itself.
|
|
11
12
|
*/
|
|
12
|
-
type Accent = 'orange' | 'emerald'
|
|
13
|
+
type Accent = 'orange' | 'emerald' | 'red'
|
|
13
14
|
|
|
14
15
|
interface Props {
|
|
15
16
|
title?: string
|
|
@@ -19,6 +20,15 @@ interface Props {
|
|
|
19
20
|
currency?: string
|
|
20
21
|
decimals?: number
|
|
21
22
|
accent?: Accent
|
|
23
|
+
/**
|
|
24
|
+
* Treat the card as a signed P&L figure. When `true` and the amount is
|
|
25
|
+
* negative, the card renders the loss theme automatically: the `red` accent
|
|
26
|
+
* (border / amount / subtitle / icon-box recoloured) AND the trend glyph
|
|
27
|
+
* flipped to point DOWN. A non-negative amount keeps the chosen `accent` and
|
|
28
|
+
* the upward glyph. Defaults to `false` so existing consumers — which use the
|
|
29
|
+
* card for always-positive totals like lifetime earnings — are unaffected.
|
|
30
|
+
*/
|
|
31
|
+
signed?: boolean
|
|
22
32
|
}
|
|
23
33
|
|
|
24
34
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -29,6 +39,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
29
39
|
currency: '$',
|
|
30
40
|
decimals: 2,
|
|
31
41
|
accent: 'orange',
|
|
42
|
+
signed: false,
|
|
32
43
|
})
|
|
33
44
|
|
|
34
45
|
const ACCENTS: Record<Accent, {
|
|
@@ -55,22 +66,40 @@ const ACCENTS: Record<Accent, {
|
|
|
55
66
|
iconBox: 'bg-emerald-50 dark:bg-emerald-900/30',
|
|
56
67
|
icon: 'text-emerald-500',
|
|
57
68
|
},
|
|
69
|
+
red: {
|
|
70
|
+
border: 'border-red-300 dark:border-red-700',
|
|
71
|
+
badge: 'bg-red-100 text-red-600 dark:bg-red-900/40 dark:text-red-400',
|
|
72
|
+
amount: 'text-red-500 dark:text-red-400',
|
|
73
|
+
subtitle: 'text-red-500 dark:text-red-400',
|
|
74
|
+
iconBox: 'bg-red-50 dark:bg-red-900/30',
|
|
75
|
+
icon: 'text-red-500',
|
|
76
|
+
},
|
|
58
77
|
}
|
|
59
78
|
|
|
60
|
-
|
|
79
|
+
// Coerce once: the amount can arrive as a JSON string (e.g. "-0.01"); fall back
|
|
80
|
+
// to 0 for non-finite input (NaN/Infinity) so the card never renders "NaN".
|
|
81
|
+
const numericAmount = computed(() => {
|
|
82
|
+
const n = Number(props.amount)
|
|
83
|
+
return Number.isFinite(n) ? n : 0
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// A loss only when the card is in signed mode and the value is below zero.
|
|
87
|
+
const isLoss = computed(() => props.signed && numericAmount.value < 0)
|
|
61
88
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
89
|
+
// In signed mode a negative amount forces the red loss theme; otherwise the
|
|
90
|
+
// caller-chosen accent stands.
|
|
91
|
+
const effectiveAccent = computed<Accent>(() => (isLoss.value ? 'red' : props.accent))
|
|
92
|
+
|
|
93
|
+
const cls = computed(() => ACCENTS[effectiveAccent.value])
|
|
94
|
+
|
|
95
|
+
const formattedAmount = computed(
|
|
96
|
+
() =>
|
|
67
97
|
props.currency +
|
|
68
|
-
|
|
98
|
+
numericAmount.value.toLocaleString('en-US', {
|
|
69
99
|
minimumFractionDigits: props.decimals,
|
|
70
100
|
maximumFractionDigits: props.decimals,
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
})
|
|
101
|
+
}),
|
|
102
|
+
)
|
|
74
103
|
</script>
|
|
75
104
|
|
|
76
105
|
<template>
|
|
@@ -121,8 +150,16 @@ const formattedAmount = computed(() => {
|
|
|
121
150
|
stroke-linecap="round"
|
|
122
151
|
stroke-linejoin="round"
|
|
123
152
|
>
|
|
124
|
-
|
|
125
|
-
|
|
153
|
+
<!-- Loss: a downward trend line + down-right arrowhead (the upward
|
|
154
|
+
glyph mirrored vertically). Otherwise the standard up-trend. -->
|
|
155
|
+
<template v-if="isLoss">
|
|
156
|
+
<polyline points="22 17 13.5 8.5 8.5 13.5 2 7" />
|
|
157
|
+
<polyline points="16 17 22 17 22 11" />
|
|
158
|
+
</template>
|
|
159
|
+
<template v-else>
|
|
160
|
+
<polyline points="22 7 13.5 15.5 8.5 10.5 2 17" />
|
|
161
|
+
<polyline points="16 7 22 7 22 13" />
|
|
162
|
+
</template>
|
|
126
163
|
</svg>
|
|
127
164
|
</div>
|
|
128
165
|
</div>
|
package/src/index.ts
CHANGED
|
@@ -34,6 +34,13 @@ export { default as BaseGoogleSignInButton } from './components/BaseGoogleSignIn
|
|
|
34
34
|
export { default as BaseLoginForm } from './components/BaseLoginForm.vue'
|
|
35
35
|
export { default as BaseNotificationPanel } from './components/BaseNotificationPanel.vue'
|
|
36
36
|
|
|
37
|
+
// Components — extracted from TradeAutomation
|
|
38
|
+
export { default as BaseInput } from './components/BaseInput.vue'
|
|
39
|
+
export { default as BaseSelect } from './components/BaseSelect.vue'
|
|
40
|
+
export { default as BaseSegmentedControl } from './components/BaseSegmentedControl.vue'
|
|
41
|
+
export { default as BaseTable } from './components/BaseTable.vue'
|
|
42
|
+
export { default as BaseSpecFields } from './components/BaseSpecFields.vue'
|
|
43
|
+
|
|
37
44
|
// Composables
|
|
38
45
|
export { useTheme, initTheme } from './composables/useTheme'
|
|
39
46
|
export type { UseThemeOptions } from './composables/useTheme'
|
|
@@ -65,6 +72,9 @@ export type { NavItem, NavSection } from './types/sidebar'
|
|
|
65
72
|
export type { EntityPickerItem } from './types/entityPicker'
|
|
66
73
|
export type { LoginCredentials } from './types/auth'
|
|
67
74
|
export type { NotificationItem } from './types/notification'
|
|
75
|
+
export type { SegmentedOption } from './types/segmented'
|
|
76
|
+
export type { TableColumn } from './types/table'
|
|
77
|
+
export type { SpecField, SpecFieldType, SpecFieldValue } from './types/specField'
|
|
68
78
|
|
|
69
79
|
// Utils
|
|
70
80
|
export { getBaseColor, getBaseColorOf } from './utils/util'
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One choice in a BaseSegmentedControl. `value` is what the control emits,
|
|
3
|
+
* `label` what the button shows; `title` becomes the button's tooltip.
|
|
4
|
+
* Generic so callers with union-typed values ('all' | 'STOCK' | …) keep
|
|
5
|
+
* full type-safety through v-model.
|
|
6
|
+
*/
|
|
7
|
+
export interface SegmentedOption<T extends string | number = string | number> {
|
|
8
|
+
value: T
|
|
9
|
+
label: string
|
|
10
|
+
title?: string
|
|
11
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Which input a BaseSpecFields entry renders. */
|
|
2
|
+
export type SpecFieldType = 'decimal' | 'integer' | 'boolean' | 'select'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* One field of a spec-driven form: the field's key in the value map, its
|
|
6
|
+
* label, which input to render, and optional validation/rendering hints.
|
|
7
|
+
* Members are loose (`| null` allowed) so backend-described catalogues can be
|
|
8
|
+
* passed straight through without mapping.
|
|
9
|
+
*/
|
|
10
|
+
export interface SpecField {
|
|
11
|
+
key: string
|
|
12
|
+
label: string
|
|
13
|
+
type: SpecFieldType
|
|
14
|
+
/** Choices for `type: 'select'`. */
|
|
15
|
+
options?: string[] | null
|
|
16
|
+
/** Step for numeric inputs; defaults to 1 (integer) / 0.01 (decimal). */
|
|
17
|
+
step?: number | null
|
|
18
|
+
/** Minimum for numeric inputs. */
|
|
19
|
+
min?: number | null
|
|
20
|
+
/** Help text rendered under the field. */
|
|
21
|
+
help?: string | null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** A field's current value in the owner's form state. */
|
|
25
|
+
export type SpecFieldValue = number | boolean | string
|