From c6c24987e31c2325f702ccf8c4dcbd891353cb64 Mon Sep 17 00:00:00 2001 From: Arbyhisenaj <41119392+Arbyhisenaj@users.noreply.github.com> Date: Wed, 1 Apr 2026 16:06:31 +0100 Subject: [PATCH] mdl published by org --- .../components/DefaultInspectorPreview.tsx | 8 +++ .../superadmin/data-sources/[id]/page.tsx | 3 ++ .../(dashboards)/superadmin/page.tsx | 2 + .../(private)/hooks/useDataSourceListCache.ts | 18 ++++++- .../InspectorPanel/DataRecordsPanel.tsx | 12 ++++- src/components/DataSourceItem.tsx | 52 ++++++++++++++++-- src/server/trpc/routers/dataSource.ts | 54 +++++++++++++++++-- 7 files changed, 136 insertions(+), 13 deletions(-) diff --git a/src/app/(private)/(dashboards)/superadmin/data-sources/[id]/components/DefaultInspectorPreview.tsx b/src/app/(private)/(dashboards)/superadmin/data-sources/[id]/components/DefaultInspectorPreview.tsx index 96b31332..ac291140 100644 --- a/src/app/(private)/(dashboards)/superadmin/data-sources/[id]/components/DefaultInspectorPreview.tsx +++ b/src/app/(private)/(dashboards)/superadmin/data-sources/[id]/components/DefaultInspectorPreview.tsx @@ -3,6 +3,7 @@ import { useQuery } from "@tanstack/react-query"; import DataRecordsPanel from "@/app/(private)/map/[id]/components/InspectorPanel/DataRecordsPanel"; import { useInspectorDataSourceConfig } from "@/app/(private)/map/[id]/hooks/useInspectorDataSourceConfig"; +import { useDataSources } from "@/hooks/useDataSources"; import { useTRPC } from "@/services/trpc/react"; import { cn } from "@/shadcn/utils"; @@ -16,6 +17,8 @@ export function DefaultInspectorPreview({ const trpc = useTRPC(); const inspectorConfig = useInspectorDataSourceConfig(dataSourceId); + const { data: dataSources } = useDataSources(); + const dataSource = dataSources?.find((ds) => ds.id === dataSourceId); const selectedCount = inspectorConfig?.items.filter((i) => i.type === "column").length ?? 0; @@ -58,6 +61,11 @@ export function DefaultInspectorPreview({ /> )} +
+

+ Organisation: {dataSource?.organisationName ?? "—"} +

+
); } diff --git a/src/app/(private)/(dashboards)/superadmin/data-sources/[id]/page.tsx b/src/app/(private)/(dashboards)/superadmin/data-sources/[id]/page.tsx index 658e8ced..117ac0b3 100644 --- a/src/app/(private)/(dashboards)/superadmin/data-sources/[id]/page.tsx +++ b/src/app/(private)/(dashboards)/superadmin/data-sources/[id]/page.tsx @@ -163,6 +163,9 @@ export default function DataSourceConfigPage() {

{dataSource.name}

+

+ Organisation: {dataSource.organisationName ?? "—"} +

Configure default inspector settings for this public data source.

diff --git a/src/app/(private)/(dashboards)/superadmin/page.tsx b/src/app/(private)/(dashboards)/superadmin/page.tsx index b97fc4d4..e3d4f54d 100644 --- a/src/app/(private)/(dashboards)/superadmin/page.tsx +++ b/src/app/(private)/(dashboards)/superadmin/page.tsx @@ -311,6 +311,7 @@ export default function SuperadminPage() { Name + Organisation Record type Records Inspector config @@ -331,6 +332,7 @@ export default function SuperadminPage() { publicDataSources?.map((ds) => ( {ds.name} + {ds.organisationName ?? "-"} {ds.recordType} {ds.recordCount.toLocaleString()} diff --git a/src/app/(private)/hooks/useDataSourceListCache.ts b/src/app/(private)/hooks/useDataSourceListCache.ts index ccb63a7b..0cffeb57 100644 --- a/src/app/(private)/hooks/useDataSourceListCache.ts +++ b/src/app/(private)/hooks/useDataSourceListCache.ts @@ -58,7 +58,14 @@ export function useDataSourceListCache() { (old: DataSourceByOrganisation[] | undefined) => old?.map((ds) => ds.id === dataSourceId - ? { ...ds, ...updater({ ...ds, organisationOverride: null }) } + ? { + ...ds, + ...updater({ + ...ds, + organisationName: "", + organisationOverride: null, + }), + } : ds, ), ); @@ -66,7 +73,14 @@ export function useDataSourceListCache() { { queryKey: trpc.dataSource.byId.queryKey() }, (old: DataSourceById | undefined) => { if (!old || old.id !== dataSourceId) return old; - return { ...old, ...updater({ ...old, organisationOverride: null }) }; + return { + ...old, + ...updater({ + ...old, + organisationName: "", + organisationOverride: null, + }), + }; }, ); }, diff --git a/src/app/(private)/map/[id]/components/InspectorPanel/DataRecordsPanel.tsx b/src/app/(private)/map/[id]/components/InspectorPanel/DataRecordsPanel.tsx index ffc79c40..c6263212 100644 --- a/src/app/(private)/map/[id]/components/InspectorPanel/DataRecordsPanel.tsx +++ b/src/app/(private)/map/[id]/components/InspectorPanel/DataRecordsPanel.tsx @@ -1,6 +1,6 @@ "use client"; -import { Settings } from "lucide-react"; +import { BookOpen, BookOpenIcon, Settings } from "lucide-react"; import TogglePanel from "@/app/(private)/map/[id]/components/TogglePanel"; import DataSourceIcon from "@/components/DataSourceIcon"; import IconButtonWithTooltip from "@/components/IconButtonWithTooltip"; @@ -102,6 +102,16 @@ export default function DataRecordsPanel({ ))} )} + + {dataSource?.organisationName && ( +
+ +

+ Published by {" "} + {dataSource.organisationName} +

+
+ )} ); diff --git a/src/components/DataSourceItem.tsx b/src/components/DataSourceItem.tsx index 2c3b4c69..8145e91b 100644 --- a/src/components/DataSourceItem.tsx +++ b/src/components/DataSourceItem.tsx @@ -66,6 +66,29 @@ function LastImportedOrDateAddedMeta({ return null; } +function OrganisationMeta({ + organisationName, + compact, +}: { + organisationName: string | null | undefined; + compact?: boolean; +}) { + const name = organisationName?.trim(); + if (!name) return null; + return ( + + Published by + + {name} + + ); +} + export interface DataSourceItemProps { dataSource: DataSourceWithImportInfo; className?: string; @@ -235,15 +258,25 @@ export function DataSourceItem({

)} - {(lastImportedText || dataSource.createdAt) && ( +
+ +
-
- )} + {(lastImportedText || dataSource.createdAt) && ( +
+ +
+ )} +
{columnPills.length > 0 && columnPreviewVariant === "pills" && (
@@ -405,6 +438,13 @@ export function DataSourceItem({ )}
) : null} + +
+ +
); @@ -463,6 +503,8 @@ export function DataSourceItem({ )} + + {showColumnPreview && columnPreviewVariant === "text" ? ( diff --git a/src/server/trpc/routers/dataSource.ts b/src/server/trpc/routers/dataSource.ts index 28a25b35..c8aa17f5 100644 --- a/src/server/trpc/routers/dataSource.ts +++ b/src/server/trpc/routers/dataSource.ts @@ -63,9 +63,20 @@ import type { DataSourceUpdate } from "@/server/models/DataSource"; export const dataSourceRouter = router({ listPublic: superadminProcedure.query(async () => { - const dataSources = await findPublicDataSources(); + const dataSources = await db + .selectFrom("dataSource") + .leftJoin("organisation", "dataSource.organisationId", "organisation.id") + .where("dataSource.public", "=", true) + .selectAll("dataSource") + .select(["organisation.name as organisationName"]) + .execute(); + const withImportInfo = await addImportInfo(dataSources); - return withImportInfo.map((ds) => ({ ...ds, organisationOverride: null })); + return withImportInfo.map((ds) => ({ + ...ds, + organisationName: ds.organisationName ?? "", + organisationOverride: null, + })); }), updateDefaultInspectorConfig: superadminProcedure .input( @@ -100,7 +111,26 @@ export const dataSourceRouter = router({ const ids = getVisualisedDataSourceIds(map.config, view); if (!ids.length) return []; const dataSources = await findDataSourcesByIds(ids); - const withImportInfo = await addImportInfo(dataSources); + const organisationIds = [ + ...new Set(dataSources.map((ds) => ds.organisationId).filter(Boolean)), + ]; + const organisations = + organisationIds.length > 0 + ? await db + .selectFrom("organisation") + .where("id", "in", organisationIds) + .select(["id", "name"]) + .execute() + : []; + const organisationNameById = new Map( + organisations.map((o) => [o.id, o.name ?? null]), + ); + const withOrg = dataSources.map((ds) => ({ + ...ds, + organisationName: organisationNameById.get(ds.organisationId) ?? "", + })); + + const withImportInfo = await addImportInfo(withOrg); return withImportInfo.map((ds) => ({ ...ds, organisationOverride: null, @@ -137,6 +167,7 @@ export const dataSourceRouter = router({ return eb.or(filter); }) .selectAll("dataSource") + .select(["organisation.name as organisationName"]) .execute(); const orgId = input?.activeOrganisationId; @@ -162,6 +193,7 @@ export const dataSourceRouter = router({ const withImportInfo = await addImportInfo(filteredDataSources); return withImportInfo.map((ds) => ({ ...ds, + organisationName: ds.organisationName ?? "", organisationOverride: overrideMap.get(ds.id) ?? null, })); }), @@ -172,9 +204,20 @@ export const dataSourceRouter = router({ .selectAll("dataSource") .execute(); - return addImportInfo(dataSources); + const withOrg = dataSources.map((ds) => ({ + ...ds, + organisationName: ctx.organisation.name ?? "", + })); + + return addImportInfo(withOrg); }), byId: dataSourceOwnerProcedure.query(async ({ ctx }) => { + const organisation = await db + .selectFrom("organisation") + .where("id", "=", ctx.dataSource.organisationId) + .select(["name"]) + .executeTakeFirst(); + const recordCount = await db .selectFrom("dataRecord") .where("dataSourceId", "=", ctx.dataSource.id) @@ -196,6 +239,7 @@ export const dataSourceRouter = router({ ]); return { ...ctx.dataSource, + organisationName: organisation?.name ?? "", config: { ...ctx.dataSource.config, __SERIALIZE_CREDENTIALS: true, @@ -760,7 +804,7 @@ export const dataSourceRouter = router({ }), }); -const addImportInfo = async (dataSources: DataSource[]) => { +const addImportInfo = async (dataSources: T[]) => { // Get import info for all data sources const importInfos = await Promise.all( dataSources.map((dataSource) =>