@lobb-js/lobb-ext-reports 0.13.0 → 0.15.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/dist/lib/components/dv_fields_buttons/query_ai_button/index.svelte +1 -1
- package/dist/lib/components/gridStack.svelte +12 -15
- package/dist/lib/components/pages/reports/components/chart.svelte +5 -26
- package/dist/lib/components/pages/reports/components/report.svelte +16 -26
- package/dist/lib/components/pages/reports/index.svelte +13 -2
- package/dist/lib/components/pages/shared_report/index.svelte +1 -1
- package/dist/lib/components/reportBody.svelte +3 -5
- package/extensions/reports/studio/lib/components/dv_fields_buttons/query_ai_button/index.svelte +1 -1
- package/extensions/reports/studio/lib/components/gridStack.svelte +12 -15
- package/extensions/reports/studio/lib/components/pages/reports/components/chart.svelte +5 -26
- package/extensions/reports/studio/lib/components/pages/reports/components/report.svelte +16 -26
- package/extensions/reports/studio/lib/components/pages/reports/index.svelte +13 -2
- package/extensions/reports/studio/lib/components/pages/shared_report/index.svelte +1 -1
- package/extensions/reports/studio/lib/components/reportBody.svelte +3 -5
- package/package.json +3 -3
|
@@ -9,16 +9,14 @@
|
|
|
9
9
|
column?: number;
|
|
10
10
|
cellHeight?: string;
|
|
11
11
|
editable?: boolean;
|
|
12
|
-
allowEdit?: boolean;
|
|
13
12
|
onLayoutChange?: (layout: LayoutItem[]) => void;
|
|
14
13
|
children: Snippet;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
let {
|
|
18
17
|
column = 12,
|
|
19
|
-
cellHeight = "
|
|
18
|
+
cellHeight = "50px",
|
|
20
19
|
editable = $bindable(false),
|
|
21
|
-
allowEdit = false,
|
|
22
20
|
onLayoutChange,
|
|
23
21
|
children,
|
|
24
22
|
}: Props = $props();
|
|
@@ -31,18 +29,16 @@
|
|
|
31
29
|
let containerWidth = $state(0);
|
|
32
30
|
let saveTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
33
31
|
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
// Editability is decided by the gridStack itself, not the parent.
|
|
33
|
+
// The signal that a parent cares about edits is `onLayoutChange` —
|
|
34
|
+
// no callback means view-only, even on a wide screen. Width gates
|
|
35
|
+
// the rest: drag handles aren't usable on a tiny grid.
|
|
36
|
+
$effect(() => {
|
|
37
|
+
editable = !!onLayoutChange && containerWidth >= 768;
|
|
38
|
+
});
|
|
41
39
|
|
|
42
40
|
function getResponsiveColumns(width: number): number {
|
|
43
|
-
if (width >=
|
|
44
|
-
if (width >= 768) return 6;
|
|
45
|
-
if (width >= 480) return 2;
|
|
41
|
+
if (width >= 768) return 12;
|
|
46
42
|
return 1;
|
|
47
43
|
}
|
|
48
44
|
|
|
@@ -63,7 +59,7 @@
|
|
|
63
59
|
}
|
|
64
60
|
|
|
65
61
|
$effect(() => {
|
|
66
|
-
const mode =
|
|
62
|
+
const mode = editable; // force-read so always tracked
|
|
67
63
|
const ready = initialized; // force-read so always tracked
|
|
68
64
|
if (!grid || !ready) return;
|
|
69
65
|
if (mode) {
|
|
@@ -101,7 +97,7 @@
|
|
|
101
97
|
window.dispatchEvent(new Event("resize"));
|
|
102
98
|
lastWidth = entries[0]?.contentRect.width ?? gridEl!.offsetWidth;
|
|
103
99
|
containerWidth = lastWidth;
|
|
104
|
-
if (!
|
|
100
|
+
if (!editable) {
|
|
105
101
|
grid?.column(getResponsiveColumns(lastWidth));
|
|
106
102
|
}
|
|
107
103
|
});
|
|
@@ -132,6 +128,7 @@
|
|
|
132
128
|
.grid-stack-wrap {
|
|
133
129
|
overflow-x: hidden;
|
|
134
130
|
width: 100%;
|
|
131
|
+
height: 100%;
|
|
135
132
|
}
|
|
136
133
|
|
|
137
134
|
:global(.grid-stack) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { onMount } from "svelte";
|
|
3
2
|
import type { ExtensionProps } from "@lobb-js/studio";
|
|
3
|
+
import { CanAccess } from "@lobb-js/studio";
|
|
4
4
|
import Table from "./charts/table.svelte";
|
|
5
5
|
import ChartJs from "./charts/chartJs.svelte";
|
|
6
6
|
import Metric from "./charts/metric.svelte";
|
|
@@ -33,27 +33,6 @@
|
|
|
33
33
|
const { UpdateDetailViewButton, Button, Tooltip } = utils.components;
|
|
34
34
|
const icons = utils.components.Icons;
|
|
35
35
|
|
|
36
|
-
// Button visibility comes from the caller's actual permissions on
|
|
37
|
-
// reports_charts. A logged-in admin sees edit + delete; a viewer without
|
|
38
|
-
// those grants (including a share-token recipient) doesn't see either.
|
|
39
|
-
let canUpdate = $state(false);
|
|
40
|
-
let canDelete = $state(false);
|
|
41
|
-
|
|
42
|
-
onMount(async () => {
|
|
43
|
-
const [updateAllowed, deleteAllowed] = await Promise.all([
|
|
44
|
-
utils.emitEvent("auth.canAccess", {
|
|
45
|
-
collection: "reports_charts",
|
|
46
|
-
action: "update",
|
|
47
|
-
}),
|
|
48
|
-
utils.emitEvent("auth.canAccess", {
|
|
49
|
-
collection: "reports_charts",
|
|
50
|
-
action: "delete",
|
|
51
|
-
}),
|
|
52
|
-
]);
|
|
53
|
-
canUpdate = updateAllowed === true;
|
|
54
|
-
canDelete = deleteAllowed === true;
|
|
55
|
-
});
|
|
56
|
-
|
|
57
36
|
function findCustomChartComponent(type: string): any {
|
|
58
37
|
const extensions = utils.ctx?.extensions ?? {};
|
|
59
38
|
for (const ext of Object.values(extensions) as any[]) {
|
|
@@ -116,7 +95,7 @@
|
|
|
116
95
|
{/if}
|
|
117
96
|
</div>
|
|
118
97
|
<div class="flex">
|
|
119
|
-
|
|
98
|
+
<CanAccess collection="reports_charts" action="update">
|
|
120
99
|
<UpdateDetailViewButton
|
|
121
100
|
collectionName="reports_charts"
|
|
122
101
|
recordId={chartRecord.id}
|
|
@@ -125,8 +104,8 @@
|
|
|
125
104
|
Icon={icons.Pencil}
|
|
126
105
|
onSuccessfullSave={handleEditOnSuccessfull}
|
|
127
106
|
></UpdateDetailViewButton>
|
|
128
|
-
|
|
129
|
-
|
|
107
|
+
</CanAccess>
|
|
108
|
+
<CanAccess collection="reports_charts" action="delete">
|
|
130
109
|
<Button
|
|
131
110
|
class="h-6 w-6 text-muted-foreground hover:bg-transparent"
|
|
132
111
|
variant="ghost"
|
|
@@ -134,7 +113,7 @@
|
|
|
134
113
|
Icon={icons.Trash}
|
|
135
114
|
onclick={handleChartDelete}
|
|
136
115
|
></Button>
|
|
137
|
-
|
|
116
|
+
</CanAccess>
|
|
138
117
|
</div>
|
|
139
118
|
</div>
|
|
140
119
|
{/if}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { CanAccess, isHidden } from "@lobb-js/studio";
|
|
3
3
|
import ReportBody from "../../../reportBody.svelte";
|
|
4
4
|
|
|
5
5
|
const { reportId, utils }: { reportId: string; utils: any } = $props();
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
let report: any = $state(null);
|
|
14
14
|
let saving = $state(false);
|
|
15
15
|
let editable = $state(false);
|
|
16
|
-
let canShare = $state(false);
|
|
17
16
|
let sharing = $state(false);
|
|
18
17
|
// Handle captured from ReportBody via its onReady callback — lets us
|
|
19
18
|
// trigger a reload of charts after creating a new one without duplicating
|
|
@@ -49,14 +48,6 @@
|
|
|
49
48
|
});
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
async function checkSharePermission() {
|
|
53
|
-
const allowed = await utils.emitEvent("auth.canAccess", {
|
|
54
|
-
collection: "auth_shares",
|
|
55
|
-
action: "create",
|
|
56
|
-
});
|
|
57
|
-
canShare = allowed === true;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
51
|
async function handleShare() {
|
|
61
52
|
sharing = true;
|
|
62
53
|
try {
|
|
@@ -101,9 +92,6 @@
|
|
|
101
92
|
}
|
|
102
93
|
}
|
|
103
94
|
|
|
104
|
-
onMount(() => {
|
|
105
|
-
checkSharePermission();
|
|
106
|
-
});
|
|
107
95
|
</script>
|
|
108
96
|
|
|
109
97
|
<div class="report-layout">
|
|
@@ -111,7 +99,7 @@
|
|
|
111
99
|
<div class="flex gap-2">
|
|
112
100
|
<div class="mt-1"><SidebarTrigger /></div>
|
|
113
101
|
<div class="flex flex-col justify-center">
|
|
114
|
-
<h2 class="font-medium text-
|
|
102
|
+
<h2 class="font-medium text-foreground">{report?.name ?? ""}</h2>
|
|
115
103
|
<div class="text-xs text-muted-foreground">{report?.description ?? ""}</div>
|
|
116
104
|
</div>
|
|
117
105
|
</div>
|
|
@@ -132,23 +120,25 @@
|
|
|
132
120
|
Expand window to rearrange
|
|
133
121
|
</div>
|
|
134
122
|
{/if}
|
|
135
|
-
{#if
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
123
|
+
{#if !isHidden(utils.ctx, "reports.dashboardShareButton")}
|
|
124
|
+
<CanAccess collection="auth_shares" action="create">
|
|
125
|
+
<Button
|
|
126
|
+
variant="outline"
|
|
127
|
+
size="sm"
|
|
128
|
+
Icon={sharing ? Icons.LoaderCircle : Icons.Share2}
|
|
129
|
+
disabled={sharing}
|
|
130
|
+
onclick={handleShare}
|
|
131
|
+
>
|
|
132
|
+
{sharing ? "Sharing..." : "Share"}
|
|
133
|
+
</Button>
|
|
134
|
+
</CanAccess>
|
|
145
135
|
{/if}
|
|
146
136
|
{#if report}
|
|
147
137
|
<CreateDetailViewButton
|
|
148
138
|
collectionName="reports_charts"
|
|
149
139
|
values={{ report_id: { id: reportId, name: report.name } }}
|
|
150
140
|
variant="default"
|
|
151
|
-
|
|
141
|
+
size="sm"
|
|
152
142
|
Icon={Icons.Plus}
|
|
153
143
|
onSuccessfullSave={async () => await body?.reload()}
|
|
154
144
|
>
|
|
@@ -162,7 +152,7 @@
|
|
|
162
152
|
<ReportBody
|
|
163
153
|
{reportId}
|
|
164
154
|
{utils}
|
|
165
|
-
|
|
155
|
+
bind:editable
|
|
166
156
|
onLayoutChange={handleLayoutChange}
|
|
167
157
|
onChartClick={handleChartClick}
|
|
168
158
|
onReportLoaded={handleReportLoaded}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { ExtensionProps } from "@lobb-js/studio";
|
|
3
|
+
import { ExtensionsComponents } from "@lobb-js/studio";
|
|
3
4
|
import Report from "./components/report.svelte";
|
|
4
5
|
|
|
5
6
|
const { utils, ...props }: ExtensionProps = $props();
|
|
@@ -58,7 +59,7 @@
|
|
|
58
59
|
|
|
59
60
|
<Sidebar title="Dashboards" data={sideBarData}>
|
|
60
61
|
{#snippet belowSearch()}
|
|
61
|
-
<div class="p-2">
|
|
62
|
+
<div class="flex flex-col gap-2 p-2">
|
|
62
63
|
<CreateDetailViewButton
|
|
63
64
|
collectionName="reports_dashboards"
|
|
64
65
|
variant="outline"
|
|
@@ -71,6 +72,16 @@
|
|
|
71
72
|
>
|
|
72
73
|
Create a report
|
|
73
74
|
</CreateDetailViewButton>
|
|
75
|
+
<!-- Extension hook: apps can register components for
|
|
76
|
+
`reports.sidebar.actions` to drop actions into the
|
|
77
|
+
dashboards sidebar (below Create). The `url` prop is
|
|
78
|
+
forwarded so the consumer's `when` predicate can
|
|
79
|
+
scope by route. -->
|
|
80
|
+
<ExtensionsComponents
|
|
81
|
+
name="reports.sidebar.actions"
|
|
82
|
+
{utils}
|
|
83
|
+
url={utils.page.url}
|
|
84
|
+
/>
|
|
74
85
|
</div>
|
|
75
86
|
{/snippet}
|
|
76
87
|
{#snippet elementRightSide(item: any)}
|
|
@@ -120,7 +131,7 @@
|
|
|
120
131
|
<CreateDetailViewButton
|
|
121
132
|
collectionName="reports_dashboards"
|
|
122
133
|
variant="default"
|
|
123
|
-
|
|
134
|
+
size="sm"
|
|
124
135
|
Icon={icons.Plus}
|
|
125
136
|
onSuccessfullSave={async (record: any) =>
|
|
126
137
|
utils.goto(`?report=${record.id}`)}
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
{:else}
|
|
76
76
|
<div class="flex shrink-0 items-center justify-between gap-2 border-b bg-background p-2">
|
|
77
77
|
<div class="flex flex-col justify-center">
|
|
78
|
-
<h2 class="font-medium text-
|
|
78
|
+
<h2 class="font-medium text-foreground">{report?.name ?? ""}</h2>
|
|
79
79
|
<div class="text-xs text-muted-foreground">{report?.description ?? ""}</div>
|
|
80
80
|
</div>
|
|
81
81
|
<div class="text-xs text-muted-foreground">Shared view</div>
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
interface Props extends ExtensionProps {
|
|
8
8
|
reportId: string | number;
|
|
9
9
|
editable?: boolean;
|
|
10
|
-
allowEdit?: boolean;
|
|
11
10
|
showChartHeaders?: boolean;
|
|
12
11
|
onLayoutChange?: (changes: any[]) => void;
|
|
13
12
|
onChartClick?: (event: any, elements: any[], chart: any) => void;
|
|
@@ -15,10 +14,9 @@
|
|
|
15
14
|
onReady?: (api: { reload: () => Promise<void> }) => void;
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
let {
|
|
19
18
|
reportId,
|
|
20
|
-
editable = false,
|
|
21
|
-
allowEdit = false,
|
|
19
|
+
editable = $bindable(false),
|
|
22
20
|
showChartHeaders = true,
|
|
23
21
|
onLayoutChange,
|
|
24
22
|
onChartClick,
|
|
@@ -95,7 +93,7 @@
|
|
|
95
93
|
</div>
|
|
96
94
|
{:else}
|
|
97
95
|
{#key charts}
|
|
98
|
-
<GridStackComponent
|
|
96
|
+
<GridStackComponent bind:editable {onLayoutChange}>
|
|
99
97
|
{#each [...charts].sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0)) as chart (chart.id)}
|
|
100
98
|
<div
|
|
101
99
|
class="grid-stack-item"
|
|
@@ -9,16 +9,14 @@
|
|
|
9
9
|
column?: number;
|
|
10
10
|
cellHeight?: string;
|
|
11
11
|
editable?: boolean;
|
|
12
|
-
allowEdit?: boolean;
|
|
13
12
|
onLayoutChange?: (layout: LayoutItem[]) => void;
|
|
14
13
|
children: Snippet;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
let {
|
|
18
17
|
column = 12,
|
|
19
|
-
cellHeight = "
|
|
18
|
+
cellHeight = "50px",
|
|
20
19
|
editable = $bindable(false),
|
|
21
|
-
allowEdit = false,
|
|
22
20
|
onLayoutChange,
|
|
23
21
|
children,
|
|
24
22
|
}: Props = $props();
|
|
@@ -31,18 +29,16 @@
|
|
|
31
29
|
let containerWidth = $state(0);
|
|
32
30
|
let saveTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
33
31
|
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
// Editability is decided by the gridStack itself, not the parent.
|
|
33
|
+
// The signal that a parent cares about edits is `onLayoutChange` —
|
|
34
|
+
// no callback means view-only, even on a wide screen. Width gates
|
|
35
|
+
// the rest: drag handles aren't usable on a tiny grid.
|
|
36
|
+
$effect(() => {
|
|
37
|
+
editable = !!onLayoutChange && containerWidth >= 768;
|
|
38
|
+
});
|
|
41
39
|
|
|
42
40
|
function getResponsiveColumns(width: number): number {
|
|
43
|
-
if (width >=
|
|
44
|
-
if (width >= 768) return 6;
|
|
45
|
-
if (width >= 480) return 2;
|
|
41
|
+
if (width >= 768) return 12;
|
|
46
42
|
return 1;
|
|
47
43
|
}
|
|
48
44
|
|
|
@@ -63,7 +59,7 @@
|
|
|
63
59
|
}
|
|
64
60
|
|
|
65
61
|
$effect(() => {
|
|
66
|
-
const mode =
|
|
62
|
+
const mode = editable; // force-read so always tracked
|
|
67
63
|
const ready = initialized; // force-read so always tracked
|
|
68
64
|
if (!grid || !ready) return;
|
|
69
65
|
if (mode) {
|
|
@@ -101,7 +97,7 @@
|
|
|
101
97
|
window.dispatchEvent(new Event("resize"));
|
|
102
98
|
lastWidth = entries[0]?.contentRect.width ?? gridEl!.offsetWidth;
|
|
103
99
|
containerWidth = lastWidth;
|
|
104
|
-
if (!
|
|
100
|
+
if (!editable) {
|
|
105
101
|
grid?.column(getResponsiveColumns(lastWidth));
|
|
106
102
|
}
|
|
107
103
|
});
|
|
@@ -132,6 +128,7 @@
|
|
|
132
128
|
.grid-stack-wrap {
|
|
133
129
|
overflow-x: hidden;
|
|
134
130
|
width: 100%;
|
|
131
|
+
height: 100%;
|
|
135
132
|
}
|
|
136
133
|
|
|
137
134
|
:global(.grid-stack) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { onMount } from "svelte";
|
|
3
2
|
import type { ExtensionProps } from "@lobb-js/studio";
|
|
3
|
+
import { CanAccess } from "@lobb-js/studio";
|
|
4
4
|
import Table from "./charts/table.svelte";
|
|
5
5
|
import ChartJs from "./charts/chartJs.svelte";
|
|
6
6
|
import Metric from "./charts/metric.svelte";
|
|
@@ -33,27 +33,6 @@
|
|
|
33
33
|
const { UpdateDetailViewButton, Button, Tooltip } = utils.components;
|
|
34
34
|
const icons = utils.components.Icons;
|
|
35
35
|
|
|
36
|
-
// Button visibility comes from the caller's actual permissions on
|
|
37
|
-
// reports_charts. A logged-in admin sees edit + delete; a viewer without
|
|
38
|
-
// those grants (including a share-token recipient) doesn't see either.
|
|
39
|
-
let canUpdate = $state(false);
|
|
40
|
-
let canDelete = $state(false);
|
|
41
|
-
|
|
42
|
-
onMount(async () => {
|
|
43
|
-
const [updateAllowed, deleteAllowed] = await Promise.all([
|
|
44
|
-
utils.emitEvent("auth.canAccess", {
|
|
45
|
-
collection: "reports_charts",
|
|
46
|
-
action: "update",
|
|
47
|
-
}),
|
|
48
|
-
utils.emitEvent("auth.canAccess", {
|
|
49
|
-
collection: "reports_charts",
|
|
50
|
-
action: "delete",
|
|
51
|
-
}),
|
|
52
|
-
]);
|
|
53
|
-
canUpdate = updateAllowed === true;
|
|
54
|
-
canDelete = deleteAllowed === true;
|
|
55
|
-
});
|
|
56
|
-
|
|
57
36
|
function findCustomChartComponent(type: string): any {
|
|
58
37
|
const extensions = utils.ctx?.extensions ?? {};
|
|
59
38
|
for (const ext of Object.values(extensions) as any[]) {
|
|
@@ -116,7 +95,7 @@
|
|
|
116
95
|
{/if}
|
|
117
96
|
</div>
|
|
118
97
|
<div class="flex">
|
|
119
|
-
|
|
98
|
+
<CanAccess collection="reports_charts" action="update">
|
|
120
99
|
<UpdateDetailViewButton
|
|
121
100
|
collectionName="reports_charts"
|
|
122
101
|
recordId={chartRecord.id}
|
|
@@ -125,8 +104,8 @@
|
|
|
125
104
|
Icon={icons.Pencil}
|
|
126
105
|
onSuccessfullSave={handleEditOnSuccessfull}
|
|
127
106
|
></UpdateDetailViewButton>
|
|
128
|
-
|
|
129
|
-
|
|
107
|
+
</CanAccess>
|
|
108
|
+
<CanAccess collection="reports_charts" action="delete">
|
|
130
109
|
<Button
|
|
131
110
|
class="h-6 w-6 text-muted-foreground hover:bg-transparent"
|
|
132
111
|
variant="ghost"
|
|
@@ -134,7 +113,7 @@
|
|
|
134
113
|
Icon={icons.Trash}
|
|
135
114
|
onclick={handleChartDelete}
|
|
136
115
|
></Button>
|
|
137
|
-
|
|
116
|
+
</CanAccess>
|
|
138
117
|
</div>
|
|
139
118
|
</div>
|
|
140
119
|
{/if}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { CanAccess, isHidden } from "@lobb-js/studio";
|
|
3
3
|
import ReportBody from "../../../reportBody.svelte";
|
|
4
4
|
|
|
5
5
|
const { reportId, utils }: { reportId: string; utils: any } = $props();
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
let report: any = $state(null);
|
|
14
14
|
let saving = $state(false);
|
|
15
15
|
let editable = $state(false);
|
|
16
|
-
let canShare = $state(false);
|
|
17
16
|
let sharing = $state(false);
|
|
18
17
|
// Handle captured from ReportBody via its onReady callback — lets us
|
|
19
18
|
// trigger a reload of charts after creating a new one without duplicating
|
|
@@ -49,14 +48,6 @@
|
|
|
49
48
|
});
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
async function checkSharePermission() {
|
|
53
|
-
const allowed = await utils.emitEvent("auth.canAccess", {
|
|
54
|
-
collection: "auth_shares",
|
|
55
|
-
action: "create",
|
|
56
|
-
});
|
|
57
|
-
canShare = allowed === true;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
51
|
async function handleShare() {
|
|
61
52
|
sharing = true;
|
|
62
53
|
try {
|
|
@@ -101,9 +92,6 @@
|
|
|
101
92
|
}
|
|
102
93
|
}
|
|
103
94
|
|
|
104
|
-
onMount(() => {
|
|
105
|
-
checkSharePermission();
|
|
106
|
-
});
|
|
107
95
|
</script>
|
|
108
96
|
|
|
109
97
|
<div class="report-layout">
|
|
@@ -111,7 +99,7 @@
|
|
|
111
99
|
<div class="flex gap-2">
|
|
112
100
|
<div class="mt-1"><SidebarTrigger /></div>
|
|
113
101
|
<div class="flex flex-col justify-center">
|
|
114
|
-
<h2 class="font-medium text-
|
|
102
|
+
<h2 class="font-medium text-foreground">{report?.name ?? ""}</h2>
|
|
115
103
|
<div class="text-xs text-muted-foreground">{report?.description ?? ""}</div>
|
|
116
104
|
</div>
|
|
117
105
|
</div>
|
|
@@ -132,23 +120,25 @@
|
|
|
132
120
|
Expand window to rearrange
|
|
133
121
|
</div>
|
|
134
122
|
{/if}
|
|
135
|
-
{#if
|
|
136
|
-
<
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
123
|
+
{#if !isHidden(utils.ctx, "reports.dashboardShareButton")}
|
|
124
|
+
<CanAccess collection="auth_shares" action="create">
|
|
125
|
+
<Button
|
|
126
|
+
variant="outline"
|
|
127
|
+
size="sm"
|
|
128
|
+
Icon={sharing ? Icons.LoaderCircle : Icons.Share2}
|
|
129
|
+
disabled={sharing}
|
|
130
|
+
onclick={handleShare}
|
|
131
|
+
>
|
|
132
|
+
{sharing ? "Sharing..." : "Share"}
|
|
133
|
+
</Button>
|
|
134
|
+
</CanAccess>
|
|
145
135
|
{/if}
|
|
146
136
|
{#if report}
|
|
147
137
|
<CreateDetailViewButton
|
|
148
138
|
collectionName="reports_charts"
|
|
149
139
|
values={{ report_id: { id: reportId, name: report.name } }}
|
|
150
140
|
variant="default"
|
|
151
|
-
|
|
141
|
+
size="sm"
|
|
152
142
|
Icon={Icons.Plus}
|
|
153
143
|
onSuccessfullSave={async () => await body?.reload()}
|
|
154
144
|
>
|
|
@@ -162,7 +152,7 @@
|
|
|
162
152
|
<ReportBody
|
|
163
153
|
{reportId}
|
|
164
154
|
{utils}
|
|
165
|
-
|
|
155
|
+
bind:editable
|
|
166
156
|
onLayoutChange={handleLayoutChange}
|
|
167
157
|
onChartClick={handleChartClick}
|
|
168
158
|
onReportLoaded={handleReportLoaded}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { ExtensionProps } from "@lobb-js/studio";
|
|
3
|
+
import { ExtensionsComponents } from "@lobb-js/studio";
|
|
3
4
|
import Report from "./components/report.svelte";
|
|
4
5
|
|
|
5
6
|
const { utils, ...props }: ExtensionProps = $props();
|
|
@@ -58,7 +59,7 @@
|
|
|
58
59
|
|
|
59
60
|
<Sidebar title="Dashboards" data={sideBarData}>
|
|
60
61
|
{#snippet belowSearch()}
|
|
61
|
-
<div class="p-2">
|
|
62
|
+
<div class="flex flex-col gap-2 p-2">
|
|
62
63
|
<CreateDetailViewButton
|
|
63
64
|
collectionName="reports_dashboards"
|
|
64
65
|
variant="outline"
|
|
@@ -71,6 +72,16 @@
|
|
|
71
72
|
>
|
|
72
73
|
Create a report
|
|
73
74
|
</CreateDetailViewButton>
|
|
75
|
+
<!-- Extension hook: apps can register components for
|
|
76
|
+
`reports.sidebar.actions` to drop actions into the
|
|
77
|
+
dashboards sidebar (below Create). The `url` prop is
|
|
78
|
+
forwarded so the consumer's `when` predicate can
|
|
79
|
+
scope by route. -->
|
|
80
|
+
<ExtensionsComponents
|
|
81
|
+
name="reports.sidebar.actions"
|
|
82
|
+
{utils}
|
|
83
|
+
url={utils.page.url}
|
|
84
|
+
/>
|
|
74
85
|
</div>
|
|
75
86
|
{/snippet}
|
|
76
87
|
{#snippet elementRightSide(item: any)}
|
|
@@ -120,7 +131,7 @@
|
|
|
120
131
|
<CreateDetailViewButton
|
|
121
132
|
collectionName="reports_dashboards"
|
|
122
133
|
variant="default"
|
|
123
|
-
|
|
134
|
+
size="sm"
|
|
124
135
|
Icon={icons.Plus}
|
|
125
136
|
onSuccessfullSave={async (record: any) =>
|
|
126
137
|
utils.goto(`?report=${record.id}`)}
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
{:else}
|
|
76
76
|
<div class="flex shrink-0 items-center justify-between gap-2 border-b bg-background p-2">
|
|
77
77
|
<div class="flex flex-col justify-center">
|
|
78
|
-
<h2 class="font-medium text-
|
|
78
|
+
<h2 class="font-medium text-foreground">{report?.name ?? ""}</h2>
|
|
79
79
|
<div class="text-xs text-muted-foreground">{report?.description ?? ""}</div>
|
|
80
80
|
</div>
|
|
81
81
|
<div class="text-xs text-muted-foreground">Shared view</div>
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
interface Props extends ExtensionProps {
|
|
8
8
|
reportId: string | number;
|
|
9
9
|
editable?: boolean;
|
|
10
|
-
allowEdit?: boolean;
|
|
11
10
|
showChartHeaders?: boolean;
|
|
12
11
|
onLayoutChange?: (changes: any[]) => void;
|
|
13
12
|
onChartClick?: (event: any, elements: any[], chart: any) => void;
|
|
@@ -15,10 +14,9 @@
|
|
|
15
14
|
onReady?: (api: { reload: () => Promise<void> }) => void;
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
let {
|
|
19
18
|
reportId,
|
|
20
|
-
editable = false,
|
|
21
|
-
allowEdit = false,
|
|
19
|
+
editable = $bindable(false),
|
|
22
20
|
showChartHeaders = true,
|
|
23
21
|
onLayoutChange,
|
|
24
22
|
onChartClick,
|
|
@@ -95,7 +93,7 @@
|
|
|
95
93
|
</div>
|
|
96
94
|
{:else}
|
|
97
95
|
{#key charts}
|
|
98
|
-
<GridStackComponent
|
|
96
|
+
<GridStackComponent bind:editable {onLayoutChange}>
|
|
99
97
|
{#each [...charts].sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0)) as chart (chart.id)}
|
|
100
98
|
<div
|
|
101
99
|
class="grid-stack-item"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobb-js/lobb-ext-reports",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"package": "svelte-package --input extensions/reports/studio"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@lobb-js/core": "^0.
|
|
35
|
+
"@lobb-js/core": "^0.36.0",
|
|
36
36
|
"chart.js": "^4.4.8",
|
|
37
37
|
"gridstack": "^12.6.0",
|
|
38
38
|
"hono": "^4.7.0",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@faker-js/faker": "^9.6.0",
|
|
46
|
-
"@lobb-js/studio": "^0.
|
|
46
|
+
"@lobb-js/studio": "^0.36.0",
|
|
47
47
|
"@lucide/svelte": "^0.563.1",
|
|
48
48
|
"@sveltejs/adapter-node": "^5.5.4",
|
|
49
49
|
"@sveltejs/kit": "^2.60.1",
|