@lobb-js/lobb-ext-reports 0.11.1 → 0.12.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/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  import type { Extension, ExtensionUtils } from "@lobb-js/studio";
2
+ export { default as ReportBody } from "./lib/components/reportBody.svelte";
2
3
  export default function extension(utils: ExtensionUtils): Extension;
package/dist/index.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import Reports from "./lib/components/pages/reports/index.svelte";
2
2
  import SharedReport from "./lib/components/pages/shared_report/index.svelte";
3
3
  import QueryAiButton from "./lib/components/dv_fields_buttons/query_ai_button/index.svelte";
4
+ export { default as ReportBody } from "./lib/components/reportBody.svelte";
4
5
  export default function extension(utils) {
5
6
  return {
6
7
  name: "reports",
7
8
  components: {
8
9
  "dvFields.topRight.reports_charts.query": QueryAiButton,
9
- "pages.analytics": Reports,
10
+ "pages.dashboards": Reports,
10
11
  // Lives at /studio/public/reports/shared_report — no auth required, the
11
12
  // page itself swaps the LobbClient bearer to the share_token in the URL.
12
13
  "publicPages.shared_report": SharedReport,
@@ -14,9 +15,9 @@ export default function extension(utils) {
14
15
  dashboardNavs: {
15
16
  middle: [
16
17
  {
17
- href: "/studio/extensions/reports/analytics",
18
+ href: "/studio/extensions/reports/dashboards",
18
19
  icon: utils.components.Icons.ChartNoAxesCombined,
19
- label: "Analytics",
20
+ label: "Dashboards",
20
21
  represents: "reports_dashboards",
21
22
  },
22
23
  ],
@@ -9,6 +9,7 @@
9
9
  column?: number;
10
10
  cellHeight?: string;
11
11
  editable?: boolean;
12
+ allowEdit?: boolean;
12
13
  onLayoutChange?: (layout: LayoutItem[]) => void;
13
14
  children: Snippet;
14
15
  }
@@ -17,6 +18,7 @@
17
18
  column = 12,
18
19
  cellHeight = "150px",
19
20
  editable = $bindable(false),
21
+ allowEdit = false,
20
22
  onLayoutChange,
21
23
  children,
22
24
  }: Props = $props();
@@ -29,7 +31,10 @@
29
31
  let containerWidth = $state(0);
30
32
  let saveTimeout: ReturnType<typeof setTimeout> | null = null;
31
33
 
32
- let editMode = $derived(containerWidth >= 1200);
34
+ // Edit mode only ever turns on when the parent explicitly opts in.
35
+ // Without allowEdit, container width is irrelevant — the grid stays
36
+ // disabled forever.
37
+ let editMode = $derived(allowEdit && containerWidth >= 1200);
33
38
 
34
39
  // Keep editable in sync so parent can read it
35
40
  $effect(() => { editable = editMode; });
@@ -112,8 +117,10 @@
112
117
  });
113
118
  </script>
114
119
 
115
- <div class="grid-stack" bind:this={gridEl}>
116
- {@render children()}
120
+ <div class="grid-stack-wrap">
121
+ <div class="grid-stack" bind:this={gridEl}>
122
+ {@render children()}
123
+ </div>
117
124
  </div>
118
125
 
119
126
  <style>
@@ -122,9 +129,14 @@
122
129
  position: absolute;
123
130
  }
124
131
 
132
+ .grid-stack-wrap {
133
+ overflow-x: hidden;
134
+ width: 100%;
135
+ }
136
+
125
137
  :global(.grid-stack) {
126
138
  background: transparent;
127
- padding: 0.5rem;
139
+ margin: 0 -0.5rem;
128
140
  }
129
141
 
