mountain-ui 1.0.109 → 1.0.111
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/index.d.ts +29 -0
- package/index.js +29 -0
- package/package.json +1 -1
- package/src/lib/registry/navigationRegistry/components/NavigationGroupRow.svelte +30 -32
- package/src/lib/registry/navigationRegistry/components/NewNavbarGroupDialog.svelte +43 -39
- package/src/lib/registry/navigationRegistry/registerNavigation.js +1 -3
- package/src/lib/registry/projectRegistry/createProject.js +2 -0
- package/src/lib/registry/widgetGroupRegistry/components/EmptySlot.svelte +47 -0
- package/src/lib/registry/widgetGroupRegistry/components/GroupPreview.svelte +142 -0
- package/src/lib/registry/widgetGroupRegistry/components/NewWidgetGroup.svelte +122 -0
- package/src/lib/registry/widgetGroupRegistry/components/WidgetGroup.svelte +39 -0
- package/src/lib/registry/widgetGroupRegistry/createWidget.js +17 -0
- package/src/lib/registry/widgetGroupRegistry/createWidgetGroup.js +19 -0
- package/src/lib/registry/widgetGroupRegistry/registerWidgetGroups.js +37 -0
- package/src/lib/registry/widgetGroupRegistry/registry.svelte.js +47 -0
- package/src/lib/registry/widgetTypeRegistry/components/CreateWidgetDialog.svelte +25 -0
- package/src/lib/registry/widgetTypeRegistry/components/Widget.svelte +18 -0
- package/src/lib/registry/widgetTypeRegistry/components/WidgetConfig.svelte +16 -0
- package/src/lib/registry/widgetTypeRegistry/components/WidgetPreview.svelte +59 -0
- package/src/lib/registry/widgetTypeRegistry/components/WidgetTypeButton.svelte +20 -0
- package/src/lib/registry/widgetTypeRegistry/components/WidgetTypeSelector.svelte +32 -0
- package/src/lib/registry/widgetTypeRegistry/createWidgetType.js +20 -0
- package/src/lib/registry/widgetTypeRegistry/registerWidgetTypes.js +14 -0
- package/src/lib/registry/widgetTypeRegistry/registry.svelte.js +43 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/Bar/Bar.svelte +79 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/Bar/ConfigBar.svelte +176 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/Bar/index.js +13 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/Bar/text.js +21 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/Evolution/ConfigEvolution.svelte +180 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/Evolution/Evolution.svelte +81 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/Evolution/index.js +13 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/Evolution/text.js +21 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/KPI/ConfigKPI.svelte +201 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/KPI/KPI.svelte +118 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/KPI/index.js +12 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/KPI/text.js +22 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/List/ConfigList.svelte +199 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/List/List.svelte +97 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/List/index.js +12 -0
- package/src/lib/registry/widgetTypeRegistry/widgetTypes/List/text.js +21 -0
- package/src/routes/exemple/[project_normalized_name]/+layout.svelte +1 -1
- package/src/routes/exemple/[project_normalized_name]/home/+page.svelte +61 -1
- package/src/routes/exemple/[project_normalized_name]/navigation/+page.svelte +28 -0
- package/src/routes/exemple/[project_normalized_name]/widgets/+page.svelte +92 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { Button, DropdownMenu, Input, sendRequest } from 'hamzus-ui';
|
|
3
|
+
import { frequencyText, frequencyTextForConfig } from './text';
|
|
4
|
+
import { entityRegistry } from '$lib/registry/entityRegistry/registry.svelte';
|
|
5
|
+
|
|
6
|
+
let { id = null, save = () => {} } = $props();
|
|
7
|
+
|
|
8
|
+
let title = $state('');
|
|
9
|
+
|
|
10
|
+
const entities = entityRegistry.getAll()
|
|
11
|
+
|
|
12
|
+
let entityId = $state(null);
|
|
13
|
+
const entity = $derived(entityRegistry.get(entityId));
|
|
14
|
+
|
|
15
|
+
let frequency = $state('month'); // day | month | year
|
|
16
|
+
const frequencies = ['day', 'month', 'year'];
|
|
17
|
+
|
|
18
|
+
let valueFieldId = $state(null);
|
|
19
|
+
const valueField = $derived(entityRegistry.getField(valueFieldId));
|
|
20
|
+
|
|
21
|
+
let dateFieldId = $state(null);
|
|
22
|
+
const dateField = $derived(entityRegistry.getField(dateFieldId));
|
|
23
|
+
const dateFieldTypes = ['time-created_at', 'time-datetime', 'time-updated_at'];
|
|
24
|
+
|
|
25
|
+
let operation = $state('count'); // count | sum | avg | min | max
|
|
26
|
+
const operations = ['count', 'sum', 'avg', 'min', 'max'];
|
|
27
|
+
|
|
28
|
+
let size = $state('4/1');
|
|
29
|
+
const sizes = ['4/1', '6/1', '8/1', '12/1'];
|
|
30
|
+
|
|
31
|
+
function handleSelectEntity(entity) {
|
|
32
|
+
entityId = entity.id;
|
|
33
|
+
|
|
34
|
+
// parcourir les fields pour set auto le field de value et de date
|
|
35
|
+
for (const field of entity.fields) {
|
|
36
|
+
if (field.type.startsWith('number-') || field.type.startsWith('calculation-')) {
|
|
37
|
+
valueFieldId = field.id;
|
|
38
|
+
}
|
|
39
|
+
if (dateFieldTypes.includes(field.type)) {
|
|
40
|
+
dateFieldId = field.id;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function handleSave() {
|
|
46
|
+
save({
|
|
47
|
+
id,
|
|
48
|
+
title,
|
|
49
|
+
entityId,
|
|
50
|
+
frequency,
|
|
51
|
+
valueFieldId,
|
|
52
|
+
dateFieldId,
|
|
53
|
+
operation,
|
|
54
|
+
size
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<div class="config">
|
|
60
|
+
<Input label="titre" bind:value={title} placeholder="exemple"></Input>
|
|
61
|
+
<h5>opération</h5>
|
|
62
|
+
<DropdownMenu.Root direction="bottom">
|
|
63
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
64
|
+
<Button variant="secondary">
|
|
65
|
+
<h5>{operation ?? 'choisissez une opération'}</h5>
|
|
66
|
+
</Button>
|
|
67
|
+
</DropdownMenu.Trigger>
|
|
68
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
69
|
+
{#each operations as operationEl}
|
|
70
|
+
<Button
|
|
71
|
+
onClick={() => {
|
|
72
|
+
operation = operationEl;
|
|
73
|
+
}}
|
|
74
|
+
variant="ghost"
|
|
75
|
+
style="width:100%;"><h5>{operationEl}</h5></Button
|
|
76
|
+
>
|
|
77
|
+
{/each}
|
|
78
|
+
</DropdownMenu.Content>
|
|
79
|
+
</DropdownMenu.Root>
|
|
80
|
+
<h5>Entité</h5>
|
|
81
|
+
<DropdownMenu.Root direction="bottom">
|
|
82
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
83
|
+
<Button variant="secondary">
|
|
84
|
+
<h5>{entity?.plural_name ?? 'choisissez une entité'}</h5>
|
|
85
|
+
</Button>
|
|
86
|
+
</DropdownMenu.Trigger>
|
|
87
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
88
|
+
{#each $entities as entity}
|
|
89
|
+
<Button
|
|
90
|
+
onClick={() => {
|
|
91
|
+
handleSelectEntity(entity);
|
|
92
|
+
}}
|
|
93
|
+
variant="ghost"
|
|
94
|
+
style="width:100%;"><h5>{entity.plural_name}</h5></Button
|
|
95
|
+
>
|
|
96
|
+
{/each}
|
|
97
|
+
</DropdownMenu.Content>
|
|
98
|
+
</DropdownMenu.Root>
|
|
99
|
+
{#if entity}
|
|
100
|
+
{@const availedValueFields = entity.fields.filter(
|
|
101
|
+
(field) => field.type.startsWith('number-') || field.type.startsWith('calculation-')
|
|
102
|
+
)}
|
|
103
|
+
{@const availedDateFields = entity.fields.filter((field) =>
|
|
104
|
+
dateFieldTypes.includes(field.type)
|
|
105
|
+
)}
|
|
106
|
+
{#if operation !== 'count'}
|
|
107
|
+
<h5>Champs pour valeurs</h5>
|
|
108
|
+
<DropdownMenu.Root direction="bottom">
|
|
109
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
110
|
+
<Button variant="secondary">
|
|
111
|
+
<h5>{valueField?.name ?? 'choisissez un champ pour valeur'}</h5>
|
|
112
|
+
</Button>
|
|
113
|
+
</DropdownMenu.Trigger>
|
|
114
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
115
|
+
{#if availedValueFields.length > 0}
|
|
116
|
+
{#each availedValueFields as field}
|
|
117
|
+
<Button
|
|
118
|
+
onClick={() => {
|
|
119
|
+
valueFieldId = field.id;
|
|
120
|
+
}}
|
|
121
|
+
variant="ghost"
|
|
122
|
+
style="width:100%;"><h5>{field.name}</h5></Button
|
|
123
|
+
>
|
|
124
|
+
{/each}
|
|
125
|
+
{:else}
|
|
126
|
+
<div class="nothing">
|
|
127
|
+
<h3>Aucun champs de valeur disponible</h3>
|
|
128
|
+
<p>
|
|
129
|
+
L'entité {entity?.plural_name} ne contient pas de champs nombre, permettant les calculs.
|
|
130
|
+
</p>
|
|
131
|
+
</div>
|
|
132
|
+
{/if}
|
|
133
|
+
</DropdownMenu.Content>
|
|
134
|
+
</DropdownMenu.Root>
|
|
135
|
+
{/if}
|
|
136
|
+
<h5>Champs pour date</h5>
|
|
137
|
+
<DropdownMenu.Root direction="bottom">
|
|
138
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
139
|
+
<Button variant="secondary">
|
|
140
|
+
<h5>{dateField?.name ?? 'choisissez un champ pour date'}</h5>
|
|
141
|
+
</Button>
|
|
142
|
+
</DropdownMenu.Trigger>
|
|
143
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
144
|
+
{#if availedDateFields.length > 0}
|
|
145
|
+
{#each availedDateFields as field}
|
|
146
|
+
<Button
|
|
147
|
+
onClick={() => {
|
|
148
|
+
dateFieldId = field.id;
|
|
149
|
+
}}
|
|
150
|
+
variant="ghost"
|
|
151
|
+
style="width:100%;"><h5>{field.name}</h5></Button
|
|
152
|
+
>
|
|
153
|
+
{/each}
|
|
154
|
+
{:else}
|
|
155
|
+
<div class="nothing">
|
|
156
|
+
<h3>Aucun champs de date disponible</h3>
|
|
157
|
+
<p>L'entité {entity?.plural_name} ne contient pas de champs de date.</p>
|
|
158
|
+
</div>
|
|
159
|
+
{/if}
|
|
160
|
+
</DropdownMenu.Content>
|
|
161
|
+
</DropdownMenu.Root>
|
|
162
|
+
<h5>fréquence</h5>
|
|
163
|
+
<DropdownMenu.Root direction="bottom">
|
|
164
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
165
|
+
<Button variant="secondary">
|
|
166
|
+
<h5>{frequency ? frequencyTextForConfig[frequency] : 'choisissez une fréquence'}</h5>
|
|
167
|
+
</Button>
|
|
168
|
+
</DropdownMenu.Trigger>
|
|
169
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
170
|
+
{#each frequencies as frequencyEl}
|
|
171
|
+
<Button
|
|
172
|
+
onClick={() => {
|
|
173
|
+
frequency = frequencyEl;
|
|
174
|
+
}}
|
|
175
|
+
variant="ghost"
|
|
176
|
+
style="width:100%;"><h5>{frequencyTextForConfig[frequencyEl]}</h5></Button
|
|
177
|
+
>
|
|
178
|
+
{/each}
|
|
179
|
+
</DropdownMenu.Content>
|
|
180
|
+
</DropdownMenu.Root>
|
|
181
|
+
{/if}
|
|
182
|
+
|
|
183
|
+
<Button onClick={handleSave}>
|
|
184
|
+
<h5>enregistrer</h5>
|
|
185
|
+
</Button>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<style>
|
|
189
|
+
.config {
|
|
190
|
+
display: flex;
|
|
191
|
+
flex-direction: column;
|
|
192
|
+
row-gap: var(--pad-m);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.nothing {
|
|
196
|
+
display: flex;
|
|
197
|
+
flex-direction: column;
|
|
198
|
+
row-gap: var(--pad-m);
|
|
199
|
+
padding: var(--pad-m);
|
|
200
|
+
}
|
|
201
|
+
</style>
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { sendRequest, Skeleton } from 'hamzus-ui';
|
|
3
|
+
import { frequencyText, operationText } from './text';
|
|
4
|
+
import { onMount } from 'svelte';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
id = '',
|
|
8
|
+
title = '',
|
|
9
|
+
entityId = '',
|
|
10
|
+
frequency = '',
|
|
11
|
+
valueFieldId = '',
|
|
12
|
+
dateFieldId = '',
|
|
13
|
+
operation = '',
|
|
14
|
+
} = $props()
|
|
15
|
+
|
|
16
|
+
let total = $state(null);
|
|
17
|
+
let previousTotal = $state(null);
|
|
18
|
+
|
|
19
|
+
onMount(async () => {
|
|
20
|
+
const request = await sendRequest({
|
|
21
|
+
path: '/get-widget-data',
|
|
22
|
+
data: {
|
|
23
|
+
widgetLinkId: id
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
total = request.data[0];
|
|
28
|
+
previousTotal = request.data[1];
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
function getRandomSizes() {
|
|
32
|
+
const sizes = [60, 120, 80];
|
|
33
|
+
|
|
34
|
+
return sizes[Math.floor(Math.random() * sizes.length)];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getPercent() {
|
|
38
|
+
if (previousTotal === 0) {
|
|
39
|
+
if (total === 0) return 0;
|
|
40
|
+
return null; // augmentation non calculable
|
|
41
|
+
}
|
|
42
|
+
return ((total - previousTotal) / previousTotal) * 100;
|
|
43
|
+
}
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<div class="kpi-container">
|
|
47
|
+
<div class="kpi">
|
|
48
|
+
<h4>{title}</h4>
|
|
49
|
+
{#if total === null}
|
|
50
|
+
<Skeleton style="width:{getRandomSizes()}px;height:32px;"></Skeleton>
|
|
51
|
+
{:else}
|
|
52
|
+
<h3>{total}</h3>
|
|
53
|
+
{/if}
|
|
54
|
+
<div class="bottom">
|
|
55
|
+
<h5>{frequencyText[frequency] ?? '-'} | {operationText[operation] ?? '-'}</h5>
|
|
56
|
+
<div class="right">
|
|
57
|
+
{#if total === null}
|
|
58
|
+
<h4 style="color: var(--green);">+</h4>
|
|
59
|
+
<Skeleton style="width:24px;height:16px;" />
|
|
60
|
+
<h4 style="color: var(--green);">%</h4>
|
|
61
|
+
{:else}
|
|
62
|
+
{@const percent = getPercent()}
|
|
63
|
+
{#if percent !== null}
|
|
64
|
+
<h4 style="color: var(--green);">+{getPercent()} %</h4>
|
|
65
|
+
<svg
|
|
66
|
+
width="24"
|
|
67
|
+
height="24"
|
|
68
|
+
viewBox="0 0 24 24"
|
|
69
|
+
fill="none"
|
|
70
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
71
|
+
>
|
|
72
|
+
<path
|
|
73
|
+
opacity="0.4"
|
|
74
|
+
d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z"
|
|
75
|
+
fill="#34C759"
|
|
76
|
+
/>
|
|
77
|
+
<path
|
|
78
|
+
d="M15.5295 10.9699L12.5295 7.96994C12.2395 7.67994 11.7595 7.67994 11.4695 7.96994L8.46945 10.9699C8.17945 11.2599 8.17945 11.7399 8.46945 12.0299C8.75945 12.3199 9.23945 12.3199 9.52945 12.0299L11.2495 10.3099V15.4999C11.2495 15.9099 11.5895 16.2499 11.9995 16.2499C12.4095 16.2499 12.7495 15.9099 12.7495 15.4999V10.3099L14.4695 12.0299C14.6195 12.1799 14.8095 12.2499 14.9995 12.2499C15.1895 12.2499 15.3795 12.1799 15.5295 12.0299C15.8195 11.7399 15.8195 11.2599 15.5295 10.9699Z"
|
|
79
|
+
fill="#34C759"
|
|
80
|
+
/>
|
|
81
|
+
</svg>
|
|
82
|
+
{/if}
|
|
83
|
+
{/if}
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<style>
|
|
90
|
+
.kpi-container {
|
|
91
|
+
width: 100%;
|
|
92
|
+
height: 100%;
|
|
93
|
+
padding: var(--pad-m);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.kpi {
|
|
97
|
+
display: flex;
|
|
98
|
+
flex-direction: column;
|
|
99
|
+
row-gap: var(--pad-m);
|
|
100
|
+
padding: var(--pad-xxl);
|
|
101
|
+
width: 100%;
|
|
102
|
+
height: 100%;
|
|
103
|
+
background-color: var(--bg-blur);
|
|
104
|
+
border-radius: var(--radius-m);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.bottom {
|
|
108
|
+
display: flex;
|
|
109
|
+
align-items: center;
|
|
110
|
+
justify-content: space-between;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.right {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
column-gap: var(--pad-m);
|
|
117
|
+
}
|
|
118
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ConfigKPI from "./ConfigKPI.svelte";
|
|
2
|
+
import KPI from "./KPI.svelte";
|
|
3
|
+
import { createWidgetType } from "../../createWidgetType";
|
|
4
|
+
|
|
5
|
+
export default createWidgetType({
|
|
6
|
+
type: "KPI",
|
|
7
|
+
name: "KPI",
|
|
8
|
+
components: {
|
|
9
|
+
config: ConfigKPI,
|
|
10
|
+
component: KPI,
|
|
11
|
+
}
|
|
12
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export const frequencyText = {
|
|
4
|
+
'day': 'aujourd\'hui',
|
|
5
|
+
'month': 'ce mois-ci',
|
|
6
|
+
'year': 'cette année'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const frequencyTextForConfig = {
|
|
10
|
+
'day': 'par jour',
|
|
11
|
+
'month': 'par mois',
|
|
12
|
+
'year': 'par an'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
export const operationText = {
|
|
17
|
+
'count': 'nombre d\'entrée',
|
|
18
|
+
'sum': 'somme',
|
|
19
|
+
'avg': 'moyenne',
|
|
20
|
+
'min': "min",
|
|
21
|
+
'max': "max"
|
|
22
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { Button, DropdownMenu, Input, sendRequest } from 'hamzus-ui';
|
|
3
|
+
import { entityRegistry } from '$lib/registry/entityRegistry/registry.svelte';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
id = null,
|
|
7
|
+
save = () => {},
|
|
8
|
+
} = $props()
|
|
9
|
+
|
|
10
|
+
let title = $state('');
|
|
11
|
+
|
|
12
|
+
const entities = entityRegistry.getAll()
|
|
13
|
+
|
|
14
|
+
let entityId = $state(null);
|
|
15
|
+
const entity = $derived(entityRegistry.get(entityId));
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
let valueFieldId = $state(null);
|
|
19
|
+
const valueField = $derived(entityRegistry.getField(valueFieldId));
|
|
20
|
+
let selectedValueOfValueField = $state(null);
|
|
21
|
+
|
|
22
|
+
let sortFieldId = $state(null);
|
|
23
|
+
const dateField = $derived(entityRegistry.getField(sortFieldId));
|
|
24
|
+
const dateFieldTypes = ['time-created_at', 'time-datetime', 'time-updated_at', 'primary'];
|
|
25
|
+
|
|
26
|
+
let operation = $state('count'); // count | sum | avg | min | max
|
|
27
|
+
const operations = ['count', 'sum', 'avg', 'min', 'max'];
|
|
28
|
+
|
|
29
|
+
let size = $state('12/2');
|
|
30
|
+
const sizes = ['8/2', '12/2'];
|
|
31
|
+
|
|
32
|
+
function handleSelectEntity(entity) {
|
|
33
|
+
entityId = entity.id;
|
|
34
|
+
|
|
35
|
+
// parcourir les fields pour set auto le field de value et de date
|
|
36
|
+
for (const field of entity.fields) {
|
|
37
|
+
// if (field.type.startsWith('number-') || field.type.startsWith('calculation-')) {
|
|
38
|
+
// valueFieldId = field.id;
|
|
39
|
+
// }
|
|
40
|
+
if (dateFieldTypes.includes(field.type)) {
|
|
41
|
+
sortFieldId = field.id;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function handleSave() {
|
|
47
|
+
save({
|
|
48
|
+
id,
|
|
49
|
+
title,
|
|
50
|
+
entityId,
|
|
51
|
+
valueFieldId,
|
|
52
|
+
selectedValueOfValueField,
|
|
53
|
+
sortFieldId,
|
|
54
|
+
operation,
|
|
55
|
+
size
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<div class="config">
|
|
61
|
+
<Input label="titre" bind:value={title} placeholder="exemple"></Input>
|
|
62
|
+
<h5>Entité</h5>
|
|
63
|
+
<DropdownMenu.Root direction="bottom">
|
|
64
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
65
|
+
<Button variant="secondary">
|
|
66
|
+
<h5>{entity?.plural_name ?? 'choisissez une entité'}</h5>
|
|
67
|
+
</Button>
|
|
68
|
+
</DropdownMenu.Trigger>
|
|
69
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
70
|
+
{#each $entities as entity}
|
|
71
|
+
<Button
|
|
72
|
+
onClick={() => {
|
|
73
|
+
handleSelectEntity(entity);
|
|
74
|
+
}}
|
|
75
|
+
variant="ghost"
|
|
76
|
+
style="width:100%;"><h5>{entity.plural_name}</h5></Button
|
|
77
|
+
>
|
|
78
|
+
{/each}
|
|
79
|
+
</DropdownMenu.Content>
|
|
80
|
+
</DropdownMenu.Root>
|
|
81
|
+
{#if entity}
|
|
82
|
+
{@const availedValueFields = entity.fields.filter((field) => field.type === 'list-radio')}
|
|
83
|
+
{@const availedDateFields = entity.fields.filter((field) =>
|
|
84
|
+
dateFieldTypes.includes(field.type)
|
|
85
|
+
)}
|
|
86
|
+
<h5>Précisé un status</h5>
|
|
87
|
+
<DropdownMenu.Root direction="bottom">
|
|
88
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
89
|
+
<Button variant="secondary">
|
|
90
|
+
<h5>{valueField?.name ?? 'choisissez un champ pour status'}</h5>
|
|
91
|
+
</Button>
|
|
92
|
+
</DropdownMenu.Trigger>
|
|
93
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
94
|
+
{#if availedValueFields.length > 0}
|
|
95
|
+
{#each availedValueFields as field}
|
|
96
|
+
<Button
|
|
97
|
+
onClick={() => {
|
|
98
|
+
valueFieldId = field.id;
|
|
99
|
+
}}
|
|
100
|
+
variant="ghost"
|
|
101
|
+
style="width:100%;"><h5>{field.name}</h5></Button
|
|
102
|
+
>
|
|
103
|
+
{/each}
|
|
104
|
+
{:else}
|
|
105
|
+
<div class="nothing">
|
|
106
|
+
<h3>Aucun champs de status disponible</h3>
|
|
107
|
+
<p>
|
|
108
|
+
L'entité {entity?.plural_name} ne contient pas de champs nombre, permettant les calculs.
|
|
109
|
+
</p>
|
|
110
|
+
</div>
|
|
111
|
+
{/if}
|
|
112
|
+
</DropdownMenu.Content>
|
|
113
|
+
</DropdownMenu.Root>
|
|
114
|
+
{#if valueField}
|
|
115
|
+
<h5>status</h5>
|
|
116
|
+
<DropdownMenu.Root direction="bottom">
|
|
117
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
118
|
+
<Button variant="secondary">
|
|
119
|
+
<h5>{selectedValueOfValueField ?? 'choisissez un status'}</h5>
|
|
120
|
+
</Button>
|
|
121
|
+
</DropdownMenu.Trigger>
|
|
122
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
123
|
+
{#if valueField.props.choices.length > 0}
|
|
124
|
+
<Button
|
|
125
|
+
onClick={() => {
|
|
126
|
+
selectedValueOfValueField = null;
|
|
127
|
+
}}
|
|
128
|
+
variant="ghost"
|
|
129
|
+
style="width:100%;">aucun</Button
|
|
130
|
+
>
|
|
131
|
+
{#each valueField.props.choices as choice}
|
|
132
|
+
<Button
|
|
133
|
+
onClick={() => {
|
|
134
|
+
selectedValueOfValueField = choice.value;
|
|
135
|
+
}}
|
|
136
|
+
variant="ghost"
|
|
137
|
+
style="width:100%;">
|
|
138
|
+
<h5 style="color: var({choice.color});">{choice.value}</h5></Button
|
|
139
|
+
>
|
|
140
|
+
{/each}
|
|
141
|
+
{:else}
|
|
142
|
+
<div class="nothing">
|
|
143
|
+
<h3>Aucun champs de status disponible</h3>
|
|
144
|
+
<p>
|
|
145
|
+
L'entité {entity?.plural_name} ne contient pas de champs nombre, permettant les calculs.
|
|
146
|
+
</p>
|
|
147
|
+
</div>
|
|
148
|
+
{/if}
|
|
149
|
+
</DropdownMenu.Content>
|
|
150
|
+
</DropdownMenu.Root>
|
|
151
|
+
{/if}
|
|
152
|
+
<h5>Trié par</h5>
|
|
153
|
+
<DropdownMenu.Root direction="bottom">
|
|
154
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
155
|
+
<Button variant="secondary">
|
|
156
|
+
<h5>{dateField?.name ?? 'choisissez un champ pour date'}</h5>
|
|
157
|
+
</Button>
|
|
158
|
+
</DropdownMenu.Trigger>
|
|
159
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
160
|
+
{#if availedDateFields.length > 0}
|
|
161
|
+
{#each availedDateFields as field}
|
|
162
|
+
<Button
|
|
163
|
+
onClick={() => {
|
|
164
|
+
sortFieldId = field.id;
|
|
165
|
+
}}
|
|
166
|
+
variant="ghost"
|
|
167
|
+
style="width:100%;">
|
|
168
|
+
<h5>{field.name}</h5></Button
|
|
169
|
+
>
|
|
170
|
+
{/each}
|
|
171
|
+
{:else}
|
|
172
|
+
<div class="nothing">
|
|
173
|
+
<h3>Aucun champs de date disponible</h3>
|
|
174
|
+
<p>L'entité {entity?.plural_name} ne contient pas de champs de date.</p>
|
|
175
|
+
</div>
|
|
176
|
+
{/if}
|
|
177
|
+
</DropdownMenu.Content>
|
|
178
|
+
</DropdownMenu.Root>
|
|
179
|
+
{/if}
|
|
180
|
+
|
|
181
|
+
<Button onClick={handleSave}>
|
|
182
|
+
<h5>enregistrer</h5>
|
|
183
|
+
</Button>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<style>
|
|
187
|
+
.config {
|
|
188
|
+
display: flex;
|
|
189
|
+
flex-direction: column;
|
|
190
|
+
row-gap: var(--pad-m);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.nothing {
|
|
194
|
+
display: flex;
|
|
195
|
+
flex-direction: column;
|
|
196
|
+
row-gap: var(--pad-m);
|
|
197
|
+
padding: var(--pad-m);
|
|
198
|
+
}
|
|
199
|
+
</style>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { ChartLine, sendRequest, Skeleton } from 'hamzus-ui';
|
|
3
|
+
import { frequencyText, operationText } from './text';
|
|
4
|
+
import { onMount } from 'svelte';
|
|
5
|
+
import { entityRegistry } from '$lib/registry/entityRegistry/registry.svelte';
|
|
6
|
+
import ViewEntriesTypeComponent from '$lib/registry/viewEntriesTypeRegistry/components/ViewEntriesTypeComponent.svelte';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
id = '',
|
|
10
|
+
title = '',
|
|
11
|
+
entityId = '',
|
|
12
|
+
valueFieldId = '',
|
|
13
|
+
selectedValueOfValueField = '',
|
|
14
|
+
sortFieldId = '',
|
|
15
|
+
size = ''
|
|
16
|
+
} = $props();
|
|
17
|
+
|
|
18
|
+
let entity = $state(null);
|
|
19
|
+
let filters = $state(null);
|
|
20
|
+
|
|
21
|
+
onMount(async () => {
|
|
22
|
+
entity = entityRegistry.get(entityId);
|
|
23
|
+
|
|
24
|
+
let newFilters = {};
|
|
25
|
+
if (valueFieldId) {
|
|
26
|
+
const filterFieldName = entityRegistry.getField(valueFieldId);
|
|
27
|
+
newFilters = {
|
|
28
|
+
[filterFieldName.normalized_name]: selectedValueOfValueField
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
filters = { ...newFilters };
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
function getRandomSizes() {
|
|
35
|
+
const sizes = [60, 120, 80];
|
|
36
|
+
|
|
37
|
+
return sizes[Math.floor(Math.random() * sizes.length)];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getPercent() {
|
|
41
|
+
if (previousTotal === 0) {
|
|
42
|
+
if (total === 0) return 0;
|
|
43
|
+
return null; // augmentation non calculable
|
|
44
|
+
}
|
|
45
|
+
return ((total - previousTotal) / previousTotal) * 100;
|
|
46
|
+
}
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<div class="kpi-container">
|
|
50
|
+
<div class="kpi">
|
|
51
|
+
{#if filters}
|
|
52
|
+
<ViewEntriesTypeComponent invisibleFilters={filters} type="table" entityId={82}>
|
|
53
|
+
<h4 slot="actions">{title}</h4>
|
|
54
|
+
</ViewEntriesTypeComponent>
|
|
55
|
+
{:else}
|
|
56
|
+
<div class="title">
|
|
57
|
+
<h4>{title}</h4>
|
|
58
|
+
</div>
|
|
59
|
+
{/if}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<style>
|
|
64
|
+
.kpi-container {
|
|
65
|
+
width: 100%;
|
|
66
|
+
height: 100%;
|
|
67
|
+
min-height: 250px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.kpi {
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-direction: column;
|
|
73
|
+
row-gap: var(--pad-m);
|
|
74
|
+
padding-bottom: var(--pad-m);
|
|
75
|
+
width: 100%;
|
|
76
|
+
height: 100%;
|
|
77
|
+
border: 1px solid var(--stroke);
|
|
78
|
+
border-radius: var(--radius-m);
|
|
79
|
+
overflow: auto;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.title {
|
|
83
|
+
padding: var(--pad-m);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.bottom {
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
justify-content: space-between;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.right {
|
|
93
|
+
display: flex;
|
|
94
|
+
align-items: center;
|
|
95
|
+
column-gap: var(--pad-m);
|
|
96
|
+
}
|
|
97
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ConfigList from "./ConfigList.svelte";
|
|
2
|
+
import List from "./List.svelte";
|
|
3
|
+
import { createWidgetType } from "../../createWidgetType";
|
|
4
|
+
|
|
5
|
+
export default createWidgetType({
|
|
6
|
+
type: "List",
|
|
7
|
+
name: "Liste d'entrée",
|
|
8
|
+
components: {
|
|
9
|
+
config: ConfigList,
|
|
10
|
+
component: List,
|
|
11
|
+
}
|
|
12
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export const frequencyText = {
|
|
4
|
+
'day': 'aujourd\'hui',
|
|
5
|
+
'month': 'ce mois-ci',
|
|
6
|
+
'year': 'cette année'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const frequencyTextForConfig = {
|
|
10
|
+
'day': 'par jour',
|
|
11
|
+
'month': 'par mois',
|
|
12
|
+
'year': 'par an'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const operationText = {
|
|
16
|
+
'count': 'nombre d\'entrée',
|
|
17
|
+
'sum': 'somme',
|
|
18
|
+
'avg': 'moyenne',
|
|
19
|
+
'min': "min",
|
|
20
|
+
'max': "max"
|
|
21
|
+
}
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
<NavbarBtn href="{$targetProjectHref}/navigation" text="Navigation">
|
|
86
86
|
<NavbarIcon slot="endContent" color="var(--font-2)" icon="Grid|grid-4"></NavbarIcon>
|
|
87
87
|
</NavbarBtn>
|
|
88
|
-
<NavbarBtn text="Accueil">
|
|
88
|
+
<NavbarBtn href="{$targetProjectHref}/widgets" text="Accueil">
|
|
89
89
|
<NavbarIcon slot="endContent" color="var(--font-2)" icon="Essetionnal|home-2"></NavbarIcon>
|
|
90
90
|
</NavbarBtn>
|
|
91
91
|
<NavbarBtn text="Constantes">
|