mountain-ui 1.0.110 → 1.0.112
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/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,39 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { handlers } from 'svelte/legacy';
|
|
3
|
+
import EmptySlot from './EmptySlot.svelte';
|
|
4
|
+
import CreateWidgetDialog from '../../widgetTypeRegistry/components/CreateWidgetDialog.svelte';
|
|
5
|
+
import { sendRequest, toast } from 'hamzus-ui';
|
|
6
|
+
import { registerWidgetGroups } from '../registerWidgetGroups';
|
|
7
|
+
import { widgetGroupRegistry } from '../registry.svelte';
|
|
8
|
+
import Widget from '../../widgetTypeRegistry/components/Widget.svelte';
|
|
9
|
+
|
|
10
|
+
let { id = null, numberOfColumn = 1, children = [] } = $props();
|
|
11
|
+
|
|
12
|
+
const group = $derived(widgetGroupRegistry.get(id))
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
{#if group && group.children && group.children.length > 0}
|
|
16
|
+
<div class="group-container" style="grid-template-columns:repeat({numberOfColumn}, minmax(0, 1fr));">
|
|
17
|
+
{#each Array.from({ length: numberOfColumn }) as column, index}
|
|
18
|
+
{const child = group.getChild(index + 1)}
|
|
19
|
+
{#if child}
|
|
20
|
+
<Widget type={child.type} {...child.props}></Widget>
|
|
21
|
+
{:else}
|
|
22
|
+
<div class="empty">
|
|
23
|
+
|
|
24
|
+
</div>
|
|
25
|
+
{/if}
|
|
26
|
+
{/each}
|
|
27
|
+
</div>
|
|
28
|
+
{/if}
|
|
29
|
+
|
|
30
|
+
<style>
|
|
31
|
+
.group-container {
|
|
32
|
+
width: 100%;
|
|
33
|
+
display: grid;
|
|
34
|
+
padding: var(--pad-xl);
|
|
35
|
+
column-gap: var(--pad-m);
|
|
36
|
+
min-height: 150px;
|
|
37
|
+
transition: border 0.2s ease-out;
|
|
38
|
+
}
|
|
39
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { sendRequest } from "hamzus-ui";
|
|
2
|
+
import { widgetGroupRegistry } from "./registry.svelte";
|
|
3
|
+
|
|
4
|
+
export function createWidgetGroup(config = {}) {
|
|
5
|
+
const widgetGroup = {
|
|
6
|
+
id: null,
|
|
7
|
+
column_number:1,
|
|
8
|
+
position:1,
|
|
9
|
+
children:[],
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
Object.assign(widgetGroup, config);
|
|
13
|
+
|
|
14
|
+
widgetGroup.getChild = (position) =>{
|
|
15
|
+
return widgetGroup.children.find(c=>c.position === position)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return widgetGroup;
|
|
19
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { sendRequest } from "hamzus-ui";
|
|
2
|
+
import { widgetGroupRegistry } from "./registry.svelte";
|
|
3
|
+
import { createWidgetGroup } from "./createWidgetGroup";
|
|
4
|
+
import { createWidget } from "./createWidget";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export async function registerWidgetGroups() {
|
|
8
|
+
|
|
9
|
+
widgetGroupRegistry.unregisterAll()
|
|
10
|
+
|
|
11
|
+
const request = await sendRequest({
|
|
12
|
+
path:"/get-all-widget-groups",
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
for (const group of request.data) {
|
|
16
|
+
let config = {
|
|
17
|
+
id: group.id,
|
|
18
|
+
column_number:group.column_number,
|
|
19
|
+
position:group.position,
|
|
20
|
+
children:[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (const child of group.children) {
|
|
24
|
+
config.children.push(createWidget({
|
|
25
|
+
id:child.id,
|
|
26
|
+
group_id:child.group_id,
|
|
27
|
+
position:child.position,
|
|
28
|
+
type:child.type,
|
|
29
|
+
title:child.title,
|
|
30
|
+
props:child.props,
|
|
31
|
+
}))
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
widgetGroupRegistry.register(createWidgetGroup(config))
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { writable, derived, get } from 'svelte/store';
|
|
2
|
+
|
|
3
|
+
function createWidgetGroupRegistry() {
|
|
4
|
+
const store = writable({});
|
|
5
|
+
|
|
6
|
+
const all = derived(store, ($widgetGroups) => Object.values($widgetGroups));
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
subscribe: store.subscribe,
|
|
10
|
+
|
|
11
|
+
register(widgetGroup) {
|
|
12
|
+
store.update((widgetGroups) => ({
|
|
13
|
+
...widgetGroups,
|
|
14
|
+
[widgetGroup.id]: widgetGroup
|
|
15
|
+
}));
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
unregisterAll() {
|
|
19
|
+
store.update((widgetGroups) => {
|
|
20
|
+
return {};
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
unregister(id) {
|
|
24
|
+
store.update((widgetGroups) => {
|
|
25
|
+
const copy = { ...widgetGroups };
|
|
26
|
+
|
|
27
|
+
delete copy[id];
|
|
28
|
+
|
|
29
|
+
return copy;
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
get(id) {
|
|
34
|
+
return get(store)[id];
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
select(id) {
|
|
38
|
+
return derived(store, ($widgetGroups) => $widgetGroups[id]);
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
getAll() {
|
|
42
|
+
return all;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const widgetGroupRegistry = createWidgetGroupRegistry();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { AlertCard, Button, Dialog } from "hamzus-ui";
|
|
3
|
+
import WidgetTypeSelector from "./WidgetTypeSelector.svelte";
|
|
4
|
+
import WidgetConfig from "./WidgetConfig.svelte";
|
|
5
|
+
|
|
6
|
+
let {isOpen = $bindable(false), save = ()=>{}, ...props} = $props()
|
|
7
|
+
|
|
8
|
+
let type = $state(null)
|
|
9
|
+
|
|
10
|
+
async function handleSave(data) {
|
|
11
|
+
await save(data)
|
|
12
|
+
|
|
13
|
+
isOpen = false
|
|
14
|
+
}
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<Dialog bind:isOpen>
|
|
18
|
+
<div style="display:flex;row-gap:var(--pad-m);flex-direction:column;">
|
|
19
|
+
<h3>Créer un nouveau widget</h3>
|
|
20
|
+
<WidgetTypeSelector onChange={(newType)=>{type = newType}}></WidgetTypeSelector>
|
|
21
|
+
{#if type}
|
|
22
|
+
<WidgetConfig save={handleSave} {type}></WidgetConfig>
|
|
23
|
+
{/if}
|
|
24
|
+
</div>
|
|
25
|
+
</Dialog>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { widgetTypeRegistry } from "../registry.svelte";
|
|
3
|
+
|
|
4
|
+
let {type, ...props} = $props()
|
|
5
|
+
|
|
6
|
+
const widgetType = $derived(widgetTypeRegistry.get(type))
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<div class="widget-case">
|
|
10
|
+
<svelte:component this={widgetType.components.component} {...props}></svelte:component>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<style>
|
|
14
|
+
.widget-case {
|
|
15
|
+
width: 100%;
|
|
16
|
+
height: 100%;
|
|
17
|
+
}
|
|
18
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { widgetTypeRegistry } from "../registry.svelte";
|
|
3
|
+
|
|
4
|
+
let {type, save = (data)=>{}, ...props} = $props()
|
|
5
|
+
|
|
6
|
+
const widgetType = $derived(widgetTypeRegistry.get(type))
|
|
7
|
+
|
|
8
|
+
function handleSave(data) {
|
|
9
|
+
save({
|
|
10
|
+
...data,
|
|
11
|
+
type:widgetType.type
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<svelte:component this={widgetType.components.config} save={handleSave} {...props}></svelte:component>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { IconButton } from 'hamzus-ui';
|
|
3
|
+
import { widgetTypeRegistry } from '../registry.svelte';
|
|
4
|
+
|
|
5
|
+
let { type, onDelete = () => {}, ...props } = $props();
|
|
6
|
+
|
|
7
|
+
const widgetType = $derived(widgetTypeRegistry.get(type));
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<div class="widget-case">
|
|
11
|
+
<svelte:component this={widgetType.components.component} {...props}></svelte:component>
|
|
12
|
+
<div class="btn">
|
|
13
|
+
<IconButton variant="secondary" onClick={onDelete}>
|
|
14
|
+
<svg
|
|
15
|
+
width="24"
|
|
16
|
+
height="24"
|
|
17
|
+
viewBox="0 0 24 24"
|
|
18
|
+
fill="none"
|
|
19
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
20
|
+
>
|
|
21
|
+
<path
|
|
22
|
+
d="M20.9997 6.72998C20.9797 6.72998 20.9497 6.72998 20.9197 6.72998C15.6297 6.19998 10.3497 5.99998 5.11967 6.52998L3.07967 6.72998C2.65967 6.76998 2.28967 6.46998 2.24967 6.04998C2.20967 5.62998 2.50967 5.26998 2.91967 5.22998L4.95967 5.02998C10.2797 4.48998 15.6697 4.69998 21.0697 5.22998C21.4797 5.26998 21.7797 5.63998 21.7397 6.04998C21.7097 6.43998 21.3797 6.72998 20.9997 6.72998Z"
|
|
23
|
+
fill="#292D32"
|
|
24
|
+
/>
|
|
25
|
+
<path
|
|
26
|
+
d="M8.50074 5.72C8.46074 5.72 8.42074 5.72 8.37074 5.71C7.97074 5.64 7.69074 5.25 7.76074 4.85L7.98074 3.54C8.14074 2.58 8.36074 1.25 10.6907 1.25H13.3107C15.6507 1.25 15.8707 2.63 16.0207 3.55L16.2407 4.85C16.3107 5.26 16.0307 5.65 15.6307 5.71C15.2207 5.78 14.8307 5.5 14.7707 5.1L14.5507 3.8C14.4107 2.93 14.3807 2.76 13.3207 2.76H10.7007C9.64074 2.76 9.62074 2.9 9.47074 3.79L9.24074 5.09C9.18074 5.46 8.86074 5.72 8.50074 5.72Z"
|
|
27
|
+
fill="#292D32"
|
|
28
|
+
/>
|
|
29
|
+
<path
|
|
30
|
+
d="M15.2104 22.7501H8.79039C5.30039 22.7501 5.16039 20.8201 5.05039 19.2601L4.40039 9.19007C4.37039 8.78007 4.69039 8.42008 5.10039 8.39008C5.52039 8.37008 5.87039 8.68008 5.90039 9.09008L6.55039 19.1601C6.66039 20.6801 6.70039 21.2501 8.79039 21.2501H15.2104C17.3104 21.2501 17.3504 20.6801 17.4504 19.1601L18.1004 9.09008C18.1304 8.68008 18.4904 8.37008 18.9004 8.39008C19.3104 8.42008 19.6304 8.77007 19.6004 9.19007L18.9504 19.2601C18.8404 20.8201 18.7004 22.7501 15.2104 22.7501Z"
|
|
31
|
+
fill="#292D32"
|
|
32
|
+
/>
|
|
33
|
+
<path
|
|
34
|
+
d="M13.6601 17.25H10.3301C9.92008 17.25 9.58008 16.91 9.58008 16.5C9.58008 16.09 9.92008 15.75 10.3301 15.75H13.6601C14.0701 15.75 14.4101 16.09 14.4101 16.5C14.4101 16.91 14.0701 17.25 13.6601 17.25Z"
|
|
35
|
+
fill="#292D32"
|
|
36
|
+
/>
|
|
37
|
+
<path
|
|
38
|
+
d="M14.5 13.25H9.5C9.09 13.25 8.75 12.91 8.75 12.5C8.75 12.09 9.09 11.75 9.5 11.75H14.5C14.91 11.75 15.25 12.09 15.25 12.5C15.25 12.91 14.91 13.25 14.5 13.25Z"
|
|
39
|
+
fill="#292D32"
|
|
40
|
+
/>
|
|
41
|
+
</svg>
|
|
42
|
+
</IconButton>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<style>
|
|
47
|
+
.widget-case {
|
|
48
|
+
width: 100%;
|
|
49
|
+
height: 100%;
|
|
50
|
+
position: relative;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.btn {
|
|
54
|
+
position: absolute;
|
|
55
|
+
top: var(--pad-s);
|
|
56
|
+
right: var(--pad-s);
|
|
57
|
+
z-index: 3;
|
|
58
|
+
}
|
|
59
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { Button } from "hamzus-ui";
|
|
3
|
+
import { widgetTypeRegistry } from "../registry.svelte";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
type="",
|
|
7
|
+
style="",
|
|
8
|
+
variant="ghost",
|
|
9
|
+
...props
|
|
10
|
+
} = $props()
|
|
11
|
+
|
|
12
|
+
const widgetType = $derived(widgetTypeRegistry.get(type))
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
<Button {variant} style="width:100%;{style}" {...props}>
|
|
17
|
+
<h5>{widgetType.name}</h5>
|
|
18
|
+
</Button>
|
|
19
|
+
|
|
20
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { Button, DropdownMenu } from 'hamzus-ui';
|
|
3
|
+
import { widgetTypeRegistry } from '../registry.svelte';
|
|
4
|
+
import WidgetTypeButton from './WidgetTypeButton.svelte';
|
|
5
|
+
|
|
6
|
+
let { value = $bindable(null), onChange = (type) => {} } = $props();
|
|
7
|
+
|
|
8
|
+
const widgetTypes = widgetTypeRegistry.getAll();
|
|
9
|
+
|
|
10
|
+
function handleClick(type) {
|
|
11
|
+
value = type;
|
|
12
|
+
onChange(type);
|
|
13
|
+
}
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<DropdownMenu.Root direction="bottom">
|
|
17
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
18
|
+
<Button variant="secondary">
|
|
19
|
+
{#if value}
|
|
20
|
+
{@const widgetType = widgetTypeRegistry.get(value)}
|
|
21
|
+
<h5>{widgetType.name}</h5>
|
|
22
|
+
{:else}
|
|
23
|
+
<h5>séléctionner</h5>
|
|
24
|
+
{/if}
|
|
25
|
+
</Button>
|
|
26
|
+
</DropdownMenu.Trigger>
|
|
27
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
28
|
+
{#each $widgetTypes as widgetType}
|
|
29
|
+
<WidgetTypeButton type={widgetType.type} onClick={() => {handleClick(widgetType.type)}}></WidgetTypeButton>
|
|
30
|
+
{/each}
|
|
31
|
+
</DropdownMenu.Content>
|
|
32
|
+
</DropdownMenu.Root>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { sendRequest } from "hamzus-ui";
|
|
2
|
+
import { widgetTypeRegistry } from "./registry.svelte";
|
|
3
|
+
|
|
4
|
+
export function createWidgetType(config = {}) {
|
|
5
|
+
const widgetType = {
|
|
6
|
+
type: null,
|
|
7
|
+
name: null,
|
|
8
|
+
default_props:{},
|
|
9
|
+
components:{
|
|
10
|
+
config:null,
|
|
11
|
+
preview:null,
|
|
12
|
+
component:null,
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
Object.assign(widgetType, config);
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
return widgetType;
|
|
20
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { sendRequest } from "hamzus-ui";
|
|
2
|
+
import { widgetTypeRegistry } from "./registry.svelte";
|
|
3
|
+
import Bar from "./widgetTypes/Bar";
|
|
4
|
+
import Evolution from "./widgetTypes/Evolution";
|
|
5
|
+
import KPI from "./widgetTypes/KPI";
|
|
6
|
+
import List from "./widgetTypes/List";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export async function registerWidgetTypes() {
|
|
10
|
+
widgetTypeRegistry.register(Bar)
|
|
11
|
+
widgetTypeRegistry.register(Evolution)
|
|
12
|
+
widgetTypeRegistry.register(KPI)
|
|
13
|
+
widgetTypeRegistry.register(List)
|
|
14
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { writable, derived, get } from 'svelte/store';
|
|
2
|
+
|
|
3
|
+
function createWidgetTypeRegistry() {
|
|
4
|
+
const store = writable({});
|
|
5
|
+
|
|
6
|
+
const all = derived(store, ($widgetTypes) => Object.values($widgetTypes));
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
subscribe: store.subscribe,
|
|
10
|
+
|
|
11
|
+
register(widgetGroup) {
|
|
12
|
+
store.update((widgetTypes) => ({
|
|
13
|
+
...widgetTypes,
|
|
14
|
+
[widgetGroup.type]: widgetGroup
|
|
15
|
+
}));
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
unregister(type) {
|
|
19
|
+
store.update((widgetTypes) => {
|
|
20
|
+
const copy = { ...widgetTypes };
|
|
21
|
+
|
|
22
|
+
delete copy[type];
|
|
23
|
+
|
|
24
|
+
return copy;
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
get(type) {
|
|
29
|
+
return get(store)[type];
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
select(type) {
|
|
33
|
+
return derived(store, ($widgetTypes) => $widgetTypes[type]);
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
getAll() {
|
|
37
|
+
return all;
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const widgetTypeRegistry = createWidgetTypeRegistry();
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { ChartBar, ChartLine, sendRequest, Skeleton } from 'hamzus-ui';
|
|
3
|
+
import { frequencyText, operationText } from './text';
|
|
4
|
+
import { onMount } from 'svelte';
|
|
5
|
+
|
|
6
|
+
let {id = '', title = '', entityId = '', frequency = '', valueFieldId = '', dateFieldId = '', operation = ''} = $props()
|
|
7
|
+
|
|
8
|
+
let data = $state(null);
|
|
9
|
+
let previousTotal = $state(null);
|
|
10
|
+
|
|
11
|
+
onMount(async () => {
|
|
12
|
+
const request = await sendRequest({
|
|
13
|
+
path: '/get-widget-data',
|
|
14
|
+
data: {
|
|
15
|
+
widgetLinkId: id
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
data = request.data
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
function getRandomSizes() {
|
|
24
|
+
const sizes = [60, 120, 80];
|
|
25
|
+
|
|
26
|
+
return sizes[Math.floor(Math.random() * sizes.length)];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getPercent() {
|
|
30
|
+
if (previousTotal === 0) {
|
|
31
|
+
if (total === 0) return 0;
|
|
32
|
+
return null; // augmentation non calculable
|
|
33
|
+
}
|
|
34
|
+
return ((total - previousTotal) / previousTotal) * 100;
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<div class="kpi-container">
|
|
39
|
+
<div class="kpi">
|
|
40
|
+
<h4>{title}</h4>
|
|
41
|
+
{#if data === null}
|
|
42
|
+
<Skeleton style="width:{getRandomSizes()}px;height:32px;"></Skeleton>
|
|
43
|
+
{:else}
|
|
44
|
+
<ChartBar {data}
|
|
45
|
+
></ChartBar>
|
|
46
|
+
{/if}
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<style>
|
|
51
|
+
.kpi-container {
|
|
52
|
+
width: 100%;
|
|
53
|
+
height: 100%;
|
|
54
|
+
padding: var(--pad-m);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.kpi {
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
row-gap: var(--pad-m);
|
|
61
|
+
padding: var(--pad-xxl);
|
|
62
|
+
width: 100%;
|
|
63
|
+
height: 100%;
|
|
64
|
+
background-color: var(--bg-blur);
|
|
65
|
+
border-radius: var(--radius-m);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.bottom {
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
justify-content: space-between;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.right {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
column-gap: var(--pad-m);
|
|
78
|
+
}
|
|
79
|
+
</style>
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { Button, DropdownMenu, Input, sendRequest } from 'hamzus-ui';
|
|
3
|
+
import { frequencyText, frequencyTextForConfig } from './text';
|
|
4
|
+
import { entityRegistry } from '../../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 = 'count'; // count | sum | avg | min | max
|
|
26
|
+
const operations = ['count', 'sum', 'avg', 'min', 'max'];
|
|
27
|
+
|
|
28
|
+
function handleSelectEntity(entity) {
|
|
29
|
+
entityId = entity.id;
|
|
30
|
+
|
|
31
|
+
// parcourir les fields pour set auto le field de value et de date
|
|
32
|
+
for (const field of entity.fields) {
|
|
33
|
+
if (field.type.startsWith('number-') || field.type.startsWith('calculation-')) {
|
|
34
|
+
valueFieldId = field.id;
|
|
35
|
+
}
|
|
36
|
+
if (dateFieldTypes.includes(field.type)) {
|
|
37
|
+
dateFieldId = field.id;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function handleSave() {
|
|
43
|
+
save({
|
|
44
|
+
id,
|
|
45
|
+
title,
|
|
46
|
+
entityId,
|
|
47
|
+
frequency,
|
|
48
|
+
valueFieldId,
|
|
49
|
+
dateFieldId,
|
|
50
|
+
operation
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<div class="config">
|
|
56
|
+
<Input label="titre" bind:value={title} placeholder="exemple"></Input>
|
|
57
|
+
<h5>Entité</h5>
|
|
58
|
+
<DropdownMenu.Root direction="bottom">
|
|
59
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
60
|
+
<Button variant="secondary">
|
|
61
|
+
<h5>{entity?.plural_name ?? 'choisissez une entité'}</h5>
|
|
62
|
+
</Button>
|
|
63
|
+
</DropdownMenu.Trigger>
|
|
64
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
65
|
+
{#each $entities as entity}
|
|
66
|
+
<Button
|
|
67
|
+
onClick={() => {
|
|
68
|
+
handleSelectEntity(entity);
|
|
69
|
+
}}
|
|
70
|
+
variant="ghost"
|
|
71
|
+
style="width:100%;"><h5>{entity.plural_name}</h5></Button
|
|
72
|
+
>
|
|
73
|
+
{/each}
|
|
74
|
+
</DropdownMenu.Content>
|
|
75
|
+
</DropdownMenu.Root>
|
|
76
|
+
{#if entity}
|
|
77
|
+
{@const availedValueFields = entity.fields.filter(
|
|
78
|
+
(field) => field.type.startsWith('number-') || field.type.startsWith('calculation-')
|
|
79
|
+
)}
|
|
80
|
+
{@const availedDateFields = entity.fields.filter((field) =>
|
|
81
|
+
dateFieldTypes.includes(field.type)
|
|
82
|
+
)}
|
|
83
|
+
<h5>Champs pour valeurs</h5>
|
|
84
|
+
<DropdownMenu.Root direction="bottom">
|
|
85
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
86
|
+
<Button variant="secondary">
|
|
87
|
+
<h5>{valueField?.name ?? 'choisissez un champ pour valeur'}</h5>
|
|
88
|
+
</Button>
|
|
89
|
+
</DropdownMenu.Trigger>
|
|
90
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
91
|
+
{#if availedValueFields.length > 0}
|
|
92
|
+
{#each availedValueFields as field}
|
|
93
|
+
<Button
|
|
94
|
+
onClick={() => {
|
|
95
|
+
valueFieldId = field.id;
|
|
96
|
+
}}
|
|
97
|
+
variant="ghost"
|
|
98
|
+
style="width:100%;"><h5>{field.name}</h5></Button
|
|
99
|
+
>
|
|
100
|
+
{/each}
|
|
101
|
+
{:else}
|
|
102
|
+
<div class="nothing">
|
|
103
|
+
<h3>Aucun champs de valeur disponible</h3>
|
|
104
|
+
<p>
|
|
105
|
+
L'entité {entity?.plural_name} ne contient pas de champs nombre, permettant les calculs.
|
|
106
|
+
</p>
|
|
107
|
+
</div>
|
|
108
|
+
{/if}
|
|
109
|
+
</DropdownMenu.Content>
|
|
110
|
+
</DropdownMenu.Root>
|
|
111
|
+
<h5>Champs pour date</h5>
|
|
112
|
+
<DropdownMenu.Root direction="bottom">
|
|
113
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
114
|
+
<Button variant="secondary">
|
|
115
|
+
<h5>{dateField?.name ?? 'choisissez un champ pour date'}</h5>
|
|
116
|
+
</Button>
|
|
117
|
+
</DropdownMenu.Trigger>
|
|
118
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
119
|
+
{#if availedDateFields.length > 0}
|
|
120
|
+
{#each availedDateFields as field}
|
|
121
|
+
<Button
|
|
122
|
+
onClick={() => {
|
|
123
|
+
dateFieldId = field.id;
|
|
124
|
+
}}
|
|
125
|
+
variant="ghost"
|
|
126
|
+
style="width:100%;"><h5>{field.name}</h5></Button
|
|
127
|
+
>
|
|
128
|
+
{/each}
|
|
129
|
+
{:else}
|
|
130
|
+
<div class="nothing">
|
|
131
|
+
<h3>Aucun champs de date disponible</h3>
|
|
132
|
+
<p>L'entité {entity?.plural_name} ne contient pas de champs de date.</p>
|
|
133
|
+
</div>
|
|
134
|
+
{/if}
|
|
135
|
+
</DropdownMenu.Content>
|
|
136
|
+
</DropdownMenu.Root>
|
|
137
|
+
<h5>fréquence</h5>
|
|
138
|
+
<DropdownMenu.Root direction="bottom">
|
|
139
|
+
<DropdownMenu.Trigger slot="trigger">
|
|
140
|
+
<Button variant="secondary">
|
|
141
|
+
<h5>{frequency ? frequencyTextForConfig[frequency] : 'choisissez une fréquence'}</h5>
|
|
142
|
+
</Button>
|
|
143
|
+
</DropdownMenu.Trigger>
|
|
144
|
+
<DropdownMenu.Content slot="content" width="250px">
|
|
145
|
+
{#each frequencies as frequencyEl}
|
|
146
|
+
<Button
|
|
147
|
+
onClick={() => {
|
|
148
|
+
frequency = frequencyEl;
|
|
149
|
+
}}
|
|
150
|
+
variant="ghost"
|
|
151
|
+
style="width:100%;"><h5>{frequencyTextForConfig[frequencyEl]}</h5></Button
|
|
152
|
+
>
|
|
153
|
+
{/each}
|
|
154
|
+
</DropdownMenu.Content>
|
|
155
|
+
</DropdownMenu.Root>
|
|
156
|
+
{/if}
|
|
157
|
+
|
|
158
|
+
<Button onClick={handleSave}>
|
|
159
|
+
<h5>enregistrer</h5>
|
|
160
|
+
</Button>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<style>
|
|
164
|
+
.config {
|
|
165
|
+
display: flex;
|
|
166
|
+
flex-direction: column;
|
|
167
|
+
row-gap: var(--pad-m);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.nothing {
|
|
171
|
+
display: flex;
|
|
172
|
+
flex-direction: column;
|
|
173
|
+
row-gap: var(--pad-m);
|
|
174
|
+
padding: var(--pad-m);
|
|
175
|
+
}
|
|
176
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import ConfigBar from "./ConfigBar.svelte";
|
|
2
|
+
import Bar from "./Bar.svelte";
|
|
3
|
+
import { createWidgetType } from "../../createWidgetType";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export default createWidgetType({
|
|
7
|
+
type:"Bar",
|
|
8
|
+
name:"Graphique en bar",
|
|
9
|
+
components:{
|
|
10
|
+
config:ConfigBar,
|
|
11
|
+
component:Bar,
|
|
12
|
+
}
|
|
13
|
+
})
|