-
Notifications
You must be signed in to change notification settings - Fork 13
feat: single deposition table updates #1852
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e1cba65
ad09f35
ee2272c
564df8b
f443922
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { useMemo } from 'react' | ||
|
||
import { Link } from 'app/components/Link' | ||
import { useI18n } from 'app/hooks/useI18n' | ||
import type { I18nKeys } from 'app/types/i18n' | ||
import { DASHED_UNDERLINED_CLASSES } from 'app/utils/classNames' | ||
|
||
interface DepositItem { | ||
label: I18nKeys | ||
url: string | ||
value?: string | ||
} | ||
|
||
export function DepositedInTableCell({ | ||
datasetId, | ||
datasetTitle, | ||
runId, | ||
runName, | ||
}: { | ||
datasetId?: number | ||
datasetTitle?: string | ||
runId?: number | ||
runName?: string | ||
}) { | ||
const depositItems: DepositItem[] = useMemo( | ||
() => [ | ||
...(datasetId && datasetTitle | ||
? [ | ||
{ | ||
label: 'dataset' as I18nKeys, | ||
value: datasetTitle, | ||
url: `/datasets/${datasetId}`, | ||
}, | ||
] | ||
: []), | ||
|
||
...(runId && runName | ||
? [ | ||
{ | ||
label: 'run' as I18nKeys, | ||
value: runName, | ||
url: `/runs/${runId}`, | ||
}, | ||
] | ||
: []), | ||
], | ||
[datasetId, datasetTitle, runId, runName], | ||
) | ||
codemonkey800 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
const { t } = useI18n() | ||
|
||
if (depositItems.length === 0) { | ||
return '--' | ||
} | ||
|
||
return ( | ||
<div className="flex flex-col gap-sds-s"> | ||
{depositItems.map(({ label, value, url }) => ( | ||
<Link | ||
className="text-sds-body-xxs-400-wide tracking-sds-body-xxs-400-wide leading-sds-body-xxs" | ||
key={label} | ||
to={url} | ||
> | ||
<span className="font-semibold">{t(label)}: </span> | ||
<span className={DASHED_UNDERLINED_CLASSES}>{value ?? '--'}</span> | ||
</Link> | ||
))} | ||
</div> | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import { ColumnDef } from '@tanstack/react-table' | ||
import { useMemo } from 'react' | ||
|
||
import { | ||
Annotation_File_Shape_Type_Enum, | ||
Annotation_Method_Type_Enum, | ||
type GetDepositionAnnotationsQuery, | ||
} from 'app/__generated_v2__/graphql' | ||
import { useAnnotationNameColumn } from 'app/components/AnnotationTable/useAnnotationNameColumn' | ||
import { useShapeTypeColumn } from 'app/components/AnnotationTable/useShapeTypeColumn' | ||
import { PageTable } from 'app/components/Table' | ||
import { DepositionAnnotationTableWidths } from 'app/constants/table' | ||
import { useDepositionById } from 'app/hooks/useDepositionById' | ||
import { useIsLoading } from 'app/hooks/useIsLoading' | ||
|
||
import { useMethodTypeColumn } from '../AnnotationTable/useMethodTypeColumn' | ||
import { useDepositedInColumn } from './useDepositedInColumn' | ||
|
||
export type DepositionAnnotationTableData = | ||
GetDepositionAnnotationsQuery['annotationShapes'][number] | ||
|
||
const LOADING_ANNOTATIONS = Array.from( | ||
{ length: 10 }, | ||
(_, index) => | ||
({ | ||
id: index, | ||
shapeType: Annotation_File_Shape_Type_Enum.Point, | ||
|
||
annotation: { | ||
id: index, | ||
objectName: 'object name', | ||
groundTruthStatus: false, | ||
methodType: Annotation_Method_Type_Enum.Automated, | ||
}, | ||
|
||
annotationFiles: { | ||
edges: [ | ||
{ | ||
node: { | ||
s3Path: 's3://example.com/loading', | ||
codemonkey800 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
}, | ||
], | ||
}, | ||
}) as DepositionAnnotationTableData, | ||
) | ||
|
||
export function DepositionAnnotationTable() { | ||
const { annotations } = useDepositionById() | ||
|
||
const { isLoadingDebounced } = useIsLoading() | ||
|
||
const annotationNameColumn = useAnnotationNameColumn({ | ||
width: DepositionAnnotationTableWidths.name, | ||
}) | ||
|
||
const shapeTypeColumn = useShapeTypeColumn( | ||
DepositionAnnotationTableWidths.objectShapeType, | ||
) | ||
|
||
const methodTypeColumn = useMethodTypeColumn({ | ||
width: DepositionAnnotationTableWidths.methodType, | ||
}) | ||
|
||
const depositedInColumn = useDepositedInColumn<DepositionAnnotationTableData>( | ||
{ | ||
width: DepositionAnnotationTableWidths.depositedIn, | ||
|
||
getDepositedInData: ({ annotation }) => ({ | ||
datasetId: annotation?.run?.dataset?.id, | ||
datasetTitle: annotation?.run?.dataset?.title, | ||
runId: annotation?.run?.id, | ||
runName: annotation?.run?.name, | ||
}), | ||
}, | ||
) | ||
|
||
const columns = useMemo( | ||
() => | ||
[ | ||
annotationNameColumn, | ||
shapeTypeColumn, | ||
methodTypeColumn, | ||
depositedInColumn, | ||
] as ColumnDef<DepositionAnnotationTableData>[], | ||
[ | ||
annotationNameColumn, | ||
depositedInColumn, | ||
methodTypeColumn, | ||
shapeTypeColumn, | ||
], | ||
) | ||
|
||
return ( | ||
<PageTable | ||
data={ | ||
isLoadingDebounced | ||
? LOADING_ANNOTATIONS | ||
: annotations?.annotationShapes ?? [] | ||
} | ||
columns={columns} | ||
hoverType="none" | ||
/> | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { useMemo } from 'react' | ||
|
||
import { useDepositionById } from 'app/hooks/useDepositionById' | ||
import { useI18n } from 'app/hooks/useI18n' | ||
import { cns } from 'app/utils/cns' | ||
import { getDataContents } from 'app/utils/deposition' | ||
|
||
import { DepositionTabs } from './DepositionTabs' | ||
|
||
export function DepositionFilters() { | ||
const { allRuns } = useDepositionById() | ||
const dataContents = getDataContents(allRuns) | ||
const dataContentItems = useMemo( | ||
() => | ||
[ | ||
{ | ||
label: 'tiltSeries', | ||
isAvailable: dataContents.tiltSeriesAvailable, | ||
}, | ||
{ | ||
label: 'frames', | ||
isAvailable: dataContents.framesAvailable, | ||
}, | ||
{ | ||
label: 'ctf', | ||
isAvailable: dataContents.ctfAvailable, | ||
}, | ||
{ | ||
label: 'alignment', | ||
isAvailable: dataContents.alignmentAvailable, | ||
}, | ||
] as const, | ||
[dataContents], | ||
) | ||
|
||
const { t } = useI18n() | ||
|
||
return ( | ||
<div className="flex flex-col pt-sds-xl"> | ||
<h4 | ||
className={cns( | ||
'text-sds-header-m-600-wide tracking-sds-header-m-600-wide', | ||
'font-semibold leading-sds-header-m px-sds-xl mb-sds-s', | ||
)} | ||
> | ||
{t('dataContents')} | ||
</h4> | ||
|
||
<DepositionTabs /> | ||
|
||
<div className="flex flex-col justify-between gap-sds-s mt-sds-l"> | ||
{dataContentItems.map(({ label, isAvailable }) => ( | ||
<div | ||
key={label} | ||
className={cns( | ||
'flex items-center justify-between px-sds-xl', | ||
'text-sds-body-xs-400-wide tracking-sds-body-xs-400-wide leading-sds-body-xs', | ||
'text-light-sds-color-semantic-base-text-secondary', | ||
)} | ||
> | ||
<span>{t(label)}</span> | ||
|
||
<span>{t(isAvailable ? 'available' : 'na')}</span> | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. need some padding/margin bottom here when there is no data/loading |
||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { DepositionTab, useDepositionTab } from 'app/hooks/useDepositionTab' | ||
|
||
import { DepositionAnnotationTable } from './DepositionAnnotationTable' | ||
import { DepositionTomogramTable } from './DepositionTomogramTable' | ||
|
||
export function DepositionTableRenderer() { | ||
const [tab] = useDepositionTab() | ||
|
||
switch (tab) { | ||
case DepositionTab.Tomograms: | ||
return <DepositionTomogramTable /> | ||
|
||
default: | ||
return <DepositionAnnotationTable /> | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { Icon } from '@czi-sds/components' | ||
import { useMemo } from 'react' | ||
|
||
import { type TabData, Tabs } from 'app/components/Tabs' | ||
import { useDepositionById } from 'app/hooks/useDepositionById' | ||
import { DepositionTab, useDepositionTab } from 'app/hooks/useDepositionTab' | ||
import { useI18n } from 'app/hooks/useI18n' | ||
import { cns } from 'app/utils/cns' | ||
|
||
export function DepositionTabs() { | ||
const [tab, setTab] = useDepositionTab() | ||
const tabs = useTabs(tab) | ||
|
||
return <Tabs tabs={tabs} value={tab} onChange={setTab} vertical /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Future Nice-to-have suggestion: It would be good if changing tabs didn't bring you to the top of the page and also it might be nice if the headers loaded as well as the data - or maybe they just change to the next tabs head (like if you click on Tomograms then the headers change immediately even while we wait for the data to load There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there's a way to do that in remix: https://remix.run/docs/en/main/components/scroll-restoration#preventing-scroll-reset
I agree, I think this is limitation to how we implemented the frontend in the beginning because data fetching has to complete before it can show the next page. I think this was before I think overall we should look into deferring the data fetches and using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I opened an issue to track this: #1861 |
||
} | ||
|
||
function useTabs(activeTab: DepositionTab) { | ||
const { t } = useI18n() | ||
const { annotationsCount, tomogramsCount } = useDepositionById() | ||
|
||
return useMemo<TabData<DepositionTab>[]>(() => { | ||
const tabData = [ | ||
{ | ||
tab: DepositionTab.Annotations, | ||
label: 'annotations', | ||
icon: 'Cube', | ||
count: annotationsCount, | ||
}, | ||
{ | ||
tab: DepositionTab.Tomograms, | ||
label: 'tomograms', | ||
icon: 'FlagOutline', | ||
count: tomogramsCount, | ||
}, | ||
] as const | ||
|
||
return tabData.map(({ tab, label, icon, count }) => ({ | ||
value: tab, | ||
label: ( | ||
<span | ||
className={cns( | ||
'font-semibold text-sds-body-s-600-wide', | ||
'tracking-sds-body-s-600-wide leading-sds-body-s', | ||
'w-full flex items-center justify-between', | ||
)} | ||
> | ||
<span className="flex items-center gap-sds-xs"> | ||
<Icon | ||
className={ | ||
activeTab === tab | ||
? '!text-black' | ||
: '!text-light-sds-color-primitive-gray-600' | ||
} | ||
sdsIcon={icon} | ||
sdsSize="xs" | ||
/> | ||
|
||
<span>{t(label)}</span> | ||
</span> | ||
|
||
<span>{count.toLocaleString()}</span> | ||
</span> | ||
), | ||
})) | ||
}, [activeTab, annotationsCount, t, tomogramsCount]) | ||
} |
Uh oh!
There was an error while loading. Please reload this page.