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) =>