Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
c8f22e0
Rename
g-bar Aug 13, 2025
70f3580
div
g-bar Aug 13, 2025
96c0a7a
breadcrumbs
g-bar Aug 13, 2025
5e00690
Merge branch 'task/v2' into new-ui-detail-view
g-bar Aug 13, 2025
04857c9
Fetch entity
g-bar Aug 13, 2025
70787c0
breadcrumbs
g-bar Aug 13, 2025
811681f
fetch on server
g-bar Aug 13, 2025
f1fed6f
Use slug
g-bar Aug 14, 2025
3f19f5d
navigation
g-bar Aug 14, 2025
63ae4b1
root redirect to overview
g-bar Aug 14, 2025
bac0290
Merge branch 'task/v2' into new-ui-detail-view
g-bar Aug 15, 2025
d1d31d1
actions
g-bar Aug 15, 2025
8f62e9d
copy action
g-bar Aug 18, 2025
0cdf6bb
Add actoins
g-bar Aug 18, 2025
90ed149
working on bookmarks
g-bar Aug 18, 2025
664d2db
add bookmark categories
g-bar Aug 19, 2025
a4b34e4
Merge branch 'task/v2' into new-ui-detail-view
g-bar Aug 19, 2025
0c01d24
add bookmark
g-bar Aug 19, 2025
c9c85ec
bookmark funtion
g-bar Aug 19, 2025
9867932
Download
g-bar Aug 20, 2025
22ec219
lint
g-bar Aug 20, 2025
46e7768
lint
g-bar Aug 20, 2025
e6fc07a
Merge branch 'new-ui-detail-view' of github.com:openbraininstitute/co…
g-bar Aug 20, 2025
2edc00f
Merge branch 'task/v2' into new-ui-detail-view
g-bar Aug 20, 2025
c31091f
bookmarks
g-bar Aug 20, 2025
25a756e
bookmarkCategory:
g-bar Aug 20, 2025
d042019
Linting
g-bar Aug 20, 2025
83fe9b8
restore lefthook for now
g-bar Aug 20, 2025
e02b4f0
restore lefthook for now
g-bar Aug 20, 2025
f008e6a
Merge branch 'new-ui-detail-view' of github.com:openbraininstitute/co…
g-bar Aug 20, 2025
50776f1
test
g-bar Aug 20, 2025
81698a5
Merge branch 'new-ui-detail-view' of github.com:openbraininstitute/co…
g-bar Aug 20, 2025
b47d665
download entity
g-bar Aug 20, 2025
0ad7690
Working on detail view
g-bar Aug 20, 2025
45a9aae
Add overview page
g-bar Aug 20, 2025
73785b0
add morphology page
g-bar Aug 20, 2025
b08439a
add electrophysiology viz
g-bar Aug 20, 2025
a7a78a1
prettier
g-bar Aug 25, 2025
0adef03
entity one required
g-bar Aug 25, 2025
8ab450f
Feedback
g-bar Aug 26, 2025
d1be917
Merge branch 'task/v2' into new-ux-detail-view-content
g-bar Aug 26, 2025
fc46c14
minor
g-bar Aug 26, 2025
7e88436
remove unused routes
g-bar Aug 26, 2025
f589cdc
Redirect to overview
g-bar Aug 26, 2025
eea7c9a
Visualization
g-bar Aug 27, 2025
589985c
analysis tab
g-bar Aug 27, 2025
1cff217
emodel related artifacts
g-bar Aug 27, 2025
bfa97db
ion channel to ion channel model
g-bar Aug 27, 2025
2b0bb72
emodel, memodel
g-bar Aug 27, 2025
dd6c471
add synaptome
g-bar Aug 27, 2025
02ad811
clean up
g-bar Aug 27, 2025
87ec2b5
circuit
g-bar Aug 28, 2025
a6a75f1
fields
g-bar Aug 28, 2025
1f5558a
add single neuron simulation
g-bar Aug 28, 2025
3e204df
results
g-bar Aug 29, 2025
75a96c1
add synaptome
g-bar Aug 29, 2025
97bdc00
add synaptome
g-bar Aug 29, 2025
86dcf10
Merge branch 'task/v2' into new-ux-detail-view-pages
g-bar Aug 29, 2025
b7dd528
Merge branch 'task/v2' into new-ux-detail-view-pages
g-bar Sep 3, 2025
7e0508e
redirect detail root to overview
g-bar Sep 3, 2025
1e9b56e
adding name to pages
g-bar Sep 3, 2025
7227cf7
name
g-bar Sep 4, 2025
6b9f724
explore -> data
g-bar Sep 4, 2025
463cbda
remove double name
g-bar Sep 4, 2025
4695601
add related publications tab
g-bar Sep 4, 2025
4e4bcc2
main scree
g-bar Sep 4, 2025
650e0c9
add circuit viz
g-bar Sep 5, 2025
ba95ebc
Add circuit analysis
g-bar Sep 5, 2025
f2b5b3a
add related publications
g-bar Sep 5, 2025
37efabf
add mtype, etype to synaptome
g-bar Sep 8, 2025
b0cef05
add related circuits
g-bar Sep 8, 2025
d0044d4
add microcircuit
g-bar Sep 8, 2025
5950228
Merge branch 'task/v2' into circuit-detail
g-bar Sep 8, 2025
aeb2693
lint
g-bar Sep 8, 2025
6164b85
fix analysis
g-bar Sep 8, 2025
c96c538
implement back
g-bar Sep 8, 2025
4b9dea3
fix circuit fields layout
g-bar Sep 8, 2025
d44e153
viz in overview for circuite
g-bar Sep 8, 2025
f1e5397
tabbed component
g-bar Sep 8, 2025
8ac843b
improve styles of related publications
g-bar Sep 8, 2025
7275bb9
add tabs
g-bar Sep 8, 2025
ab9cd86
provenance
g-bar Sep 8, 2025
8f9752f
improve analysis styles
g-bar Sep 8, 2025
645b5fa
improve layout
g-bar Sep 8, 2025
a150522
Default message
g-bar Sep 8, 2025
5fb86bc
merge
g-bar Sep 8, 2025
e8f921e
move viz to overview
g-bar Sep 9, 2025
ab9a2eb
ephys viewer
g-bar Sep 9, 2025
d2cf2c1
working on fields
g-bar Sep 9, 2025
c436599
fix render mtypes, etypes
g-bar Sep 9, 2025
ac71046
Add mtype etypes
g-bar Sep 9, 2025
0671155
header layout
g-bar Sep 9, 2025
7a5aefd
improve analysis style
g-bar Sep 9, 2025
986a8ab
Merge branch 'task/v2' into detail-view-styles-2
g-bar Sep 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -42,9 +41,6 @@ export default async function Page({
if (section === 'overview') {
return <Overview entity={entity} extendedType={entityType.extendedType} ctx={ctx} />;
}
if (section === 'visualization') {
return <Visualization entity={entity} ctx={ctx} />;
}
if (section === 'analysis') {
return <Analysis entity={entity} extendedType={entityType.extendedType} />;
}
Expand Down
40 changes: 26 additions & 14 deletions src/entity-configuration/definitions/fields-defs/experimental.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ import type {
import type { FieldsDefinitionRegistry } from '@/entity-configuration/definitions/types';
import type { IEType, IMType } from '@/api/entitycore/types/shared/global';

const morphologyMtypes = (morphology?: IReconstructionMorphology) => {
if (!morphology) return [];
return renderEmptyOrValue(renderArray(morphology.mtypes?.map((m) => m.pref_label) || []));
};

const emodelEtypes = (emodel?: IEModel) => {
if (!emodel) return [];
return renderEmptyOrValue(renderArray(emodel.etypes?.map((m) => m.pref_label) || []));
};

export const FieldsDefinition: Partial<FieldsDefinitionRegistry<EntityCoreObjectTypes>> = {
[EntityCoreFields.License]: {
title: 'License',
Expand Down Expand Up @@ -86,19 +96,20 @@ export const FieldsDefinition: Partial<FieldsDefinitionRegistry<EntityCoreObject
fieldType: CoreFieldType.CellType,
title: 'M-Type',
filter: CoreFieldFilterTypeEnum.CheckList,
render: (r) => {
render(r: EntityCoreObjectTypes) {
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<IMType> | null }
).mtypes?.map((m) => m.pref_label) || []
(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(r.morphology);
}
return renderEmptyOrValue(
renderArray(
(r as EntityCoreObjectTypes & { mtypes: Array<IMType> | null }).mtypes?.map(
Expand Down Expand Up @@ -132,19 +143,20 @@ export const FieldsDefinition: Partial<FieldsDefinitionRegistry<EntityCoreObject
fieldType: CoreFieldType.CellType,
title: 'E-Type',
filter: CoreFieldFilterTypeEnum.CheckList,
render: (r) => {
render(r: EntityCoreObjectTypes) {
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<IEType> | null }).etypes?.map(
(e) => e.pref_label
) || []
(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<IEType> | null }).etypes?.map(
Expand Down
1 change: 0 additions & 1 deletion src/entity-configuration/definitions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ export type FieldsDefinitionRegistry<T extends EntityCoreIdentifiable> = Record<
export type DetailViewSection =
| 'overview'
| 'results'
| 'visualization'
| 'analysis'
| 'related-publications'
| 'related-artifacts'
Expand Down
26 changes: 13 additions & 13 deletions src/entity-configuration/definitions/view-defs/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down Expand Up @@ -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 },
Expand Down Expand Up @@ -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,
// },
Expand Down
10 changes: 5 additions & 5 deletions src/entity-configuration/definitions/view-defs/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down Expand Up @@ -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 },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const ElectricalCellRecording: EntityCoreTypeConfig<IElectricalCellRecord
asset: {
extension: 'application/nwb',
},
detailViewSections: ['overview', 'visualization', 'related-publications'],
detailViewSections: ['overview', 'related-publications'],
isDownloadable: true,
isBookmarkable: true,
isCopyable: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const ReconstructionMorphology: EntityCoreTypeConfig<
extension: 'application/swc',
},
viewDefinition: ViewsDefinitionRegistry[ExtendedEntitiesTypeDict.ReconstructionMorphology],
detailViewSections: ['overview', 'visualization', 'related-publications'],
detailViewSections: ['overview', 'related-publications'],
isDownloadable: true,
isBookmarkable: true,
isCopyable: true,
Expand Down
2 changes: 2 additions & 0 deletions src/features/ephys-viewer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

import dynamic from 'next/dynamic';

const EphysViewer = dynamic(() => import('./ephys-viewer'), { ssr: false });
Expand Down
112 changes: 14 additions & 98 deletions src/features/model-analysis/viewer/container.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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 (
<span>
{title}
<span style={{ marginLeft: 8, color: '#999' }}>({count})</span>
</span>
);
}

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<string | undefined>(allowedValidationResults?.[0]?.id);
const tabs = useMemo(
() => [
{ key: 'all', label: <TabLabel title="All" count={allCount || 0} /> },
...(allowedValidationResults?.map((item) => ({
key: item.id,
label: (
<TabLabel
title={cleanTitle(item.name)}
count={
item.assets?.filter((o) => AllowedTypes.includes(o.content_type as TAllowedTypes))
.length || 0
}
/>
),
})) ?? []),
],
[allCount, allowedValidationResults]
);
const groupedvalidationResults = groupBy(allowedValidationResults, 'name');

return (
<div className="bg-neutral-1 h-full w-full">
<div className="bg-neutral-1 sticky -top-10 z-10 flex flex-wrap gap-5">
{tabs.map((t) => (
<Button
key={t.key}
onClick={() => setType(t.key)}
active={type === t.key}
variant="outline"
>
{t.label}
</Button>
))}
</div>

<div className="w-full">
<div className="w-full min-w-full">
{type === 'all' ? (
<Collapse
bordered={false}
className="bg-white"
items={allValidationResultsMap?.map(([{ id, name }, resultItem]) => ({
key: `result-item-cls/${id}/${resultItem.id}`,
label: (
<span className="text-primary-8">
{cleanTitle(name)}{' '}
<span className="text-neutral-4">
(
{resultItem.assets?.filter((o) =>
AllowedTypes.includes(o.content_type as TAllowedTypes)
).length || 0}
)
</span>
</span>
),
children: <Viewer validationResult={resultItem} key={resultItem.id} />,
}))}
/>
) : (
validationResults
?.filter((o) => o.id === type)
?.map((resultItem) => {
return <Viewer validationResult={resultItem} key={resultItem.id} />;
})
)}
</div>
</div>
</div>
<Tabs defaultMessage="No validation results found">
{Object.entries(groupedvalidationResults).map(([name, results]) => {
return (
<Tab label={name} key={name}>
{results.map((r) => (
<Viewer validationResult={r} key={r.id} />
))}
</Tab>
);
})}
</Tabs>
);
}
9 changes: 3 additions & 6 deletions src/features/model-analysis/viewer/viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,17 @@ export default function AssetViewer({ validationResult }: Props) {
);

return (
<div data-testid="documents-container" className="bg-neutral-1 mt-4 flex flex-col items-center">
<div data-testid="documents-container" className="mt-4 flex flex-col items-center">
{validationResult.assets
?.filter((o) => AllowedTypes.includes(o.content_type as TAllowedTypes))
.map((asset, ix) => {
.map((asset) => {
return (
<div
id={`document_${asset.id}`}
key={`document_${asset.id}`}
className="mb-5 flex w-full flex-col items-center"
>
<h2 className="text-primary-8 mb-6 flex w-max items-center justify-center self-start p-3 text-center text-xl font-bold capitalize">
<span className="bg-neutral-1 flex h-12! w-12! items-center justify-center">
{ix + 1}
</span>
<h2 className="text-primary-8 border-neutral-2 mb-6 block! w-full rounded-full border p-3 text-xl font-bold capitalize">
<span className="ml-4">{lowerCase(asset.path.split('.').at(0))}</span>
</h2>
{content(asset)}
Expand Down
Loading