-
-
Notifications
You must be signed in to change notification settings - Fork 0
mdl published by org #405
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
mdl published by org #405
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,15 +58,29 @@ export function useDataSourceListCache() { | |
| (old: DataSourceByOrganisation[] | undefined) => | ||
| old?.map((ds) => | ||
| ds.id === dataSourceId | ||
| ? { ...ds, ...updater({ ...ds, organisationOverride: null }) } | ||
| ? { | ||
| ...ds, | ||
| ...updater({ | ||
| ...ds, | ||
| organisationName: "", | ||
| organisationOverride: null, | ||
| }), | ||
| } | ||
| : ds, | ||
| ), | ||
| ); | ||
| queryClient.setQueriesData( | ||
| { 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, | ||
| }), | ||
| }; | ||
|
Comment on lines
73
to
+83
|
||
| }, | ||
| ); | ||
| }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,6 @@ | ||||||||
| "use client"; | ||||||||
|
|
||||||||
| import { Settings } from "lucide-react"; | ||||||||
| import { BookOpen, Settings } from "lucide-react"; | ||||||||
|
||||||||
| import { BookOpen, Settings } from "lucide-react"; | |
| import { Settings } from "lucide-react"; |
Copilot
AI
Apr 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This UI shows "Published by" based solely on dataSource.organisationName. If dataSource.public is false (private/internal data sources in the inspector), this copy becomes misleading compared to the existing "Published" badge semantics elsewhere. Consider gating this on dataSource.public or adjusting the copy to not imply publication.
| Published by <span className="text-neutral-400">•</span>{" "} | |
| {dataSource.public ? "Published by" : "Provided by"}{" "} | |
| <span className="text-neutral-400">•</span>{" "} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 ( | ||
| <span | ||
| className={cn( | ||
| "inline-flex items-center gap-1 whitespace-nowrap text-muted-foreground", | ||
| compact ? "text-[11px]" : "text-xs", | ||
| )} | ||
| > | ||
| Published by | ||
| <span className="text-neutral-400">•</span> | ||
| <span className="truncate">{name}</span> | ||
| </span> | ||
| ); | ||
| } | ||
|
Comment on lines
+69
to
+90
|
||
|
|
||
| export interface DataSourceItemProps { | ||
| dataSource: DataSourceWithImportInfo; | ||
| className?: string; | ||
|
|
@@ -235,15 +258,23 @@ export function DataSourceItem({ | |
| </p> | ||
| )} | ||
|
|
||
| {(lastImportedText || dataSource.createdAt) && ( | ||
| <div className="flex items-center gap-4"> | ||
| <div className="mt-1"> | ||
| <LastImportedOrDateAddedMeta | ||
| lastImportedText={lastImportedText} | ||
| createdAt={dataSource.createdAt} | ||
| <OrganisationMeta | ||
| organisationName={dataSource.organisationName} | ||
| compact | ||
| /> | ||
| </div> | ||
| )} | ||
| {(lastImportedText || dataSource.createdAt) && ( | ||
| <div className="mt-1"> | ||
| <LastImportedOrDateAddedMeta | ||
| lastImportedText={lastImportedText} | ||
| createdAt={dataSource.createdAt} | ||
| compact | ||
| /> | ||
| </div> | ||
| )} | ||
| </div> | ||
|
|
||
| {columnPills.length > 0 && columnPreviewVariant === "pills" && ( | ||
| <div className="flex flex-wrap gap-1.5 mt-2"> | ||
|
|
@@ -405,6 +436,13 @@ export function DataSourceItem({ | |
| )} | ||
| </div> | ||
| ) : null} | ||
|
|
||
| <div className="col-span-2 pt-1"> | ||
| <OrganisationMeta | ||
| organisationName={dataSource.organisationName} | ||
| compact | ||
| /> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
|
|
@@ -463,6 +501,8 @@ export function DataSourceItem({ | |
| </span> | ||
| )} | ||
| </div> | ||
|
|
||
| <OrganisationMeta organisationName={dataSource.organisationName} /> | ||
| </div> | ||
|
|
||
| {showColumnPreview && columnPreviewVariant === "text" ? ( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,7 +28,6 @@ import { | |
| deleteDataSource, | ||
| findDataSourceById, | ||
| findDataSourcesByIds, | ||
| findPublicDataSources, | ||
| getJobInfo, | ||
| getUniqueColumnValues, | ||
| updateDataSource, | ||
|
|
@@ -57,15 +56,25 @@ import { | |
| router, | ||
| superadminProcedure, | ||
| } from "../index"; | ||
| import type { DataSource } from "@/models/DataSource"; | ||
| import type { DataSourceEvent } from "@/server/events"; | ||
| 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); | ||
|
Comment on lines
+65
to
72
|
||
| return withImportInfo.map((ds) => ({ ...ds, organisationOverride: null })); | ||
| return withImportInfo.map((ds) => ({ | ||
| ...ds, | ||
| organisationName: ds.organisationName ?? "", | ||
| organisationOverride: null, | ||
| })); | ||
| }), | ||
| updateDefaultInspectorConfig: superadminProcedure | ||
| .input( | ||
|
|
@@ -100,7 +109,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 +165,7 @@ export const dataSourceRouter = router({ | |
| return eb.or(filter); | ||
| }) | ||
| .selectAll("dataSource") | ||
| .select(["organisation.name as organisationName"]) | ||
| .execute(); | ||
|
|
||
| const orgId = input?.activeOrganisationId; | ||
|
|
@@ -162,6 +191,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 +202,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 +237,7 @@ export const dataSourceRouter = router({ | |
| ]); | ||
| return { | ||
| ...ctx.dataSource, | ||
| organisationName: organisation?.name ?? "", | ||
| config: { | ||
| ...ctx.dataSource.config, | ||
| __SERIALIZE_CREDENTIALS: true, | ||
|
|
@@ -760,7 +802,7 @@ export const dataSourceRouter = router({ | |
| }), | ||
| }); | ||
|
|
||
| const addImportInfo = async (dataSources: DataSource[]) => { | ||
| const addImportInfo = async <T extends { id: string }>(dataSources: T[]) => { | ||
| // Get import info for all data sources | ||
| const importInfos = await Promise.all( | ||
| dataSources.map((dataSource) => | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In
updateDataSource, the wrapper passed toupdater()overwritesorganisationNamewith an empty string before calling the updater. If the updater returns{ ...ds, ... }(common pattern), this will clear an existing organisation name in the cache until the next refetch. Preserve the existingorganisationNamewhen present (only default it when missing).