130
142
  :global(.grid-stack-placeholder > .placeholder-content) {
@@ -14,6 +14,7 @@
14
14
 
15
15
  interface Props extends ExtensionProps {
16
16
  chartRecord: any;
17
+ showHeader?: boolean;
17
18
  onChartDeleted?: () => Promise<void>;
18
19
  onChartEdited?: () => Promise<void>;
19
20
  onChartClick?: (event: ChartClickEvent) => void;
@@ -21,6 +22,7 @@
21
22
 
22
23
  const {
23
24
  chartRecord,
25
+ showHeader = true,
24
26
  onChartDeleted,
25
27
  onChartEdited,
26
28
  onChartClick,
@@ -95,43 +97,47 @@
95
97
  }
96
98
  </script>
97
99
 
98
- <div class="gridContainer h-full rounded-md border bg-background">
99
- <div class="flex items-center justify-between border-b p-2 text-sm">
100
- <div class="flex items-center gap-1.5">
101
- <span>{chartRecord.title}</span>
102
- {#if chartRecord.description}
103
- <Tooltip.Root>
104
- <Tooltip.Trigger>
105
- <icons.CircleHelp class="text-muted-foreground" size={14} />
106
- </Tooltip.Trigger>
107
- <Tooltip.Content class="max-w-64 text-xs">
108
- {chartRecord.description}
109
- </Tooltip.Content>
110
- </Tooltip.Root>
111
- {/if}
112
- </div>
113
- <div class="flex">
114
- {#if canUpdate}
115
- <UpdateDetailViewButton
116
- collectionName="reports_charts"
117
- recordId={chartRecord.id}
118
- variant="ghost"
119
- class="h-7 w-7 text-muted-foreground hover:bg-transparent"
120
- Icon={icons.Pencil}
121
- onSuccessfullSave={handleEditOnSuccessfull}
122
- ></UpdateDetailViewButton>
123
- {/if}
124
- {#if canDelete}
125
- <Button
126
- class="h-6 w-6 text-muted-foreground hover:bg-transparent"
127
- variant="ghost"
128
- size="icon"
129
- Icon={icons.Trash}
130
- onclick={handleChartDelete}
131
- ></Button>
132
- {/if}
100
+ <div
101
+ class="grid h-full rounded-md border bg-background {showHeader ? 'grid-rows-[auto_1fr]' : 'grid-rows-[1fr]'}"
102
+ >
103
+ {#if showHeader}
104
+ <div class="flex items-center justify-between border-b p-2 text-sm">
105
+ <div class="flex items-center gap-1.5">
106
+ <span>{chartRecord.title}</span>
107
+ {#if chartRecord.description}
108
+ <Tooltip.Root>
109
+ <Tooltip.Trigger>
110
+ <icons.CircleHelp class="text-muted-foreground" size={14} />
111
+ </Tooltip.Trigger>
112
+ <Tooltip.Content class="max-w-64 text-xs">
113
+ {chartRecord.description}
114
+ </Tooltip.Content>
115
+ </Tooltip.Root>
116
+ {/if}
117
+ </div>
118
+ <div class="flex">
119
+ {#if canUpdate}
120
+ <UpdateDetailViewButton
121
+ collectionName="reports_charts"
122
+ recordId={chartRecord.id}
123
+ variant="ghost"
124
+ class="h-7 w-7 text-muted-foreground hover:bg-transparent"
125
+ Icon={icons.Pencil}
126
+ onSuccessfullSave={handleEditOnSuccessfull}
127
+ ></UpdateDetailViewButton>
128
+ {/if}
129
+ {#if canDelete}
130
+ <Button
131
+ class="h-6 w-6 text-muted-foreground hover:bg-transparent"
132
+ variant="ghost"
133
+ size="icon"
134
+ Icon={icons.Trash}
135
+ onclick={handleChartDelete}
136
+ ></Button>
137
+ {/if}
138
+ </div>
133
139
  </div>
134
- </div>
140
+ {/if}
135
141
  <div class="flex min-h-0 items-center justify-center overflow-hidden">
136
142
  <svelte:boundary>
137
143
  {#snippet failed(error, reset)}
@@ -183,10 +189,3 @@
183
189
  </svelte:boundary>
184
190
  </div>
185
191
  </div>
186
-
187
- <style>
188
- .gridContainer {
189
- display: grid;
190
- grid-template-rows: auto 1fr;
191
- }
192
- </style>
@@ -62,16 +62,24 @@
62
62
  async function handleShare() {
63
63
  sharing = true;
64
64
  try {
65
- // Snapshot grants exactly the read access a recipient needs to view
66
- // this one report — the dashboard row and the charts that belong to it.
67
- const permissions = {
65
+ const event = {
66
+ reportId: String(reportId),
67
+ report,
68
+ handled: false,
69
+ };
70
+ await utils.emitEvent("reports.share", event);
71
+ if (event.handled) return;
72
+
73
+ const basePermissions: Record<string, any> = {
68
74
  reports_dashboards: { read: { filter: { id: Number(reportId) } } },
69
75
  reports_charts: { read: { filter: { report_id: Number(reportId) } } },
70
- // TODO: the above ones are ok. the below ones are specific to only this risk project
71
- // can you please add them using a different way
72
- risk_config: true,
73
- risks: true,
74
76
  };
77
+ const shareReadAccess = (utils.ctx.meta?.extensions?.reports?.shareReadAccess ?? {}) as Record<string, any>;
78
+ const extraPermissions: Record<string, any> = {};
79
+ for (const [collectionName, readGrant] of Object.entries(shareReadAccess)) {
80
+ extraPermissions[collectionName] = { read: readGrant };
81
+ }
82
+ const permissions = { ...basePermissions, ...extraPermissions };
75
83
  const response = await utils.lobb.createOne("auth_shares", {
76
84
  label: `Share for: ${report?.name ?? "report"}`,
77
85
  permissions: JSON.stringify(permissions),
@@ -154,7 +162,7 @@
154
162
  <ReportBody
155
163
  {reportId}
156
164
  {utils}
157
- editable
165
+ allowEdit
158
166
  onLayoutChange={handleLayoutChange}
159
167
  onChartClick={handleChartClick}
160
168
  onReportLoaded={handleReportLoaded}
@@ -4,7 +4,7 @@
4
4
 
5
5
  const { utils, ...props }: ExtensionProps = $props();
6
6
 
7
- let extensionPagePath = "/studio/extensions/reports/analytics";
7
+ let extensionPagePath = "/studio/extensions/reports/dashboards";
8
8
  let reportId: string | null = $derived(utils.page.url.pathname.split("/")[5] ?? null);
9
9
  let sideBarData: any = $state(null);
10
10
  const {
@@ -56,7 +56,7 @@
56
56
 
57
57
  </script>
58
58
 
59
- <Sidebar title="Reports" data={sideBarData}>
59
+ <Sidebar title="Dashboards" data={sideBarData}>
60
60
  {#snippet belowSearch()}
61
61
  <div class="p-2">
62
62
  <CreateDetailViewButton
@@ -7,6 +7,8 @@
7
7
  interface Props extends ExtensionProps {
8
8
  reportId: string | number;
9
9
  editable?: boolean;
10
+ allowEdit?: boolean;
11
+ showChartHeaders?: boolean;
10
12
  onLayoutChange?: (changes: any[]) => void;
11
13
  onChartClick?: (event: any, elements: any[], chart: any) => void;
12
14
  onReportLoaded?: (report: any) => void;
@@ -16,6 +18,8 @@
16
18
  const {
17
19
  reportId,
18
20
  editable = false,
21
+ allowEdit = false,
22
+ showChartHeaders = true,
19
23
  onLayoutChange,
20
24
  onChartClick,
21
25
  onReportLoaded,
@@ -91,7 +95,7 @@
91
95
  </div>
92
96
  {:else}
93
97
  {#key charts}
94
- <GridStackComponent {editable} {onLayoutChange}>
98
+ <GridStackComponent {editable} {allowEdit} {onLayoutChange}>
95
99
  {#each [...charts].sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0)) as chart (chart.id)}
96
100
  <div
97
101
  class="grid-stack-item"
@@ -101,6 +105,7 @@
101
105
  <div class="grid-stack-item-content">
102
106
  <Chart
103
107
  chartRecord={chart}
108
+ showHeader={showChartHeaders}
104
109
  onChartDeleted={async () => await loadCharts()}
105
110
  onChartEdited={async () => await loadCharts()}
106
111
  {onChartClick}
@@ -3,12 +3,35 @@ import type { Extension } from "@lobb-js/core";
3
3
  import packageJson from "../../package.json" with { type: "json" };
4
4
  import { collections } from "./collections/index.ts";
5
5
  import { migrations } from "./migrations.ts";
6
- import { meta } from "./meta.ts";
6
+ import { meta as buildMeta } from "./meta.ts";
7
7
  import { workflows } from "./workflows.ts";
8
8
  import { openapi } from "./openapi.ts";
9
9
  import { relations } from "./relations.ts";
10
10
 
11
- export default function extension(): Extension {
11
+ type ShareReadPermission =
12
+ | true
13
+ | { filter?: Record<string, unknown>; fields?: Record<string, true> };
14
+
15
+ export interface ReportsExtensionConfig {
16
+ /**
17
+ * Domain-specific collections a share recipient needs read access to in
18
+ * addition to `reports_dashboards` + `reports_charts`. Reports often
19
+ * drill down into other collections (e.g. risk charts opening the
20
+ * `risks` table on click) — those collections have to be readable by
21
+ * the share token or the drill-down requests will 403.
22
+ *
23
+ * @example
24
+ * reports({
25
+ * shareReadAccess: {
26
+ * risks: true,
27
+ * risk_config: true,
28
+ * },
29
+ * })
30
+ */
31
+ shareReadAccess?: Record<string, ShareReadPermission>;
32
+ }
33
+
34
+ export default function extension(config: ReportsExtensionConfig = {}): Extension {
12
35
  return {
13
36
  version: packageJson.version,
14
37
  name: "reports",
@@ -17,7 +40,12 @@ export default function extension(): Extension {
17
40
  collections: collections,
18
41
  relations: relations,
19
42
  migrations: migrations,
20
- meta: meta,
43
+ // Surface the config to the studio via meta so the Share button can
44
+ // include it in the per-report share permissions snapshot.
45
+ meta: async (lobb) => ({
46
+ ...(await buildMeta(lobb)),
47
+ shareReadAccess: config.shareReadAccess ?? {},
48
+ }),
21
49
  openapi: openapi,
22
50
  };
23
51
  }
@@ -3,12 +3,14 @@ import Reports from "./lib/components/pages/reports/index.svelte";
3
3
  import SharedReport from "./lib/components/pages/shared_report/index.svelte";
4
4
  import QueryAiButton from "./lib/components/dv_fields_buttons/query_ai_button/index.svelte";
5
5
 
6
+ export { default as ReportBody } from "./lib/components/reportBody.svelte";
7
+
6
8
  export default function extension(utils: ExtensionUtils): Extension {
7
9
  return {
8
10
  name: "reports",
9
11
  components: {
10
12
  "dvFields.topRight.reports_charts.query": QueryAiButton,
11
- "pages.analytics": Reports,
13
+ "pages.dashboards": Reports,
12
14
  // Lives at /studio/public/reports/shared_report — no auth required, the
13
15
  // page itself swaps the LobbClient bearer to the share_token in the URL.
14
16
  "publicPages.shared_report": SharedReport,
@@ -16,9 +18,9 @@ export default function extension(utils: ExtensionUtils): Extension {
16
18
  dashboardNavs: {
17
19
  middle: [
18
20
  {
19
- href: "/studio/extensions/reports/analytics",
21
+ href: "/studio/extensions/reports/dashboards",
20
22
  icon: utils.components.Icons.ChartNoAxesCombined,
21
- label: "Analytics",
23
+ label: "Dashboards",
22
24
  represents: "reports_dashboards",
23
25
  },
24
26
  ],
@@ -9,6 +9,7 @@
9
9
  column?: number;
10
10
  cellHeight?: string;
11
11
  editable?: boolean;
12
+ allowEdit?: boolean;
12
13
  onLayoutChange?: (layout: LayoutItem[]) => void;
13
14
  children: Snippet;
14
15
  }
@@ -17,6 +18,7 @@
17
18
  column = 12,
18
19
  cellHeight = "150px",
19
20
  editable = $bindable(false),
21
+ allowEdit = false,
20
22
  onLayoutChange,
21
23
  children,
22
24
  }: Props = $props();
@@ -29,7 +31,10 @@
29
31
  let containerWidth = $state(0);
30
32
  let saveTimeout: ReturnType<typeof setTimeout> | null = null;
31
33
 
32
- let editMode = $derived(containerWidth >= 1200);
34
+ // Edit mode only ever turns on when the parent explicitly opts in.
35
+ // Without allowEdit, container width is irrelevant — the grid stays
36
+ // disabled forever.
37
+ let editMode = $derived(allowEdit && containerWidth >= 1200);
33
38
 
34
39
  // Keep editable in sync so parent can read it
35
40
  $effect(() => { editable = editMode; });
@@ -112,8 +117,10 @@
112
117
  });
113
118
  </script>
114
119
 
115
- <div class="grid-stack" bind:this={gridEl}>
116
- {@render children()}
120
+ <div class="grid-stack-wrap">
121
+ <div class="grid-stack" bind:this={gridEl}>
122
+ {@render children()}
123
+ </div>
117
124
  </div>
118
125
 
119
126
  <style>
@@ -122,9 +129,14 @@
122
129
  position: absolute;
123
130
  }
124
131
 
132
+ .grid-stack-wrap {
133
+ overflow-x: hidden;
134
+ width: 100%;
135
+ }
136
+
125
137
  :global(.grid-stack) {
126
138
  background: transparent;
127
- padding: 0.5rem;
139
+ margin: 0 -0.5rem;
128
140
  }
129
141
 
130
142
  :global(.grid-stack-placeholder > .placeholder-content) {
@@ -14,6 +14,7 @@
14
14
 
15
15
  interface Props extends ExtensionProps {
16
16
  chartRecord: any;
17
+ showHeader?: boolean;
17
18
  onChartDeleted?: () => Promise<void>;
18
19
  onChartEdited?: () => Promise<void>;
19
20
  onChartClick?: (event: ChartClickEvent) => void;
@@ -21,6 +22,7 @@
21
22
 
22
23
  const {
23
24
  chartRecord,
25
+ showHeader = true,
24
26
  onChartDeleted,
25
27
  onChartEdited,
26
28
  onChartClick,
@@ -95,43 +97,47 @@
95
97
  }
96
98
  </script>
97
99
 
98
- <div class="gridContainer h-full rounded-md border bg-background">
99
- <div class="flex items-center justify-between border-b p-2 text-sm">
100
- <div class="flex items-center gap-1.5">
101
- <span>{chartRecord.title}</span>
102
- {#if chartRecord.description}
103
- <Tooltip.Root>
104
- <Tooltip.Trigger>
105
- <icons.CircleHelp class="text-muted-foreground" size={14} />
106
- </Tooltip.Trigger>
107
- <Tooltip.Content class="max-w-64 text-xs">
108
- {chartRecord.description}
109
- </Tooltip.Content>
110
- </Tooltip.Root>
111
- {/if}
112
- </div>
113
- <div class="flex">
114
- {#if canUpdate}
115
- <UpdateDetailViewButton
116
- collectionName="reports_charts"
117
- recordId={chartRecord.id}
118
- variant="ghost"
119
- class="h-7 w-7 text-muted-foreground hover:bg-transparent"
120
- Icon={icons.Pencil}
121
- onSuccessfullSave={handleEditOnSuccessfull}
122
- ></UpdateDetailViewButton>
123
- {/if}
124
- {#if canDelete}
125
- <Button
126
- class="h-6 w-6 text-muted-foreground hover:bg-transparent"
127
- variant="ghost"
128
- size="icon"
129
- Icon={icons.Trash}
130
- onclick={handleChartDelete}
131
- ></Button>
132
- {/if}
100
+ <div
101
+ class="grid h-full rounded-md border bg-background {showHeader ? 'grid-rows-[auto_1fr]' : 'grid-rows-[1fr]'}"
102
+ >
103
+ {#if showHeader}
104
+ <div class="flex items-center justify-between border-b p-2 text-sm">
105
+ <div class="flex items-center gap-1.5">
106
+ <span>{chartRecord.title}</span>
107
+ {#if chartRecord.description}
108
+ <Tooltip.Root>
109
+ <Tooltip.Trigger>
110
+ <icons.CircleHelp class="text-muted-foreground" size={14} />
111
+ </Tooltip.Trigger>
112
+ <Tooltip.Content class="max-w-64 text-xs">
113
+ {chartRecord.description}
114
+ </Tooltip.Content>
115
+ </Tooltip.Root>
116
+ {/if}
117
+ </div>
118
+ <div class="flex">
119
+ {#if canUpdate}
120
+ <UpdateDetailViewButton
121
+ collectionName="reports_charts"
122
+ recordId={chartRecord.id}
123
+ variant="ghost"
124
+ class="h-7 w-7 text-muted-foreground hover:bg-transparent"
125
+ Icon={icons.Pencil}
126
+ onSuccessfullSave={handleEditOnSuccessfull}
127
+ ></UpdateDetailViewButton>
128
+ {/if}
129
+ {#if canDelete}
130
+ <Button
131
+ class="h-6 w-6 text-muted-foreground hover:bg-transparent"
132
+ variant="ghost"
133
+ size="icon"
134
+ Icon={icons.Trash}
135
+ onclick={handleChartDelete}
136
+ ></Button>
137
+ {/if}
138
+ </div>
133
139
  </div>
134
- </div>
140
+ {/if}
135
141
  <div class="flex min-h-0 items-center justify-center overflow-hidden">
136
142
  <svelte:boundary>
137
143
  {#snippet failed(error, reset)}
@@ -183,10 +189,3 @@
183
189
  </svelte:boundary>
184
190
  </div>
185
191
  </div>
186
-
187
- <style>
188
- .gridContainer {
189
- display: grid;
190
- grid-template-rows: auto 1fr;
191
- }
192
- </style>
@@ -62,16 +62,24 @@
62
62
  async function handleShare() {
63
63
  sharing = true;
64
64
  try {
65
- // Snapshot grants exactly the read access a recipient needs to view
66
- // this one report — the dashboard row and the charts that belong to it.
67
- const permissions = {
65
+ const event = {
66
+ reportId: String(reportId),
67
+ report,
68
+ handled: false,
69
+ };
70
+ await utils.emitEvent("reports.share", event);
71
+ if (event.handled) return;
72
+
73
+ const basePermissions: Record<string, any> = {
68
74
  reports_dashboards: { read: { filter: { id: Number(reportId) } } },
69
75
  reports_charts: { read: { filter: { report_id: Number(reportId) } } },
70
- // TODO: the above ones are ok. the below ones are specific to only this risk project
71
- // can you please add them using a different way
72
- risk_config: true,
73
- risks: true,
74
76
  };
77
+ const shareReadAccess = (utils.ctx.meta?.extensions?.reports?.shareReadAccess ?? {}) as Record<string, any>;
78
+ const extraPermissions: Record<string, any> = {};
79
+ for (const [collectionName, readGrant] of Object.entries(shareReadAccess)) {
80
+ extraPermissions[collectionName] = { read: readGrant };
81
+ }
82
+ const permissions = { ...basePermissions, ...extraPermissions };
75
83
  const response = await utils.lobb.createOne("auth_shares", {
76
84
  label: `Share for: ${report?.name ?? "report"}`,
77
85
  permissions: JSON.stringify(permissions),
@@ -154,7 +162,7 @@
154
162
  <ReportBody
155
163
  {reportId}
156
164
  {utils}
157
- editable
165
+ allowEdit
158
166
  onLayoutChange={handleLayoutChange}
159
167
  onChartClick={handleChartClick}
160
168
  onReportLoaded={handleReportLoaded}
@@ -4,7 +4,7 @@
4
4
 
5
5
  const { utils, ...props }: ExtensionProps = $props();
6
6
 
7
- let extensionPagePath = "/studio/extensions/reports/analytics";
7
+ let extensionPagePath = "/studio/extensions/reports/dashboards";
8
8
  let reportId: string | null = $derived(utils.page.url.pathname.split("/")[5] ?? null);
9
9
  let sideBarData: any = $state(null);
10
10
  const {
@@ -56,7 +56,7 @@
56
56
 
57
57
  </script>
58
58
 
59
- <Sidebar title="Reports" data={sideBarData}>
59
+ <Sidebar title="Dashboards" data={sideBarData}>
60
60
  {#snippet belowSearch()}
61
61
  <div class="p-2">
62
62
  <CreateDetailViewButton
@@ -7,6 +7,8 @@
7
7
  interface Props extends ExtensionProps {
8
8
  reportId: string | number;
9
9
  editable?: boolean;
10
+ allowEdit?: boolean;
11
+ showChartHeaders?: boolean;
10
12
  onLayoutChange?: (changes: any[]) => void;
11
13
  onChartClick?: (event: any, elements: any[], chart: any) => void;
12
14
  onReportLoaded?: (report: any) => void;
@@ -16,6 +18,8 @@
16
18
  const {
17
19
  reportId,
18
20
  editable = false,
21
+ allowEdit = false,
22
+ showChartHeaders = true,
19
23
  onLayoutChange,
20
24
  onChartClick,
21
25
  onReportLoaded,
@@ -91,7 +95,7 @@
91
95
  </div>
92
96
  {:else}
93
97
  {#key charts}
94
- <GridStackComponent {editable} {onLayoutChange}>
98
+ <GridStackComponent {editable} {allowEdit} {onLayoutChange}>
95
99
  {#each [...charts].sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0)) as chart (chart.id)}
96
100
  <div
97
101
  class="grid-stack-item"
@@ -101,6 +105,7 @@
101
105
  <div class="grid-stack-item-content">
102
106
  <Chart
103
107
  chartRecord={chart}
108
+ showHeader={showChartHeaders}
104
109
  onChartDeleted={async () => await loadCharts()}
105
110
  onChartEdited={async () => await loadCharts()}
106
111
  {onChartClick}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobb-js/lobb-ext-reports",
3
- "version": "0.11.1",
3
+ "version": "0.12.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.32.1",
35
+ "@lobb-js/core": "^0.33.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.29.1",
46
+ "@lobb-js/studio": "^0.31.0",
47
47
  "@lucide/svelte": "^0.563.1",
48
48
  "@sveltejs/adapter-node": "^5.5.4",
49
49
  "@sveltejs/kit": "^2.60.1",