From c8f22e0fe726918e6949cfeb3aa90a25c37304e8 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 13 Aug 2025 11:10:43 +0200 Subject: [PATCH 01/80] Rename --- .../[projectId]/explore/{ => browse}/[type]/[id]/page.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/app/app/v2/[virtualLabId]/[projectId]/explore/{ => browse}/[type]/[id]/page.tsx (100%) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/[type]/[id]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/browse/[type]/[id]/page.tsx similarity index 100% rename from src/app/app/v2/[virtualLabId]/[projectId]/explore/[type]/[id]/page.tsx rename to src/app/app/v2/[virtualLabId]/[projectId]/explore/browse/[type]/[id]/page.tsx From 70f35808d71cdaf7a46c25b2a37db2443e0f38ed Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 13 Aug 2025 11:47:12 +0200 Subject: [PATCH 02/80] div --- .../[projectId]/explore/browse/[type]/[id]/page.tsx | 0 .../[projectId]/explore/view/[type]/[id]/layout.tsx | 7 +++++++ .../[projectId]/explore/view/[type]/[id]/page.tsx | 3 +++ 3 files changed, 10 insertions(+) delete mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/browse/[type]/[id]/page.tsx create mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx create mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/page.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/browse/[type]/[id]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/browse/[type]/[id]/page.tsx deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx new file mode 100644 index 000000000..ba6fbdc52 --- /dev/null +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -0,0 +1,7 @@ +import { ReactNode } from 'react'; + +export default function Layout({ children }: { children: ReactNode }) { + return ( +
{children}
+ ); +} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/page.tsx new file mode 100644 index 000000000..4f3000ecf --- /dev/null +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/page.tsx @@ -0,0 +1,3 @@ +export default function DetailView() { + return 'here'; +} From 96c0a7a3c90b2a33d58e2d8a7963a85f2a51e3ce Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 13 Aug 2025 16:33:53 +0200 Subject: [PATCH 03/80] breadcrumbs --- lefthook.yml | 2 +- .../explore/view/[type]/[id]/layout.tsx | 31 ++++++++++++++++++- src/ui/molecules/breadcrumb.tsx | 21 +++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/ui/molecules/breadcrumb.tsx diff --git a/lefthook.yml b/lefthook.yml index 498ff5529..407bfe5ee 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -1,4 +1,4 @@ -pre-commit: +pre-push: parallel: true commands: eslint: diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index ba6fbdc52..2e5c219b2 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -1,7 +1,36 @@ +'use client'; + import { ReactNode } from 'react'; +import NextLink from 'next/link'; +import { useParams } from 'next/navigation'; +import Breadcrumb from '@/ui/molecules/breadcrumb'; +import { basePath } from '@/config'; +import useWorkspace from '@/ui/hooks/use-workspace'; export default function Layout({ children }: { children: ReactNode }) { + const { virtualLabId, projectId } = useWorkspace(); + const { type } = useParams(); + return ( -
{children}
+
+
+
+ + + Explore + + + + + {type} + + + One +
+
+
{children}
+
); } diff --git a/src/ui/molecules/breadcrumb.tsx b/src/ui/molecules/breadcrumb.tsx new file mode 100644 index 000000000..78bc37943 --- /dev/null +++ b/src/ui/molecules/breadcrumb.tsx @@ -0,0 +1,21 @@ +import { RightOutlined } from '@ant-design/icons'; +import { ReactNode } from 'react'; + +export default function Breadcrumb({ + children, + showChevron = true, +}: { + children?: ReactNode; + showChevron?: boolean; +}) { + return ( +
+ {children} + {showChevron && ( +
+ +
+ )} +
+ ); +} From 04857c9a004c9d19d9974557d7732d6457f90771 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 13 Aug 2025 17:11:08 +0200 Subject: [PATCH 04/80] Fetch entity --- .../explore/view/[type]/[id]/layout.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index 2e5c219b2..00395f836 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -2,14 +2,27 @@ import { ReactNode } from 'react'; import NextLink from 'next/link'; -import { useParams } from 'next/navigation'; +import { notFound, useParams } from 'next/navigation'; import Breadcrumb from '@/ui/molecules/breadcrumb'; import { basePath } from '@/config'; -import useWorkspace from '@/ui/hooks/use-workspace'; +import { useWorkspace } from '@/ui/hooks/use-workspace'; +import { getEntityByExtendedType } from '@/entity-configuration/domain/helpers'; +import { TExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; export default function Layout({ children }: { children: ReactNode }) { const { virtualLabId, projectId } = useWorkspace(); - const { type } = useParams(); + const { type, id } = useParams(); + + if (!type || !id) notFound(); + + const entityType = getEntityByExtendedType({ type: type as TExtendedEntitiesTypeDict }); + if (!entityType) notFound(); + + const fetchEntity = entityType.api.query.one; + const entity = + fetchEntity && (await fetchEntity({ id: id as string, context: { virtualLabId, projectId } })); + + if (!entity) notFound(); return (
From 70787c00241d0a58ce7fb705067e3e752be94859 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 13 Aug 2025 17:50:29 +0200 Subject: [PATCH 05/80] breadcrumbs --- .../explore/view/[type]/[id]/layout.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index 00395f836..ece91502f 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -1,6 +1,7 @@ 'use client'; import { ReactNode } from 'react'; +import { useQuery } from '@tanstack/react-query'; import NextLink from 'next/link'; import { notFound, useParams } from 'next/navigation'; import Breadcrumb from '@/ui/molecules/breadcrumb'; @@ -19,14 +20,19 @@ export default function Layout({ children }: { children: ReactNode }) { if (!entityType) notFound(); const fetchEntity = entityType.api.query.one; - const entity = - fetchEntity && (await fetchEntity({ id: id as string, context: { virtualLabId, projectId } })); - if (!entity) notFound(); + if (!fetchEntity) throw Error(`No fetch one function defined for type ${entityType}`); + + const { data, error } = useQuery({ + queryKey: [id as string], + queryFn: async () => fetchEntity({ id: id as string, context: { virtualLabId, projectId } }), + }); + + if (error) notFound(); return (
-
+
@@ -40,7 +46,7 @@ export default function Layout({ children }: { children: ReactNode }) { {type} - One + {data?.name}
{children}
From 811681fa552fe226a6085442f670fa0ea6b422de Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 13 Aug 2025 19:23:52 +0200 Subject: [PATCH 06/80] fetch on server --- .../explore/view/[type]/[id]/layout.tsx | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index ece91502f..8c9b7662f 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -1,18 +1,26 @@ -'use client'; - import { ReactNode } from 'react'; -import { useQuery } from '@tanstack/react-query'; import NextLink from 'next/link'; -import { notFound, useParams } from 'next/navigation'; +import { notFound } from 'next/navigation'; import Breadcrumb from '@/ui/molecules/breadcrumb'; import { basePath } from '@/config'; -import { useWorkspace } from '@/ui/hooks/use-workspace'; import { getEntityByExtendedType } from '@/entity-configuration/domain/helpers'; import { TExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; -export default function Layout({ children }: { children: ReactNode }) { - const { virtualLabId, projectId } = useWorkspace(); - const { type, id } = useParams(); +interface Params { + virtualLabId: string; + projectId: string; + id: string; + type: string; +} + +export default async function Layout({ + children, + params, +}: { + children: ReactNode; + params: Promise; +}) { + const { virtualLabId, projectId, type, id } = await params; if (!type || !id) notFound(); @@ -23,12 +31,8 @@ export default function Layout({ children }: { children: ReactNode }) { if (!fetchEntity) throw Error(`No fetch one function defined for type ${entityType}`); - const { data, error } = useQuery({ - queryKey: [id as string], - queryFn: async () => fetchEntity({ id: id as string, context: { virtualLabId, projectId } }), - }); - - if (error) notFound(); + const entity = await fetchEntity({ id, context: { virtualLabId, projectId } }); + if (!entity) notFound(); return (
@@ -46,7 +50,7 @@ export default function Layout({ children }: { children: ReactNode }) { {type} - {data?.name} + {entity.name}
{children}
From f1fed6fca3af82505401837c5165b49c28238ea0 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Thu, 14 Aug 2025 18:26:33 +0200 Subject: [PATCH 07/80] Use slug --- .../explore/view/[type]/[id]/layout.tsx | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index 8c9b7662f..fbcf7caf1 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -3,14 +3,14 @@ import NextLink from 'next/link'; import { notFound } from 'next/navigation'; import Breadcrumb from '@/ui/molecules/breadcrumb'; import { basePath } from '@/config'; -import { getEntityByExtendedType } from '@/entity-configuration/domain/helpers'; -import { TExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; +import { getEntityBySlug } from '@/entity-configuration/domain/helpers'; +import { EntitySlugValue } from '@/entity-configuration/domain/slug'; interface Params { virtualLabId: string; projectId: string; id: string; - type: string; + type: EntitySlugValue; } export default async function Layout({ @@ -24,14 +24,22 @@ export default async function Layout({ if (!type || !id) notFound(); - const entityType = getEntityByExtendedType({ type: type as TExtendedEntitiesTypeDict }); + const entityType = getEntityBySlug({ slug: type }); if (!entityType) notFound(); const fetchEntity = entityType.api.query.one; if (!fetchEntity) throw Error(`No fetch one function defined for type ${entityType}`); + type AwaitedType = T extends Promise ? U : T; + + let entity: AwaitedType> | undefined; + + try { + entity = await fetchEntity({ id, context: { virtualLabId, projectId } }); + } catch { + notFound(); + } - const entity = await fetchEntity({ id, context: { virtualLabId, projectId } }); if (!entity) notFound(); return ( @@ -47,7 +55,7 @@ export default async function Layout({ - {type} + {entityType.title} {entity.name} From 3f19f5d30446d3c58bf153f1796c6362ec70c360 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Thu, 14 Aug 2025 19:30:41 +0200 Subject: [PATCH 08/80] navigation --- .../view/[type]/[id]/analysis/page.tsx | 3 ++ .../view/[type]/[id]/artifacts/page.tsx | 3 ++ .../explore/view/[type]/[id]/layout.tsx | 4 +++ .../view/[type]/[id]/overview/page.tsx | 3 ++ .../explore/view/[type]/[id]/page.tsx | 9 ++++-- .../view/[type]/[id]/publications/page.tsx | 3 ++ src/ui/molecules/tab.tsx | 28 ++++++++++++++++++ src/ui/segments/explore/detail-menu.tsx | 29 +++++++++++++++++++ 8 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/analysis/page.tsx create mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/artifacts/page.tsx create mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx create mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/publications/page.tsx create mode 100644 src/ui/molecules/tab.tsx create mode 100644 src/ui/segments/explore/detail-menu.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/analysis/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/analysis/page.tsx new file mode 100644 index 000000000..55f2f38df --- /dev/null +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/analysis/page.tsx @@ -0,0 +1,3 @@ +export default function Analysis() { + return 'analysis'; +} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/artifacts/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/artifacts/page.tsx new file mode 100644 index 000000000..7595c9aa0 --- /dev/null +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/artifacts/page.tsx @@ -0,0 +1,3 @@ +export default function Artifacts() { + return 'artifacts'; +} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index fbcf7caf1..d1cf4df4a 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -5,6 +5,7 @@ import Breadcrumb from '@/ui/molecules/breadcrumb'; import { basePath } from '@/config'; import { getEntityBySlug } from '@/entity-configuration/domain/helpers'; import { EntitySlugValue } from '@/entity-configuration/domain/slug'; +import DetailMenu from '@/ui/segments/explore/detail-menu'; interface Params { virtualLabId: string; @@ -60,6 +61,9 @@ export default async function Layout({ {entity.name}
+
+ +
{children}
diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx new file mode 100644 index 000000000..64dd56bb2 --- /dev/null +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx @@ -0,0 +1,3 @@ +export default function Overview() { + return 'page'; +} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/page.tsx index 4f3000ecf..31d105b90 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/page.tsx @@ -1,3 +1,8 @@ -export default function DetailView() { - return 'here'; +'use client'; + +import { redirect, usePathname } from 'next/navigation'; + +export default function DetailPage() { + const path = usePathname(); + redirect(`${path}/overview`); } diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/publications/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/publications/page.tsx new file mode 100644 index 000000000..c320ef5c5 --- /dev/null +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/publications/page.tsx @@ -0,0 +1,3 @@ +export default function Publications() { + return 'publications'; +} diff --git a/src/ui/molecules/tab.tsx b/src/ui/molecules/tab.tsx new file mode 100644 index 000000000..728152f41 --- /dev/null +++ b/src/ui/molecules/tab.tsx @@ -0,0 +1,28 @@ +import { RightOutlined } from '@ant-design/icons'; +import { ReactNode } from 'react'; +import { cn } from '@/utils/css-class'; + +export default function Tab({ + children, + highlight, + href, +}: { + children: ReactNode; + highlight: boolean; + href: string; +}) { + return ( + + {children} +
+ +
+
+ ); +} diff --git a/src/ui/segments/explore/detail-menu.tsx b/src/ui/segments/explore/detail-menu.tsx new file mode 100644 index 000000000..02e585c46 --- /dev/null +++ b/src/ui/segments/explore/detail-menu.tsx @@ -0,0 +1,29 @@ +'use client'; + +import { usePathname } from 'next/navigation'; +import Tab from '@/ui/molecules/tab'; + +export default function DetailMenu() { + const path = usePathname(); + const parentPath = path.split('/').slice(0, -1).join('/'); + const page = path.split('/').pop(); + + console.log(parentPath) + + return ( + <> + + Overview + + + Analysis + + + Related Publications + + + Related Artifacts + + + ); +} From 63ae4b1df353f1e8ea478f8ad42a488ee23fb292 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Thu, 14 Aug 2025 19:40:39 +0200 Subject: [PATCH 09/80] root redirect to overview --- .../explore/view/[type]/{ => (root)}/[id]/page.tsx | 0 src/ui/molecules/tab.tsx | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/{ => (root)}/[id]/page.tsx (100%) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/(root)/[id]/page.tsx similarity index 100% rename from src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/page.tsx rename to src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/(root)/[id]/page.tsx diff --git a/src/ui/molecules/tab.tsx b/src/ui/molecules/tab.tsx index 728152f41..70dc19e02 100644 --- a/src/ui/molecules/tab.tsx +++ b/src/ui/molecules/tab.tsx @@ -15,13 +15,13 @@ export default function Tab({ {children}
- +
); From d1d31d136c3571c211aa6c529cda40ef7ed29711 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Fri, 15 Aug 2025 18:05:55 +0200 Subject: [PATCH 10/80] actions --- .../explore/view/[type]/[id]/layout.tsx | 9 +++++++++ src/ui/molecules/side-menu-action.tsx | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 src/ui/molecules/side-menu-action.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index d1cf4df4a..65a1a046e 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -6,6 +6,8 @@ import { basePath } from '@/config'; import { getEntityBySlug } from '@/entity-configuration/domain/helpers'; import { EntitySlugValue } from '@/entity-configuration/domain/slug'; import DetailMenu from '@/ui/segments/explore/detail-menu'; +import Action from '@/ui/molecules/side-menu-action'; +import { CopyOutlined } from '@ant-design/icons'; interface Params { virtualLabId: string; @@ -64,6 +66,13 @@ export default async function Layout({
+
+ Copy Id + {/*
Simulate
+
Clone Model
+
Bookmark
+
Download
*/} +
{children}
diff --git a/src/ui/molecules/side-menu-action.tsx b/src/ui/molecules/side-menu-action.tsx new file mode 100644 index 000000000..ca4a1ebe8 --- /dev/null +++ b/src/ui/molecules/side-menu-action.tsx @@ -0,0 +1,19 @@ +import { ReactNode } from 'react'; +import { AntdIconProps } from '@ant-design/icons/lib/components/AntdIcon'; + +export default function Action({ + children, + Icon +}: { + children: ReactNode; + Icon: React.ComponentType; +}) { + return ( +
+
{children}
+
+ +
+
+ ); +} From 8f62e9d707c48171038c45c11fb2a2aec3356184 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 18 Aug 2025 16:42:38 +0200 Subject: [PATCH 11/80] copy action --- .../explore/view/[type]/[id]/layout.tsx | 11 +---- src/ui/molecules/side-menu-action.tsx | 12 ++---- src/ui/segments/action-menu.tsx | 40 +++++++++++++++++++ 3 files changed, 45 insertions(+), 18 deletions(-) create mode 100644 src/ui/segments/action-menu.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index 65a1a046e..88f62052b 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -6,8 +6,7 @@ import { basePath } from '@/config'; import { getEntityBySlug } from '@/entity-configuration/domain/helpers'; import { EntitySlugValue } from '@/entity-configuration/domain/slug'; import DetailMenu from '@/ui/segments/explore/detail-menu'; -import Action from '@/ui/molecules/side-menu-action'; -import { CopyOutlined } from '@ant-design/icons'; +import ActionMenu from '@/ui/segments/action-menu'; interface Params { virtualLabId: string; @@ -66,13 +65,7 @@ export default async function Layout({
-
- Copy Id - {/*
Simulate
-
Clone Model
-
Bookmark
-
Download
*/} -
+
{children}
diff --git a/src/ui/molecules/side-menu-action.tsx b/src/ui/molecules/side-menu-action.tsx index ca4a1ebe8..f26f8eef4 100644 --- a/src/ui/molecules/side-menu-action.tsx +++ b/src/ui/molecules/side-menu-action.tsx @@ -1,18 +1,12 @@ import { ReactNode } from 'react'; import { AntdIconProps } from '@ant-design/icons/lib/components/AntdIcon'; -export default function Action({ - children, - Icon -}: { - children: ReactNode; - Icon: React.ComponentType; -}) { +export default function Action({ children, icon }: { children: ReactNode; icon: ReactNode }) { return (
{children}
-
- +
+ {icon}
); diff --git a/src/ui/segments/action-menu.tsx b/src/ui/segments/action-menu.tsx new file mode 100644 index 000000000..5c4b6073a --- /dev/null +++ b/src/ui/segments/action-menu.tsx @@ -0,0 +1,40 @@ +'use client'; + +import { useState } from 'react'; +import { CopyOutlined } from '@ant-design/icons'; +import Action from '../molecules/side-menu-action'; + +import { EntityCoreIdentifiable } from '@/api/entitycore/types/shared/global'; + +export default function ActionMenu({ entity }: { entity: T }) { + const [copied, setCopied] = useState(false); + return ( +
+ { + if (copied) return; + setCopied(true); + navigator.clipboard.writeText(entity.id); + window.setTimeout(() => setCopied(false), 5000); + }} + /> + ) : ( + + check + + + ) + } + > + {copied ? 'Copied' : 'Copy ID'} + + {/*
Simulate
+
Clone Model
+
Bookmark
+
Download
*/} +
+ ); +} From 0cdf6bb1ed7b128b5afa2e2bc744c27b92c360b1 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 18 Aug 2025 16:59:26 +0200 Subject: [PATCH 12/80] Add actoins --- src/ui/segments/action-menu.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/ui/segments/action-menu.tsx b/src/ui/segments/action-menu.tsx index 5c4b6073a..4b4e6b634 100644 --- a/src/ui/segments/action-menu.tsx +++ b/src/ui/segments/action-menu.tsx @@ -1,7 +1,12 @@ 'use client'; import { useState } from 'react'; -import { CopyOutlined } from '@ant-design/icons'; +import { + BookOutlined, + CopyOutlined, + DownloadOutlined, + ExperimentOutlined, +} from '@ant-design/icons'; import Action from '../molecules/side-menu-action'; import { EntityCoreIdentifiable } from '@/api/entitycore/types/shared/global'; @@ -31,10 +36,9 @@ export default function ActionMenu({ entity }: > {copied ? 'Copied' : 'Copy ID'} - {/*
Simulate
-
Clone Model
-
Bookmark
-
Download
*/} + }>Simulate + }>Bookmark + }>Download
); } From 90ed1492f82a41b06f4f824f7bbd5424ffd59fb3 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 18 Aug 2025 18:18:46 +0200 Subject: [PATCH 13/80] working on bookmarks --- .../explore/view/[type]/[id]/layout.tsx | 7 +++++- src/ui/segments/action-menu.tsx | 25 +++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index 88f62052b..5c91247d1 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -65,7 +65,12 @@ export default async function Layout({
- +
{children}
diff --git a/src/ui/segments/action-menu.tsx b/src/ui/segments/action-menu.tsx index 4b4e6b634..bf53aee47 100644 --- a/src/ui/segments/action-menu.tsx +++ b/src/ui/segments/action-menu.tsx @@ -10,9 +10,30 @@ import { import Action from '../molecules/side-menu-action'; import { EntityCoreIdentifiable } from '@/api/entitycore/types/shared/global'; +import { useQuery } from '@tanstack/react-query'; +import { getAllBookmarksByCategory } from '@/api/virtual-lab-svc/queries/bookmark'; +import { TEntityCoreConfigurationItem } from '@/entity-configuration/domain'; -export default function ActionMenu({ entity }: { entity: T }) { +export default function ActionMenu({ + entity, + entityType, + isBookmarkable, + ctx, +}: { + entity: T; + entityType: TEntityCoreConfigurationItem['type']; + isBookmarkable: boolean; + ctx: { virtualLabId: string; projectId: string }; +}) { const [copied, setCopied] = useState(false); + + const bookmarks = useQuery({ + queryKey: [ctx.projectId, ctx.virtualLabId, entityType], + queryFn: async () => getAllBookmarksByCategory(ctx, { category: entityType }), + }); + + console.log(bookmarks.data); + return (
({ entity }: {copied ? 'Copied' : 'Copy ID'} }>Simulate - }>Bookmark + {isBookmarkable && }>Bookmark} }>Download
); From 664d2db6b2c8e97a4aadd3a99a567849d75c1beb Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 19 Aug 2025 16:14:57 +0200 Subject: [PATCH 14/80] add bookmark categories --- src/api/virtual-lab-svc/queries/bookmark.ts | 15 ++++++++++++++- .../domain/experimental/bouton-density.ts | 1 + .../experimental/electrical-cell-recording.ts | 1 + .../domain/experimental/neuron-density.ts | 1 + .../experimental/reconstruction-morphology.ts | 1 + .../domain/experimental/synapse-per-connection.ts | 1 + src/entity-configuration/domain/model/e-model.ts | 1 + src/entity-configuration/domain/model/me-model.ts | 1 + .../domain/simulation/simulation-campaign.ts | 1 + .../domain/simulation/single-neuron-simulation.ts | 1 + .../single-neuron-synaptome-simulation.ts | 1 + src/entity-configuration/domain/types.ts | 2 ++ 12 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/api/virtual-lab-svc/queries/bookmark.ts b/src/api/virtual-lab-svc/queries/bookmark.ts index 86004df39..2c0d50bb4 100644 --- a/src/api/virtual-lab-svc/queries/bookmark.ts +++ b/src/api/virtual-lab-svc/queries/bookmark.ts @@ -41,6 +41,19 @@ export async function bookmarkToProjectLibrary( }); } +export type BookmarkCategory = + | 'ExperimentalBoutonDensity' + | 'ExperimentalNeuronDensity' + | 'ExperimentalElectroPhysiology' + | 'ExperimentalSynapsePerConnection' + | 'ExperimentalNeuronMorphology' + | 'SimulationCampaign' + | 'CircuitEModel' + | 'CircuitMEModel' + | 'SingleNeuronSynaptome' + | 'SingleNeuronSimulation' + | 'SynaptomeSimulation'; + /** * Retrieves all bookmarks for a specific project, grouped by category. * @@ -50,7 +63,7 @@ export async function bookmarkToProjectLibrary( */ export async function getAllBookmarksByCategory( { virtualLabId, projectId }: WorkspaceContext, - { category }: { category?: TExtendedEntitiesTypeDict } + { category }: { category: BookmarkCategory } ): Promise { const api = await virtualLabRootApi(); const url = `${baseUri}/${virtualLabId}/projects/${projectId}/bookmarks`; diff --git a/src/entity-configuration/domain/experimental/bouton-density.ts b/src/entity-configuration/domain/experimental/bouton-density.ts index 0d79a91cb..47fbd291d 100644 --- a/src/entity-configuration/domain/experimental/bouton-density.ts +++ b/src/entity-configuration/domain/experimental/bouton-density.ts @@ -34,4 +34,5 @@ export const BoutonDensity: EntityCoreTypeConfig = { }, // viewDefinition: ViewsDefinitionRegistry[DataType.ExperimentalBoutonDensity], isBookmarkable: true, + bookmarkCategory: 'ExperimentalBoutonDensity', } as const; diff --git a/src/entity-configuration/domain/experimental/electrical-cell-recording.ts b/src/entity-configuration/domain/experimental/electrical-cell-recording.ts index 6cc76d420..18c2f5f38 100644 --- a/src/entity-configuration/domain/experimental/electrical-cell-recording.ts +++ b/src/entity-configuration/domain/experimental/electrical-cell-recording.ts @@ -40,4 +40,5 @@ export const ElectricalCellRecording: EntityCoreTypeConfig = { extension: 'application/json', }, isBookmarkable: true, + bookmarkCategory: 'ExperimentalNeuronDensity', } as const; diff --git a/src/entity-configuration/domain/experimental/reconstruction-morphology.ts b/src/entity-configuration/domain/experimental/reconstruction-morphology.ts index 0b6487502..fff4615e9 100644 --- a/src/entity-configuration/domain/experimental/reconstruction-morphology.ts +++ b/src/entity-configuration/domain/experimental/reconstruction-morphology.ts @@ -39,4 +39,5 @@ export const ReconstructionMorphology: EntityCoreTypeConfig< }, viewDefinition: ViewsDefinitionRegistry[ExtendedEntitiesTypeDict.ReconstructionMorphology], isBookmarkable: true, + bookmarkCategory: 'ExperimentalNeuronMorphology', } as const; diff --git a/src/entity-configuration/domain/experimental/synapse-per-connection.ts b/src/entity-configuration/domain/experimental/synapse-per-connection.ts index 4a3200a2c..3d440ad64 100644 --- a/src/entity-configuration/domain/experimental/synapse-per-connection.ts +++ b/src/entity-configuration/domain/experimental/synapse-per-connection.ts @@ -32,4 +32,5 @@ export const SynapsePerConnection: EntityCoreTypeConfig = { extension: undefined, }, isBookmarkable: true, + bookmarkCategory: 'CircuitEModel', } as const; diff --git a/src/entity-configuration/domain/model/me-model.ts b/src/entity-configuration/domain/model/me-model.ts index 6aef7b830..aefc3896a 100644 --- a/src/entity-configuration/domain/model/me-model.ts +++ b/src/entity-configuration/domain/model/me-model.ts @@ -30,4 +30,5 @@ export const MEmodel: EntityCoreTypeConfig = { extension: undefined, }, isBookmarkable: true, + bookmarkCategory: 'CircuitMEModel', } as const; diff --git a/src/entity-configuration/domain/simulation/simulation-campaign.ts b/src/entity-configuration/domain/simulation/simulation-campaign.ts index 7929986e7..798ea8925 100644 --- a/src/entity-configuration/domain/simulation/simulation-campaign.ts +++ b/src/entity-configuration/domain/simulation/simulation-campaign.ts @@ -125,6 +125,7 @@ export const SimulationCampaign: EntityCoreTypeConfig = { @@ -34,6 +35,7 @@ export type EntityCoreTypeConfig = { }; viewDefinition?: ViewDefinitionConfig | null; isBookmarkable: boolean; + bookmarkCategory?: BookmarkCategory; }; export type SerializedEntityCoreTypeConfig = Omit< From 0c01d240bb58b3ef7b1af8c792d5a5f638ef8a9c Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 19 Aug 2025 17:32:07 +0200 Subject: [PATCH 15/80] add bookmark --- src/api/virtual-lab-svc/queries/bookmark.ts | 17 +------- src/api/virtual-lab-svc/queries/types.ts | 17 +++++++- .../explore/(view)/view/[type]/[id]/page.tsx | 18 -------- .../explore/(view)/view/layout.tsx | 5 --- .../explore/view/[type]/[id]/layout.tsx | 2 +- src/entity-configuration/domain/types.ts | 2 +- src/ui/segments/action-menu.tsx | 43 ++++++++++++------- 7 files changed, 47 insertions(+), 57 deletions(-) delete mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/(view)/view/[type]/[id]/page.tsx delete mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/(view)/view/layout.tsx diff --git a/src/api/virtual-lab-svc/queries/bookmark.ts b/src/api/virtual-lab-svc/queries/bookmark.ts index 2c0d50bb4..9ff43b7d3 100644 --- a/src/api/virtual-lab-svc/queries/bookmark.ts +++ b/src/api/virtual-lab-svc/queries/bookmark.ts @@ -1,9 +1,9 @@ import { virtualLabRootApi } from '@/api/virtual-lab-svc/utils'; -import type { TExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; import type { WorkspaceContext } from '@/types/common'; import type { BookmarkRequest, + BookmarkCategory, AddBookmarkResponse, DeleteBookmarksResponse, VlmGetProjectBookmarksResponse, @@ -41,19 +41,6 @@ export async function bookmarkToProjectLibrary( }); } -export type BookmarkCategory = - | 'ExperimentalBoutonDensity' - | 'ExperimentalNeuronDensity' - | 'ExperimentalElectroPhysiology' - | 'ExperimentalSynapsePerConnection' - | 'ExperimentalNeuronMorphology' - | 'SimulationCampaign' - | 'CircuitEModel' - | 'CircuitMEModel' - | 'SingleNeuronSynaptome' - | 'SingleNeuronSimulation' - | 'SynaptomeSimulation'; - /** * Retrieves all bookmarks for a specific project, grouped by category. * @@ -63,7 +50,7 @@ export type BookmarkCategory = */ export async function getAllBookmarksByCategory( { virtualLabId, projectId }: WorkspaceContext, - { category }: { category: BookmarkCategory } + { category }: { category?: BookmarkCategory } ): Promise { const api = await virtualLabRootApi(); const url = `${baseUri}/${virtualLabId}/projects/${projectId}/bookmarks`; diff --git a/src/api/virtual-lab-svc/queries/types.ts b/src/api/virtual-lab-svc/queries/types.ts index ce9b9ba18..3f5d54967 100644 --- a/src/api/virtual-lab-svc/queries/types.ts +++ b/src/api/virtual-lab-svc/queries/types.ts @@ -401,10 +401,23 @@ interface UserGroupsResponse { groups: Array; } +export type BookmarkCategory = + | 'ExperimentalBoutonDensity' + | 'ExperimentalNeuronDensity' + | 'ExperimentalElectroPhysiology' + | 'ExperimentalSynapsePerConnection' + | 'ExperimentalNeuronMorphology' + | 'SimulationCampaign' + | 'CircuitEModel' + | 'CircuitMEModel' + | 'SingleNeuronSynaptome' + | 'SingleNeuronSimulation' + | 'SynaptomeSimulation'; + export type BookmarkRequest = { resource_id?: string; entity_id: string; - category: TExtendedEntitiesTypeDict; + category: BookmarkCategory; }; export type DeleteBookmarksResponse = { @@ -418,7 +431,7 @@ export interface AddBookmarkResponse extends BookmarkRequest { export interface LibraryBookmark extends AddBookmarkResponse {} -type BookmarksByCategoryResponse = Record>; +type BookmarksByCategoryResponse = Record>; export type VlmGetSubscriptionResponse = VlmResponse; export type VlmCreateSubscriptionResponse = VlmResponse; diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/(view)/view/[type]/[id]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/(view)/view/[type]/[id]/page.tsx deleted file mode 100644 index 16dfcda2a..000000000 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/(view)/view/[type]/[id]/page.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { TExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; -import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; -import type { KebabCase } from '@/utils/type'; - -export default async function Page({ - params, -}: ServerSideComponentProp< - WorkspaceContext & { type: KebabCase; id: string }, - null ->) { - const { virtualLabId, projectId, type, id } = await params; - - return ( -
-
{JSON.stringify({ virtualLabId, projectId, type, id }, null, 2)}
-
- ); -} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/(view)/view/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/(view)/view/layout.tsx deleted file mode 100644 index 1b07ece54..000000000 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/(view)/view/layout.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import type { ReactNode } from 'react'; - -export default function Layout({ children }: { children: ReactNode }) { - return
{children}
; -} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index 5c91247d1..451d89b71 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -69,7 +69,7 @@ export default async function Layout({ entity={entity} entityType={entityType.type} ctx={{ virtualLabId, projectId }} - isBookmarkable={entityType.isBookmarkable} + bookmarkCategory={entityType.bookmarkCategory} />
{children}
diff --git a/src/entity-configuration/domain/types.ts b/src/entity-configuration/domain/types.ts index 5de42443f..89cfd4ce0 100644 --- a/src/entity-configuration/domain/types.ts +++ b/src/entity-configuration/domain/types.ts @@ -5,7 +5,7 @@ import type { EntityCoreResponse } from '@/api/entitycore/types/shared/response' import type { EntitySlugValue } from '@/entity-configuration/domain/slug'; import type { TEntityTypeDict } from '@/api/entitycore/types'; import type { WorkspaceContext } from '@/types/common'; -import { BookmarkCategory } from '@/api/virtual-lab-svc/queries/bookmark'; +import type { BookmarkCategory } from '@/api/virtual-lab-svc/queries/types'; export type EntityCoreTypeGroup = 'experimental' | 'models' | 'simulations'; export type EntityCoreTypeConfig = { diff --git a/src/ui/segments/action-menu.tsx b/src/ui/segments/action-menu.tsx index bf53aee47..b8eee1c22 100644 --- a/src/ui/segments/action-menu.tsx +++ b/src/ui/segments/action-menu.tsx @@ -10,29 +10,22 @@ import { import Action from '../molecules/side-menu-action'; import { EntityCoreIdentifiable } from '@/api/entitycore/types/shared/global'; -import { useQuery } from '@tanstack/react-query'; -import { getAllBookmarksByCategory } from '@/api/virtual-lab-svc/queries/bookmark'; -import { TEntityCoreConfigurationItem } from '@/entity-configuration/domain'; + +import { bookmarkToProjectLibrary } from '@/api/virtual-lab-svc/queries/bookmark'; +import { BookmarkCategory } from '@/api/virtual-lab-svc/queries/types'; +import { useAppNotification } from '@/components/notification'; export default function ActionMenu({ entity, - entityType, - isBookmarkable, + bookmarkCategory, ctx, }: { entity: T; - entityType: TEntityCoreConfigurationItem['type']; - isBookmarkable: boolean; + bookmarkCategory?: BookmarkCategory; ctx: { virtualLabId: string; projectId: string }; }) { const [copied, setCopied] = useState(false); - - const bookmarks = useQuery({ - queryKey: [ctx.projectId, ctx.virtualLabId, entityType], - queryFn: async () => getAllBookmarksByCategory(ctx, { category: entityType }), - }); - - console.log(bookmarks.data); + const notification = useAppNotification(); return (
@@ -58,7 +51,27 @@ export default function ActionMenu({ {copied ? 'Copied' : 'Copy ID'} }>Simulate - {isBookmarkable && }>Bookmark} + {bookmarkCategory && ( + { + try { + await bookmarkToProjectLibrary(ctx, { + entity_id: entity.id, + category: bookmarkCategory, + }); + notification.success({ message: 'Entity successfully bookmarked' }); + } catch { + notification.error({ message: "Couldn't add entity to bookmarks" }); + } + }} + /> + } + > + Bookmark + + )} }>Download
); From c9c85ec542c83ea57413cd7cc9c005a4d0b6d694 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 19 Aug 2025 19:00:11 +0200 Subject: [PATCH 16/80] bookmark funtion --- .../explore/view/[type]/[id]/layout.tsx | 3 +- src/ui/molecules/side-menu-action.tsx | 3 +- src/ui/segments/action-menu.tsx | 100 +++++++++++++++--- 3 files changed, 85 insertions(+), 21 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index 451d89b71..b21f07e08 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -67,9 +67,8 @@ export default async function Layout({
{children}
diff --git a/src/ui/molecules/side-menu-action.tsx b/src/ui/molecules/side-menu-action.tsx index f26f8eef4..a693bdfa5 100644 --- a/src/ui/molecules/side-menu-action.tsx +++ b/src/ui/molecules/side-menu-action.tsx @@ -1,11 +1,10 @@ import { ReactNode } from 'react'; -import { AntdIconProps } from '@ant-design/icons/lib/components/AntdIcon'; export default function Action({ children, icon }: { children: ReactNode; icon: ReactNode }) { return (
{children}
-
+
{icon}
diff --git a/src/ui/segments/action-menu.tsx b/src/ui/segments/action-menu.tsx index b8eee1c22..ae0ee4965 100644 --- a/src/ui/segments/action-menu.tsx +++ b/src/ui/segments/action-menu.tsx @@ -1,19 +1,25 @@ 'use client'; import { useState } from 'react'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; import { BookOutlined, CopyOutlined, DownloadOutlined, ExperimentOutlined, + LoadingOutlined, } from '@ant-design/icons'; import Action from '../molecules/side-menu-action'; import { EntityCoreIdentifiable } from '@/api/entitycore/types/shared/global'; -import { bookmarkToProjectLibrary } from '@/api/virtual-lab-svc/queries/bookmark'; -import { BookmarkCategory } from '@/api/virtual-lab-svc/queries/types'; +import { + bookmarkToProjectLibrary, + getAllBookmarksByCategory, +} from '@/api/virtual-lab-svc/queries/bookmark'; import { useAppNotification } from '@/components/notification'; +import { BookmarkCategory } from '@/api/virtual-lab-svc/queries/types'; +import { deleteBookmarksFromProjectLibrary } from '@/features/bookmark/actions'; export default function ActionMenu({ entity, @@ -24,9 +30,77 @@ export default function ActionMenu({ bookmarkCategory?: BookmarkCategory; ctx: { virtualLabId: string; projectId: string }; }) { + const queryClient = useQueryClient(); const [copied, setCopied] = useState(false); + const [loading, setLoading] = useState(false); const notification = useAppNotification(); + const bookmarks = useQuery({ + queryKey: [ctx.projectId, ctx.virtualLabId, bookmarkCategory], + queryFn: async () => getAllBookmarksByCategory(ctx, { category: bookmarkCategory }), + }); + + const existingBookmarks = bookmarkCategory + ? bookmarks.data?.data?.[bookmarkCategory]?.map((b) => b.entity_id) + : undefined; + + const isBookmarked = existingBookmarks && existingBookmarks.includes(entity.id); + + const handleBookmark = async () => { + if (!bookmarkCategory) return; + setLoading(true); + try { + await bookmarkToProjectLibrary(ctx, { + entity_id: entity.id, + category: bookmarkCategory, + }); + + await queryClient.invalidateQueries({ + queryKey: [ctx.projectId, ctx.virtualLabId, bookmarkCategory], + }); + + notification.success({ message: 'Entity successfully bookmarked' }); + } catch { + notification.error({ message: "Couldn't add entity to bookmarks" }); + } finally { + setLoading(false); + } + }; + + const handleRemoveBookmark = async () => { + if (!bookmarkCategory) return; + + setLoading(true); + try { + await deleteBookmarksFromProjectLibrary({ + virtualLabId: ctx.virtualLabId, + projectId: ctx.projectId, + bookmarks: [ + { + entity_id: entity.id, + category: bookmarkCategory, + }, + ], + }); + + await queryClient.invalidateQueries({ + queryKey: [ctx.projectId, ctx.virtualLabId, bookmarkCategory], + }); + + notification.success({ message: 'Bookmark removed from library' }); + } catch { + notification.error({ message: "Couldn't remove bookmark" }); + } finally { + setLoading(false); + } + }; + + const getBookmarkHandler = () => { + if (loading) return undefined; + if (!isBookmarked) return handleBookmark; + return handleRemoveBookmark; + }; + return (
({ {copied ? 'Copied' : 'Copy ID'} }>Simulate - {bookmarkCategory && ( + {bookmarkCategory && bookmarks.data && ( { - try { - await bookmarkToProjectLibrary(ctx, { - entity_id: entity.id, - category: bookmarkCategory, - }); - notification.success({ message: 'Entity successfully bookmarked' }); - } catch { - notification.error({ message: "Couldn't add entity to bookmarks" }); - } - }} - /> + <> + {!loading && } + {loading && } + } > - Bookmark + <>{!isBookmarked ? 'Bookmark' : 'Remove from bookmarks'} )} + }>Download
); From 9867932ede58da0764a3221931b0926a31b9a45b Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 20 Aug 2025 10:05:07 +0200 Subject: [PATCH 17/80] Download --- .../explore/view/[type]/[id]/layout.tsx | 1 + src/ui/segments/action-menu.tsx | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index b21f07e08..8870747f0 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -67,6 +67,7 @@ export default async function Layout({
diff --git a/src/ui/segments/action-menu.tsx b/src/ui/segments/action-menu.tsx index ae0ee4965..0b88f6760 100644 --- a/src/ui/segments/action-menu.tsx +++ b/src/ui/segments/action-menu.tsx @@ -20,21 +20,31 @@ import { import { useAppNotification } from '@/components/notification'; import { BookmarkCategory } from '@/api/virtual-lab-svc/queries/types'; import { deleteBookmarksFromProjectLibrary } from '@/features/bookmark/actions'; +import { downloadArchive } from '@/services/entity-download'; +import { EntitySlugValue } from '@/entity-configuration/domain/slug'; +import { getEntityBySlug } from '@/entity-configuration/domain/helpers'; export default function ActionMenu({ entity, bookmarkCategory, ctx, + entitySlug, }: { entity: T; bookmarkCategory?: BookmarkCategory; ctx: { virtualLabId: string; projectId: string }; + entitySlug: EntitySlugValue; }) { const queryClient = useQueryClient(); const [copied, setCopied] = useState(false); const [loading, setLoading] = useState(false); const notification = useAppNotification(); + const entityType = getEntityBySlug({ slug: entitySlug }); + if (!entityType) throw Error('Invalid entity type'); + + console.log(entityType); + const bookmarks = useQuery({ queryKey: [ctx.projectId, ctx.virtualLabId, bookmarkCategory], queryFn: async () => getAllBookmarksByCategory(ctx, { category: bookmarkCategory }), @@ -138,7 +148,11 @@ export default function ActionMenu({ )} - }>Download + downloadArchive(entityType.type, [entity.id])} />} + > + Download + ); } From 22ec2193b4754f357c7b254e1e176608ab35d66a Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 20 Aug 2025 10:07:54 +0200 Subject: [PATCH 18/80] lint --- lefthook.yml | 2 +- src/api/virtual-lab-svc/queries/types.ts | 2 -- src/ui/segments/action-menu.tsx | 2 -- src/ui/segments/explore/detail-menu.tsx | 2 -- 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/lefthook.yml b/lefthook.yml index 407bfe5ee..53763ce9f 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -1,4 +1,4 @@ -pre-push: +pre-commands: parallel: true commands: eslint: diff --git a/src/api/virtual-lab-svc/queries/types.ts b/src/api/virtual-lab-svc/queries/types.ts index 3f5d54967..9c39ca8c3 100644 --- a/src/api/virtual-lab-svc/queries/types.ts +++ b/src/api/virtual-lab-svc/queries/types.ts @@ -1,5 +1,3 @@ -import type { TExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; - interface VlmResponse { message: string; data: T | null; diff --git a/src/ui/segments/action-menu.tsx b/src/ui/segments/action-menu.tsx index 0b88f6760..eadbfcdbe 100644 --- a/src/ui/segments/action-menu.tsx +++ b/src/ui/segments/action-menu.tsx @@ -43,8 +43,6 @@ export default function ActionMenu({ const entityType = getEntityBySlug({ slug: entitySlug }); if (!entityType) throw Error('Invalid entity type'); - console.log(entityType); - const bookmarks = useQuery({ queryKey: [ctx.projectId, ctx.virtualLabId, bookmarkCategory], queryFn: async () => getAllBookmarksByCategory(ctx, { category: bookmarkCategory }), diff --git a/src/ui/segments/explore/detail-menu.tsx b/src/ui/segments/explore/detail-menu.tsx index 02e585c46..840450125 100644 --- a/src/ui/segments/explore/detail-menu.tsx +++ b/src/ui/segments/explore/detail-menu.tsx @@ -8,8 +8,6 @@ export default function DetailMenu() { const parentPath = path.split('/').slice(0, -1).join('/'); const page = path.split('/').pop(); - console.log(parentPath) - return ( <> From 46e7768adc97ef1f32743900bcb5596206a6043d Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 20 Aug 2025 10:07:54 +0200 Subject: [PATCH 19/80] lint --- lefthook.yml | 26 ++++++++++++------------ src/api/virtual-lab-svc/queries/types.ts | 2 -- src/ui/segments/action-menu.tsx | 2 -- src/ui/segments/explore/detail-menu.tsx | 2 -- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/lefthook.yml b/lefthook.yml index 407bfe5ee..43f5595c8 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -1,13 +1,13 @@ -pre-push: - parallel: true - commands: - eslint: - glob: '*.{js,jsx,ts,tsx}' - run: npx eslint --fix {staged_files} - prettier: - glob: '*.{js,jsx,ts,tsx,json}' - run: | - npx prettier --write {staged_files} - git add {staged_files} - typescript: - run: npm run typecheck +# pre-commit: +# parallel: true +# commands: +# eslint: +# glob: '*.{js,jsx,ts,tsx}' +# run: npx eslint --fix {staged_files} +# prettier: +# glob: '*.{js,jsx,ts,tsx,json}' +# run: | +# npx prettier --write {staged_files} +# git add {staged_files} +# typescript: +# run: npm run typecheck diff --git a/src/api/virtual-lab-svc/queries/types.ts b/src/api/virtual-lab-svc/queries/types.ts index 3f5d54967..9c39ca8c3 100644 --- a/src/api/virtual-lab-svc/queries/types.ts +++ b/src/api/virtual-lab-svc/queries/types.ts @@ -1,5 +1,3 @@ -import type { TExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; - interface VlmResponse { message: string; data: T | null; diff --git a/src/ui/segments/action-menu.tsx b/src/ui/segments/action-menu.tsx index 0b88f6760..eadbfcdbe 100644 --- a/src/ui/segments/action-menu.tsx +++ b/src/ui/segments/action-menu.tsx @@ -43,8 +43,6 @@ export default function ActionMenu({ const entityType = getEntityBySlug({ slug: entitySlug }); if (!entityType) throw Error('Invalid entity type'); - console.log(entityType); - const bookmarks = useQuery({ queryKey: [ctx.projectId, ctx.virtualLabId, bookmarkCategory], queryFn: async () => getAllBookmarksByCategory(ctx, { category: bookmarkCategory }), diff --git a/src/ui/segments/explore/detail-menu.tsx b/src/ui/segments/explore/detail-menu.tsx index 02e585c46..840450125 100644 --- a/src/ui/segments/explore/detail-menu.tsx +++ b/src/ui/segments/explore/detail-menu.tsx @@ -8,8 +8,6 @@ export default function DetailMenu() { const parentPath = path.split('/').slice(0, -1).join('/'); const page = path.split('/').pop(); - console.log(parentPath) - return ( <> From c31091f906690a572eb7acf29bf297773d7aac13 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 20 Aug 2025 10:36:42 +0200 Subject: [PATCH 20/80] bookmarks --- src/api/virtual-lab-svc/queries/bookmark.ts | 4 ++-- src/api/virtual-lab-svc/queries/types.ts | 15 +-------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/api/virtual-lab-svc/queries/bookmark.ts b/src/api/virtual-lab-svc/queries/bookmark.ts index 6dff6ca3a..16793b16c 100644 --- a/src/api/virtual-lab-svc/queries/bookmark.ts +++ b/src/api/virtual-lab-svc/queries/bookmark.ts @@ -4,13 +4,13 @@ import type { TEntityTypeDict } from '@/api/entitycore/types/entity-type'; import type { WorkspaceContext } from '@/types/common'; import type { BookmarkRequest, - BookmarkCategory, AddBookmarkResponse, DeleteBookmarksResponse, VlmGetProjectBookmarksResponse, VlmGetProjectLibraryCategories, VlmGetProjectLibraryPerCategory, } from '@/api/virtual-lab-svc/queries/types'; +import { TExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; const baseUri = '/virtual-labs'; @@ -52,7 +52,7 @@ export async function bookmarkToProjectLibrary( */ export async function getAllBookmarksByCategory( { virtualLabId, projectId }: WorkspaceContext, - { category }: { category?: BookmarkCategory } + { category }: { category?: TExtendedEntitiesTypeDict } ): Promise { const api = await virtualLabRootApi(); const url = `${baseUri}/${virtualLabId}/projects/${projectId}/bookmarks`; diff --git a/src/api/virtual-lab-svc/queries/types.ts b/src/api/virtual-lab-svc/queries/types.ts index a273b3b84..ee50a54ea 100644 --- a/src/api/virtual-lab-svc/queries/types.ts +++ b/src/api/virtual-lab-svc/queries/types.ts @@ -402,22 +402,9 @@ interface UserGroupsResponse { groups: Array; } -export type BookmarkCategory = - | 'ExperimentalBoutonDensity' - | 'ExperimentalNeuronDensity' - | 'ExperimentalElectroPhysiology' - | 'ExperimentalSynapsePerConnection' - | 'ExperimentalNeuronMorphology' - | 'SimulationCampaign' - | 'CircuitEModel' - | 'CircuitMEModel' - | 'SingleNeuronSynaptome' - | 'SingleNeuronSimulation' - | 'SynaptomeSimulation'; - export type BookmarkRequest = { entity_id: string; - category: BookmarkCategory; + category: TExtendedEntitiesTypeDict; }; export type DeleteBookmarksResponse = { From 25a756e921af025578c57bfe36c1aa13aac572cc Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 20 Aug 2025 10:41:49 +0200 Subject: [PATCH 21/80] bookmarkCategory: --- .../explore/view/[type]/[id]/layout.tsx | 7 +---- .../domain/experimental/bouton-density.ts | 1 - .../experimental/electrical-cell-recording.ts | 1 - .../domain/experimental/neuron-density.ts | 1 - .../experimental/reconstruction-morphology.ts | 1 - .../experimental/synapse-per-connection.ts | 1 - .../domain/model/e-model.ts | 1 - .../domain/model/me-model.ts | 1 - .../domain/simulation/simulation-campaign.ts | 1 - .../simulation/single-neuron-simulation.ts | 1 - .../single-neuron-synaptome-simulation.ts | 1 - src/entity-configuration/domain/types.ts | 1 - src/ui/segments/action-menu.tsx | 27 +++++++------------ 13 files changed, 10 insertions(+), 35 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index 8870747f0..e294baa4e 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -65,12 +65,7 @@ export default async function Layout({
- +
{children}
diff --git a/src/entity-configuration/domain/experimental/bouton-density.ts b/src/entity-configuration/domain/experimental/bouton-density.ts index 7e5e07b9b..54e4bc1c4 100644 --- a/src/entity-configuration/domain/experimental/bouton-density.ts +++ b/src/entity-configuration/domain/experimental/bouton-density.ts @@ -35,5 +35,4 @@ export const BoutonDensity: EntityCoreTypeConfig = { }, // viewDefinition: ViewsDefinitionRegistry[DataType.ExperimentalBoutonDensity], isBookmarkable: true, - bookmarkCategory: 'ExperimentalBoutonDensity', } as const; diff --git a/src/entity-configuration/domain/experimental/electrical-cell-recording.ts b/src/entity-configuration/domain/experimental/electrical-cell-recording.ts index d952c104b..b40dfc53e 100644 --- a/src/entity-configuration/domain/experimental/electrical-cell-recording.ts +++ b/src/entity-configuration/domain/experimental/electrical-cell-recording.ts @@ -41,5 +41,4 @@ export const ElectricalCellRecording: EntityCoreTypeConfig = { extension: 'application/json', }, isBookmarkable: true, - bookmarkCategory: 'ExperimentalNeuronDensity', } as const; diff --git a/src/entity-configuration/domain/experimental/reconstruction-morphology.ts b/src/entity-configuration/domain/experimental/reconstruction-morphology.ts index 377000474..291d258c1 100644 --- a/src/entity-configuration/domain/experimental/reconstruction-morphology.ts +++ b/src/entity-configuration/domain/experimental/reconstruction-morphology.ts @@ -40,5 +40,4 @@ export const ReconstructionMorphology: EntityCoreTypeConfig< }, viewDefinition: ViewsDefinitionRegistry[ExtendedEntitiesTypeDict.ReconstructionMorphology], isBookmarkable: true, - bookmarkCategory: 'ExperimentalNeuronMorphology', } as const; diff --git a/src/entity-configuration/domain/experimental/synapse-per-connection.ts b/src/entity-configuration/domain/experimental/synapse-per-connection.ts index 9c178dfda..11fd69b6a 100644 --- a/src/entity-configuration/domain/experimental/synapse-per-connection.ts +++ b/src/entity-configuration/domain/experimental/synapse-per-connection.ts @@ -33,5 +33,4 @@ export const SynapsePerConnection: EntityCoreTypeConfig = { extension: undefined, }, isBookmarkable: true, - bookmarkCategory: 'CircuitEModel', } as const; diff --git a/src/entity-configuration/domain/model/me-model.ts b/src/entity-configuration/domain/model/me-model.ts index 80bfb3133..452363c30 100644 --- a/src/entity-configuration/domain/model/me-model.ts +++ b/src/entity-configuration/domain/model/me-model.ts @@ -31,5 +31,4 @@ export const MEmodel: EntityCoreTypeConfig = { extension: undefined, }, isBookmarkable: true, - bookmarkCategory: 'CircuitMEModel', } as const; diff --git a/src/entity-configuration/domain/simulation/simulation-campaign.ts b/src/entity-configuration/domain/simulation/simulation-campaign.ts index 8ac6d4ebf..d149d1f2c 100644 --- a/src/entity-configuration/domain/simulation/simulation-campaign.ts +++ b/src/entity-configuration/domain/simulation/simulation-campaign.ts @@ -125,7 +125,6 @@ export const SimulationCampaign: EntityCoreTypeConfig = { }; viewDefinition?: ViewDefinitionConfig | null; isBookmarkable: boolean; - bookmarkCategory?: BookmarkCategory; }; export type SerializedEntityCoreTypeConfig = Omit< diff --git a/src/ui/segments/action-menu.tsx b/src/ui/segments/action-menu.tsx index eadbfcdbe..b7f3bf250 100644 --- a/src/ui/segments/action-menu.tsx +++ b/src/ui/segments/action-menu.tsx @@ -18,7 +18,6 @@ import { getAllBookmarksByCategory, } from '@/api/virtual-lab-svc/queries/bookmark'; import { useAppNotification } from '@/components/notification'; -import { BookmarkCategory } from '@/api/virtual-lab-svc/queries/types'; import { deleteBookmarksFromProjectLibrary } from '@/features/bookmark/actions'; import { downloadArchive } from '@/services/entity-download'; import { EntitySlugValue } from '@/entity-configuration/domain/slug'; @@ -26,12 +25,10 @@ import { getEntityBySlug } from '@/entity-configuration/domain/helpers'; export default function ActionMenu({ entity, - bookmarkCategory, ctx, entitySlug, }: { entity: T; - bookmarkCategory?: BookmarkCategory; ctx: { virtualLabId: string; projectId: string }; entitySlug: EntitySlugValue; }) { @@ -44,27 +41,23 @@ export default function ActionMenu({ if (!entityType) throw Error('Invalid entity type'); const bookmarks = useQuery({ - queryKey: [ctx.projectId, ctx.virtualLabId, bookmarkCategory], - queryFn: async () => getAllBookmarksByCategory(ctx, { category: bookmarkCategory }), + queryKey: [ctx.projectId, ctx.virtualLabId, entityType.type], + queryFn: async () => getAllBookmarksByCategory(ctx, { category: entityType.type }), }); - const existingBookmarks = bookmarkCategory - ? bookmarks.data?.data?.[bookmarkCategory]?.map((b) => b.entity_id) - : undefined; - - const isBookmarked = existingBookmarks && existingBookmarks.includes(entity.id); + const existingBookmarks = bookmarks.data?.data?.[entityType.type]?.map((b) => b.entity_id); + const isBookmarked = !!existingBookmarks && existingBookmarks.includes(entity.id); const handleBookmark = async () => { - if (!bookmarkCategory) return; setLoading(true); try { await bookmarkToProjectLibrary(ctx, { entity_id: entity.id, - category: bookmarkCategory, + category: entityType.type, }); await queryClient.invalidateQueries({ - queryKey: [ctx.projectId, ctx.virtualLabId, bookmarkCategory], + queryKey: [ctx.projectId, ctx.virtualLabId, entityType.type], }); notification.success({ message: 'Entity successfully bookmarked' }); @@ -76,8 +69,6 @@ export default function ActionMenu({ }; const handleRemoveBookmark = async () => { - if (!bookmarkCategory) return; - setLoading(true); try { await deleteBookmarksFromProjectLibrary({ @@ -86,13 +77,13 @@ export default function ActionMenu({ bookmarks: [ { entity_id: entity.id, - category: bookmarkCategory, + category: entityType.type, }, ], }); await queryClient.invalidateQueries({ - queryKey: [ctx.projectId, ctx.virtualLabId, bookmarkCategory], + queryKey: [ctx.projectId, ctx.virtualLabId, entityType.type], }); notification.success({ message: 'Bookmark removed from library' }); @@ -133,7 +124,7 @@ export default function ActionMenu({ {copied ? 'Copied' : 'Copy ID'} }>Simulate - {bookmarkCategory && bookmarks.data && ( + {bookmarks.data && ( From d042019699f213018d9a346846664afb2eecc7cf Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 20 Aug 2025 10:51:22 +0200 Subject: [PATCH 22/80] Linting --- lefthook.yml | 22 +++++++++------------- package.json | 2 +- src/entity-configuration/domain/types.ts | 1 - 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/lefthook.yml b/lefthook.yml index 43f5595c8..022a7862e 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -1,13 +1,9 @@ -# pre-commit: -# parallel: true -# commands: -# eslint: -# glob: '*.{js,jsx,ts,tsx}' -# run: npx eslint --fix {staged_files} -# prettier: -# glob: '*.{js,jsx,ts,tsx,json}' -# run: | -# npx prettier --write {staged_files} -# git add {staged_files} -# typescript: -# run: npm run typecheck +pre-push: + parallel: true + commands: + eslint: + run: pnpm run lint + prettier: + run: pnpm run prettier --check + typescript: + run: pnpm run typecheck diff --git a/package.json b/package.json index b68857493..203e48d60 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "lint": "npx eslint src && npx tsc --noEmit", "lint:only": "next lint", "lint:fix": "next lint --fix", - "prettier:check": "prettier --check . --log-level warn", + "prettier:check": "prettier --check . --log-level error", "prettier:write": "prettier --write . --log-level warn", "test": "vitest -w tests", "test:ci": "vitest run tests", diff --git a/src/entity-configuration/domain/types.ts b/src/entity-configuration/domain/types.ts index 84a25abde..c14bf19fe 100644 --- a/src/entity-configuration/domain/types.ts +++ b/src/entity-configuration/domain/types.ts @@ -6,7 +6,6 @@ import type { TEntityTypeGroup } from '@/entity-configuration/domain/group'; import type { EntitySlugValue } from '@/entity-configuration/domain/slug'; import type { TEntityTypeDict } from '@/api/entitycore/types'; import type { WorkspaceContext } from '@/types/common'; -import type { BookmarkCategory } from '@/api/virtual-lab-svc/queries/types'; export type EntityCoreTypeConfig = { group: TEntityTypeGroup; From 83fe9b83e6bb334e3166711a4e73dc3990e0f952 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 20 Aug 2025 11:02:49 +0200 Subject: [PATCH 23/80] restore lefthook for now --- lefthook.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lefthook.yml b/lefthook.yml index 022a7862e..498ff5529 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -1,9 +1,13 @@ -pre-push: +pre-commit: parallel: true commands: eslint: - run: pnpm run lint + glob: '*.{js,jsx,ts,tsx}' + run: npx eslint --fix {staged_files} prettier: - run: pnpm run prettier --check + glob: '*.{js,jsx,ts,tsx,json}' + run: | + npx prettier --write {staged_files} + git add {staged_files} typescript: - run: pnpm run typecheck + run: npm run typecheck From e02b4f0c53d72278250da582e0c28e2247fbe194 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 20 Aug 2025 11:02:49 +0200 Subject: [PATCH 24/80] restore lefthook for now --- lefthook.yml | 12 ++++++++---- package.json | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lefthook.yml b/lefthook.yml index 022a7862e..498ff5529 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -1,9 +1,13 @@ -pre-push: +pre-commit: parallel: true commands: eslint: - run: pnpm run lint + glob: '*.{js,jsx,ts,tsx}' + run: npx eslint --fix {staged_files} prettier: - run: pnpm run prettier --check + glob: '*.{js,jsx,ts,tsx,json}' + run: | + npx prettier --write {staged_files} + git add {staged_files} typescript: - run: pnpm run typecheck + run: npm run typecheck diff --git a/package.json b/package.json index 203e48d60..b68857493 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "lint": "npx eslint src && npx tsc --noEmit", "lint:only": "next lint", "lint:fix": "next lint --fix", - "prettier:check": "prettier --check . --log-level error", + "prettier:check": "prettier --check . --log-level warn", "prettier:write": "prettier --write . --log-level warn", "test": "vitest -w tests", "test:ci": "vitest run tests", From b47d6655a895b892ee27d97e420854560ad65ad5 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 20 Aug 2025 14:38:28 +0200 Subject: [PATCH 25/80] download entity --- .../explore/view/[type]/[id]/layout.tsx | 36 +++++++++++---- .../view/[type]/[id]/overview/page.tsx | 46 ++++++++++++++++++- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index e294baa4e..c1d1889df 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -7,23 +7,22 @@ import { getEntityBySlug } from '@/entity-configuration/domain/helpers'; import { EntitySlugValue } from '@/entity-configuration/domain/slug'; import DetailMenu from '@/ui/segments/explore/detail-menu'; import ActionMenu from '@/ui/segments/action-menu'; +import type { WorkspaceContext } from '@/types/common'; interface Params { - virtualLabId: string; - projectId: string; id: string; type: EntitySlugValue; } -export default async function Layout({ - children, - params, +export async function downloadEntity({ + type, + id, + ctx, }: { - children: ReactNode; - params: Promise; + type: EntitySlugValue; + id: string; + ctx: WorkspaceContext; }) { - const { virtualLabId, projectId, type, id } = await params; - if (!type || !id) notFound(); const entityType = getEntityBySlug({ slug: type }); @@ -37,12 +36,29 @@ export default async function Layout({ let entity: AwaitedType> | undefined; try { - entity = await fetchEntity({ id, context: { virtualLabId, projectId } }); + entity = await fetchEntity({ id, context: ctx }); } catch { notFound(); } if (!entity) notFound(); + return { entity, entityType }; +} + +export default async function Layout({ + children, + params, +}: { + children: ReactNode; + params: Promise; +}) { + const { virtualLabId, projectId, type, id } = await params; + + const { entity, entityType } = await downloadEntity({ + type, + id, + ctx: { virtualLabId, projectId }, + }); return (
diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx index 64dd56bb2..d9f94446d 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx @@ -1,3 +1,45 @@ -export default function Overview() { - return 'page'; +import type { TExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; +import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; +import type { KebabCase } from '@/utils/type'; +import Overview from '@/features/details-view/overview'; +import { + CommonSummaryViewFields, + getViewDefinitionByExtendedType, +} from '@/entity-configuration/definitions/view-defs'; +import { getEntityByExtendedType, getEntityBySlug } from '@/entity-configuration/domain/helpers'; +import { EntitySlugValue } from '@/entity-configuration/domain/slug'; +import { notFound } from 'next/navigation'; +// import { Field } from '@/features/details-view/overview'; + +export default async function Page({ + params, +}: ServerSideComponentProp) { + const { virtualLabId, projectId, type, id } = await params; + + const entityType = getEntityBySlug({ slug: type }); + if (!entityType) notFound(); + + const fields = getViewDefinitionByExtendedType(entityType.extendedType)?.summaryViewFields; + if (!fields) notFound(); + + const commonFields = CommonSummaryViewFields; + + + + return ( +
+ {/* {commonFields.length > 0 && ( +
+ {commonFields.map(({ className, field }) => ( + // + ))} +
+ )} +
+ {fields.map(({ className, field }) => ( + // + ))} +
*/} +
+ ); } From 0ad7690e0c75ed5637830dc376b7191746156f23 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 20 Aug 2025 15:09:45 +0200 Subject: [PATCH 26/80] Working on detail view --- .../view/[type]/[id]/overview/page.tsx | 38 +++++++++---------- src/features/details-view/overview.tsx | 2 + 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx index d9f94446d..52d40be3c 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx @@ -1,7 +1,6 @@ import type { TExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; import type { KebabCase } from '@/utils/type'; -import Overview from '@/features/details-view/overview'; import { CommonSummaryViewFields, getViewDefinitionByExtendedType, @@ -9,37 +8,38 @@ import { import { getEntityByExtendedType, getEntityBySlug } from '@/entity-configuration/domain/helpers'; import { EntitySlugValue } from '@/entity-configuration/domain/slug'; import { notFound } from 'next/navigation'; -// import { Field } from '@/features/details-view/overview'; +import { downloadEntity } from '../layout'; +import { Field } from '@/features/details-view/overview'; +import { getFieldDefinition } from '@/entity-configuration/definitions'; export default async function Page({ params, }: ServerSideComponentProp) { const { virtualLabId, projectId, type, id } = await params; - const entityType = getEntityBySlug({ slug: type }); - if (!entityType) notFound(); + const { entity, entityType } = await downloadEntity({ + type, + ctx: { virtualLabId, projectId }, + id, + }); const fields = getViewDefinitionByExtendedType(entityType.extendedType)?.summaryViewFields; if (!fields) notFound(); const commonFields = CommonSummaryViewFields; - - return ( -
- {/* {commonFields.length > 0 && ( -
- {commonFields.map(({ className, field }) => ( - // - ))} -
- )} -
- {fields.map(({ className, field }) => ( - // - ))} -
*/} +
+
+
Name
+
{entity.name}
+
+
+ {[...commonFields, ...fields].map(({ className, field }) => { + const fieldObj = getFieldDefinition(field); + return fieldObj?.render?.(entity); + })} +
); } diff --git a/src/features/details-view/overview.tsx b/src/features/details-view/overview.tsx index 76b9f2f26..fb17de9b9 100644 --- a/src/features/details-view/overview.tsx +++ b/src/features/details-view/overview.tsx @@ -1,3 +1,5 @@ +'use client'; + import { ReactNode } from 'react'; import Header from '@/features/details-view/header'; From 45a9aae270ab5b0f69ba1976c0914cac60048803 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 20 Aug 2025 15:28:15 +0200 Subject: [PATCH 27/80] Add overview page --- .../explore/view/[type]/[id]/overview/page.tsx | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx index 52d40be3c..6060ca9f4 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx @@ -1,16 +1,12 @@ -import type { TExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; +import { notFound } from 'next/navigation'; +import { downloadEntity } from '../layout'; import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; -import type { KebabCase } from '@/utils/type'; import { CommonSummaryViewFields, getViewDefinitionByExtendedType, } from '@/entity-configuration/definitions/view-defs'; -import { getEntityByExtendedType, getEntityBySlug } from '@/entity-configuration/domain/helpers'; import { EntitySlugValue } from '@/entity-configuration/domain/slug'; -import { notFound } from 'next/navigation'; -import { downloadEntity } from '../layout'; import { Field } from '@/features/details-view/overview'; -import { getFieldDefinition } from '@/entity-configuration/definitions'; export default async function Page({ params, @@ -34,10 +30,9 @@ export default async function Page({
Name
{entity.name}
-
+
{[...commonFields, ...fields].map(({ className, field }) => { - const fieldObj = getFieldDefinition(field); - return fieldObj?.render?.(entity); + return ; })}
From 73785b0f02917b19fd64b03899c5bc624c53401a Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 20 Aug 2025 16:02:00 +0200 Subject: [PATCH 28/80] add morphology page --- .../explore/view/[type]/[id]/overview/page.tsx | 15 ++++++++++++++- .../reconstruction-morphology/detail-view.tsx | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx index 6060ca9f4..9d60b83ae 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx @@ -7,6 +7,8 @@ import { } from '@/entity-configuration/definitions/view-defs'; import { EntitySlugValue } from '@/entity-configuration/domain/slug'; import { Field } from '@/features/details-view/overview'; +import { MorphoViewerLoaderMemo } from '@/features/entities/reconstruction-morphology/detail-view'; +import { IReconstructionMorphology } from '@/api/entitycore/types'; export default async function Page({ params, @@ -24,8 +26,18 @@ export default async function Page({ const commonFields = CommonSummaryViewFields; + function renderViz() { + if (entity.type === 'reconstruction_morphology') { + return ; + } + + return null; + } + + const viz = renderViz(); + return ( -
+
Name
{entity.name}
@@ -35,6 +47,7 @@ export default async function Page({ return ; })}
+ {viz &&
{viz}
}
); } diff --git a/src/features/entities/reconstruction-morphology/detail-view.tsx b/src/features/entities/reconstruction-morphology/detail-view.tsx index 214d6ce3d..e33358aa2 100644 --- a/src/features/entities/reconstruction-morphology/detail-view.tsx +++ b/src/features/entities/reconstruction-morphology/detail-view.tsx @@ -74,7 +74,7 @@ function MorphoViewerLoader({ resource }: { resource: IReconstructionMorphology case 'hasData': return morphologyData.data ? ( Date: Wed, 20 Aug 2025 17:12:11 +0200 Subject: [PATCH 29/80] add electrophysiology viz --- .../view/[type]/[id]/overview/page.tsx | 21 +++++---------- src/ui/segments/viz.tsx | 26 +++++++++++++++++++ 2 files changed, 32 insertions(+), 15 deletions(-) create mode 100644 src/ui/segments/viz.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx index 9d60b83ae..71e92675e 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx @@ -7,17 +7,17 @@ import { } from '@/entity-configuration/definitions/view-defs'; import { EntitySlugValue } from '@/entity-configuration/domain/slug'; import { Field } from '@/features/details-view/overview'; -import { MorphoViewerLoaderMemo } from '@/features/entities/reconstruction-morphology/detail-view'; -import { IReconstructionMorphology } from '@/api/entitycore/types'; +import Visualization from '@/ui/segments/viz'; export default async function Page({ params, }: ServerSideComponentProp) { const { virtualLabId, projectId, type, id } = await params; + const ctx = { virtualLabId, projectId }; const { entity, entityType } = await downloadEntity({ type, - ctx: { virtualLabId, projectId }, + ctx, id, }); @@ -26,28 +26,19 @@ export default async function Page({ const commonFields = CommonSummaryViewFields; - function renderViz() { - if (entity.type === 'reconstruction_morphology') { - return ; - } - - return null; - } - - const viz = renderViz(); - return (
Name
{entity.name}
-
+
{[...commonFields, ...fields].map(({ className, field }) => { return ; })}
- {viz &&
{viz}
} + +
); } diff --git a/src/ui/segments/viz.tsx b/src/ui/segments/viz.tsx new file mode 100644 index 000000000..58e6154f1 --- /dev/null +++ b/src/ui/segments/viz.tsx @@ -0,0 +1,26 @@ +'use client'; + +import { MorphoViewerLoaderMemo } from '@/features/entities/reconstruction-morphology/detail-view'; +import { IReconstructionMorphology, IElectricalCellRecording } from '@/api/entitycore/types'; +import EphysViewer from '@/features/ephys-viewer'; + +import { downloadEntity } from '@/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout'; +import { WorkspaceContext } from '@/types/common'; + +type AwaitedType = T extends Promise ? U : T; +export default function Visualization({ + entity, + ctx, +}: { + entity: AwaitedType>['entity']; + ctx: WorkspaceContext; +}) { + if (entity.type === 'reconstruction_morphology') { + return ; + } + if (entity.type === 'electrical_cell_recording') { + return ; + } + + return null; +} From a7a78a144ef514693613bc19ee54b98e2afc490e Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 25 Aug 2025 14:45:12 +0200 Subject: [PATCH 30/80] prettier --- .../view/[type]/[id]/[section]/page.tsx | 36 +++++++++++++++ .../view/[type]/[id]/analysis/page.tsx | 3 -- .../view/[type]/[id]/artifacts/page.tsx | 3 -- .../explore/view/[type]/[id]/layout.tsx | 4 +- .../view/[type]/[id]/overview/page.tsx | 44 ------------------- .../view/[type]/[id]/publications/page.tsx | 3 -- src/entity-configuration/definitions/types.ts | 7 +++ .../domain/experimental/bouton-density.ts | 1 + .../experimental/electrical-cell-recording.ts | 1 + .../domain/experimental/neuron-density.ts | 1 + .../experimental/reconstruction-morphology.ts | 1 + .../experimental/synapse-per-connection.ts | 1 + src/entity-configuration/domain/index.ts | 5 +++ .../domain/model/circuit.ts | 1 + .../domain/model/e-model.ts | 1 + .../domain/model/me-model.ts | 1 + .../domain/model/mirocircuit.ts | 1 + .../domain/model/paired-neurons.ts | 1 + .../domain/model/single-neuron-synaptome.ts | 1 + .../domain/model/small-microcircuit.ts | 1 + .../simulation/paired-neurons-simulation.ts | 1 + .../domain/simulation/simulation-campaign.ts | 1 + .../simulation/single-neuron-simulation.ts | 1 + .../single-neuron-synaptome-simulation.ts | 1 + .../small-microcircuit-simulation.ts | 1 + src/entity-configuration/domain/types.ts | 2 + src/ui/segments/detail-view/overview.tsx | 34 ++++++++++++++ src/ui/segments/explore/detail-menu.tsx | 21 ++++----- 28 files changed, 111 insertions(+), 68 deletions(-) create mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx delete mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/analysis/page.tsx delete mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/artifacts/page.tsx delete mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx delete mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/publications/page.tsx create mode 100644 src/ui/segments/detail-view/overview.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx new file mode 100644 index 000000000..19d131ac2 --- /dev/null +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx @@ -0,0 +1,36 @@ +import { notFound } from 'next/navigation'; +import { downloadEntity } from '../layout'; +import { DetailViewSection } from '@/entity-configuration/definitions/types'; +import { getEntityBySlug } from '@/entity-configuration/domain/helpers'; +import { EntitySlugValue } from '@/entity-configuration/domain/slug'; +import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; +import Overview from '@/ui/segments/detail-view/overview'; +import { getViewDefinitionByExtendedType } from '@/entity-configuration/definitions/view-defs'; + +export default async function Page({ + params, +}: ServerSideComponentProp< + WorkspaceContext & { section: DetailViewSection; id: string; type: EntitySlugValue }, + null +>) { + const { virtualLabId, projectId, section, type, id } = await params; + const ctx = { virtualLabId, projectId }; + + const entityType = getEntityBySlug({ slug: type }); + if (!entityType || !entityType.detailViewSections.includes(section)) notFound(); + + if (section === 'overview') { + const { entity } = await downloadEntity({ + type, + ctx, + id, + }); + + const fields = getViewDefinitionByExtendedType(entityType.extendedType)?.summaryViewFields; + + if (!fields || !entity) notFound(); + return ; + } + + return notFound(); +} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/analysis/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/analysis/page.tsx deleted file mode 100644 index 55f2f38df..000000000 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/analysis/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Analysis() { - return 'analysis'; -} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/artifacts/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/artifacts/page.tsx deleted file mode 100644 index 7595c9aa0..000000000 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/artifacts/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Artifacts() { - return 'artifacts'; -} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index c1d1889df..440d0a95e 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -13,6 +13,7 @@ interface Params { id: string; type: EntitySlugValue; } +type AwaitedType = T extends Promise ? U : T; export async function downloadEntity({ type, @@ -31,7 +32,6 @@ export async function downloadEntity({ const fetchEntity = entityType.api.query.one; if (!fetchEntity) throw Error(`No fetch one function defined for type ${entityType}`); - type AwaitedType = T extends Promise ? U : T; let entity: AwaitedType> | undefined; @@ -79,7 +79,7 @@ export default async function Layout({ {entity.name}
- +
diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx deleted file mode 100644 index 71e92675e..000000000 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { notFound } from 'next/navigation'; -import { downloadEntity } from '../layout'; -import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; -import { - CommonSummaryViewFields, - getViewDefinitionByExtendedType, -} from '@/entity-configuration/definitions/view-defs'; -import { EntitySlugValue } from '@/entity-configuration/domain/slug'; -import { Field } from '@/features/details-view/overview'; -import Visualization from '@/ui/segments/viz'; - -export default async function Page({ - params, -}: ServerSideComponentProp) { - const { virtualLabId, projectId, type, id } = await params; - const ctx = { virtualLabId, projectId }; - - const { entity, entityType } = await downloadEntity({ - type, - ctx, - id, - }); - - const fields = getViewDefinitionByExtendedType(entityType.extendedType)?.summaryViewFields; - if (!fields) notFound(); - - const commonFields = CommonSummaryViewFields; - - return ( -
-
-
Name
-
{entity.name}
-
-
- {[...commonFields, ...fields].map(({ className, field }) => { - return ; - })} -
- - -
- ); -} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/publications/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/publications/page.tsx deleted file mode 100644 index c320ef5c5..000000000 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/publications/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Publications() { - return 'publications'; -} diff --git a/src/entity-configuration/definitions/types.ts b/src/entity-configuration/definitions/types.ts index c4929438c..50690fc0a 100644 --- a/src/entity-configuration/definitions/types.ts +++ b/src/entity-configuration/definitions/types.ts @@ -117,3 +117,10 @@ export type FieldsDefinitionRegistry = Record< Partial, FieldDefinition >; + +export type DetailViewSection = + | 'overview' + | 'analysis' + | 'related-publications' + | 'related-artifacts' + | 'configuration'; diff --git a/src/entity-configuration/domain/experimental/bouton-density.ts b/src/entity-configuration/domain/experimental/bouton-density.ts index 54e4bc1c4..7c8379c95 100644 --- a/src/entity-configuration/domain/experimental/bouton-density.ts +++ b/src/entity-configuration/domain/experimental/bouton-density.ts @@ -35,4 +35,5 @@ export const BoutonDensity: EntityCoreTypeConfig = { }, // viewDefinition: ViewsDefinitionRegistry[DataType.ExperimentalBoutonDensity], isBookmarkable: true, + detailViewSections: ['overview'], } as const; diff --git a/src/entity-configuration/domain/experimental/electrical-cell-recording.ts b/src/entity-configuration/domain/experimental/electrical-cell-recording.ts index b40dfc53e..180922285 100644 --- a/src/entity-configuration/domain/experimental/electrical-cell-recording.ts +++ b/src/entity-configuration/domain/experimental/electrical-cell-recording.ts @@ -41,4 +41,5 @@ export const ElectricalCellRecording: EntityCoreTypeConfig = { extension: 'application/json', }, isBookmarkable: true, + detailViewSections: ['overview'], } as const; diff --git a/src/entity-configuration/domain/experimental/reconstruction-morphology.ts b/src/entity-configuration/domain/experimental/reconstruction-morphology.ts index 291d258c1..63d03fd78 100644 --- a/src/entity-configuration/domain/experimental/reconstruction-morphology.ts +++ b/src/entity-configuration/domain/experimental/reconstruction-morphology.ts @@ -40,4 +40,5 @@ export const ReconstructionMorphology: EntityCoreTypeConfig< }, viewDefinition: ViewsDefinitionRegistry[ExtendedEntitiesTypeDict.ReconstructionMorphology], isBookmarkable: true, + detailViewSections: ['overview'], } as const; diff --git a/src/entity-configuration/domain/experimental/synapse-per-connection.ts b/src/entity-configuration/domain/experimental/synapse-per-connection.ts index 11fd69b6a..139c6efd1 100644 --- a/src/entity-configuration/domain/experimental/synapse-per-connection.ts +++ b/src/entity-configuration/domain/experimental/synapse-per-connection.ts @@ -33,4 +33,5 @@ export const SynapsePerConnection: EntityCoreTypeConfig = T extends EntityCoreTypeConfig ? U : never; + +export type EntityTypeValue = InnerEntityType; diff --git a/src/entity-configuration/domain/model/circuit.ts b/src/entity-configuration/domain/model/circuit.ts index 5b4a20bc9..fcd9c6ab2 100644 --- a/src/entity-configuration/domain/model/circuit.ts +++ b/src/entity-configuration/domain/model/circuit.ts @@ -30,4 +30,5 @@ export const Circuit: EntityCoreTypeConfig = { // configfile: AssetLabel.single_neuron_synaptome_config, }, isBookmarkable: true, + detailViewSections: ['overview'], } as const; diff --git a/src/entity-configuration/domain/model/e-model.ts b/src/entity-configuration/domain/model/e-model.ts index 8b33cccf1..d134ffb6d 100644 --- a/src/entity-configuration/domain/model/e-model.ts +++ b/src/entity-configuration/domain/model/e-model.ts @@ -35,4 +35,5 @@ export const Emodel: EntityCoreTypeConfig = { extension: undefined, }, isBookmarkable: true, + detailViewSections: ['overview'], } as const; diff --git a/src/entity-configuration/domain/model/me-model.ts b/src/entity-configuration/domain/model/me-model.ts index 452363c30..30867dbd2 100644 --- a/src/entity-configuration/domain/model/me-model.ts +++ b/src/entity-configuration/domain/model/me-model.ts @@ -31,4 +31,5 @@ export const MEmodel: EntityCoreTypeConfig = { extension: undefined, }, isBookmarkable: true, + detailViewSections: ['overview'], } as const; diff --git a/src/entity-configuration/domain/model/mirocircuit.ts b/src/entity-configuration/domain/model/mirocircuit.ts index 87d1a74c1..703fe80f3 100644 --- a/src/entity-configuration/domain/model/mirocircuit.ts +++ b/src/entity-configuration/domain/model/mirocircuit.ts @@ -34,4 +34,5 @@ export const Microcircuit: EntityCoreTypeConfig = { extension: 'application/json', }, isBookmarkable: true, + detailViewSections: ['overview'], } as const; diff --git a/src/entity-configuration/domain/model/paired-neurons.ts b/src/entity-configuration/domain/model/paired-neurons.ts index ffe90a252..0c67b4e2a 100644 --- a/src/entity-configuration/domain/model/paired-neurons.ts +++ b/src/entity-configuration/domain/model/paired-neurons.ts @@ -34,4 +34,5 @@ export const PairedNeuronCircuit: EntityCoreTypeConfig = { extension: 'application/json', }, isBookmarkable: true, + detailViewSections: ['overview'], } as const; diff --git a/src/entity-configuration/domain/model/single-neuron-synaptome.ts b/src/entity-configuration/domain/model/single-neuron-synaptome.ts index b51af4ee1..748d56fa1 100644 --- a/src/entity-configuration/domain/model/single-neuron-synaptome.ts +++ b/src/entity-configuration/domain/model/single-neuron-synaptome.ts @@ -48,4 +48,5 @@ export const SingleNeuronSynaptome: EntityCoreTypeConfig configfile: AssetLabel.single_neuron_synaptome_config, }, isBookmarkable: true, + detailViewSections: ['overview'], } as const; diff --git a/src/entity-configuration/domain/model/small-microcircuit.ts b/src/entity-configuration/domain/model/small-microcircuit.ts index cbdb8517b..45be4fd4a 100644 --- a/src/entity-configuration/domain/model/small-microcircuit.ts +++ b/src/entity-configuration/domain/model/small-microcircuit.ts @@ -34,4 +34,5 @@ export const SmallMicrocircuit: EntityCoreTypeConfig = { extension: 'application/json', }, isBookmarkable: true, + detailViewSections: ['overview'], } as const; diff --git a/src/entity-configuration/domain/simulation/paired-neurons-simulation.ts b/src/entity-configuration/domain/simulation/paired-neurons-simulation.ts index e6a49647c..96f30b1b2 100644 --- a/src/entity-configuration/domain/simulation/paired-neurons-simulation.ts +++ b/src/entity-configuration/domain/simulation/paired-neurons-simulation.ts @@ -154,4 +154,5 @@ export const PairedNeuronCircuitSimulation: EntityCoreTypeConfig = { group: TEntityTypeGroup; @@ -34,6 +35,7 @@ export type EntityCoreTypeConfig = { }; viewDefinition?: ViewDefinitionConfig | null; isBookmarkable: boolean; + detailViewSections: DetailViewSection[]; }; export type SerializedEntityCoreTypeConfig = Omit< diff --git a/src/ui/segments/detail-view/overview.tsx b/src/ui/segments/detail-view/overview.tsx new file mode 100644 index 000000000..dd12d1f14 --- /dev/null +++ b/src/ui/segments/detail-view/overview.tsx @@ -0,0 +1,34 @@ +import type { WorkspaceContext } from '@/types/common'; +import { CommonSummaryViewFields } from '@/entity-configuration/definitions/view-defs'; +import { Field } from '@/features/details-view/overview'; +import Visualization from '@/ui/segments/viz'; +import { EntityTypeValue } from '@/entity-configuration/domain'; +import { TypeSummaryProps } from '@/entity-configuration/definitions/view-defs/types'; + +export default function Overview({ + entity, + summaryViewFields, + ctx, +}: { + entity: EntityTypeValue; + summaryViewFields: TypeSummaryProps[]; + ctx: WorkspaceContext; +}) { + const commonFields = CommonSummaryViewFields; + + return ( +
+
+
Name
+
{entity.name}
+
+
+ {[...commonFields, ...summaryViewFields].map(({ className, field }) => { + return ; + })} +
+ + +
+ ); +} diff --git a/src/ui/segments/explore/detail-menu.tsx b/src/ui/segments/explore/detail-menu.tsx index 840450125..d6bee3842 100644 --- a/src/ui/segments/explore/detail-menu.tsx +++ b/src/ui/segments/explore/detail-menu.tsx @@ -1,27 +1,22 @@ 'use client'; import { usePathname } from 'next/navigation'; +import capitalize from 'lodash/capitalize'; import Tab from '@/ui/molecules/tab'; +import { DetailViewSection } from '@/entity-configuration/definitions/types'; -export default function DetailMenu() { +export default function DetailMenu({ sections }: { sections: DetailViewSection[] }) { const path = usePathname(); const parentPath = path.split('/').slice(0, -1).join('/'); const page = path.split('/').pop(); return ( <> - - Overview - - - Analysis - - - Related Publications - - - Related Artifacts - + {sections.map((s) => ( + + {capitalize(s.replaceAll('-', ' '))} + + ))} ); } From 0adef03b044737f64d07c961d84b1b0ca6228670 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 25 Aug 2025 21:08:49 +0200 Subject: [PATCH 31/80] entity one required --- .../[projectId]/explore/view/[type]/[id]/layout.tsx | 2 -- src/entity-configuration/domain/types.ts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index 440d0a95e..c7edaa607 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -31,8 +31,6 @@ export async function downloadEntity({ const fetchEntity = entityType.api.query.one; - if (!fetchEntity) throw Error(`No fetch one function defined for type ${entityType}`); - let entity: AwaitedType> | undefined; try { diff --git a/src/entity-configuration/domain/types.ts b/src/entity-configuration/domain/types.ts index a32fd78fd..b6d747b0a 100644 --- a/src/entity-configuration/domain/types.ts +++ b/src/entity-configuration/domain/types.ts @@ -20,7 +20,7 @@ export type EntityCoreTypeConfig = { }; query: { list?: (query: any) => Promise>; - one?: (query: { id: string; context?: WorkspaceContext | null }) => Promise; + one: (query: { id: string; context?: WorkspaceContext | null }) => Promise; create?: (query: any) => Promise; }; expand?: Record Promise>; From 8ab450fcaad74aedc7510ba48137577021b0ccd8 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 26 Aug 2025 11:28:00 +0200 Subject: [PATCH 32/80] Feedback --- .../view/[type]/[id]/[section]/page.tsx | 16 ++++++---- .../explore/view/[type]/[id]/layout.tsx | 29 ++++++++++++------- src/types/common.ts | 1 + src/ui/segments/action-menu.tsx | 15 ++++++---- src/ui/segments/viz.tsx | 2 +- 5 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx index 19d131ac2..5c61f03f4 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx @@ -1,8 +1,12 @@ import { notFound } from 'next/navigation'; +import snakeCase from 'lodash/snakeCase'; import { downloadEntity } from '../layout'; import { DetailViewSection } from '@/entity-configuration/definitions/types'; -import { getEntityBySlug } from '@/entity-configuration/domain/helpers'; -import { EntitySlugValue } from '@/entity-configuration/domain/slug'; +import { + EntityCoreExtendedType, + getEntityByExtendedType, +} from '@/entity-configuration/domain/helpers'; + import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; import Overview from '@/ui/segments/detail-view/overview'; import { getViewDefinitionByExtendedType } from '@/entity-configuration/definitions/view-defs'; @@ -10,18 +14,18 @@ import { getViewDefinitionByExtendedType } from '@/entity-configuration/definiti export default async function Page({ params, }: ServerSideComponentProp< - WorkspaceContext & { section: DetailViewSection; id: string; type: EntitySlugValue }, + WorkspaceContext & { section: DetailViewSection; id: string; type: string }, null >) { const { virtualLabId, projectId, section, type, id } = await params; const ctx = { virtualLabId, projectId }; - const entityType = getEntityBySlug({ slug: type }); + const entityType = getEntityByExtendedType({ type: snakeCase(type) as EntityCoreExtendedType }); if (!entityType || !entityType.detailViewSections.includes(section)) notFound(); if (section === 'overview') { - const { entity } = await downloadEntity({ - type, + const entity = await downloadEntity({ + type: snakeCase(type) as EntityCoreExtendedType, ctx, id, }); diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index c7edaa607..1a1517d29 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -1,32 +1,34 @@ import { ReactNode } from 'react'; import NextLink from 'next/link'; +import snakeCase from 'lodash/snakeCase'; import { notFound } from 'next/navigation'; import Breadcrumb from '@/ui/molecules/breadcrumb'; import { basePath } from '@/config'; -import { getEntityBySlug } from '@/entity-configuration/domain/helpers'; -import { EntitySlugValue } from '@/entity-configuration/domain/slug'; +import { + EntityCoreExtendedType, + getEntityByExtendedType, +} from '@/entity-configuration/domain/helpers'; import DetailMenu from '@/ui/segments/explore/detail-menu'; import ActionMenu from '@/ui/segments/action-menu'; -import type { WorkspaceContext } from '@/types/common'; +import type { WorkspaceContext, AwaitedType } from '@/types/common'; interface Params { id: string; - type: EntitySlugValue; + type: string; } -type AwaitedType = T extends Promise ? U : T; export async function downloadEntity({ type, id, ctx, }: { - type: EntitySlugValue; + type: EntityCoreExtendedType; id: string; ctx: WorkspaceContext; }) { if (!type || !id) notFound(); - const entityType = getEntityBySlug({ slug: type }); + const entityType = getEntityByExtendedType({ type }); if (!entityType) notFound(); const fetchEntity = entityType.api.query.one; @@ -40,7 +42,7 @@ export async function downloadEntity({ } if (!entity) notFound(); - return { entity, entityType }; + return entity; } export default async function Layout({ @@ -50,9 +52,14 @@ export default async function Layout({ children: ReactNode; params: Promise; }) { - const { virtualLabId, projectId, type, id } = await params; + const awaitedParams = await params; + const { virtualLabId, projectId, id } = awaitedParams; + const type = snakeCase(awaitedParams.type) as EntityCoreExtendedType; - const { entity, entityType } = await downloadEntity({ + const entityType = getEntityByExtendedType({ type }); + if (!entityType) notFound(); + + const entity = await downloadEntity({ type, id, ctx: { virtualLabId, projectId }, @@ -79,7 +86,7 @@ export default async function Layout({
- +
{children}
diff --git a/src/types/common.ts b/src/types/common.ts index b9040924f..374b28edc 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -41,3 +41,4 @@ export const WorkspaceContextSchema = z.object({ }); export type WorkspaceContext = z.infer; +export type AwaitedType = T extends Promise ? U : T; diff --git a/src/ui/segments/action-menu.tsx b/src/ui/segments/action-menu.tsx index b7f3bf250..d16adeda5 100644 --- a/src/ui/segments/action-menu.tsx +++ b/src/ui/segments/action-menu.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { notFound } from 'next/navigation'; import { BookOutlined, CopyOutlined, @@ -20,25 +21,27 @@ import { import { useAppNotification } from '@/components/notification'; import { deleteBookmarksFromProjectLibrary } from '@/features/bookmark/actions'; import { downloadArchive } from '@/services/entity-download'; -import { EntitySlugValue } from '@/entity-configuration/domain/slug'; -import { getEntityBySlug } from '@/entity-configuration/domain/helpers'; +import { + EntityCoreExtendedType, + getEntityByExtendedType, +} from '@/entity-configuration/domain/helpers'; export default function ActionMenu({ entity, ctx, - entitySlug, + type, }: { entity: T; ctx: { virtualLabId: string; projectId: string }; - entitySlug: EntitySlugValue; + type: EntityCoreExtendedType; }) { const queryClient = useQueryClient(); const [copied, setCopied] = useState(false); const [loading, setLoading] = useState(false); const notification = useAppNotification(); - const entityType = getEntityBySlug({ slug: entitySlug }); - if (!entityType) throw Error('Invalid entity type'); + const entityType = getEntityByExtendedType({ type }); + if (!entityType) notFound(); const bookmarks = useQuery({ queryKey: [ctx.projectId, ctx.virtualLabId, entityType.type], diff --git a/src/ui/segments/viz.tsx b/src/ui/segments/viz.tsx index 58e6154f1..c5d77ab18 100644 --- a/src/ui/segments/viz.tsx +++ b/src/ui/segments/viz.tsx @@ -12,7 +12,7 @@ export default function Visualization({ entity, ctx, }: { - entity: AwaitedType>['entity']; + entity: AwaitedType>; ctx: WorkspaceContext; }) { if (entity.type === 'reconstruction_morphology') { From fc46c14d807cb0149941cd604435f5f4799d5748 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 26 Aug 2025 11:51:24 +0200 Subject: [PATCH 33/80] minor --- src/ui/segments/action-menu.tsx | 71 ++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/src/ui/segments/action-menu.tsx b/src/ui/segments/action-menu.tsx index d16adeda5..3d61b1f43 100644 --- a/src/ui/segments/action-menu.tsx +++ b/src/ui/segments/action-menu.tsx @@ -1,8 +1,9 @@ 'use client'; import { useState } from 'react'; -import { useQuery, useQueryClient } from '@tanstack/react-query'; import { notFound } from 'next/navigation'; + +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { BookOutlined, CopyOutlined, @@ -25,6 +26,7 @@ import { EntityCoreExtendedType, getEntityByExtendedType, } from '@/entity-configuration/domain/helpers'; +import { keyBuilder } from '@/ui/use-query-keys/workspace'; export default function ActionMenu({ entity, @@ -37,44 +39,51 @@ export default function ActionMenu({ }) { const queryClient = useQueryClient(); const [copied, setCopied] = useState(false); - const [loading, setLoading] = useState(false); const notification = useAppNotification(); const entityType = getEntityByExtendedType({ type }); if (!entityType) notFound(); const bookmarks = useQuery({ - queryKey: [ctx.projectId, ctx.virtualLabId, entityType.type], + queryKey: keyBuilder.bookmarks({ + virtualLabId: ctx.virtualLabId, + projectId: ctx.projectId, + category: entityType.extendedType, + }), queryFn: async () => getAllBookmarksByCategory(ctx, { category: entityType.type }), }); const existingBookmarks = bookmarks.data?.data?.[entityType.type]?.map((b) => b.entity_id); const isBookmarked = !!existingBookmarks && existingBookmarks.includes(entity.id); - const handleBookmark = async () => { - setLoading(true); - try { - await bookmarkToProjectLibrary(ctx, { + const mutation = useMutation({ + mutationFn: () => + bookmarkToProjectLibrary(ctx, { entity_id: entity.id, category: entityType.type, - }); - + }), + onSuccess: async () => { await queryClient.invalidateQueries({ - queryKey: [ctx.projectId, ctx.virtualLabId, entityType.type], + queryKey: keyBuilder.bookmarks({ + virtualLabId: ctx.virtualLabId, + projectId: ctx.projectId, + category: entityType.extendedType, + }), }); - notification.success({ message: 'Entity successfully bookmarked' }); - } catch { + }, + onError: () => { notification.error({ message: "Couldn't add entity to bookmarks" }); - } finally { - setLoading(false); - } + }, + }); + + const handleBookmark = () => { + mutation.mutate(); }; - const handleRemoveBookmark = async () => { - setLoading(true); - try { - await deleteBookmarksFromProjectLibrary({ + const removeBookmarkMutation = useMutation({ + mutationFn: () => + deleteBookmarksFromProjectLibrary({ virtualLabId: ctx.virtualLabId, projectId: ctx.projectId, bookmarks: [ @@ -83,20 +92,28 @@ export default function ActionMenu({ category: entityType.type, }, ], - }); - + }), + onSuccess: async () => { await queryClient.invalidateQueries({ - queryKey: [ctx.projectId, ctx.virtualLabId, entityType.type], + queryKey: keyBuilder.bookmarks({ + virtualLabId: ctx.virtualLabId, + projectId: ctx.projectId, + category: entityType.extendedType, + }), }); - notification.success({ message: 'Bookmark removed from library' }); - } catch { + }, + onError: () => { notification.error({ message: "Couldn't remove bookmark" }); - } finally { - setLoading(false); - } + }, + }); + + const handleRemoveBookmark = () => { + removeBookmarkMutation.mutate(); }; + const loading = mutation.isPending || removeBookmarkMutation.isPending; + const getBookmarkHandler = () => { if (loading) return undefined; if (!isBookmarked) return handleBookmark; From 7e884367af9dfcfa12a1fc88c782730eb969523f Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 26 Aug 2025 11:55:57 +0200 Subject: [PATCH 34/80] remove unused routes --- .../[projectId]/explore/view/[type]/(root)/[id]/page.tsx | 8 -------- .../explore/view/[type]/[id]/analysis/page.tsx | 3 --- .../explore/view/[type]/[id]/artifacts/page.tsx | 3 --- .../explore/view/[type]/[id]/overview/page.tsx | 3 --- .../explore/view/[type]/[id]/publications/page.tsx | 3 --- 5 files changed, 20 deletions(-) delete mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/(root)/[id]/page.tsx delete mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/analysis/page.tsx delete mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/artifacts/page.tsx delete mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx delete mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/publications/page.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/(root)/[id]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/(root)/[id]/page.tsx deleted file mode 100644 index 31d105b90..000000000 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/(root)/[id]/page.tsx +++ /dev/null @@ -1,8 +0,0 @@ -'use client'; - -import { redirect, usePathname } from 'next/navigation'; - -export default function DetailPage() { - const path = usePathname(); - redirect(`${path}/overview`); -} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/analysis/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/analysis/page.tsx deleted file mode 100644 index 55f2f38df..000000000 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/analysis/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Analysis() { - return 'analysis'; -} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/artifacts/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/artifacts/page.tsx deleted file mode 100644 index 7595c9aa0..000000000 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/artifacts/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Artifacts() { - return 'artifacts'; -} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx deleted file mode 100644 index 64dd56bb2..000000000 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/overview/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Overview() { - return 'page'; -} diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/publications/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/publications/page.tsx deleted file mode 100644 index c320ef5c5..000000000 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/publications/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Publications() { - return 'publications'; -} From f589cdca6ff2dbc1921bc7ce57cdf63073eb4150 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 26 Aug 2025 13:23:01 +0200 Subject: [PATCH 35/80] Redirect to overview --- .../[projectId]/explore/view/[type]/(root)/[id]/page.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/(root)/[id]/page.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/(root)/[id]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/(root)/[id]/page.tsx new file mode 100644 index 000000000..31d105b90 --- /dev/null +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/(root)/[id]/page.tsx @@ -0,0 +1,8 @@ +'use client'; + +import { redirect, usePathname } from 'next/navigation'; + +export default function DetailPage() { + const path = usePathname(); + redirect(`${path}/overview`); +} From eea7c9a5589b3be2f993e9dd613afda4d48ae96b Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 27 Aug 2025 11:00:20 +0200 Subject: [PATCH 36/80] Visualization --- .../view/[type]/[id]/[section]/page.tsx | 27 ++++++++++------- src/entity-configuration/definitions/types.ts | 1 + .../experimental/electrical-cell-recording.ts | 2 +- .../experimental/reconstruction-morphology.ts | 2 +- src/ui/segments/detail-view/overview.tsx | 29 ++++++++++--------- src/ui/segments/viz.tsx | 3 +- 6 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx index 5c61f03f4..a0500d1a2 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx @@ -1,3 +1,4 @@ +import { JSX } from 'react'; import { notFound } from 'next/navigation'; import snakeCase from 'lodash/snakeCase'; import { downloadEntity } from '../layout'; @@ -9,7 +10,7 @@ import { import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; import Overview from '@/ui/segments/detail-view/overview'; -import { getViewDefinitionByExtendedType } from '@/entity-configuration/definitions/view-defs'; +import Visualization from '@/ui/segments/viz'; export default async function Page({ params, @@ -23,18 +24,22 @@ export default async function Page({ const entityType = getEntityByExtendedType({ type: snakeCase(type) as EntityCoreExtendedType }); if (!entityType || !entityType.detailViewSections.includes(section)) notFound(); - if (section === 'overview') { - const entity = await downloadEntity({ - type: snakeCase(type) as EntityCoreExtendedType, - ctx, - id, - }); + const entity = await downloadEntity({ + type: snakeCase(type) as EntityCoreExtendedType, + ctx, + id, + }); - const fields = getViewDefinitionByExtendedType(entityType.extendedType)?.summaryViewFields; + let content: JSX.Element | undefined; - if (!fields || !entity) notFound(); - return ; + if (section === 'overview') { + content = ; } + if (section === 'visualization') { + content = ; + } + + if (!content) notFound(); - return notFound(); + return
{content}
; } diff --git a/src/entity-configuration/definitions/types.ts b/src/entity-configuration/definitions/types.ts index 50690fc0a..86b311a2e 100644 --- a/src/entity-configuration/definitions/types.ts +++ b/src/entity-configuration/definitions/types.ts @@ -120,6 +120,7 @@ export type FieldsDefinitionRegistry = Record< export type DetailViewSection = | 'overview' + | 'visualization' | 'analysis' | 'related-publications' | 'related-artifacts' diff --git a/src/entity-configuration/domain/experimental/electrical-cell-recording.ts b/src/entity-configuration/domain/experimental/electrical-cell-recording.ts index 180922285..75683b244 100644 --- a/src/entity-configuration/domain/experimental/electrical-cell-recording.ts +++ b/src/entity-configuration/domain/experimental/electrical-cell-recording.ts @@ -41,5 +41,5 @@ export const ElectricalCellRecording: EntityCoreTypeConfig + <>
Name
{entity.name}
- {[...commonFields, ...summaryViewFields].map(({ className, field }) => { + {[...commonFields, ...fields].map(({ className, field }) => { return ; })}
- - - + ); } diff --git a/src/ui/segments/viz.tsx b/src/ui/segments/viz.tsx index c5d77ab18..de3641f54 100644 --- a/src/ui/segments/viz.tsx +++ b/src/ui/segments/viz.tsx @@ -1,5 +1,6 @@ 'use client'; +import { notFound } from 'next/navigation'; import { MorphoViewerLoaderMemo } from '@/features/entities/reconstruction-morphology/detail-view'; import { IReconstructionMorphology, IElectricalCellRecording } from '@/api/entitycore/types'; import EphysViewer from '@/features/ephys-viewer'; @@ -22,5 +23,5 @@ export default function Visualization({ return ; } - return null; + notFound(); } From 589985cd807dfa91c8fa8df97f28439daa00e53a Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 27 Aug 2025 11:47:57 +0200 Subject: [PATCH 37/80] analysis tab --- .../view/[type]/[id]/[section]/page.tsx | 4 +++ .../domain/model/e-model.ts | 2 +- .../domain/model/me-model.ts | 2 +- .../model-analysis/explorer/container.tsx | 8 ++++-- .../model-analysis/viewer/container.tsx | 28 +++++++++++-------- src/features/model-analysis/viewer/viewer.tsx | 2 +- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx index a0500d1a2..a31a2e742 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx @@ -11,6 +11,7 @@ import { import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; import Overview from '@/ui/segments/detail-view/overview'; import Visualization from '@/ui/segments/viz'; +import Analysis from '@/features/model-analysis/explorer/container'; export default async function Page({ params, @@ -38,6 +39,9 @@ export default async function Page({ if (section === 'visualization') { content = ; } + if (section === 'analysis') { + content = ; + } if (!content) notFound(); diff --git a/src/entity-configuration/domain/model/e-model.ts b/src/entity-configuration/domain/model/e-model.ts index d134ffb6d..4a7334a4a 100644 --- a/src/entity-configuration/domain/model/e-model.ts +++ b/src/entity-configuration/domain/model/e-model.ts @@ -35,5 +35,5 @@ export const Emodel: EntityCoreTypeConfig = { extension: undefined, }, isBookmarkable: true, - detailViewSections: ['overview'], + detailViewSections: ['overview', 'analysis'], } as const; diff --git a/src/entity-configuration/domain/model/me-model.ts b/src/entity-configuration/domain/model/me-model.ts index 30867dbd2..902f0039d 100644 --- a/src/entity-configuration/domain/model/me-model.ts +++ b/src/entity-configuration/domain/model/me-model.ts @@ -31,5 +31,5 @@ export const MEmodel: EntityCoreTypeConfig = { extension: undefined, }, isBookmarkable: true, - detailViewSections: ['overview'], + detailViewSections: ['overview', 'analysis'], } as const; diff --git a/src/features/model-analysis/explorer/container.tsx b/src/features/model-analysis/explorer/container.tsx index b715350b9..8348c467b 100644 --- a/src/features/model-analysis/explorer/container.tsx +++ b/src/features/model-analysis/explorer/container.tsx @@ -1,7 +1,7 @@ 'use client'; import { LoadingOutlined } from '@ant-design/icons'; -import { useParams } from 'next/navigation'; +import { notFound, useParams } from 'next/navigation'; import { match } from 'ts-pattern'; import { useMemo } from 'react'; import { Spin } from 'antd'; @@ -11,8 +11,12 @@ import { ViewerContainer } from '@/features/model-analysis/viewer/container'; import { useLoadableValue } from '@/hooks/hooks'; import type { WorkspaceContext } from '@/types/common'; +import { EntityCoreExtendedType } from '@/entity-configuration/domain/helpers'; + +export default function Analysis({ extendedType }: { extendedType?: EntityCoreExtendedType }) { + const validTypes: EntityCoreExtendedType[] = ['emodel', 'memodel']; + if (!extendedType || !validTypes.includes(extendedType)) notFound(); -export default function AnalysisContainer() { const { virtualLabId, projectId, id } = useParams(); const results = useLoadableValue( diff --git a/src/features/model-analysis/viewer/container.tsx b/src/features/model-analysis/viewer/container.tsx index fe21f8c51..a9fa73183 100644 --- a/src/features/model-analysis/viewer/container.tsx +++ b/src/features/model-analysis/viewer/container.tsx @@ -1,12 +1,12 @@ import { useMemo, useState } from 'react'; -import { Tabs, Collapse } from 'antd'; +import { Collapse } from 'antd'; import dynamic from 'next/dynamic'; import { AllowedTypes } from '@/features/model-analysis/viewer/storage'; -import { classNames } from '@/util/utils'; import type { IValidationConstructedResult } from '@/features/model-analysis/explorer/context'; import type { TAllowedTypes } from '@/features/model-analysis/viewer/storage'; +import { Button } from '@/ui/molecules/button'; const Viewer = dynamic(() => import('@/features/model-analysis/viewer/viewer'), { ssr: false, @@ -70,17 +70,21 @@ export function ViewerContainer({ validationResults }: Props) { ); return ( -
-
- +
+
+ {tabs.map((t) => ( + + ))}
-
+ +
{type === 'all' ? ( +
{validationResult.assets ?.filter((o) => AllowedTypes.includes(o.content_type as TAllowedTypes)) .map((asset, ix) => { From 1cff2172503698ed246b911e8048c613a6c7e6ed Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 27 Aug 2025 12:30:57 +0200 Subject: [PATCH 38/80] emodel related artifacts --- .../view/[type]/[id]/[section]/page.tsx | 5 ++ .../domain/model/e-model.ts | 2 +- .../detail-view/related-artifacts.tsx | 49 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/ui/segments/detail-view/related-artifacts.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx index a31a2e742..d0f1a0469 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx @@ -12,6 +12,7 @@ import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; import Overview from '@/ui/segments/detail-view/overview'; import Visualization from '@/ui/segments/viz'; import Analysis from '@/features/model-analysis/explorer/container'; +import RelatedArtifacts from '@/ui/segments/detail-view/related-artifacts'; export default async function Page({ params, @@ -43,6 +44,10 @@ export default async function Page({ content = ; } + if (section === 'related-artifacts') { + content = ; + } + if (!content) notFound(); return
{content}
; diff --git a/src/entity-configuration/domain/model/e-model.ts b/src/entity-configuration/domain/model/e-model.ts index 4a7334a4a..b1db3c7f3 100644 --- a/src/entity-configuration/domain/model/e-model.ts +++ b/src/entity-configuration/domain/model/e-model.ts @@ -35,5 +35,5 @@ export const Emodel: EntityCoreTypeConfig = { extension: undefined, }, isBookmarkable: true, - detailViewSections: ['overview', 'analysis'], + detailViewSections: ['overview', 'analysis', 'related-artifacts'], } as const; diff --git a/src/ui/segments/detail-view/related-artifacts.tsx b/src/ui/segments/detail-view/related-artifacts.tsx new file mode 100644 index 000000000..169d59add --- /dev/null +++ b/src/ui/segments/detail-view/related-artifacts.tsx @@ -0,0 +1,49 @@ +import { notFound } from 'next/navigation'; +import { + EntityCoreExtendedType, + getEntityByExtendedType, +} from '@/entity-configuration/domain/helpers'; + +import EModelView from '@/components/build-section/cell-model-assignment/e-model/EModelView'; +import { EntityTypeValue } from '@/entity-configuration/domain'; +import { WorkspaceContext } from '@/types/common'; +import { + IEModel, + IReconstructionMorphology, + IReconstructionMorphologyExpanded, +} from '@/api/entitycore/types'; +import { getReconstructionMorphology } from '@/api/entitycore/queries'; + +export default async function RelatedArtifacts({ + entity, + extendedType, + ctx, +}: { + entity: EntityTypeValue; + extendedType: EntityCoreExtendedType; + ctx: WorkspaceContext; +}) { + const entityType = getEntityByExtendedType({ type: extendedType }); + if (!entityType) notFound(); + + if (extendedType === 'emodel') { + let morphology: IReconstructionMorphologyExpanded | IReconstructionMorphology; + + try { + morphology = await getReconstructionMorphology({ + id: (entity as IEModel).exemplar_morphology.id, + expand: 'measurement_annotation', + context: ctx, + }); + } catch { + notFound(); + } + + return ( + + ); + } +} From bfa97dbb207da9f4c87e95a33f95590584132934 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 27 Aug 2025 16:45:22 +0200 Subject: [PATCH 39/80] ion channel to ion channel model --- .../e-model/EModelView/ion-channels.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/build-section/cell-model-assignment/e-model/EModelView/ion-channels.tsx b/src/components/build-section/cell-model-assignment/e-model/EModelView/ion-channels.tsx index e6ee0b19c..30c9f9125 100644 --- a/src/components/build-section/cell-model-assignment/e-model/EModelView/ion-channels.tsx +++ b/src/components/build-section/cell-model-assignment/e-model/EModelView/ion-channels.tsx @@ -67,19 +67,19 @@ export default function IonChannels({ source }: Props) { if (error) return ( - Ion channels + Ion channel models ); if (!channels) return ( - Ion channels + Ion channel models ); return (
-
Ion channels
+
Ion channel models
); From 2b0bb725f6f11304ca646cc77e36096bae3b90fa Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 27 Aug 2025 17:09:46 +0200 Subject: [PATCH 40/80] emodel, memodel --- .../view/[type]/[id]/[section]/page.tsx | 5 ++ .../explore/view/[type]/[id]/layout.tsx | 2 +- .../domain/model/e-model.ts | 2 +- .../domain/model/me-model.ts | 2 +- .../entities/me-model/detail-view/index.tsx | 3 +- .../me-model/detail-view/simulation.tsx | 5 +- src/ui/segments/detail-view/configuration.tsx | 57 +++++++++++++++++++ .../detail-view/related-artifacts.tsx | 25 ++------ 8 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 src/ui/segments/detail-view/configuration.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx index d0f1a0469..0191121fa 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx @@ -13,6 +13,7 @@ import Overview from '@/ui/segments/detail-view/overview'; import Visualization from '@/ui/segments/viz'; import Analysis from '@/features/model-analysis/explorer/container'; import RelatedArtifacts from '@/ui/segments/detail-view/related-artifacts'; +import Configuration from '@/ui/segments/detail-view/configuration'; export default async function Page({ params, @@ -44,6 +45,10 @@ export default async function Page({ content = ; } + if (section === 'configuration') { + content = ; + } + if (section === 'related-artifacts') { content = ; } diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx index 1a1517d29..6392c79ef 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/layout.tsx @@ -76,7 +76,7 @@ export default async function Layout({ {entityType.title} diff --git a/src/entity-configuration/domain/model/e-model.ts b/src/entity-configuration/domain/model/e-model.ts index b1db3c7f3..d3d00af10 100644 --- a/src/entity-configuration/domain/model/e-model.ts +++ b/src/entity-configuration/domain/model/e-model.ts @@ -35,5 +35,5 @@ export const Emodel: EntityCoreTypeConfig = { extension: undefined, }, isBookmarkable: true, - detailViewSections: ['overview', 'analysis', 'related-artifacts'], + detailViewSections: ['overview', 'analysis', 'configuration'], } as const; diff --git a/src/entity-configuration/domain/model/me-model.ts b/src/entity-configuration/domain/model/me-model.ts index 902f0039d..a0aaf6b7c 100644 --- a/src/entity-configuration/domain/model/me-model.ts +++ b/src/entity-configuration/domain/model/me-model.ts @@ -31,5 +31,5 @@ export const MEmodel: EntityCoreTypeConfig = { extension: undefined, }, isBookmarkable: true, - detailViewSections: ['overview', 'analysis'], + detailViewSections: ['overview', 'analysis', 'configuration', 'related-artifacts'], } as const; diff --git a/src/features/entities/me-model/detail-view/index.tsx b/src/features/entities/me-model/detail-view/index.tsx index 114a0dc64..d35caa515 100644 --- a/src/features/entities/me-model/detail-view/index.tsx +++ b/src/features/entities/me-model/detail-view/index.tsx @@ -15,7 +15,6 @@ import If from '@/components/ConditionalRenderer/If'; import { useClearClientStorageCacheByKey } from '@/features/model-analysis/viewer/storage'; import { EntityCoreFields } from '@/entity-configuration/definitions/fields-defs/enums'; -import { EntitySlug } from '@/entity-configuration/domain/slug'; import { ExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; import { resolveExperimentUrl } from '@/utils/url-builder'; import { EntityTypeDict } from '@/api/entitycore/types'; @@ -91,7 +90,7 @@ export default function SummaryView({ showViewMode = false, payload: { source } - +
diff --git a/src/features/entities/me-model/detail-view/simulation.tsx b/src/features/entities/me-model/detail-view/simulation.tsx index 719bf9c17..9fe8caeda 100644 --- a/src/features/entities/me-model/detail-view/simulation.tsx +++ b/src/features/entities/me-model/detail-view/simulation.tsx @@ -1,3 +1,5 @@ +'use client'; + import { LoadingOutlined } from '@ant-design/icons'; import { ErrorBoundary } from '@sentry/nextjs'; import { useEffect, useState } from 'react'; @@ -10,13 +12,10 @@ import { getSingleNeuronSimulations } from '@/api/entitycore/queries'; import { EntityTypeDict } from '@/api/entitycore/types'; import { tryCatch } from '@/api/utils'; -import type { EntitySlugValue } from '@/entity-configuration/domain/slug'; import type { ISingleNeuronSimulation } from '@/api/entitycore/types'; import type { WorkspaceContext } from '@/types/common'; type Props = { - // eslint-disable-next-line react/no-unused-prop-types - type: EntitySlugValue; modelId: string; }; diff --git a/src/ui/segments/detail-view/configuration.tsx b/src/ui/segments/detail-view/configuration.tsx new file mode 100644 index 000000000..0264d5ab5 --- /dev/null +++ b/src/ui/segments/detail-view/configuration.tsx @@ -0,0 +1,57 @@ +import { notFound } from 'next/navigation'; +import { + EntityCoreExtendedType, + getEntityByExtendedType, +} from '@/entity-configuration/domain/helpers'; + +import EModelView from '@/components/build-section/cell-model-assignment/e-model/EModelView'; +import { EntityTypeValue } from '@/entity-configuration/domain'; +import { WorkspaceContext } from '@/types/common'; +import { + IEModel, + IMEModel, + IReconstructionMorphology, + IReconstructionMorphologyExpanded, +} from '@/api/entitycore/types'; +import { getReconstructionMorphology } from '@/api/entitycore/queries'; +import MEModelConfig from '@/features/entities/me-model/detail-view/configuration'; + +export default async function Configuration({ + entity, + extendedType, + ctx, +}: { + entity: EntityTypeValue; + extendedType: EntityCoreExtendedType; + ctx: WorkspaceContext; +}) { + const entityType = getEntityByExtendedType({ type: extendedType }); + if (!entityType) notFound(); + + if (extendedType === 'emodel') { + let morphology: IReconstructionMorphologyExpanded | IReconstructionMorphology; + + try { + morphology = await getReconstructionMorphology({ + id: (entity as IEModel).exemplar_morphology.id, + expand: 'measurement_annotation', + context: ctx, + }); + } catch { + notFound(); + } + + return ( + + ); + } + + if (extendedType === 'memodel') { + return ; + } + + notFound(); +} diff --git a/src/ui/segments/detail-view/related-artifacts.tsx b/src/ui/segments/detail-view/related-artifacts.tsx index 169d59add..1ad369a1b 100644 --- a/src/ui/segments/detail-view/related-artifacts.tsx +++ b/src/ui/segments/detail-view/related-artifacts.tsx @@ -4,7 +4,7 @@ import { getEntityByExtendedType, } from '@/entity-configuration/domain/helpers'; -import EModelView from '@/components/build-section/cell-model-assignment/e-model/EModelView'; +import Simulation from '@/features/entities/me-model/detail-view/simulation'; import { EntityTypeValue } from '@/entity-configuration/domain'; import { WorkspaceContext } from '@/types/common'; import { @@ -26,24 +26,9 @@ export default async function RelatedArtifacts({ const entityType = getEntityByExtendedType({ type: extendedType }); if (!entityType) notFound(); - if (extendedType === 'emodel') { - let morphology: IReconstructionMorphologyExpanded | IReconstructionMorphology; - - try { - morphology = await getReconstructionMorphology({ - id: (entity as IEModel).exemplar_morphology.id, - expand: 'measurement_annotation', - context: ctx, - }); - } catch { - notFound(); - } - - return ( - - ); + if (extendedType === 'memodel') { + return ; } + + notFound(); } From dd6c47136b20d17f5c4cc493b02971e04a57e7c1 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 27 Aug 2025 21:36:59 +0200 Subject: [PATCH 41/80] add synaptome --- .../view/[type]/[id]/[section]/page.tsx | 2 +- .../domain/model/single-neuron-synaptome.ts | 2 +- .../detail-view/configuration.tsx | 2 ++ .../detail-view/index.tsx | 6 +--- .../detail-view/simulation.tsx | 5 ++- .../explore/single-neuron-synaptome.tsx | 6 +++- src/ui/segments/detail-view/configuration.tsx | 32 ++++++++++++++++++- .../detail-view/related-artifacts.tsx | 14 +++----- 8 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx index 0191121fa..b10588ce5 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx @@ -50,7 +50,7 @@ export default async function Page({ } if (section === 'related-artifacts') { - content = ; + content = ; } if (!content) notFound(); diff --git a/src/entity-configuration/domain/model/single-neuron-synaptome.ts b/src/entity-configuration/domain/model/single-neuron-synaptome.ts index 748d56fa1..f4235834f 100644 --- a/src/entity-configuration/domain/model/single-neuron-synaptome.ts +++ b/src/entity-configuration/domain/model/single-neuron-synaptome.ts @@ -48,5 +48,5 @@ export const SingleNeuronSynaptome: EntityCoreTypeConfig configfile: AssetLabel.single_neuron_synaptome_config, }, isBookmarkable: true, - detailViewSections: ['overview'], + detailViewSections: ['overview', 'configuration', 'related-artifacts'], } as const; diff --git a/src/features/entities/single-neuron-synaptome/detail-view/configuration.tsx b/src/features/entities/single-neuron-synaptome/detail-view/configuration.tsx index e9fc37e2e..aeb08fed7 100644 --- a/src/features/entities/single-neuron-synaptome/detail-view/configuration.tsx +++ b/src/features/entities/single-neuron-synaptome/detail-view/configuration.tsx @@ -1,3 +1,5 @@ +'use client'; + import Link from 'next/link'; import { ExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; diff --git a/src/features/entities/single-neuron-synaptome/detail-view/index.tsx b/src/features/entities/single-neuron-synaptome/detail-view/index.tsx index e4d6b3d95..1d7c0889c 100644 --- a/src/features/entities/single-neuron-synaptome/detail-view/index.tsx +++ b/src/features/entities/single-neuron-synaptome/detail-view/index.tsx @@ -15,7 +15,6 @@ import If from '@/components/ConditionalRenderer/If'; import { EntityCoreFields } from '@/entity-configuration/definitions/fields-defs/enums'; import { EntityTypeDict } from '@/api/entitycore/types/entity-type'; import { ExtendedEntitiesTypeDict } from '@/api/entitycore/types/extended-entity-type'; -import { EntitySlug } from '@/entity-configuration/domain/slug'; import { resolveExperimentUrl } from '@/utils/url-builder'; import type { TSingleNeuronSynaptomeConfiguration } from '@/api/entitycore/types/entities/single-neuron-synaptome'; @@ -99,10 +98,7 @@ export default function Page({
- +
diff --git a/src/features/entities/single-neuron-synaptome/detail-view/simulation.tsx b/src/features/entities/single-neuron-synaptome/detail-view/simulation.tsx index 6c4cbd651..c8362f998 100644 --- a/src/features/entities/single-neuron-synaptome/detail-view/simulation.tsx +++ b/src/features/entities/single-neuron-synaptome/detail-view/simulation.tsx @@ -1,3 +1,5 @@ +'use client'; + import { LoadingOutlined } from '@ant-design/icons'; import { ErrorBoundary } from '@sentry/nextjs'; import { useEffect, useState } from 'react'; @@ -13,12 +15,9 @@ import { EntityTypeDict } from '@/api/entitycore/types'; import { tryCatch } from '@/api/utils'; import type { ISingleNeuronSynaptomeSimulation } from '@/api/entitycore/types'; -import type { EntitySlugValue } from '@/entity-configuration/domain/slug'; import type { WorkspaceContext } from '@/types/common'; type Props = { - // eslint-disable-next-line react/no-unused-prop-types - type: EntitySlugValue; modelId: string; }; diff --git a/src/page-wrappers/explore/single-neuron-synaptome.tsx b/src/page-wrappers/explore/single-neuron-synaptome.tsx index b1d131a0e..3114eecc6 100644 --- a/src/page-wrappers/explore/single-neuron-synaptome.tsx +++ b/src/page-wrappers/explore/single-neuron-synaptome.tsx @@ -29,7 +29,11 @@ type Props = { }; }; -async function loadExpandedSingleNeuronSynaptome({ id, virtualLabId, projectId }: Props['params']) { +export async function loadExpandedSingleNeuronSynaptome({ + id, + virtualLabId, + projectId, +}: Props['params']) { const { data: source, error } = await tryCatch( getSingleNeuronSynaptome({ id, context: { virtualLabId, projectId } }) ); diff --git a/src/ui/segments/detail-view/configuration.tsx b/src/ui/segments/detail-view/configuration.tsx index 0264d5ab5..ab6781b48 100644 --- a/src/ui/segments/detail-view/configuration.tsx +++ b/src/ui/segments/detail-view/configuration.tsx @@ -6,7 +6,7 @@ import { import EModelView from '@/components/build-section/cell-model-assignment/e-model/EModelView'; import { EntityTypeValue } from '@/entity-configuration/domain'; -import { WorkspaceContext } from '@/types/common'; +import { WorkspaceContext, AwaitedType } from '@/types/common'; import { IEModel, IMEModel, @@ -15,6 +15,9 @@ import { } from '@/api/entitycore/types'; import { getReconstructionMorphology } from '@/api/entitycore/queries'; import MEModelConfig from '@/features/entities/me-model/detail-view/configuration'; +import SynaptomeConfig from '@/features/entities/single-neuron-synaptome/detail-view/configuration'; +import SynapseGroupList from '@/features/entities/single-neuron-synaptome/detail-view/elements/list-synapses-configuration'; +import { loadExpandedSingleNeuronSynaptome } from '@/page-wrappers/explore/single-neuron-synaptome'; export default async function Configuration({ entity, @@ -53,5 +56,32 @@ export default async function Configuration({ return ; } + if (extendedType === 'single_neuron_synaptome') { + let data: AwaitedType>; + try { + data = await loadExpandedSingleNeuronSynaptome({ + virtualLabId: ctx.virtualLabId, + projectId: ctx.projectId, + id: entity.id, + }); + } catch { + notFound(); + } + + return ( +
+ + +
+ +
+
+ ); + } + notFound(); } diff --git a/src/ui/segments/detail-view/related-artifacts.tsx b/src/ui/segments/detail-view/related-artifacts.tsx index 1ad369a1b..2576f8e6b 100644 --- a/src/ui/segments/detail-view/related-artifacts.tsx +++ b/src/ui/segments/detail-view/related-artifacts.tsx @@ -6,22 +6,14 @@ import { import Simulation from '@/features/entities/me-model/detail-view/simulation'; import { EntityTypeValue } from '@/entity-configuration/domain'; -import { WorkspaceContext } from '@/types/common'; -import { - IEModel, - IReconstructionMorphology, - IReconstructionMorphologyExpanded, -} from '@/api/entitycore/types'; -import { getReconstructionMorphology } from '@/api/entitycore/queries'; +import Results from '@/features/entities/single-neuron-synaptome/detail-view/simulation'; export default async function RelatedArtifacts({ entity, extendedType, - ctx, }: { entity: EntityTypeValue; extendedType: EntityCoreExtendedType; - ctx: WorkspaceContext; }) { const entityType = getEntityByExtendedType({ type: extendedType }); if (!entityType) notFound(); @@ -30,5 +22,9 @@ export default async function RelatedArtifacts({ return ; } + if (extendedType === 'single_neuron_synaptome') { + return ; + } + notFound(); } From 02ad8113382be63feae05be69e9d6481f5a76ba2 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 27 Aug 2025 21:39:04 +0200 Subject: [PATCH 42/80] clean up --- src/ui/segments/detail-view/configuration.tsx | 4 ++-- src/ui/segments/detail-view/related-artifacts.tsx | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ui/segments/detail-view/configuration.tsx b/src/ui/segments/detail-view/configuration.tsx index ab6781b48..772de90cc 100644 --- a/src/ui/segments/detail-view/configuration.tsx +++ b/src/ui/segments/detail-view/configuration.tsx @@ -4,7 +4,7 @@ import { getEntityByExtendedType, } from '@/entity-configuration/domain/helpers'; -import EModelView from '@/components/build-section/cell-model-assignment/e-model/EModelView'; +import EModelConfig from '@/components/build-section/cell-model-assignment/e-model/EModelView'; import { EntityTypeValue } from '@/entity-configuration/domain'; import { WorkspaceContext, AwaitedType } from '@/types/common'; import { @@ -45,7 +45,7 @@ export default async function Configuration({ } return ( - diff --git a/src/ui/segments/detail-view/related-artifacts.tsx b/src/ui/segments/detail-view/related-artifacts.tsx index 2576f8e6b..7708b6036 100644 --- a/src/ui/segments/detail-view/related-artifacts.tsx +++ b/src/ui/segments/detail-view/related-artifacts.tsx @@ -4,9 +4,9 @@ import { getEntityByExtendedType, } from '@/entity-configuration/domain/helpers'; -import Simulation from '@/features/entities/me-model/detail-view/simulation'; +import MEModelResults from '@/features/entities/me-model/detail-view/simulation'; +import SynaptomeResults from '@/features/entities/single-neuron-synaptome/detail-view/simulation'; import { EntityTypeValue } from '@/entity-configuration/domain'; -import Results from '@/features/entities/single-neuron-synaptome/detail-view/simulation'; export default async function RelatedArtifacts({ entity, @@ -19,11 +19,11 @@ export default async function RelatedArtifacts({ if (!entityType) notFound(); if (extendedType === 'memodel') { - return ; + return ; } if (extendedType === 'single_neuron_synaptome') { - return ; + return ; } notFound(); From 87ec2b51b7d905c52297f2e3e1455d0bc6cf7a67 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Thu, 28 Aug 2025 10:28:15 +0200 Subject: [PATCH 43/80] circuit --- .../[projectId]/explore/view/[type]/[id]/[section]/page.tsx | 5 +++++ src/entity-configuration/definitions/view-defs/model.ts | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx index b10588ce5..9ea96a801 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx @@ -25,6 +25,9 @@ export default async function Page({ const ctx = { virtualLabId, projectId }; const entityType = getEntityByExtendedType({ type: snakeCase(type) as EntityCoreExtendedType }); + + console.log(entityType); + if (!entityType || !entityType.detailViewSections.includes(section)) notFound(); const entity = await downloadEntity({ @@ -33,6 +36,8 @@ export default async function Page({ id, }); + console.log(entity); + let content: JSX.Element | undefined; if (section === 'overview') { diff --git a/src/entity-configuration/definitions/view-defs/model.ts b/src/entity-configuration/definitions/view-defs/model.ts index 7c5058c31..d82954328 100644 --- a/src/entity-configuration/definitions/view-defs/model.ts +++ b/src/entity-configuration/definitions/view-defs/model.ts @@ -100,6 +100,12 @@ export const ViewsDefinition: { [key: string]: ViewDefinitionConfig } = { title: 'Circuit', name: EntitySlug.Circuit, curated: false, + summaryViewFields: [ + { field: EntityCoreFields.BrainRegion }, + { field: EntityCoreFields.CircuitNumberConnections }, + { field: EntityCoreFields.CircuitNumberNeurons }, + { field: EntityCoreFields.CircuitNumberSynapses }, + ], columns: [ EntityCoreFields.Name, EntityCoreFields.Description, From a6a75f1d24504f905496d1454d9d95384a2a062b Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Thu, 28 Aug 2025 13:17:08 +0200 Subject: [PATCH 44/80] fields --- src/ui/segments/detail-view/overview.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/segments/detail-view/overview.tsx b/src/ui/segments/detail-view/overview.tsx index fa9e2a893..5c2b8a046 100644 --- a/src/ui/segments/detail-view/overview.tsx +++ b/src/ui/segments/detail-view/overview.tsx @@ -14,9 +14,9 @@ export default function Overview({ entity?: EntityTypeValue; extendedType: EntityCoreExtendedType; }) { - const fields = getViewDefinitionByExtendedType(extendedType)?.summaryViewFields; + const fields = getViewDefinitionByExtendedType(extendedType)?.summaryViewFields ?? []; - if (!fields || !entity) notFound(); + if (!entity) notFound(); const commonFields = CommonSummaryViewFields; return ( From 1f5558aae6f795955705a15ba871d0a92bfb2a48 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Thu, 28 Aug 2025 14:49:10 +0200 Subject: [PATCH 45/80] add single neuron simulation --- .../view/[type]/[id]/[section]/page.tsx | 6 +---- .../SimulationDetails/configuration-tab.tsx | 2 ++ .../definitions/view-defs/experiment.ts | 1 - .../simulation/single-neuron-simulation.ts | 2 +- .../elements/me-model-details.tsx | 2 ++ src/ui/segments/detail-view/configuration.tsx | 23 +++++++++++++++++ src/ui/segments/detail-view/overview.tsx | 25 ++++++++++++++++++- 7 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx index 9ea96a801..12cafd2cd 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx @@ -26,8 +26,6 @@ export default async function Page({ const entityType = getEntityByExtendedType({ type: snakeCase(type) as EntityCoreExtendedType }); - console.log(entityType); - if (!entityType || !entityType.detailViewSections.includes(section)) notFound(); const entity = await downloadEntity({ @@ -36,12 +34,10 @@ export default async function Page({ id, }); - console.log(entity); - let content: JSX.Element | undefined; if (section === 'overview') { - content = ; + content = ; } if (section === 'visualization') { content = ; diff --git a/src/components/simulate/SimulationDetails/configuration-tab.tsx b/src/components/simulate/SimulationDetails/configuration-tab.tsx index c3332d941..d66bbc129 100644 --- a/src/components/simulate/SimulationDetails/configuration-tab.tsx +++ b/src/components/simulate/SimulationDetails/configuration-tab.tsx @@ -1,3 +1,5 @@ +'use client'; + import { RightOutlined } from '@ant-design/icons'; import { Collapse } from 'antd'; import ConditionsDetails from './ConditionsDetails'; diff --git a/src/entity-configuration/definitions/view-defs/experiment.ts b/src/entity-configuration/definitions/view-defs/experiment.ts index e2c4e2cf6..247600d0b 100644 --- a/src/entity-configuration/definitions/view-defs/experiment.ts +++ b/src/entity-configuration/definitions/view-defs/experiment.ts @@ -24,7 +24,6 @@ export const ViewsDefinition: { [key: string]: ViewDefinitionConfig } = { EntityCoreFields.CreationDate, ], summaryViewFields: [ - { field: EntityCoreFields.Description, className: 'col-span-3' }, { field: EntityCoreFields.CreatedBy }, { field: EntityCoreFields.CreationDate }, ], diff --git a/src/entity-configuration/domain/simulation/single-neuron-simulation.ts b/src/entity-configuration/domain/simulation/single-neuron-simulation.ts index e05d422b4..ed8e80e26 100644 --- a/src/entity-configuration/domain/simulation/single-neuron-simulation.ts +++ b/src/entity-configuration/domain/simulation/single-neuron-simulation.ts @@ -58,5 +58,5 @@ export const SingleNeuronSimulation: EntityCoreTypeConfig>; + try { + config = await singleNeuronSimulationApiQueryExpand.config( + entity as ISingleNeuronSimulation, + ctx + ); + } catch { + notFound(); + } + + return ( + + ); + } + notFound(); } diff --git a/src/ui/segments/detail-view/overview.tsx b/src/ui/segments/detail-view/overview.tsx index 5c2b8a046..0077642bc 100644 --- a/src/ui/segments/detail-view/overview.tsx +++ b/src/ui/segments/detail-view/overview.tsx @@ -4,21 +4,37 @@ import { getViewDefinitionByExtendedType, } from '@/entity-configuration/definitions/view-defs'; import { Field } from '@/features/details-view/overview'; +import ModelDetails from '@/features/entities/neuron-simulation/elements/me-model-details'; import { EntityTypeValue } from '@/entity-configuration/domain'; import { EntityCoreExtendedType } from '@/entity-configuration/domain/helpers'; +import { resolveSingleNeuronSimulation } from '@/entity-configuration/domain/simulation'; +import { AwaitedType, WorkspaceContext } from '@/types/common'; -export default function Overview({ +export default async function Overview({ entity, extendedType, + ctx, }: { entity?: EntityTypeValue; extendedType: EntityCoreExtendedType; + ctx: WorkspaceContext; }) { const fields = getViewDefinitionByExtendedType(extendedType)?.summaryViewFields ?? []; if (!entity) notFound(); const commonFields = CommonSummaryViewFields; + let singleNeuronSimulationPayload: + | AwaitedType> + | undefined; + if (extendedType === 'single_neuron_simulation') { + try { + singleNeuronSimulationPayload = await resolveSingleNeuronSimulation(entity.id, ctx); + } catch { + notFound(); + } + } + return ( <>
@@ -30,6 +46,13 @@ export default function Overview({ return ; })}
+ {extendedType === 'single_neuron_simulation' && singleNeuronSimulationPayload && ( + + )} ); } From 3e204dfc1d48b97f40a248bde20153a6d67fb1c3 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Fri, 29 Aug 2025 10:57:08 +0200 Subject: [PATCH 46/80] results --- .../view/[type]/[id]/[section]/page.tsx | 5 +++ src/entity-configuration/definitions/types.ts | 1 + .../simulation/single-neuron-simulation.ts | 2 +- src/ui/segments/detail-view/results.tsx | 42 +++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/ui/segments/detail-view/results.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx index 12cafd2cd..79beb1d16 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/explore/view/[type]/[id]/[section]/page.tsx @@ -14,6 +14,7 @@ import Visualization from '@/ui/segments/viz'; import Analysis from '@/features/model-analysis/explorer/container'; import RelatedArtifacts from '@/ui/segments/detail-view/related-artifacts'; import Configuration from '@/ui/segments/detail-view/configuration'; +import Results from '@/ui/segments/detail-view/results'; export default async function Page({ params, @@ -54,6 +55,10 @@ export default async function Page({ content = ; } + if (section === 'results') { + content = ; + } + if (!content) notFound(); return
{content}
; diff --git a/src/entity-configuration/definitions/types.ts b/src/entity-configuration/definitions/types.ts index 86b311a2e..34fbbc5c0 100644 --- a/src/entity-configuration/definitions/types.ts +++ b/src/entity-configuration/definitions/types.ts @@ -120,6 +120,7 @@ export type FieldsDefinitionRegistry = Record< export type DetailViewSection = | 'overview' + | 'results' | 'visualization' | 'analysis' | 'related-publications' diff --git a/src/entity-configuration/domain/simulation/single-neuron-simulation.ts b/src/entity-configuration/domain/simulation/single-neuron-simulation.ts index ed8e80e26..6388db86b 100644 --- a/src/entity-configuration/domain/simulation/single-neuron-simulation.ts +++ b/src/entity-configuration/domain/simulation/single-neuron-simulation.ts @@ -58,5 +58,5 @@ export const SingleNeuronSimulation: EntityCoreTypeConfig>; + try { + config = await singleNeuronSimulationApiQueryExpand.config( + entity as ISingleNeuronSimulation, + ctx + ); + + if (!config) notFound(); + } catch { + notFound(); + } + + return ; + } + + notFound(); +} From 75a96c1fbae017fa2382e1a241baa7bd616ef0e9 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Fri, 29 Aug 2025 19:55:00 +0200 Subject: [PATCH 47/80] add synaptome --- .../definitions/view-defs/experiment.ts | 1 - .../elements/synaptome-details.tsx | 2 ++ src/ui/segments/detail-view/overview.tsx | 35 +++++++++++++++++-- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/entity-configuration/definitions/view-defs/experiment.ts b/src/entity-configuration/definitions/view-defs/experiment.ts index 247600d0b..5463baad8 100644 --- a/src/entity-configuration/definitions/view-defs/experiment.ts +++ b/src/entity-configuration/definitions/view-defs/experiment.ts @@ -44,7 +44,6 @@ export const ViewsDefinition: { [key: string]: ViewDefinitionConfig } = { EntityCoreFields.CreationDate, ], summaryViewFields: [ - { field: EntityCoreFields.Description, className: 'col-span-3' }, { field: EntityCoreFields.CreatedBy }, { field: EntityCoreFields.CreationDate }, ], diff --git a/src/features/entities/neuron-simulation/elements/synaptome-details.tsx b/src/features/entities/neuron-simulation/elements/synaptome-details.tsx index 385398a7f..978a0ff93 100644 --- a/src/features/entities/neuron-simulation/elements/synaptome-details.tsx +++ b/src/features/entities/neuron-simulation/elements/synaptome-details.tsx @@ -1,3 +1,5 @@ +'use client'; + import Link from 'next/link'; import PreviewThumbnail from '@/features/thumbnail/preview'; diff --git a/src/ui/segments/detail-view/overview.tsx b/src/ui/segments/detail-view/overview.tsx index 0077642bc..9f8c570c4 100644 --- a/src/ui/segments/detail-view/overview.tsx +++ b/src/ui/segments/detail-view/overview.tsx @@ -4,10 +4,14 @@ import { getViewDefinitionByExtendedType, } from '@/entity-configuration/definitions/view-defs'; import { Field } from '@/features/details-view/overview'; -import ModelDetails from '@/features/entities/neuron-simulation/elements/me-model-details'; +import MEModelDetails from '@/features/entities/neuron-simulation/elements/me-model-details'; +import SynaptomeDetails from '@/features/entities/neuron-simulation/elements/synaptome-details'; import { EntityTypeValue } from '@/entity-configuration/domain'; import { EntityCoreExtendedType } from '@/entity-configuration/domain/helpers'; -import { resolveSingleNeuronSimulation } from '@/entity-configuration/domain/simulation'; +import { + resolveSingleNeuronSimulation, + resolveSingleNeuronSynaptomeSimulation, +} from '@/entity-configuration/domain/simulation'; import { AwaitedType, WorkspaceContext } from '@/types/common'; export default async function Overview({ @@ -35,6 +39,21 @@ export default async function Overview({ } } + let singleNeuronSynaptomeSimulationPayload: + | AwaitedType> + | undefined; + + if (extendedType === 'single_neuron_synaptome_simulation') { + try { + singleNeuronSynaptomeSimulationPayload = await resolveSingleNeuronSynaptomeSimulation( + entity.id, + ctx + ); + } catch { + notFound(); + } + } + return ( <>
@@ -47,12 +66,22 @@ export default async function Overview({ })}
{extendedType === 'single_neuron_simulation' && singleNeuronSimulationPayload && ( - )} + + {extendedType === 'single_neuron_synaptome_simulation' && + singleNeuronSynaptomeSimulationPayload && ( + + )} ); } From 97bdc00e5abafaaeb072064c32f067cf4713caa6 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Fri, 29 Aug 2025 20:47:25 +0200 Subject: [PATCH 48/80] add synaptome --- .../single-neuron-synaptome-simulation.ts | 2 +- src/ui/segments/detail-view/configuration.tsx | 27 ++++++++++++++++++- src/ui/segments/detail-view/results.tsx | 25 +++++++++++++++-- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/entity-configuration/domain/simulation/single-neuron-synaptome-simulation.ts b/src/entity-configuration/domain/simulation/single-neuron-synaptome-simulation.ts index de638c441..2a52c4057 100644 --- a/src/entity-configuration/domain/simulation/single-neuron-synaptome-simulation.ts +++ b/src/entity-configuration/domain/simulation/single-neuron-synaptome-simulation.ts @@ -73,5 +73,5 @@ export const SingleNeuronSynaptomeSimulation: EntityCoreTypeConfig + >; + try { + config = await singleNeuronSynaptomeSimulationApiQueryExpand.config( + entity as ISingleNeuronSynaptomeSimulation, + ctx + ); + } catch { + notFound(); + } + + return ( + + ); + } + notFound(); } diff --git a/src/ui/segments/detail-view/results.tsx b/src/ui/segments/detail-view/results.tsx index c123836e4..9d72ad908 100644 --- a/src/ui/segments/detail-view/results.tsx +++ b/src/ui/segments/detail-view/results.tsx @@ -6,8 +6,11 @@ import { import { EntityTypeValue } from '@/entity-configuration/domain'; import { WorkspaceContext, AwaitedType } from '@/types/common'; -import { ISingleNeuronSimulation } from '@/api/entitycore/types'; -import { singleNeuronSimulationApiQueryExpand } from '@/entity-configuration/domain/simulation'; +import { ISingleNeuronSimulation, ISingleNeuronSynaptomeSimulation } from '@/api/entitycore/types'; +import { + singleNeuronSimulationApiQueryExpand, + singleNeuronSynaptomeSimulationApiQueryExpand, +} from '@/entity-configuration/domain/simulation'; import SimulationResults from '@/components/simulate/SimulationDetails/recording-tab'; export default async function Results({ @@ -38,5 +41,23 @@ export default async function Results({ return ; } + if (extendedType === 'single_neuron_synaptome_simulation') { + let config: AwaitedType< + ReturnType + >; + + try { + config = await singleNeuronSynaptomeSimulationApiQueryExpand.config( + entity as ISingleNeuronSynaptomeSimulation, + ctx + ); + + if (!config) notFound(); + } catch { + notFound(); + } + return ; + } + notFound(); } From 7e0508ec4c0ad4b72c21fe8f38c164de1656038c Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 3 Sep 2025 16:16:43 +0200 Subject: [PATCH 49/80] redirect detail root to overview --- .../[projectId]/data/view/[type]/(root)/[id]/page.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/(root)/[id]/page.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/(root)/[id]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/(root)/[id]/page.tsx new file mode 100644 index 000000000..31d105b90 --- /dev/null +++ b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/(root)/[id]/page.tsx @@ -0,0 +1,8 @@ +'use client'; + +import { redirect, usePathname } from 'next/navigation'; + +export default function DetailPage() { + const path = usePathname(); + redirect(`${path}/overview`); +} From 1e9b56e21369c35c547df175cd993340cafe7eab Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Wed, 3 Sep 2025 16:37:12 +0200 Subject: [PATCH 50/80] adding name to pages --- .../data/view/[type]/[id]/[section]/page.tsx | 2 +- src/ui/segments/detail-view/configuration.tsx | 25 ++++++++++++++----- src/ui/segments/detail-view/overview.tsx | 4 --- src/ui/segments/{ => detail-view}/viz.tsx | 18 ++++++++++--- 4 files changed, 35 insertions(+), 14 deletions(-) rename src/ui/segments/{ => detail-view}/viz.tsx (60%) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx index 79beb1d16..241496059 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx @@ -10,7 +10,7 @@ import { import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; import Overview from '@/ui/segments/detail-view/overview'; -import Visualization from '@/ui/segments/viz'; +import Visualization from '@/ui/segments/detail-view/viz'; import Analysis from '@/features/model-analysis/explorer/container'; import RelatedArtifacts from '@/ui/segments/detail-view/related-artifacts'; import Configuration from '@/ui/segments/detail-view/configuration'; diff --git a/src/ui/segments/detail-view/configuration.tsx b/src/ui/segments/detail-view/configuration.tsx index 53bb46373..67fe1f19f 100644 --- a/src/ui/segments/detail-view/configuration.tsx +++ b/src/ui/segments/detail-view/configuration.tsx @@ -1,3 +1,4 @@ +import { JSX } from 'react'; import { notFound } from 'next/navigation'; import { EntityCoreExtendedType, @@ -39,6 +40,8 @@ export default async function Configuration({ const entityType = getEntityByExtendedType({ type: extendedType }); if (!entityType) notFound(); + let content: JSX.Element | undefined; + if (extendedType === 'emodel') { let morphology: IReconstructionMorphologyExpanded | IReconstructionMorphology; @@ -52,7 +55,7 @@ export default async function Configuration({ notFound(); } - return ( + content = ( ; + content = ; } if (extendedType === 'single_neuron_synaptome') { @@ -76,7 +79,7 @@ export default async function Configuration({ notFound(); } - return ( + content = (
+
+
Name
+
{entity.name}
+
+ {content} + + ); } diff --git a/src/ui/segments/detail-view/overview.tsx b/src/ui/segments/detail-view/overview.tsx index 9f8c570c4..f970da174 100644 --- a/src/ui/segments/detail-view/overview.tsx +++ b/src/ui/segments/detail-view/overview.tsx @@ -56,10 +56,6 @@ export default async function Overview({ return ( <> -
-
Name
-
{entity.name}
-
{[...commonFields, ...fields].map(({ className, field }) => { return ; diff --git a/src/ui/segments/viz.tsx b/src/ui/segments/detail-view/viz.tsx similarity index 60% rename from src/ui/segments/viz.tsx rename to src/ui/segments/detail-view/viz.tsx index 828bc66a6..1b60e5da0 100644 --- a/src/ui/segments/viz.tsx +++ b/src/ui/segments/detail-view/viz.tsx @@ -1,5 +1,6 @@ 'use client'; +import { JSX } from 'react'; import { notFound } from 'next/navigation'; import { MorphoViewerLoaderMemo } from '@/features/entities/reconstruction-morphology/detail-view'; import { IReconstructionMorphology, IElectricalCellRecording } from '@/api/entitycore/types'; @@ -16,12 +17,23 @@ export default function Visualization({ entity: AwaitedType>; ctx: WorkspaceContext; }) { + let content: JSX.Element | undefined; + if (entity.type === 'reconstruction_morphology') { - return ; + content = ; } if (entity.type === 'electrical_cell_recording') { - return ; + content = ; } - notFound(); + if (!content) notFound(); + return ( + <> +
+
Name
+
{entity.name}
+
+ {content} + + ); } From 7227cf78cee8e3036b47bc5abb78f414ac13cd29 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Thu, 4 Sep 2025 11:16:11 +0200 Subject: [PATCH 51/80] name --- .../data/view/[type]/[id]/[section]/page.tsx | 14 +++++++------- .../[projectId]/data/view/[type]/[id]/layout.tsx | 10 +++++++++- src/ui/segments/detail-view/viz.tsx | 10 +--------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx index 241496059..def38d3cc 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx @@ -38,28 +38,28 @@ export default async function Page({ let content: JSX.Element | undefined; if (section === 'overview') { - content = ; + return ; } if (section === 'visualization') { - content = ; + return ; } if (section === 'analysis') { - content = ; + return ; } if (section === 'configuration') { - content = ; + return ; } if (section === 'related-artifacts') { - content = ; + return ; } if (section === 'results') { - content = ; + return ; } if (!content) notFound(); - return
{content}
; + return content; } diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx index 6392c79ef..6fce681a8 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx @@ -88,7 +88,15 @@ export default async function Layout({
-
{children}
+
+
+
+
Name
+
{entity.name}
+
+
{children}
+
+
); } diff --git a/src/ui/segments/detail-view/viz.tsx b/src/ui/segments/detail-view/viz.tsx index 1b60e5da0..1f42341b1 100644 --- a/src/ui/segments/detail-view/viz.tsx +++ b/src/ui/segments/detail-view/viz.tsx @@ -27,13 +27,5 @@ export default function Visualization({ } if (!content) notFound(); - return ( - <> -
-
Name
-
{entity.name}
-
- {content} - - ); + return <>{content}; } From 6b9f72429490de3e9c18499cd3bc9a93a0f62d67 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Thu, 4 Sep 2025 11:35:37 +0200 Subject: [PATCH 52/80] explore -> data --- .../[projectId]/data/view/[type]/[id]/layout.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx index 6fce681a8..9aebc6eaa 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx @@ -70,13 +70,13 @@ export default async function Layout({
- + Explore {entityType.title} From 463cbdab7403a0969f905d0f8f47ed7378dd670f Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Thu, 4 Sep 2025 11:43:03 +0200 Subject: [PATCH 53/80] remove double name --- src/ui/segments/detail-view/configuration.tsx | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/ui/segments/detail-view/configuration.tsx b/src/ui/segments/detail-view/configuration.tsx index 67fe1f19f..53bb46373 100644 --- a/src/ui/segments/detail-view/configuration.tsx +++ b/src/ui/segments/detail-view/configuration.tsx @@ -1,4 +1,3 @@ -import { JSX } from 'react'; import { notFound } from 'next/navigation'; import { EntityCoreExtendedType, @@ -40,8 +39,6 @@ export default async function Configuration({ const entityType = getEntityByExtendedType({ type: extendedType }); if (!entityType) notFound(); - let content: JSX.Element | undefined; - if (extendedType === 'emodel') { let morphology: IReconstructionMorphologyExpanded | IReconstructionMorphology; @@ -55,7 +52,7 @@ export default async function Configuration({ notFound(); } - content = ( + return ( ; + return ; } if (extendedType === 'single_neuron_synaptome') { @@ -79,7 +76,7 @@ export default async function Configuration({ notFound(); } - content = ( + return (
-
-
Name
-
{entity.name}
-
- {content} - - ); + notFound(); } From 4695601087b487b120c2e4a0318be812e8ac3a63 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Thu, 4 Sep 2025 14:10:20 +0200 Subject: [PATCH 54/80] add related publications tab --- .../domain/experimental/bouton-density.ts | 2 +- .../experimental/electrical-cell-recording.ts | 2 +- .../domain/experimental/neuron-density.ts | 2 +- .../experimental/reconstruction-morphology.ts | 2 +- .../experimental/synapse-per-connection.ts | 2 +- .../domain/model/circuit.ts | 2 +- .../domain/model/e-model.ts | 2 +- .../domain/model/me-model.ts | 8 ++++++- .../domain/model/mirocircuit.ts | 2 +- .../domain/model/paired-neurons.ts | 2 +- .../domain/model/single-neuron-synaptome.ts | 2 +- .../domain/model/small-microcircuit.ts | 2 +- .../simulation/paired-neurons-simulation.ts | 2 +- .../domain/simulation/simulation-campaign.ts | 2 +- .../simulation/single-neuron-simulation.ts | 2 +- .../single-neuron-synaptome-simulation.ts | 2 +- .../small-microcircuit-simulation.ts | 2 +- .../detail-view/related-publications.tsx | 22 +++++++++++++++++++ 18 files changed, 45 insertions(+), 17 deletions(-) create mode 100644 src/ui/segments/detail-view/related-publications.tsx diff --git a/src/entity-configuration/domain/experimental/bouton-density.ts b/src/entity-configuration/domain/experimental/bouton-density.ts index c35c42599..b8ea0b796 100644 --- a/src/entity-configuration/domain/experimental/bouton-density.ts +++ b/src/entity-configuration/domain/experimental/bouton-density.ts @@ -34,7 +34,7 @@ export const BoutonDensity: EntityCoreTypeConfig = { extension: 'application/json', }, isBookmarkable: true, - detailViewSections: ['overview'], + detailViewSections: ['overview', 'related-publications'], isCopyable: true, isSimulatable: false, } as const; diff --git a/src/entity-configuration/domain/experimental/electrical-cell-recording.ts b/src/entity-configuration/domain/experimental/electrical-cell-recording.ts index 7d530c01c..7f71733d1 100644 --- a/src/entity-configuration/domain/experimental/electrical-cell-recording.ts +++ b/src/entity-configuration/domain/experimental/electrical-cell-recording.ts @@ -40,7 +40,7 @@ export const ElectricalCellRecording: EntityCoreTypeConfig = { asset: { extension: 'application/json', }, - detailViewSections: ['overview'], + detailViewSections: ['overview', 'related-publications'], isDownloadable: true, isBookmarkable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/experimental/reconstruction-morphology.ts b/src/entity-configuration/domain/experimental/reconstruction-morphology.ts index 22cab7558..dce930865 100644 --- a/src/entity-configuration/domain/experimental/reconstruction-morphology.ts +++ b/src/entity-configuration/domain/experimental/reconstruction-morphology.ts @@ -39,7 +39,7 @@ export const ReconstructionMorphology: EntityCoreTypeConfig< extension: 'application/swc', }, viewDefinition: ViewsDefinitionRegistry[ExtendedEntitiesTypeDict.ReconstructionMorphology], - detailViewSections: ['overview', 'visualization'], + detailViewSections: ['overview', 'visualization', 'related-publications'], isDownloadable: true, isBookmarkable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/experimental/synapse-per-connection.ts b/src/entity-configuration/domain/experimental/synapse-per-connection.ts index 2a0674007..f951874a4 100644 --- a/src/entity-configuration/domain/experimental/synapse-per-connection.ts +++ b/src/entity-configuration/domain/experimental/synapse-per-connection.ts @@ -32,7 +32,7 @@ export const SynapsePerConnection: EntityCoreTypeConfig = { extension: 'application/json', }, - detailViewSections: ['overview'], + detailViewSections: ['overview', 'related-publications'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/e-model.ts b/src/entity-configuration/domain/model/e-model.ts index 1988f8d90..045a4228b 100644 --- a/src/entity-configuration/domain/model/e-model.ts +++ b/src/entity-configuration/domain/model/e-model.ts @@ -34,7 +34,7 @@ export const Emodel: EntityCoreTypeConfig = { asset: { extension: undefined, }, - detailViewSections: ['overview', 'analysis', 'configuration'], + detailViewSections: ['overview', 'analysis', 'configuration', 'related-publications'], isDownloadable: true, isBookmarkable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/me-model.ts b/src/entity-configuration/domain/model/me-model.ts index 6a889c44f..8580059ad 100644 --- a/src/entity-configuration/domain/model/me-model.ts +++ b/src/entity-configuration/domain/model/me-model.ts @@ -30,7 +30,13 @@ export const MEmodel: EntityCoreTypeConfig = { asset: { extension: undefined, }, - detailViewSections: ['overview', 'analysis', 'configuration', 'related-artifacts'], + detailViewSections: [ + 'overview', + 'analysis', + 'configuration', + 'related-artifacts', + 'related-publications', + ], isDownloadable: true, isBookmarkable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/mirocircuit.ts b/src/entity-configuration/domain/model/mirocircuit.ts index 0883ae577..3af685377 100644 --- a/src/entity-configuration/domain/model/mirocircuit.ts +++ b/src/entity-configuration/domain/model/mirocircuit.ts @@ -33,7 +33,7 @@ export const Microcircuit: EntityCoreTypeConfig = { asset: { extension: 'application/json', }, - detailViewSections: ['overview'], + detailViewSections: ['overview', 'related-publications'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/paired-neurons.ts b/src/entity-configuration/domain/model/paired-neurons.ts index d9568bec7..de91fb3a7 100644 --- a/src/entity-configuration/domain/model/paired-neurons.ts +++ b/src/entity-configuration/domain/model/paired-neurons.ts @@ -33,7 +33,7 @@ export const PairedNeuronCircuit: EntityCoreTypeConfig = { asset: { extension: 'application/json', }, - detailViewSections: ['overview'], + detailViewSections: ['overview', 'related-publications'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/single-neuron-synaptome.ts b/src/entity-configuration/domain/model/single-neuron-synaptome.ts index 140b027e7..da4a15e83 100644 --- a/src/entity-configuration/domain/model/single-neuron-synaptome.ts +++ b/src/entity-configuration/domain/model/single-neuron-synaptome.ts @@ -47,7 +47,7 @@ export const SingleNeuronSynaptome: EntityCoreTypeConfig extension: 'application/json', configfile: AssetLabel.single_neuron_synaptome_config, }, - detailViewSections: ['overview', 'configuration', 'related-artifacts'], + detailViewSections: ['overview', 'configuration', 'related-artifacts', 'related-publications'], isBookmarkable: true, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/small-microcircuit.ts b/src/entity-configuration/domain/model/small-microcircuit.ts index 965fff05b..5db0776d4 100644 --- a/src/entity-configuration/domain/model/small-microcircuit.ts +++ b/src/entity-configuration/domain/model/small-microcircuit.ts @@ -33,7 +33,7 @@ export const SmallMicrocircuit: EntityCoreTypeConfig = { asset: { extension: 'application/json', }, - detailViewSections: ['overview'], + detailViewSections: ['overview', 'related-publications'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/simulation/paired-neurons-simulation.ts b/src/entity-configuration/domain/simulation/paired-neurons-simulation.ts index 0f31001bc..6e0859706 100644 --- a/src/entity-configuration/domain/simulation/paired-neurons-simulation.ts +++ b/src/entity-configuration/domain/simulation/paired-neurons-simulation.ts @@ -157,7 +157,7 @@ export const PairedNeuronCircuitSimulation: EntityCoreTypeConfig Date: Thu, 4 Sep 2025 14:39:52 +0200 Subject: [PATCH 55/80] main scree --- .../simulate/configure/circuit/[id]/page.tsx | 37 +++++++++++++++++++ src/ui/use-query-keys/data.tsx | 1 + 2 files changed, 38 insertions(+) create mode 100644 src/app/app/v2/[virtualLabId]/[projectId]/workflows/simulate/configure/circuit/[id]/page.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/workflows/simulate/configure/circuit/[id]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/workflows/simulate/configure/circuit/[id]/page.tsx new file mode 100644 index 000000000..1dcc6135f --- /dev/null +++ b/src/app/app/v2/[virtualLabId]/[projectId]/workflows/simulate/configure/circuit/[id]/page.tsx @@ -0,0 +1,37 @@ +'use client'; + +import { useSuspenseQuery } from '@tanstack/react-query'; +import { use } from 'react'; +import SimulationConfig from '@/features/small-microcircuit'; + +import type { WorkflowSimulatePanelKeys } from '@/ui/segments/workflows/simulate/single-neuron/shared/constant'; +import type { ExperimentStepKeys } from '@/ui/segments/workflows/simulate/single-neuron/shared/elements/menu'; +import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; +import { getCircuit } from '@/api/entitycore/queries/model/circuit'; + +export default function Page({ + searchParams, + params: pathParams, +}: ServerSideComponentProp< + WorkspaceContext & { id: string }, + { + step: ExperimentStepKeys; + sessionId: string; + panel: WorkflowSimulatePanelKeys; + } +>) { + const queryParams = use(searchParams); + const { virtualLabId, projectId, id: modelId } = use(pathParams); + + let sessionId = queryParams?.sessionId; + if (!sessionId) sessionId = crypto.randomUUID(); + + const { data: entity } = useSuspenseQuery({ + queryKey: [modelId], + queryFn: () => getCircuit({ id: modelId, context: { virtualLabId, projectId } }), + }); + + return ( + + ); +} diff --git a/src/ui/use-query-keys/data.tsx b/src/ui/use-query-keys/data.tsx index c45f8e4ca..75ad640c0 100644 --- a/src/ui/use-query-keys/data.tsx +++ b/src/ui/use-query-keys/data.tsx @@ -38,6 +38,7 @@ export const keyBuilder = { `${prefix}-electrical-cell-recordings-count`, { virtualLabId, projectId, brainRegionId: brainRegionId ?? '' }, ], + meModel: ({ virtualLabId, projectId, entityId }: WorkspaceContext & { entityId: string }) => [ `${prefix}-single-neuron-model`, { virtualLabId, projectId, entityId }, From 650e0c975d0dc5bf140e0b6fcc2fac719d6cd461 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Fri, 5 Sep 2025 15:35:04 +0200 Subject: [PATCH 56/80] add circuit viz --- .../(data)/model/[type]/[id]/page.tsx | 3 +++ src/entity-configuration/domain/model/circuit.ts | 2 +- .../domain/model/mirocircuit.ts | 2 +- .../domain/model/paired-neurons.ts | 2 +- .../domain/model/small-microcircuit.ts | 2 +- src/ui/segments/detail-view/viz.tsx | 16 +++++++++------- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/app/app/virtual-lab/(free)/explore/(interactive)/interactive/(data)/model/[type]/[id]/page.tsx b/src/app/app/virtual-lab/(free)/explore/(interactive)/interactive/(data)/model/[type]/[id]/page.tsx index 65e24848a..9b770c316 100644 --- a/src/app/app/virtual-lab/(free)/explore/(interactive)/interactive/(data)/model/[type]/[id]/page.tsx +++ b/src/app/app/virtual-lab/(free)/explore/(interactive)/interactive/(data)/model/[type]/[id]/page.tsx @@ -25,6 +25,9 @@ export default async function Page({ params: promisedParams, }: ServerSideComponentProp) { const params = await promisedParams; + + console.log(params); + // eslint-disable-next-line react/jsx-props-no-spreading return ; } diff --git a/src/entity-configuration/domain/model/circuit.ts b/src/entity-configuration/domain/model/circuit.ts index 1d736df9a..718f7cc98 100644 --- a/src/entity-configuration/domain/model/circuit.ts +++ b/src/entity-configuration/domain/model/circuit.ts @@ -34,7 +34,7 @@ export const Circuit: EntityCoreTypeConfig = { extension: 'application/json', }, - detailViewSections: ['overview', 'related-publications'], + detailViewSections: ['overview', 'visualization', 'related-publications'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/mirocircuit.ts b/src/entity-configuration/domain/model/mirocircuit.ts index 3af685377..7378cf75d 100644 --- a/src/entity-configuration/domain/model/mirocircuit.ts +++ b/src/entity-configuration/domain/model/mirocircuit.ts @@ -33,7 +33,7 @@ export const Microcircuit: EntityCoreTypeConfig = { asset: { extension: 'application/json', }, - detailViewSections: ['overview', 'related-publications'], + detailViewSections: ['overview', 'visualization', 'related-publications'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/paired-neurons.ts b/src/entity-configuration/domain/model/paired-neurons.ts index de91fb3a7..6779278a4 100644 --- a/src/entity-configuration/domain/model/paired-neurons.ts +++ b/src/entity-configuration/domain/model/paired-neurons.ts @@ -33,7 +33,7 @@ export const PairedNeuronCircuit: EntityCoreTypeConfig = { asset: { extension: 'application/json', }, - detailViewSections: ['overview', 'related-publications'], + detailViewSections: ['overview', 'visualization', 'related-publications'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/small-microcircuit.ts b/src/entity-configuration/domain/model/small-microcircuit.ts index 5db0776d4..f4548f97c 100644 --- a/src/entity-configuration/domain/model/small-microcircuit.ts +++ b/src/entity-configuration/domain/model/small-microcircuit.ts @@ -33,7 +33,7 @@ export const SmallMicrocircuit: EntityCoreTypeConfig = { asset: { extension: 'application/json', }, - detailViewSections: ['overview', 'related-publications'], + detailViewSections: ['overview', 'visualization', 'related-publications'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/ui/segments/detail-view/viz.tsx b/src/ui/segments/detail-view/viz.tsx index 1f42341b1..b6b092135 100644 --- a/src/ui/segments/detail-view/viz.tsx +++ b/src/ui/segments/detail-view/viz.tsx @@ -1,15 +1,17 @@ 'use client'; -import { JSX } from 'react'; import { notFound } from 'next/navigation'; import { MorphoViewerLoaderMemo } from '@/features/entities/reconstruction-morphology/detail-view'; import { IReconstructionMorphology, IElectricalCellRecording } from '@/api/entitycore/types'; import EphysViewer from '@/features/ephys-viewer'; +import CircuitViz from '@/features/entities/circuit/elements/tabs-content/visualization'; import { downloadEntity } from '@/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout'; import { WorkspaceContext } from '@/types/common'; +import { ICircuit } from '@/api/entitycore/types/entities/circuit'; type AwaitedType = T extends Promise ? U : T; + export default function Visualization({ entity, ctx, @@ -17,15 +19,15 @@ export default function Visualization({ entity: AwaitedType>; ctx: WorkspaceContext; }) { - let content: JSX.Element | undefined; - if (entity.type === 'reconstruction_morphology') { - content = ; + return ; } if (entity.type === 'electrical_cell_recording') { - content = ; + return ; + } + if (entity.type === 'circuit') { + return ; } - if (!content) notFound(); - return <>{content}; + notFound(); } From ba95ebc9d9f6522e81c250e1ba77a633a3411138 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Fri, 5 Sep 2025 15:58:31 +0200 Subject: [PATCH 57/80] Add circuit analysis --- .../data/view/[type]/[id]/[section]/page.tsx | 4 +- .../(data)/model/[type]/[id]/page.tsx | 2 - src/entity-configuration/domain/helpers.ts | 7 +++ .../domain/model/circuit.ts | 2 +- .../domain/model/mirocircuit.ts | 2 +- .../domain/model/paired-neurons.ts | 2 +- .../domain/model/small-microcircuit.ts | 2 +- src/ui/segments/detail-view/analysis.tsx | 50 +++++++++++++++++++ 8 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 src/ui/segments/detail-view/analysis.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx index def38d3cc..880b6c16c 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx @@ -11,7 +11,7 @@ import { import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; import Overview from '@/ui/segments/detail-view/overview'; import Visualization from '@/ui/segments/detail-view/viz'; -import Analysis from '@/features/model-analysis/explorer/container'; +import Analysis from '@/ui/segments/detail-view/analysis'; import RelatedArtifacts from '@/ui/segments/detail-view/related-artifacts'; import Configuration from '@/ui/segments/detail-view/configuration'; import Results from '@/ui/segments/detail-view/results'; @@ -44,7 +44,7 @@ export default async function Page({ return ; } if (section === 'analysis') { - return ; + return ; } if (section === 'configuration') { diff --git a/src/app/app/virtual-lab/(free)/explore/(interactive)/interactive/(data)/model/[type]/[id]/page.tsx b/src/app/app/virtual-lab/(free)/explore/(interactive)/interactive/(data)/model/[type]/[id]/page.tsx index 9b770c316..87da74538 100644 --- a/src/app/app/virtual-lab/(free)/explore/(interactive)/interactive/(data)/model/[type]/[id]/page.tsx +++ b/src/app/app/virtual-lab/(free)/explore/(interactive)/interactive/(data)/model/[type]/[id]/page.tsx @@ -26,8 +26,6 @@ export default async function Page({ }: ServerSideComponentProp) { const params = await promisedParams; - console.log(params); - // eslint-disable-next-line react/jsx-props-no-spreading return ; } diff --git a/src/entity-configuration/domain/helpers.ts b/src/entity-configuration/domain/helpers.ts index b2dc58986..cec46d552 100644 --- a/src/entity-configuration/domain/helpers.ts +++ b/src/entity-configuration/domain/helpers.ts @@ -13,6 +13,13 @@ import type { TEntityTypeDict } from '@/api/entitycore/types'; export type EntityCoreExtendedType = (typeof EntityCoreConfiguration)[keyof typeof EntityCoreConfiguration]['extendedType']; +export const circuitTypes: EntityCoreExtendedType[] = [ + 'circuit', + 'small_micro_circuit', + 'paired_neuron_circuit', + 'micro_circuit', +]; + export const getEntityByExtendedType = ({ type }: { type?: EntityCoreExtendedType }) => find(EntityCoreConfiguration, { extendedType: type }); diff --git a/src/entity-configuration/domain/model/circuit.ts b/src/entity-configuration/domain/model/circuit.ts index 718f7cc98..119debfb0 100644 --- a/src/entity-configuration/domain/model/circuit.ts +++ b/src/entity-configuration/domain/model/circuit.ts @@ -34,7 +34,7 @@ export const Circuit: EntityCoreTypeConfig = { extension: 'application/json', }, - detailViewSections: ['overview', 'visualization', 'related-publications'], + detailViewSections: ['overview', 'analysis', 'visualization', 'related-publications'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/mirocircuit.ts b/src/entity-configuration/domain/model/mirocircuit.ts index 7378cf75d..87e5b829a 100644 --- a/src/entity-configuration/domain/model/mirocircuit.ts +++ b/src/entity-configuration/domain/model/mirocircuit.ts @@ -33,7 +33,7 @@ export const Microcircuit: EntityCoreTypeConfig = { asset: { extension: 'application/json', }, - detailViewSections: ['overview', 'visualization', 'related-publications'], + detailViewSections: ['overview', 'analysis', 'visualization', 'related-publications'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/paired-neurons.ts b/src/entity-configuration/domain/model/paired-neurons.ts index 6779278a4..0e5e20905 100644 --- a/src/entity-configuration/domain/model/paired-neurons.ts +++ b/src/entity-configuration/domain/model/paired-neurons.ts @@ -33,7 +33,7 @@ export const PairedNeuronCircuit: EntityCoreTypeConfig = { asset: { extension: 'application/json', }, - detailViewSections: ['overview', 'visualization', 'related-publications'], + detailViewSections: ['overview', 'analysis', 'visualization', 'related-publications'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/small-microcircuit.ts b/src/entity-configuration/domain/model/small-microcircuit.ts index f4548f97c..bbc2d1b3f 100644 --- a/src/entity-configuration/domain/model/small-microcircuit.ts +++ b/src/entity-configuration/domain/model/small-microcircuit.ts @@ -33,7 +33,7 @@ export const SmallMicrocircuit: EntityCoreTypeConfig = { asset: { extension: 'application/json', }, - detailViewSections: ['overview', 'visualization', 'related-publications'], + detailViewSections: ['overview', 'analysis', 'visualization', 'related-publications'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/ui/segments/detail-view/analysis.tsx b/src/ui/segments/detail-view/analysis.tsx new file mode 100644 index 000000000..afd8869b2 --- /dev/null +++ b/src/ui/segments/detail-view/analysis.tsx @@ -0,0 +1,50 @@ +import { notFound } from 'next/navigation'; +import { + circuitTypes, + EntityCoreExtendedType, + getEntityByExtendedType, +} from '@/entity-configuration/domain/helpers'; + +import EModelConfig from '@/components/build-section/cell-model-assignment/e-model/EModelView'; +import { EntityTypeValue } from '@/entity-configuration/domain'; +import { WorkspaceContext, AwaitedType } from '@/types/common'; +import { + IEModel, + IMEModel, + IReconstructionMorphology, + IReconstructionMorphologyExpanded, + ISingleNeuronSimulation, + ISingleNeuronSynaptomeSimulation, +} from '@/api/entitycore/types'; +import { getReconstructionMorphology } from '@/api/entitycore/queries'; +import MEModelConfig from '@/features/entities/me-model/detail-view/configuration'; +import SynaptomeConfig from '@/features/entities/single-neuron-synaptome/detail-view/configuration'; +import SynapseGroupList from '@/features/entities/single-neuron-synaptome/detail-view/elements/list-synapses-configuration'; +import { loadExpandedSingleNeuronSynaptome } from '@/page-wrappers/explore/single-neuron-synaptome'; +import { + singleNeuronSimulationApiQueryExpand, + singleNeuronSynaptomeSimulationApiQueryExpand, +} from '@/entity-configuration/domain/simulation'; + +import Overview from '@/features/entities/circuit/elements/tabs-content/overview'; +import Analysis from '@/features/model-analysis/explorer/container'; +import { ICircuit } from '@/api/entitycore/types/entities/circuit'; + +export default async function Configuration({ + entity, + extendedType, + ctx, +}: { + entity: EntityTypeValue; + extendedType: EntityCoreExtendedType; + ctx: WorkspaceContext; +}) { + const entityType = getEntityByExtendedType({ type: extendedType }); + if (!entityType) notFound(); + + if (circuitTypes.includes(extendedType)) { + return ; + } + + return ; +} From f2b5b3a16595c1b679e3349f09c455322ff3c624 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Fri, 5 Sep 2025 16:47:30 +0200 Subject: [PATCH 58/80] add related publications --- .../data/view/[type]/[id]/[section]/page.tsx | 5 + .../detail-view/related-publications.tsx | 196 +++++++++++++++++- 2 files changed, 191 insertions(+), 10 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx index 880b6c16c..3966143ec 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx @@ -13,6 +13,7 @@ import Overview from '@/ui/segments/detail-view/overview'; import Visualization from '@/ui/segments/detail-view/viz'; import Analysis from '@/ui/segments/detail-view/analysis'; import RelatedArtifacts from '@/ui/segments/detail-view/related-artifacts'; +import RelatedPublications from '@/ui/segments/detail-view/related-publications'; import Configuration from '@/ui/segments/detail-view/configuration'; import Results from '@/ui/segments/detail-view/results'; @@ -51,6 +52,10 @@ export default async function Page({ return ; } + if (section === 'related-publications') { + return ; + } + if (section === 'related-artifacts') { return ; } diff --git a/src/ui/segments/detail-view/related-publications.tsx b/src/ui/segments/detail-view/related-publications.tsx index 81b75c021..4bb4da53d 100644 --- a/src/ui/segments/detail-view/related-publications.tsx +++ b/src/ui/segments/detail-view/related-publications.tsx @@ -1,14 +1,27 @@ -import { notFound } from 'next/navigation'; -import { - EntityCoreExtendedType, - getEntityByExtendedType, -} from '@/entity-configuration/domain/helpers'; +'use client'; -import MEModelResults from '@/features/entities/me-model/detail-view/simulation'; -import SynaptomeResults from '@/features/entities/single-neuron-synaptome/detail-view/simulation'; -import { EntityTypeValue } from '@/entity-configuration/domain'; +import { CloseCircleTwoTone, LoadingOutlined } from '@ant-design/icons'; +import { Collapse, Empty, List } from 'antd'; +import type { CollapseProps } from 'antd'; +import { useEffect, useState } from 'react'; +import { notFound, useParams } from 'next/navigation'; +import type { EntityTypeValue } from '@/entity-configuration/domain'; -export default async function RelatedArtifacts({ +import { getScientificArtifactPublicationLinks } from '@/api/entitycore/queries/general/scientific-artifact-publication-link'; +import { PublicationTypeDictionary } from '@/api/entitycore/types/entities/scientific-artifact-publication-link'; +import { Card } from '@/features/entities/circuit/elements/publication-item/card'; +import { classNames } from '@/util/utils'; +import type { EntityCoreExtendedType } from '@/entity-configuration/domain/helpers'; +import { tryCatch } from '@/api/utils'; + +import type { + IScientificArtifactPublicationLink, + TPublicationTypeDictionary, +} from '@/api/entitycore/types/entities/scientific-artifact-publication-link'; +import type { WorkspaceContext } from '@/types/common'; +import { getEntityByExtendedType } from '@/entity-configuration/domain/helpers'; + +export default function RelatedPublications({ entity, extendedType, }: { @@ -18,5 +31,168 @@ export default async function RelatedArtifacts({ const entityType = getEntityByExtendedType({ type: extendedType }); if (!entityType) notFound(); - return 'related publications'; + const items: CollapseProps['items'] = [ + { + key: 'entity_source', + label: 'Circuit provenance', + children: ( + + ), + }, + { + key: 'component_source', + label: 'Related artifacts provenance', + children: ( + + ), + }, + { + key: 'application', + label: 'Applications', + children: ( + + ), + }, + ]; + + return ( +
+ null} + className={classNames( + '[&_.ant-collapse-item]:mb-2', + '[&_.ant-collapse-header]:bg-primary-8 [&_.ant-collapse-header]:border-none [&_.ant-collapse-header]:text-white!', + '[&_.ant-collapse-header]:rounded-none! [&_.ant-collapse-header]:text-lg [&_.ant-collapse-header]:font-semibold' + )} + /> +
+ ); +} + +function PerTypePublications({ + entity, + type, +}: { + entity: EntityTypeValue; + type: TPublicationTypeDictionary; +}) { + const { virtualLabId, projectId } = useParams(); + const [relatedPublications, setRelatedPublications] = useState< + IScientificArtifactPublicationLink[] + >([]); + const [pagination, setPagination] = useState<{ + loading: boolean; + page: number; + pageSize: number; + error: string | null; + }>({ + loading: false, + page: 1, + pageSize: 5, + error: null, + }); + + useEffect(() => { + async function getRelatedPublications() { + setPagination((prev) => ({ + ...prev, + loading: true, + error: null, + })); + const { data: result, error } = await tryCatch( + getScientificArtifactPublicationLinks({ + filters: { + scientific_artifact__id: entity.id, + publication_type: type, + page: pagination.page, + page_size: pagination.pageSize, + }, + context: { virtualLabId, projectId }, + }) + ); + + if (error) { + setPagination((prev) => ({ + ...prev, + error: error.message ?? 'An error occurred while fetching related publications', + loading: false, + })); + return; + } + + setRelatedPublications(result?.data ?? []); + setPagination((prev) => ({ + ...prev, + loading: false, + })); + } + + getRelatedPublications(); + }, [pagination.page, pagination.pageSize, type, entity.id, virtualLabId, projectId]); + + return ( +
+ , + spinning: pagination.loading, + }} + pagination={{ + position: 'bottom', + align: 'end', + size: 'small', + responsive: true, + role: 'navigation', + showQuickJumper: false, + hideOnSinglePage: true, + pageSize: pagination.pageSize, + onChange: (page, pageSize) => { + setPagination((prev) => ({ + ...prev, + page, + pageSize, + })); + }, + }} + className="[&_.ant-pagination]:gap-1" + rowKey={(link) => link.id} + dataSource={relatedPublications} + locale={{ + emptyText: ( +
+ No related publications found

} + /> +
+ ), + }} + renderItem={(publication) => ( + + + + )} + /> + {pagination.error && ( +
+
+

{pagination.error}

+ setPagination((prev) => ({ ...prev, error: null }))} + /> +
+
+ )} +
+ ); } From 37efabf95ad23b7d61ae0cd37127be63471dbac6 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 09:10:52 +0200 Subject: [PATCH 59/80] add mtype, etype to synaptome --- src/entity-configuration/definitions/view-defs/model.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/entity-configuration/definitions/view-defs/model.ts b/src/entity-configuration/definitions/view-defs/model.ts index c902776a4..f36ba1ff5 100644 --- a/src/entity-configuration/definitions/view-defs/model.ts +++ b/src/entity-configuration/definitions/view-defs/model.ts @@ -84,8 +84,11 @@ export const ViewsDefinition: { [key: string]: ViewDefinitionConfig } = { ], curated: false, summaryViewFields: [ - { field: EntityCoreFields.BrainRegion, className: 'col-span-1' }, - { field: EntityCoreFields.License, className: 'col-span-1' }, + { field: EntityCoreFields.BrainRegion }, + { field: EntityCoreFields.License }, + { field: EntityCoreFields.MType }, + { field: EntityCoreFields.EType }, + { field: EntityCoreFields.SynaptomeUsedMEModelName }, ], miniDetailView: [ { field: EntityCoreFields.BrainRegion }, From b0cef053691e23334c21fbb9aed44f656c87973c Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 10:47:21 +0200 Subject: [PATCH 60/80] add related circuits --- src/entity-configuration/domain/model/circuit.ts | 8 +++++++- src/ui/segments/detail-view/related-artifacts.tsx | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/entity-configuration/domain/model/circuit.ts b/src/entity-configuration/domain/model/circuit.ts index 119debfb0..e0ab7cca5 100644 --- a/src/entity-configuration/domain/model/circuit.ts +++ b/src/entity-configuration/domain/model/circuit.ts @@ -34,7 +34,13 @@ export const Circuit: EntityCoreTypeConfig = { extension: 'application/json', }, - detailViewSections: ['overview', 'analysis', 'visualization', 'related-publications'], + detailViewSections: [ + 'overview', + 'analysis', + 'visualization', + 'related-publications', + 'related-artifacts', + ], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/ui/segments/detail-view/related-artifacts.tsx b/src/ui/segments/detail-view/related-artifacts.tsx index 7708b6036..57f5112fb 100644 --- a/src/ui/segments/detail-view/related-artifacts.tsx +++ b/src/ui/segments/detail-view/related-artifacts.tsx @@ -7,6 +7,8 @@ import { import MEModelResults from '@/features/entities/me-model/detail-view/simulation'; import SynaptomeResults from '@/features/entities/single-neuron-synaptome/detail-view/simulation'; import { EntityTypeValue } from '@/entity-configuration/domain'; +import RelatedCircuits from '@/features/entities/circuit/elements/tabs-content/related-circuits'; +import { ICircuit } from '@/api/entitycore/types/entities/circuit'; export default async function RelatedArtifacts({ entity, @@ -26,5 +28,9 @@ export default async function RelatedArtifacts({ return ; } + if (extendedType === 'circuit') { + return ; + } + notFound(); } From d0044d4716bdb7f67c5dfa75b5d80876acc6d263 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 10:49:27 +0200 Subject: [PATCH 61/80] add microcircuit --- src/features/small-microcircuit/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/small-microcircuit/index.tsx b/src/features/small-microcircuit/index.tsx index ed10be57f..0457ee216 100644 --- a/src/features/small-microcircuit/index.tsx +++ b/src/features/small-microcircuit/index.tsx @@ -115,7 +115,7 @@ export default function SimulationCampaignConfiguration({ } return ( -
+
From aeb2693a347c1e2f60a0bef6edced9ec56a39706 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 10:59:48 +0200 Subject: [PATCH 62/80] lint --- .../data/view/[type]/[id]/[section]/page.tsx | 2 +- src/ui/segments/detail-view/analysis.tsx | 21 ------------------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx index 3966143ec..2a9a32248 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx @@ -45,7 +45,7 @@ export default async function Page({ return ; } if (section === 'analysis') { - return ; + return ; } if (section === 'configuration') { diff --git a/src/ui/segments/detail-view/analysis.tsx b/src/ui/segments/detail-view/analysis.tsx index afd8869b2..375cd6e75 100644 --- a/src/ui/segments/detail-view/analysis.tsx +++ b/src/ui/segments/detail-view/analysis.tsx @@ -5,26 +5,7 @@ import { getEntityByExtendedType, } from '@/entity-configuration/domain/helpers'; -import EModelConfig from '@/components/build-section/cell-model-assignment/e-model/EModelView'; import { EntityTypeValue } from '@/entity-configuration/domain'; -import { WorkspaceContext, AwaitedType } from '@/types/common'; -import { - IEModel, - IMEModel, - IReconstructionMorphology, - IReconstructionMorphologyExpanded, - ISingleNeuronSimulation, - ISingleNeuronSynaptomeSimulation, -} from '@/api/entitycore/types'; -import { getReconstructionMorphology } from '@/api/entitycore/queries'; -import MEModelConfig from '@/features/entities/me-model/detail-view/configuration'; -import SynaptomeConfig from '@/features/entities/single-neuron-synaptome/detail-view/configuration'; -import SynapseGroupList from '@/features/entities/single-neuron-synaptome/detail-view/elements/list-synapses-configuration'; -import { loadExpandedSingleNeuronSynaptome } from '@/page-wrappers/explore/single-neuron-synaptome'; -import { - singleNeuronSimulationApiQueryExpand, - singleNeuronSynaptomeSimulationApiQueryExpand, -} from '@/entity-configuration/domain/simulation'; import Overview from '@/features/entities/circuit/elements/tabs-content/overview'; import Analysis from '@/features/model-analysis/explorer/container'; @@ -33,11 +14,9 @@ import { ICircuit } from '@/api/entitycore/types/entities/circuit'; export default async function Configuration({ entity, extendedType, - ctx, }: { entity: EntityTypeValue; extendedType: EntityCoreExtendedType; - ctx: WorkspaceContext; }) { const entityType = getEntityByExtendedType({ type: extendedType }); if (!entityType) notFound(); From 6164b855e79d93fbcd4cbad8c89c4329b1b7f858 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 11:31:50 +0200 Subject: [PATCH 63/80] fix analysis --- src/features/model-analysis/explorer/container.tsx | 8 ++------ src/ui/segments/detail-view/analysis.tsx | 4 +++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/features/model-analysis/explorer/container.tsx b/src/features/model-analysis/explorer/container.tsx index 8348c467b..65981a963 100644 --- a/src/features/model-analysis/explorer/container.tsx +++ b/src/features/model-analysis/explorer/container.tsx @@ -1,7 +1,7 @@ 'use client'; import { LoadingOutlined } from '@ant-design/icons'; -import { notFound, useParams } from 'next/navigation'; +import { useParams } from 'next/navigation'; import { match } from 'ts-pattern'; import { useMemo } from 'react'; import { Spin } from 'antd'; @@ -11,12 +11,8 @@ import { ViewerContainer } from '@/features/model-analysis/viewer/container'; import { useLoadableValue } from '@/hooks/hooks'; import type { WorkspaceContext } from '@/types/common'; -import { EntityCoreExtendedType } from '@/entity-configuration/domain/helpers'; - -export default function Analysis({ extendedType }: { extendedType?: EntityCoreExtendedType }) { - const validTypes: EntityCoreExtendedType[] = ['emodel', 'memodel']; - if (!extendedType || !validTypes.includes(extendedType)) notFound(); +export default function Analysis() { const { virtualLabId, projectId, id } = useParams(); const results = useLoadableValue( diff --git a/src/ui/segments/detail-view/analysis.tsx b/src/ui/segments/detail-view/analysis.tsx index 375cd6e75..f444cf7bb 100644 --- a/src/ui/segments/detail-view/analysis.tsx +++ b/src/ui/segments/detail-view/analysis.tsx @@ -19,11 +19,13 @@ export default async function Configuration({ extendedType: EntityCoreExtendedType; }) { const entityType = getEntityByExtendedType({ type: extendedType }); + if (!entityType) notFound(); if (circuitTypes.includes(extendedType)) { return ; } + if (extendedType === 'memodel' || extendedType === 'emodel') return ; - return ; + return notFound(); } From c96c53886442854256ee702da4339c9bfcc77d0b Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 13:17:17 +0200 Subject: [PATCH 64/80] implement back --- .../[projectId]/data/view/[type]/[id]/layout.tsx | 4 +++- src/ui/molecules/close.tsx | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 src/ui/molecules/close.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx index 9aebc6eaa..b34dab92d 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx @@ -11,6 +11,7 @@ import { import DetailMenu from '@/ui/segments/explore/detail-menu'; import ActionMenu from '@/ui/segments/action-menu'; import type { WorkspaceContext, AwaitedType } from '@/types/common'; +import Close from '@/ui/molecules/close'; interface Params { id: string; @@ -88,7 +89,7 @@ export default async function Layout({
-
+
Name
@@ -96,6 +97,7 @@ export default async function Layout({
{children}
+
); diff --git a/src/ui/molecules/close.tsx b/src/ui/molecules/close.tsx new file mode 100644 index 000000000..c146f8ebf --- /dev/null +++ b/src/ui/molecules/close.tsx @@ -0,0 +1,12 @@ +'use client'; + +import { CloseOutlined } from '@ant-design/icons'; +import { useRouter } from 'next/navigation'; + +export default function Close() { + const router = useRouter(); + + return ( + router.back()} /> + ); +} From 4b9dea3239837c5dc4b89a0938de26de8309fee1 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 13:20:12 +0200 Subject: [PATCH 65/80] fix circuit fields layout --- .../definitions/view-defs/model.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/entity-configuration/definitions/view-defs/model.ts b/src/entity-configuration/definitions/view-defs/model.ts index f36ba1ff5..5a93d8a62 100644 --- a/src/entity-configuration/definitions/view-defs/model.ts +++ b/src/entity-configuration/definitions/view-defs/model.ts @@ -127,33 +127,27 @@ export const ViewsDefinition: { [key: string]: ViewDefinitionConfig } = { { field: EntityCoreFields.License }, ], summaryViewFields: [ - { field: EntityCoreFields.BrainRegion, className: 'col-span-1 col-start-1' }, - { field: EntityCoreFields.CircuitRootCircuit, className: 'col-span-1 col-start-1' }, - { field: EntityCoreFields.CircuitScale, className: 'col-span-1 col-start-1' }, - { field: EntityCoreFields.License, className: 'col-span-1 col-start-1' }, + { field: EntityCoreFields.BrainRegion }, + { field: EntityCoreFields.CircuitRootCircuit }, + { field: EntityCoreFields.CircuitScale }, + { field: EntityCoreFields.License }, { field: EntityCoreFields.CircuitNumberNeurons, - className: 'col-start-2 row-start-1', }, { field: EntityCoreFields.CircuitNumberConnections, - className: 'col-start-2 row-start-2', }, { field: EntityCoreFields.CircuitNumberSynapses, - className: 'col-start-2 row-start-3', }, { field: EntityCoreFields.CircuitPublishedIn, - className: 'col-start-3 row-start-1', }, { field: EntityCoreFields.CircuitExperimentDate, - className: 'col-start-3 row-start-2', }, { field: EntityCoreFields.CircuitContactEmail, - className: 'col-start-3 row-start-3', }, ], }, From d44e1538d573352fa0b7b91c729dc6ff9c156f0f Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 13:27:03 +0200 Subject: [PATCH 66/80] viz in overview for circuite --- src/entity-configuration/domain/model/circuit.ts | 8 +------- src/entity-configuration/domain/model/mirocircuit.ts | 2 +- src/entity-configuration/domain/model/paired-neurons.ts | 2 +- .../domain/model/small-microcircuit.ts | 2 +- .../circuit/elements/tabs-content/visualization.tsx | 1 + src/ui/segments/detail-view/overview.tsx | 6 +++++- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/entity-configuration/domain/model/circuit.ts b/src/entity-configuration/domain/model/circuit.ts index cc43b468d..14b70cb40 100644 --- a/src/entity-configuration/domain/model/circuit.ts +++ b/src/entity-configuration/domain/model/circuit.ts @@ -36,13 +36,7 @@ export const Circuit: EntityCoreTypeConfig = { extension: 'application/json', }, - detailViewSections: [ - 'overview', - 'analysis', - 'visualization', - 'related-publications', - 'related-artifacts', - ], + detailViewSections: ['overview', 'analysis', 'related-publications', 'related-artifacts'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/mirocircuit.ts b/src/entity-configuration/domain/model/mirocircuit.ts index 87e5b829a..900c2c0f6 100644 --- a/src/entity-configuration/domain/model/mirocircuit.ts +++ b/src/entity-configuration/domain/model/mirocircuit.ts @@ -33,7 +33,7 @@ export const Microcircuit: EntityCoreTypeConfig = { asset: { extension: 'application/json', }, - detailViewSections: ['overview', 'analysis', 'visualization', 'related-publications'], + detailViewSections: ['overview', 'analysis', 'related-publications', 'related-artifacts'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/paired-neurons.ts b/src/entity-configuration/domain/model/paired-neurons.ts index 0e5e20905..ae8ae98b8 100644 --- a/src/entity-configuration/domain/model/paired-neurons.ts +++ b/src/entity-configuration/domain/model/paired-neurons.ts @@ -33,7 +33,7 @@ export const PairedNeuronCircuit: EntityCoreTypeConfig = { asset: { extension: 'application/json', }, - detailViewSections: ['overview', 'analysis', 'visualization', 'related-publications'], + detailViewSections: ['overview', 'analysis', 'related-publications', 'related-artifacts'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/entity-configuration/domain/model/small-microcircuit.ts b/src/entity-configuration/domain/model/small-microcircuit.ts index bbc2d1b3f..58a618f57 100644 --- a/src/entity-configuration/domain/model/small-microcircuit.ts +++ b/src/entity-configuration/domain/model/small-microcircuit.ts @@ -33,7 +33,7 @@ export const SmallMicrocircuit: EntityCoreTypeConfig = { asset: { extension: 'application/json', }, - detailViewSections: ['overview', 'analysis', 'visualization', 'related-publications'], + detailViewSections: ['overview', 'analysis', 'related-publications', 'related-artifacts'], isBookmarkable: false, isDownloadable: true, isCopyable: true, diff --git a/src/features/entities/circuit/elements/tabs-content/visualization.tsx b/src/features/entities/circuit/elements/tabs-content/visualization.tsx index 438e4e307..7e1d97022 100644 --- a/src/features/entities/circuit/elements/tabs-content/visualization.tsx +++ b/src/features/entities/circuit/elements/tabs-content/visualization.tsx @@ -30,6 +30,7 @@ export default function Visualization({ circuit }: Props) { maxWidth="100%" yPadding={16} xPadding={16} + bordered={false} />
); diff --git a/src/ui/segments/detail-view/overview.tsx b/src/ui/segments/detail-view/overview.tsx index f970da174..465dbe969 100644 --- a/src/ui/segments/detail-view/overview.tsx +++ b/src/ui/segments/detail-view/overview.tsx @@ -7,12 +7,14 @@ import { Field } from '@/features/details-view/overview'; import MEModelDetails from '@/features/entities/neuron-simulation/elements/me-model-details'; import SynaptomeDetails from '@/features/entities/neuron-simulation/elements/synaptome-details'; import { EntityTypeValue } from '@/entity-configuration/domain'; -import { EntityCoreExtendedType } from '@/entity-configuration/domain/helpers'; +import CircuitViz from '@/features/entities/circuit/elements/tabs-content/visualization'; +import { circuitTypes, EntityCoreExtendedType } from '@/entity-configuration/domain/helpers'; import { resolveSingleNeuronSimulation, resolveSingleNeuronSynaptomeSimulation, } from '@/entity-configuration/domain/simulation'; import { AwaitedType, WorkspaceContext } from '@/types/common'; +import { ICircuit } from '@/api/entitycore/types/entities/circuit'; export default async function Overview({ entity, @@ -78,6 +80,8 @@ export default async function Overview({ projectId={ctx.virtualLabId} /> )} + + {circuitTypes.includes(extendedType) && entity && } ); } From f1e539744ec8808cae6270767ec5b96bc66bec4b Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 13:41:35 +0200 Subject: [PATCH 67/80] tabbed component --- .../tabs-content/related-publications.tsx | 53 +++++-------------- src/ui/molecules/tabbed-page.tsx | 46 ++++++++++++++++ .../detail-view/related-publications.tsx | 52 +++++------------- 3 files changed, 70 insertions(+), 81 deletions(-) create mode 100644 src/ui/molecules/tabbed-page.tsx diff --git a/src/features/entities/circuit/elements/tabs-content/related-publications.tsx b/src/features/entities/circuit/elements/tabs-content/related-publications.tsx index df736ef82..86b16cbf4 100644 --- a/src/features/entities/circuit/elements/tabs-content/related-publications.tsx +++ b/src/features/entities/circuit/elements/tabs-content/related-publications.tsx @@ -1,14 +1,11 @@ import { CloseCircleTwoTone, LoadingOutlined } from '@ant-design/icons'; -import { Collapse, Empty, List } from 'antd'; +import { Empty, List } from 'antd'; import { useEffect, useState } from 'react'; import { useParams } from 'next/navigation'; -import type { CollapseProps } from 'antd'; - import { getScientificArtifactPublicationLinks } from '@/api/entitycore/queries/general/scientific-artifact-publication-link'; import { PublicationTypeDictionary } from '@/api/entitycore/types/entities/scientific-artifact-publication-link'; import { Card } from '@/features/entities/circuit/elements/publication-item/card'; -import { classNames } from '@/util/utils'; import { tryCatch } from '@/api/utils'; import type { @@ -17,6 +14,7 @@ import type { } from '@/api/entitycore/types/entities/scientific-artifact-publication-link'; import type { ICircuit } from '@/api/entitycore/types/entities/circuit'; import type { WorkspaceContext } from '@/types/common'; +import Tabs, { Tab } from '@/ui/molecules/tabbed-page'; type Props = { circuit: ICircuit; @@ -146,45 +144,18 @@ function PerTypePublications({ } export default function RelatedPublications({ circuit }: Props) { - const items: CollapseProps['items'] = [ - { - key: 'entity_source', - label: 'Circuit provenance', - children: ( + return ( + + - ), - }, - { - key: 'component_source', - label: 'Related artifacts provenance', - children: ( + + + - ), - }, - { - key: 'application', - label: 'Applications', - children: ( + + - ), - }, - ]; - - return ( -
- null} - className={classNames( - '[&_.ant-collapse-item]:mb-2', - '[&_.ant-collapse-header]:bg-primary-8 [&_.ant-collapse-header]:border-none [&_.ant-collapse-header]:text-white!', - '[&_.ant-collapse-header]:rounded-none! [&_.ant-collapse-header]:text-lg [&_.ant-collapse-header]:font-semibold' - )} - /> -
+
+
); } diff --git a/src/ui/molecules/tabbed-page.tsx b/src/ui/molecules/tabbed-page.tsx new file mode 100644 index 000000000..5c670cd69 --- /dev/null +++ b/src/ui/molecules/tabbed-page.tsx @@ -0,0 +1,46 @@ +import React, { useState, ReactNode, ReactElement } from 'react'; + +type TabProps = { + label: string; //eslint-disable-line + children: ReactNode; +}; + +export function Tab({ children }: TabProps) { + return <>{children}; +} + +type TabsProps = { + children: ReactElement | ReactElement[]; + defaultIndex?: number; +}; + +export default function Tabs({ children, defaultIndex = 0 }: TabsProps) { + const tabs = React.Children.toArray(children) as ReactElement[]; + + const [activeIndex, setActiveIndex] = useState(defaultIndex); + + if (tabs.length === 0) return
No tabs defined.
; + + return ( +
+
+ {tabs.map((tab, index) => ( + + ))} +
+ +
{tabs[activeIndex]}
+
+ ); +} diff --git a/src/ui/segments/detail-view/related-publications.tsx b/src/ui/segments/detail-view/related-publications.tsx index 4bb4da53d..91f653cad 100644 --- a/src/ui/segments/detail-view/related-publications.tsx +++ b/src/ui/segments/detail-view/related-publications.tsx @@ -1,8 +1,7 @@ 'use client'; import { CloseCircleTwoTone, LoadingOutlined } from '@ant-design/icons'; -import { Collapse, Empty, List } from 'antd'; -import type { CollapseProps } from 'antd'; +import { Empty, List } from 'antd'; import { useEffect, useState } from 'react'; import { notFound, useParams } from 'next/navigation'; import type { EntityTypeValue } from '@/entity-configuration/domain'; @@ -10,9 +9,9 @@ import type { EntityTypeValue } from '@/entity-configuration/domain'; import { getScientificArtifactPublicationLinks } from '@/api/entitycore/queries/general/scientific-artifact-publication-link'; import { PublicationTypeDictionary } from '@/api/entitycore/types/entities/scientific-artifact-publication-link'; import { Card } from '@/features/entities/circuit/elements/publication-item/card'; -import { classNames } from '@/util/utils'; import type { EntityCoreExtendedType } from '@/entity-configuration/domain/helpers'; import { tryCatch } from '@/api/utils'; +import Tabs, { Tab } from '@/ui/molecules/tabbed-page'; import type { IScientificArtifactPublicationLink, @@ -31,46 +30,19 @@ export default function RelatedPublications({ const entityType = getEntityByExtendedType({ type: extendedType }); if (!entityType) notFound(); - const items: CollapseProps['items'] = [ - { - key: 'entity_source', - label: 'Circuit provenance', - children: ( + return ( + + - ), - }, - { - key: 'component_source', - label: 'Related artifacts provenance', - children: ( + + + - ), - }, - { - key: 'application', - label: 'Applications', - children: ( + + - ), - }, - ]; - - return ( -
- null} - className={classNames( - '[&_.ant-collapse-item]:mb-2', - '[&_.ant-collapse-header]:bg-primary-8 [&_.ant-collapse-header]:border-none [&_.ant-collapse-header]:text-white!', - '[&_.ant-collapse-header]:rounded-none! [&_.ant-collapse-header]:text-lg [&_.ant-collapse-header]:font-semibold' - )} - /> -
+
+
); } From 8ac843bccd194439d7e8e439e1fa17a4c9d8b88b Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 13:49:29 +0200 Subject: [PATCH 68/80] improve styles of related publications --- src/ui/molecules/tabbed-page.tsx | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/ui/molecules/tabbed-page.tsx b/src/ui/molecules/tabbed-page.tsx index 5c670cd69..971eb88fa 100644 --- a/src/ui/molecules/tabbed-page.tsx +++ b/src/ui/molecules/tabbed-page.tsx @@ -1,4 +1,5 @@ import React, { useState, ReactNode, ReactElement } from 'react'; +import { Button } from './button'; type TabProps = { label: string; //eslint-disable-line @@ -23,24 +24,19 @@ export default function Tabs({ children, defaultIndex = 0 }: TabsProps) { return (
-
- {tabs.map((tab, index) => ( - - ))} -
- -
{tabs[activeIndex]}
+ {tabs.map((tab, index) => ( + + ))} +
{tabs[activeIndex]}
); } From 7275bb9307f8d5af876a17fbb7d12b2d5c95c2e8 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 13:55:51 +0200 Subject: [PATCH 69/80] add tabs --- src/ui/molecules/tabbed-page.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ui/molecules/tabbed-page.tsx b/src/ui/molecules/tabbed-page.tsx index 971eb88fa..e54b4e50d 100644 --- a/src/ui/molecules/tabbed-page.tsx +++ b/src/ui/molecules/tabbed-page.tsx @@ -22,16 +22,23 @@ export default function Tabs({ children, defaultIndex = 0 }: TabsProps) { if (tabs.length === 0) return
No tabs defined.
; + function rounded(index: number) { + if (index === 0) return 'rounded-l-full!'; + if (index === tabs.length - 1) return 'rounded-r-full!'; + return undefined; + } + return (
{tabs.map((tab, index) => ( From ab9cd86eb19c254859c253aab3f8d05b6f5e24b1 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 13:56:34 +0200 Subject: [PATCH 70/80] provenance --- .../circuit/elements/tabs-content/related-publications.tsx | 2 +- src/ui/segments/detail-view/related-publications.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/entities/circuit/elements/tabs-content/related-publications.tsx b/src/features/entities/circuit/elements/tabs-content/related-publications.tsx index 86b16cbf4..cb93527b6 100644 --- a/src/features/entities/circuit/elements/tabs-content/related-publications.tsx +++ b/src/features/entities/circuit/elements/tabs-content/related-publications.tsx @@ -150,7 +150,7 @@ export default function RelatedPublications({ circuit }: Props) { - + diff --git a/src/ui/segments/detail-view/related-publications.tsx b/src/ui/segments/detail-view/related-publications.tsx index 91f653cad..6baee8a64 100644 --- a/src/ui/segments/detail-view/related-publications.tsx +++ b/src/ui/segments/detail-view/related-publications.tsx @@ -36,7 +36,7 @@ export default function RelatedPublications({ - + From 8f9752f6de0937c8164ee8287da9ba7c6154e687 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 14:12:01 +0200 Subject: [PATCH 71/80] improve analysis styles --- src/features/entities/circuit/elements/section-header.tsx | 6 +++++- .../entities/circuit/elements/tabs-content/overview.tsx | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/features/entities/circuit/elements/section-header.tsx b/src/features/entities/circuit/elements/section-header.tsx index dadea1307..79f63bcc0 100644 --- a/src/features/entities/circuit/elements/section-header.tsx +++ b/src/features/entities/circuit/elements/section-header.tsx @@ -1,3 +1,7 @@ export function Header({ title }: { title: string }) { - return
{title}
; + return ( +
+ {title} +
+ ); } diff --git a/src/features/entities/circuit/elements/tabs-content/overview.tsx b/src/features/entities/circuit/elements/tabs-content/overview.tsx index 38113b22f..89b3c3951 100644 --- a/src/features/entities/circuit/elements/tabs-content/overview.tsx +++ b/src/features/entities/circuit/elements/tabs-content/overview.tsx @@ -36,8 +36,8 @@ export default function Overview({ circuit }: Props) { }); const list = { - cell: { title: 'Cell Properties', items: [cellProperties] }, - network: { title: 'Network properties', items: [networkPropertiesA, networkPropertiesB] }, + cell: { title: 'Cell statistics', items: [cellProperties] }, + network: { title: 'Network statistics', items: [networkPropertiesA, networkPropertiesB] }, }; return ( From 645b5fab3674ee8e6c3aefeb8c43459416f1e448 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 14:41:59 +0200 Subject: [PATCH 72/80] improve layout --- .../data/view/[type]/[id]/layout.tsx | 6 +- .../tabs-content/related-circuits.tsx | 78 ++++++------------- src/ui/molecules/tabbed-page.tsx | 5 +- 3 files changed, 31 insertions(+), 58 deletions(-) diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx index b34dab92d..838d4c5ed 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout.tsx @@ -68,7 +68,7 @@ export default async function Layout({ return (
-
+
@@ -89,8 +89,8 @@ export default async function Layout({
-
-
+
+
Name
{entity.name}
diff --git a/src/features/entities/circuit/elements/tabs-content/related-circuits.tsx b/src/features/entities/circuit/elements/tabs-content/related-circuits.tsx index a0e1c1029..b50acebcb 100644 --- a/src/features/entities/circuit/elements/tabs-content/related-circuits.tsx +++ b/src/features/entities/circuit/elements/tabs-content/related-circuits.tsx @@ -1,12 +1,10 @@ 'use client'; import { LoadingOutlined } from '@ant-design/icons'; -import { Collapse } from 'antd'; import { useParams } from 'next/navigation'; import { loadable } from 'jotai/utils'; import { useAtomValue } from 'jotai'; import { useMemo } from 'react'; -import isNil from 'lodash/isNil'; import { Subcircuits } from '@/features/entities/circuit/elements/related-circuits/subcircuits'; import { DerivedFrom } from '@/features/entities/circuit/elements/related-circuits/derived-from'; @@ -15,10 +13,10 @@ import { Derived } from '@/features/entities/circuit/elements/related-circuits/d import { Parent } from '@/features/entities/circuit/elements/related-circuits/parent'; import { Root } from '@/features/entities/circuit/elements/related-circuits/root'; import { useUnwrappedValue } from '@/hooks/hooks'; -import { classNames } from '@/util/utils'; import type { ICircuit } from '@/api/entitycore/types/entities/circuit'; import type { WorkspaceContext } from '@/types/common'; +import Tabs, { Tab } from '@/ui/molecules/tabbed-page'; type Props = { circuit: ICircuit; @@ -42,44 +40,6 @@ export default function RelatedCircuits({ circuit }: Props) { ) ); - const items = [ - { - key: 'parent', - label: 'Parent circuit', - children: , - visible: Boolean(result?.parent), - }, - { - key: 'root', - label: 'Root circuit', - children: , - visible: Boolean(circuit.root_circuit_id), - }, - { - key: 'derivedFrom', - label: 'Derived from', - children: , - visible: Boolean(result?.derivedFrom), - }, - { - key: 'subCircuits', - label: 'Subcircuits', - children: , - visible: Boolean(result?.subCircuits?.at(0)?.sub_circuits?.length), - }, - { - key: 'derived', - label: 'Derived circuits', - children: , - visible: Boolean(result?.derived?.at(0)?.sub_circuits?.length), - }, - ].filter((item) => item.visible); - - const shouldAppear = items - .filter((o) => o.visible) - .map((o) => o.key) - .filter((o) => !isNil(o)); - const isLoading = useAtomValue( loadable( @@ -107,19 +67,29 @@ export default function RelatedCircuits({ circuit }: Props) { return (
- null} - className={classNames( - '[&_.ant-collapse-item]:mb-2', - '[&_.ant-collapse-header]:bg-primary-8 [&_.ant-collapse-header]:border-none [&_.ant-collapse-header]:text-white!', - '[&_.ant-collapse-header]:rounded-none! [&_.ant-collapse-header]:text-lg [&_.ant-collapse-header]:font-semibold' - )} - /> + + + , + + + , + + + , + + + , + + + , + +
); } diff --git a/src/ui/molecules/tabbed-page.tsx b/src/ui/molecules/tabbed-page.tsx index e54b4e50d..7a42d3b8e 100644 --- a/src/ui/molecules/tabbed-page.tsx +++ b/src/ui/molecules/tabbed-page.tsx @@ -2,6 +2,7 @@ import React, { useState, ReactNode, ReactElement } from 'react'; import { Button } from './button'; type TabProps = { + visible?: boolean; //eslint-disable-line label: string; //eslint-disable-line children: ReactNode; }; @@ -16,7 +17,9 @@ type TabsProps = { }; export default function Tabs({ children, defaultIndex = 0 }: TabsProps) { - const tabs = React.Children.toArray(children) as ReactElement[]; + const tabs = React.Children.toArray(children).filter((t) => { + return Boolean((t as ReactElement).props.visible); + }) as ReactElement[]; const [activeIndex, setActiveIndex] = useState(defaultIndex); From a150522e38f9a5b45af7537562814f236b1fdd34 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Mon, 8 Sep 2025 14:47:56 +0200 Subject: [PATCH 73/80] Default message --- .../circuit/elements/tabs-content/related-circuits.tsx | 2 +- .../circuit/elements/tabs-content/related-publications.tsx | 2 +- src/ui/molecules/tabbed-page.tsx | 7 ++++--- src/ui/segments/detail-view/related-publications.tsx | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/features/entities/circuit/elements/tabs-content/related-circuits.tsx b/src/features/entities/circuit/elements/tabs-content/related-circuits.tsx index b50acebcb..59ee702d5 100644 --- a/src/features/entities/circuit/elements/tabs-content/related-circuits.tsx +++ b/src/features/entities/circuit/elements/tabs-content/related-circuits.tsx @@ -67,7 +67,7 @@ export default function RelatedCircuits({ circuit }: Props) { return (
- + , diff --git a/src/features/entities/circuit/elements/tabs-content/related-publications.tsx b/src/features/entities/circuit/elements/tabs-content/related-publications.tsx index cb93527b6..564ed4b4c 100644 --- a/src/features/entities/circuit/elements/tabs-content/related-publications.tsx +++ b/src/features/entities/circuit/elements/tabs-content/related-publications.tsx @@ -145,7 +145,7 @@ function PerTypePublications({ export default function RelatedPublications({ circuit }: Props) { return ( - + diff --git a/src/ui/molecules/tabbed-page.tsx b/src/ui/molecules/tabbed-page.tsx index 7a42d3b8e..da655f94b 100644 --- a/src/ui/molecules/tabbed-page.tsx +++ b/src/ui/molecules/tabbed-page.tsx @@ -14,16 +14,17 @@ export function Tab({ children }: TabProps) { type TabsProps = { children: ReactElement | ReactElement[]; defaultIndex?: number; + defaultMessage: string; }; -export default function Tabs({ children, defaultIndex = 0 }: TabsProps) { +export default function Tabs({ children, defaultIndex = 0, defaultMessage }: TabsProps) { const tabs = React.Children.toArray(children).filter((t) => { - return Boolean((t as ReactElement).props.visible); + return Boolean((t as ReactElement).props.visible ?? true); }) as ReactElement[]; const [activeIndex, setActiveIndex] = useState(defaultIndex); - if (tabs.length === 0) return
No tabs defined.
; + if (tabs.length === 0) return
{defaultMessage}
; function rounded(index: number) { if (index === 0) return 'rounded-l-full!'; diff --git a/src/ui/segments/detail-view/related-publications.tsx b/src/ui/segments/detail-view/related-publications.tsx index 6baee8a64..1121b05e5 100644 --- a/src/ui/segments/detail-view/related-publications.tsx +++ b/src/ui/segments/detail-view/related-publications.tsx @@ -31,7 +31,7 @@ export default function RelatedPublications({ if (!entityType) notFound(); return ( - + From e8f921ea62366f48bbaba1d9471a85643d4a21dd Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 9 Sep 2025 09:13:21 +0200 Subject: [PATCH 74/80] move viz to overview --- .../data/view/[type]/[id]/[section]/page.tsx | 4 --- src/entity-configuration/definitions/types.ts | 1 - .../experimental/electrical-cell-recording.ts | 2 +- .../experimental/reconstruction-morphology.ts | 2 +- src/ui/segments/detail-view/overview.tsx | 13 +++++++- src/ui/segments/detail-view/viz.tsx | 33 ------------------- 6 files changed, 14 insertions(+), 41 deletions(-) delete mode 100644 src/ui/segments/detail-view/viz.tsx diff --git a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx index 2a9a32248..68fd2eda6 100644 --- a/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx +++ b/src/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/[section]/page.tsx @@ -10,7 +10,6 @@ import { import type { ServerSideComponentProp, WorkspaceContext } from '@/types/common'; import Overview from '@/ui/segments/detail-view/overview'; -import Visualization from '@/ui/segments/detail-view/viz'; import Analysis from '@/ui/segments/detail-view/analysis'; import RelatedArtifacts from '@/ui/segments/detail-view/related-artifacts'; import RelatedPublications from '@/ui/segments/detail-view/related-publications'; @@ -41,9 +40,6 @@ export default async function Page({ if (section === 'overview') { return ; } - if (section === 'visualization') { - return ; - } if (section === 'analysis') { return ; } diff --git a/src/entity-configuration/definitions/types.ts b/src/entity-configuration/definitions/types.ts index d4ed86471..33e124b00 100644 --- a/src/entity-configuration/definitions/types.ts +++ b/src/entity-configuration/definitions/types.ts @@ -129,7 +129,6 @@ export type FieldsDefinitionRegistry = Record< export type DetailViewSection = | 'overview' | 'results' - | 'visualization' | 'analysis' | 'related-publications' | 'related-artifacts' diff --git a/src/entity-configuration/domain/experimental/electrical-cell-recording.ts b/src/entity-configuration/domain/experimental/electrical-cell-recording.ts index 7f71733d1..7094a673a 100644 --- a/src/entity-configuration/domain/experimental/electrical-cell-recording.ts +++ b/src/entity-configuration/domain/experimental/electrical-cell-recording.ts @@ -40,7 +40,7 @@ export const ElectricalCellRecording: EntityCoreTypeConfig )} - {circuitTypes.includes(extendedType) && entity && } + {circuitTypes.includes(extendedType) && } + + {extendedType === 'reconstruction_morphology' && ( + + )} + + {extendedType === 'electrical_cell_recording' && ( + + )} ); } diff --git a/src/ui/segments/detail-view/viz.tsx b/src/ui/segments/detail-view/viz.tsx deleted file mode 100644 index b6b092135..000000000 --- a/src/ui/segments/detail-view/viz.tsx +++ /dev/null @@ -1,33 +0,0 @@ -'use client'; - -import { notFound } from 'next/navigation'; -import { MorphoViewerLoaderMemo } from '@/features/entities/reconstruction-morphology/detail-view'; -import { IReconstructionMorphology, IElectricalCellRecording } from '@/api/entitycore/types'; -import EphysViewer from '@/features/ephys-viewer'; -import CircuitViz from '@/features/entities/circuit/elements/tabs-content/visualization'; - -import { downloadEntity } from '@/app/app/v2/[virtualLabId]/[projectId]/data/view/[type]/[id]/layout'; -import { WorkspaceContext } from '@/types/common'; -import { ICircuit } from '@/api/entitycore/types/entities/circuit'; - -type AwaitedType = T extends Promise ? U : T; - -export default function Visualization({ - entity, - ctx, -}: { - entity: AwaitedType>; - ctx: WorkspaceContext; -}) { - if (entity.type === 'reconstruction_morphology') { - return ; - } - if (entity.type === 'electrical_cell_recording') { - return ; - } - if (entity.type === 'circuit') { - return ; - } - - notFound(); -} From ab9a2eb20d7fd5c57ce2bdc20499806005fd8aa6 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 9 Sep 2025 09:22:27 +0200 Subject: [PATCH 75/80] ephys viewer --- src/features/ephys-viewer/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/features/ephys-viewer/index.tsx b/src/features/ephys-viewer/index.tsx index f405edc5e..9a0fa9a04 100644 --- a/src/features/ephys-viewer/index.tsx +++ b/src/features/ephys-viewer/index.tsx @@ -1,3 +1,5 @@ +'use client'; + import dynamic from 'next/dynamic'; const EphysViewer = dynamic(() => import('./ephys-viewer'), { ssr: false }); From d2cf2c13597c6c40877d1ef0eefad3f2d0d5a442 Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 9 Sep 2025 10:09:17 +0200 Subject: [PATCH 76/80] working on fields --- .../definitions/fields-defs/experimental.tsx | 100 ++++++++++-------- .../definitions/view-defs/experimental.ts | 26 ++--- src/features/details-view/overview.tsx | 2 + src/ui/segments/detail-view/overview.tsx | 16 ++- 4 files changed, 88 insertions(+), 56 deletions(-) diff --git a/src/entity-configuration/definitions/fields-defs/experimental.tsx b/src/entity-configuration/definitions/fields-defs/experimental.tsx index 4605ebe31..351a15a1e 100644 --- a/src/entity-configuration/definitions/fields-defs/experimental.tsx +++ b/src/entity-configuration/definitions/fields-defs/experimental.tsx @@ -31,6 +31,60 @@ import type { import type { FieldsDefinitionRegistry } from '@/entity-configuration/definitions/types'; import type { IEType, IMType } from '@/api/entitycore/types/shared/global'; +const morphologyMtypes = (morphology: IReconstructionMorphology) => { + return renderEmptyOrValue(renderArray(morphology.mtypes?.map((m) => m.pref_label) || [])); +}; + +const emodelEtypes = (emodel: IEModel) => { + return renderEmptyOrValue(renderArray(emodel.etypes?.map((m) => m.pref_label) || [])); +}; + +const renderMtype = (r: EntityCoreObjectTypes) => { + if (isSingleNeuronSynaptome(r)) { + return renderEmptyOrValue( + renderArray( + (r.me_model.mtypes && + r.me_model.mtypes.length > 0 && + r.me_model.mtypes.map((m) => m.pref_label)) || + morphologyMtypes(r.me_model.morphology) + ) + ); + } + if (isMemodel(r) && isEmpty(r.etypes)) { + return morphologyMtypes; + } + return renderEmptyOrValue( + renderArray( + (r as EntityCoreObjectTypes & { mtypes: Array | null }).mtypes?.map( + (m: IMType) => m.pref_label + ) || [] + ) + ); +}; + +const renderEtype = (r: EntityCoreObjectTypes) => { + if (isSingleNeuronSynaptome(r)) { + return renderEmptyOrValue( + renderArray( + (r.me_model.etypes && + r.me_model.etypes.length > 0 && + r.me_model.etypes.map((m) => m.pref_label)) || + emodelEtypes(r.me_model.emodel) + ) + ); + } + if (isMemodel(r) && isEmpty(r.etypes)) { + return emodelEtypes(r.emodel); + } + return renderEmptyOrValue( + renderArray( + (r as EntityCoreObjectTypes & { etypes: Array | null }).etypes?.map( + (e: IEType) => e.pref_label + ) || [] + ) + ); +}; + export const FieldsDefinition: Partial> = { [EntityCoreFields.License]: { title: 'License', @@ -86,27 +140,8 @@ export const FieldsDefinition: Partial { - if (isSingleNeuronSynaptome(r)) { - return renderEmptyOrValue(renderArray(r.me_model.mtypes?.map((m) => m.pref_label) || [])); - } - if (isMemodel(r) && isEmpty(r.etypes)) { - return renderEmptyOrValue( - renderArray( - ( - r.morphology as IReconstructionMorphology & { etypes: Array | null } - ).mtypes?.map((m) => m.pref_label) || [] - ) - ); - } - return renderEmptyOrValue( - renderArray( - (r as EntityCoreObjectTypes & { mtypes: Array | null }).mtypes?.map( - (m: IMType) => m.pref_label - ) || [] - ) - ); - }, + render: renderMtype, + renderForDetailView: renderMtype, vocabulary: { plural: 'M-Types', singular: 'M-Type', @@ -132,27 +167,8 @@ export const FieldsDefinition: Partial { - if (isSingleNeuronSynaptome(r)) { - return renderEmptyOrValue(renderArray(r.me_model.etypes?.map((m) => m.pref_label) || [])); - } - if (isMemodel(r) && isEmpty(r.etypes)) { - return renderEmptyOrValue( - renderArray( - (r.emodel as IEModel & { etypes: Array | null }).etypes?.map( - (e) => e.pref_label - ) || [] - ) - ); - } - return renderEmptyOrValue( - renderArray( - (r as EntityCoreObjectTypes & { etypes: Array | null }).etypes?.map( - (e: IEType) => e.pref_label - ) || [] - ) - ); - }, + render: renderEtype, + renderForDetailView: renderEtype, vocabulary: { plural: 'E-Types', singular: 'E-Type', diff --git a/src/entity-configuration/definitions/view-defs/experimental.ts b/src/entity-configuration/definitions/view-defs/experimental.ts index ebe66bc2b..df8c2fe49 100644 --- a/src/entity-configuration/definitions/view-defs/experimental.ts +++ b/src/entity-configuration/definitions/view-defs/experimental.ts @@ -178,10 +178,10 @@ export const ViewsDefinition: { [key: string]: ViewDefinitionConfig } = { { field: EntityCoreFields.Species }, { field: EntityCoreFields.License }, { field: EntityCoreFields.MType }, - { field: EntityCoreFields.SubjectAge, className: 'col-span-2' }, - { field: EntityCoreFields.EType, className: 'col-span-3' }, - { field: EntityCoreFields.NeuronDensity, className: 'col-span-3' }, - { field: EntityCoreFields.NumberOfMeasurements, className: 'col-span-3' }, + { field: EntityCoreFields.SubjectAge }, + { field: EntityCoreFields.EType }, + { field: EntityCoreFields.NeuronDensity }, + { field: EntityCoreFields.NumberOfMeasurements }, ], miniDetailView: [ { field: EntityCoreFields.BrainRegion }, @@ -211,11 +211,11 @@ export const ViewsDefinition: { [key: string]: ViewDefinitionConfig } = { { field: EntityCoreFields.Species }, { field: EntityCoreFields.License }, { field: EntityCoreFields.MType }, - { field: EntityCoreFields.SubjectAge, className: 'col-span-2' }, + { field: EntityCoreFields.SubjectAge }, { field: EntityCoreFields.MeanSTD }, - { field: EntityCoreFields.Weight, className: 'col-span-2' }, - { field: EntityCoreFields.Sem, className: 'col-span-3' }, - { field: EntityCoreFields.NumberOfMeasurements, className: 'col-span-3' }, + { field: EntityCoreFields.Weight }, + { field: EntityCoreFields.Sem }, + { field: EntityCoreFields.NumberOfMeasurements }, ], miniDetailView: [ { field: EntityCoreFields.BrainRegion }, @@ -245,12 +245,12 @@ export const ViewsDefinition: { [key: string]: ViewDefinitionConfig } = { { field: EntityCoreFields.Species }, { field: EntityCoreFields.License }, { field: EntityCoreFields.PostSynapticBrainRegion }, - { field: EntityCoreFields.SubjectAge, className: 'col-span-2' }, + { field: EntityCoreFields.SubjectAge }, { field: EntityCoreFields.PreSynapticCellType }, - { field: EntityCoreFields.Weight, className: 'col-span-2' }, - { field: EntityCoreFields.PostSynapticCellType, className: 'col-span-3' }, - { field: EntityCoreFields.MeanSTD, className: 'col-span-3' }, - { field: EntityCoreFields.Sem, className: 'col-span-3' }, + { field: EntityCoreFields.Weight }, + { field: EntityCoreFields.PostSynapticCellType }, + { field: EntityCoreFields.MeanSTD }, + { field: EntityCoreFields.Sem }, // { // field: EntityCoreFields.NumberOfConnections, // }, diff --git a/src/features/details-view/overview.tsx b/src/features/details-view/overview.tsx index 7ee28975d..970022964 100644 --- a/src/features/details-view/overview.tsx +++ b/src/features/details-view/overview.tsx @@ -21,6 +21,8 @@ type FieldProps = { export function Field({ field, className, data }: FieldProps) { const fieldObj = getFieldDefinition(field); + console.log(field, data, fieldObj?.renderForDetailView); + let renderedContent: ReactNode = null; if (fieldObj) { if (fieldObj.renderForDetailView) { diff --git a/src/ui/segments/detail-view/overview.tsx b/src/ui/segments/detail-view/overview.tsx index 42c2e003b..9e7412456 100644 --- a/src/ui/segments/detail-view/overview.tsx +++ b/src/ui/segments/detail-view/overview.tsx @@ -16,8 +16,13 @@ import { import { AwaitedType, WorkspaceContext } from '@/types/common'; import { ICircuit } from '@/api/entitycore/types/entities/circuit'; import { MorphoViewerLoaderMemo } from '@/features/entities/reconstruction-morphology/detail-view'; -import { IReconstructionMorphology, IElectricalCellRecording } from '@/api/entitycore/types'; +import { + IReconstructionMorphology, + IElectricalCellRecording, + ISingleNeuronSynaptome, +} from '@/api/entitycore/types'; import EphysViewer from '@/features/ephys-viewer'; +import { getMEModel } from '@/api/entitycore/queries'; export default async function Overview({ entity, @@ -59,6 +64,15 @@ export default async function Overview({ } } + if (extendedType === 'single_neuron_synaptome') { + const meModel = await getMEModel({ + id: (entity as ISingleNeuronSynaptome).me_model.id, + context: ctx, + }); + + (entity as ISingleNeuronSynaptome).me_model = meModel; //eslint-disable-line + } + return ( <>
From c43659914552a2cde3f3b8e1e7d9687f4cbf6b1c Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 9 Sep 2025 10:14:39 +0200 Subject: [PATCH 77/80] fix render mtypes, etypes --- .../definitions/fields-defs/experimental.tsx | 94 +++++++++---------- 1 file changed, 44 insertions(+), 50 deletions(-) diff --git a/src/entity-configuration/definitions/fields-defs/experimental.tsx b/src/entity-configuration/definitions/fields-defs/experimental.tsx index 351a15a1e..c1c78d6bb 100644 --- a/src/entity-configuration/definitions/fields-defs/experimental.tsx +++ b/src/entity-configuration/definitions/fields-defs/experimental.tsx @@ -39,52 +39,6 @@ const emodelEtypes = (emodel: IEModel) => { return renderEmptyOrValue(renderArray(emodel.etypes?.map((m) => m.pref_label) || [])); }; -const renderMtype = (r: EntityCoreObjectTypes) => { - if (isSingleNeuronSynaptome(r)) { - return renderEmptyOrValue( - renderArray( - (r.me_model.mtypes && - r.me_model.mtypes.length > 0 && - r.me_model.mtypes.map((m) => m.pref_label)) || - morphologyMtypes(r.me_model.morphology) - ) - ); - } - if (isMemodel(r) && isEmpty(r.etypes)) { - return morphologyMtypes; - } - return renderEmptyOrValue( - renderArray( - (r as EntityCoreObjectTypes & { mtypes: Array | null }).mtypes?.map( - (m: IMType) => m.pref_label - ) || [] - ) - ); -}; - -const renderEtype = (r: EntityCoreObjectTypes) => { - if (isSingleNeuronSynaptome(r)) { - return renderEmptyOrValue( - renderArray( - (r.me_model.etypes && - r.me_model.etypes.length > 0 && - r.me_model.etypes.map((m) => m.pref_label)) || - emodelEtypes(r.me_model.emodel) - ) - ); - } - if (isMemodel(r) && isEmpty(r.etypes)) { - return emodelEtypes(r.emodel); - } - return renderEmptyOrValue( - renderArray( - (r as EntityCoreObjectTypes & { etypes: Array | null }).etypes?.map( - (e: IEType) => e.pref_label - ) || [] - ) - ); -}; - export const FieldsDefinition: Partial> = { [EntityCoreFields.License]: { title: 'License', @@ -140,8 +94,28 @@ export const FieldsDefinition: Partial 0 && + r.me_model.mtypes.map((m) => m.pref_label)) || + morphologyMtypes(r.me_model.morphology) + ) + ); + } + if (isMemodel(r) && isEmpty(r.etypes)) { + return morphologyMtypes(r.morphology); + } + return renderEmptyOrValue( + renderArray( + (r as EntityCoreObjectTypes & { mtypes: Array | null }).mtypes?.map( + (m: IMType) => m.pref_label + ) || [] + ) + ); + }, vocabulary: { plural: 'M-Types', singular: 'M-Type', @@ -167,8 +141,28 @@ export const FieldsDefinition: Partial 0 && + r.me_model.etypes.map((m) => m.pref_label)) || + emodelEtypes(r.me_model.emodel) + ) + ); + } + if (isMemodel(r) && isEmpty(r.etypes)) { + return emodelEtypes(r.emodel); + } + return renderEmptyOrValue( + renderArray( + (r as EntityCoreObjectTypes & { etypes: Array | null }).etypes?.map( + (e: IEType) => e.pref_label + ) || [] + ) + ); + }, vocabulary: { plural: 'E-Types', singular: 'E-Type', From ac710461fdfb4fd7e925395c102796197e8bdb4e Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 9 Sep 2025 10:21:52 +0200 Subject: [PATCH 78/80] Add mtype etypes --- .../definitions/fields-defs/experimental.tsx | 6 ++++-- src/features/details-view/overview.tsx | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/entity-configuration/definitions/fields-defs/experimental.tsx b/src/entity-configuration/definitions/fields-defs/experimental.tsx index c1c78d6bb..c83c22ada 100644 --- a/src/entity-configuration/definitions/fields-defs/experimental.tsx +++ b/src/entity-configuration/definitions/fields-defs/experimental.tsx @@ -31,11 +31,13 @@ import type { import type { FieldsDefinitionRegistry } from '@/entity-configuration/definitions/types'; import type { IEType, IMType } from '@/api/entitycore/types/shared/global'; -const morphologyMtypes = (morphology: IReconstructionMorphology) => { +const morphologyMtypes = (morphology?: IReconstructionMorphology) => { + if (!morphology) return []; return renderEmptyOrValue(renderArray(morphology.mtypes?.map((m) => m.pref_label) || [])); }; -const emodelEtypes = (emodel: IEModel) => { +const emodelEtypes = (emodel?: IEModel) => { + if (!emodel) return []; return renderEmptyOrValue(renderArray(emodel.etypes?.map((m) => m.pref_label) || [])); }; diff --git a/src/features/details-view/overview.tsx b/src/features/details-view/overview.tsx index 970022964..7ee28975d 100644 --- a/src/features/details-view/overview.tsx +++ b/src/features/details-view/overview.tsx @@ -21,8 +21,6 @@ type FieldProps = { export function Field({ field, className, data }: FieldProps) { const fieldObj = getFieldDefinition(field); - console.log(field, data, fieldObj?.renderForDetailView); - let renderedContent: ReactNode = null; if (fieldObj) { if (fieldObj.renderForDetailView) { From 0671155b629be954ed16185f55306d7be5f8a2ac Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 9 Sep 2025 10:53:56 +0200 Subject: [PATCH 79/80] header layout --- .../definitions/view-defs/model.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/entity-configuration/definitions/view-defs/model.ts b/src/entity-configuration/definitions/view-defs/model.ts index 5a93d8a62..5c1148264 100644 --- a/src/entity-configuration/definitions/view-defs/model.ts +++ b/src/entity-configuration/definitions/view-defs/model.ts @@ -25,7 +25,7 @@ export const ViewsDefinition: { [key: string]: ViewDefinitionConfig } = { summaryViewFields: [ { field: EntityCoreFields.BrainRegion }, { field: EntityCoreFields.EModelScore }, - { field: EntityCoreFields.MType, className: 'col-span-3' }, + { field: EntityCoreFields.MType }, { field: EntityCoreFields.EType }, ], miniDetailView: [ @@ -54,10 +54,10 @@ export const ViewsDefinition: { [key: string]: ViewDefinitionConfig } = { ], curated: false, summaryViewFields: [ - { field: EntityCoreFields.BrainRegion, className: 'col-span-2' }, - { field: EntityCoreFields.MEModelValidationStatus, className: 'col-span-2 text-left' }, - { field: EntityCoreFields.MType, className: 'col-span-4' }, - { field: EntityCoreFields.EType, className: 'col-span-4' }, + { field: EntityCoreFields.BrainRegion }, + { field: EntityCoreFields.MEModelValidationStatus }, + { field: EntityCoreFields.MType }, + { field: EntityCoreFields.EType }, ], miniDetailView: [ { field: EntityCoreFields.BrainRegion }, From 7a5aefd44a95eadbb3e5fecf3aa41d094673114d Mon Sep 17 00:00:00 2001 From: Gil Arturo Barrios del Villar Date: Tue, 9 Sep 2025 11:39:33 +0200 Subject: [PATCH 80/80] improve analysis style --- .../model-analysis/viewer/container.tsx | 112 +++--------------- src/features/model-analysis/viewer/viewer.tsx | 9 +- 2 files changed, 17 insertions(+), 104 deletions(-) diff --git a/src/features/model-analysis/viewer/container.tsx b/src/features/model-analysis/viewer/container.tsx index a9fa73183..36f9861d4 100644 --- a/src/features/model-analysis/viewer/container.tsx +++ b/src/features/model-analysis/viewer/container.tsx @@ -1,12 +1,10 @@ -import { useMemo, useState } from 'react'; -import { Collapse } from 'antd'; import dynamic from 'next/dynamic'; +import groupBy from 'lodash/groupBy'; import { AllowedTypes } from '@/features/model-analysis/viewer/storage'; - import type { IValidationConstructedResult } from '@/features/model-analysis/explorer/context'; import type { TAllowedTypes } from '@/features/model-analysis/viewer/storage'; -import { Button } from '@/ui/molecules/button'; +import Tabs, { Tab } from '@/ui/molecules/tabbed-page'; const Viewer = dynamic(() => import('@/features/model-analysis/viewer/viewer'), { ssr: false, @@ -16,106 +14,24 @@ type Props = { validationResults: IValidationConstructedResult | null; }; -function cleanTitle(title: string): string { - const blacklist = ['Simulatable', 'Validation', 'Neuron']; - return title - .split(' ') - .filter((word) => !blacklist.includes(word)) - .join(' '); -} - -function TabLabel({ title, count }: { title: string; count: number }) { - return ( - - {title} - ({count}) - - ); -} - export function ViewerContainer({ validationResults }: Props) { const allowedValidationResults = validationResults?.filter((o) => o.assets?.some((obj) => AllowedTypes.includes(obj.content_type as TAllowedTypes)) ); - const allCount = allowedValidationResults?.reduce( - (acc, item) => - acc + - (item.assets?.filter((o) => AllowedTypes.includes(o.content_type as TAllowedTypes)).length || - 0), - 0 - ); - const allValidationResultsMap = allowedValidationResults?.map((validationResult) => [ - { id: validationResult.id, name: validationResult.name }, - validationResult, - ]) as Array<[{ id: string; name: string }, IValidationConstructedResult[number]]>; - const [type, setType] = useState(allowedValidationResults?.[0]?.id); - const tabs = useMemo( - () => [ - { key: 'all', label: }, - ...(allowedValidationResults?.map((item) => ({ - key: item.id, - label: ( - AllowedTypes.includes(o.content_type as TAllowedTypes)) - .length || 0 - } - /> - ), - })) ?? []), - ], - [allCount, allowedValidationResults] - ); + const groupedvalidationResults = groupBy(allowedValidationResults, 'name'); return ( -
-
- {tabs.map((t) => ( - - ))} -
- -
-
- {type === 'all' ? ( - ({ - key: `result-item-cls/${id}/${resultItem.id}`, - label: ( - - {cleanTitle(name)}{' '} - - ( - {resultItem.assets?.filter((o) => - AllowedTypes.includes(o.content_type as TAllowedTypes) - ).length || 0} - ) - - - ), - children: , - }))} - /> - ) : ( - validationResults - ?.filter((o) => o.id === type) - ?.map((resultItem) => { - return ; - }) - )} -
-
-
+ + {Object.entries(groupedvalidationResults).map(([name, results]) => { + return ( + + {results.map((r) => ( + + ))} + + ); + })} + ); } diff --git a/src/features/model-analysis/viewer/viewer.tsx b/src/features/model-analysis/viewer/viewer.tsx index 97593d6e2..9dd40664c 100644 --- a/src/features/model-analysis/viewer/viewer.tsx +++ b/src/features/model-analysis/viewer/viewer.tsx @@ -33,20 +33,17 @@ export default function AssetViewer({ validationResult }: Props) { ); return ( -
+
{validationResult.assets ?.filter((o) => AllowedTypes.includes(o.content_type as TAllowedTypes)) - .map((asset, ix) => { + .map((asset) => { return (
-

- - {ix + 1} - +

{lowerCase(asset.path.split('.').at(0))}

{content(asset)}