Skip to content

Commit b4a3550

Browse files
authored
Update overview layout (#780)
* layout: add sidebar to project overview and update routing * layout: hide pagination bar for compact views (if we only have one page to show) * layout: make it possible to edit and delete project from overview * fix: hide project breadcrumb if no project was found * fix: consider permissions for project actions * layout: add sidebar section "Metadata" * feat: setup routing for processing details and pipelines + adjust dialog structure * copy: update "Overview" -> "Project" * chore: rename parent folder from "overview" -> "project" * feat: setup new page "General" for editing project details * fix: hide general settings view if user is not allowed to update the project * chore: cleanup * fix: tweak permission logic
1 parent d494761 commit b4a3550

File tree

71 files changed

+746
-747
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+746
-747
lines changed

ui/src/app.tsx

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,19 @@ import { CollectionDetails } from 'pages/collection-details/collection-details'
2121
import { Deployments } from 'pages/deployments/deployments'
2222
import { Jobs } from 'pages/jobs/jobs'
2323
import { Occurrences } from 'pages/occurrences/occurrences'
24-
import Overview from 'pages/overview/overview'
24+
import { Collections } from 'pages/project/collections/collections'
25+
import { Devices } from 'pages/project/entities/devices'
26+
import { Sites } from 'pages/project/entities/sites'
27+
import { General } from 'pages/project/general/general'
28+
import { Pipelines } from 'pages/project/pipelines/pipelines'
29+
import { ProcessingServices } from 'pages/project/processing-services/processing-services'
30+
import Project from 'pages/project/project'
31+
import { Storage } from 'pages/project/storage/storage'
32+
import { Summary } from 'pages/project/summary/summary'
2533
import { Projects } from 'pages/projects/projects'
2634
import SessionDetails from 'pages/session-details/session-details'
2735
import { Sessions } from 'pages/sessions/sessions'
2836
import { Species } from 'pages/species/species'
29-
import { UnderConstruction } from 'pages/under-construction/under-construction'
3037
import { ReactNode, useContext, useEffect } from 'react'
3138
import { Helmet, HelmetProvider } from 'react-helmet-async'
3239
import {
@@ -91,15 +98,30 @@ export const App = () => (
9198
</Route>
9299
<Route path="projects" element={<ProjectsContainer />} />
93100
<Route path="projects/:projectId" element={<ProjectContainer />}>
94-
<Route path="" element={<Overview />} />
101+
<Route path="" element={<Project />}>
102+
<Route
103+
path=""
104+
element={<Navigate to={{ pathname: 'summary' }} replace={true} />}
105+
/>
106+
<Route path="summary" element={<Summary />} />
107+
<Route path="collections" element={<Collections />} />
108+
<Route path="collections/:id" element={<CollectionDetails />} />
109+
<Route
110+
path="processing-services/:id?"
111+
element={<ProcessingServices />}
112+
/>
113+
<Route path="pipelines/:id?" element={<Pipelines />} />
114+
<Route path="sites" element={<Sites />} />
115+
<Route path="devices" element={<Devices />} />
116+
<Route path="general" element={<General />} />
117+
<Route path="storage" element={<Storage />} />
118+
</Route>
95119
<Route path="jobs/:id?" element={<Jobs />} />
96120
<Route path="deployments/:id?" element={<Deployments />} />
97121
<Route path="sessions" element={<Sessions />} />
98122
<Route path="sessions/:id" element={<SessionDetails />} />
99123
<Route path="occurrences/:id?" element={<Occurrences />} />
100124
<Route path="taxa/:id?" element={<Species />} />
101-
<Route path="collections/:id" element={<CollectionDetails />} />
102-
<Route path="*" element={<UnderConstruction />} />
103125
</Route>
104126
<Route
105127
path="/terms-of-service"
@@ -181,15 +203,19 @@ const ProjectContainer = () => {
181203
})
182204

183205
useEffect(() => {
184-
setProjectBreadcrumb({
185-
title: projectDetails.project?.name ?? '',
186-
path: APP_ROUTES.PROJECT_DETAILS({ projectId: projectId as string }),
187-
})
206+
if (projectDetails.error) {
207+
setProjectBreadcrumb(undefined)
208+
} else {
209+
setProjectBreadcrumb({
210+
title: projectDetails.project?.name ?? '',
211+
path: APP_ROUTES.PROJECT_DETAILS({ projectId: projectId as string }),
212+
})
213+
}
188214

189215
return () => {
190216
setProjectBreadcrumb(undefined)
191217
}
192-
}, [projectDetails.project])
218+
}, [projectDetails.project, projectDetails.error])
193219

194220
return (
195221
<>

ui/src/components/breadcrumbs/breadcrumbs.module.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
display: flex;
66
align-items: center;
77

8-
:not(:last-child) {
9-
margin-right: 8px;
8+
> :not(:last-child) {
9+
margin-right: 16px;
1010
}
1111
}
1212

ui/src/components/breadcrumbs/breadcrumbs.tsx

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import classNames from 'classnames'
2-
import { Icon, IconTheme, IconType } from 'design-system/components/icon/icon'
2+
import { ChevronRightIcon } from 'lucide-react'
33
import { Fragment, useContext, useEffect } from 'react'
44
import { Link } from 'react-router-dom'
55
import { Breadcrumb, BreadcrumbContext } from 'utils/breadcrumbContext'
66
import { STRING, translate } from 'utils/language'
77
import styles from './breadcrumbs.module.scss'
88

99
export const Breadcrumbs = ({
10+
activeNavItem,
1011
navItems,
11-
activeNavItemId,
1212
}: {
13+
activeNavItem: { id: string; title: string; path?: string }
1314
navItems: { id: string; title: string; path?: string }[]
14-
activeNavItemId: string
1515
}) => {
1616
const {
1717
pageBreadcrumb,
@@ -22,20 +22,16 @@ export const Breadcrumbs = ({
2222
} = useContext(BreadcrumbContext)
2323

2424
useEffect(() => {
25-
const activeNavItem =
26-
activeNavItemId !== 'overview' &&
27-
navItems.find((navItem) => navItem.id === activeNavItemId)
28-
29-
setMainBreadcrumb(
30-
activeNavItem
31-
? { title: activeNavItem.title, path: activeNavItem.path }
32-
: undefined
33-
)
25+
if (activeNavItem.id === 'project') {
26+
setMainBreadcrumb(undefined)
27+
} else {
28+
setMainBreadcrumb(activeNavItem)
29+
}
3430

3531
return () => {
3632
setMainBreadcrumb(undefined)
3733
}
38-
}, [navItems, activeNavItemId])
34+
}, [navItems, activeNavItem])
3935

4036
const breadcrumbs = [
4137
pageBreadcrumb,
@@ -70,11 +66,7 @@ export const Breadcrumbs = ({
7066
</Link>
7167
)}
7268
{!isLast && (
73-
<Icon
74-
type={IconType.ToggleRight}
75-
theme={IconTheme.Neutral}
76-
size={8}
77-
/>
69+
<ChevronRightIcon className="w-3 h-3 text-muted-foreground/50" />
7870
)}
7971
</Fragment>
8072
)

ui/src/components/menu/menu.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ import styles from './menu.module.scss'
66

77
export const Menu = () => {
88
const navigate = useNavigate()
9-
const { navItems, activeNavItemId } = useNavItems()
9+
const { navItems, activeNavItem } = useNavItems()
1010

1111
return (
1212
<div className={styles.menu}>
1313
<div className={styles.topBar}>
14-
<Breadcrumbs navItems={navItems} activeNavItemId={activeNavItemId} />
14+
<Breadcrumbs navItems={navItems} activeNavItem={activeNavItem} />
1515
</div>
1616
<div className={styles.navigationBar}>
1717
<NavigationBar
1818
items={navItems}
19-
activeItemId={activeNavItemId}
19+
activeItemId={activeNavItem.id}
2020
onItemClick={(id) => {
2121
const item = navItems.find((i) => i.id === id)
2222
if (item?.path) {

ui/src/data-services/hooks/processing-services/useProcessingServiceDetails.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ const convertServerRecord = (record: ServerProcessingService) =>
1010
new ProcessingService(record)
1111

1212
export const useProcessingServiceDetails = (
13-
processingServiceId: string,
14-
queryParams: { projectId: string }
13+
processingServiceId: string
1514
): {
1615
processingService?: ProcessingService
1716
isLoading: boolean
@@ -21,7 +20,7 @@ export const useProcessingServiceDetails = (
2120
const { data, isLoading, isFetching, error } =
2221
useAuthorizedQuery<ProcessingService>({
2322
queryKey: [API_ROUTES.PROCESSING_SERVICES, processingServiceId],
24-
url: `${API_URL}/${API_ROUTES.PROCESSING_SERVICES}/${processingServiceId}/?project_id=${queryParams.projectId}`,
23+
url: `${API_URL}/${API_ROUTES.PROCESSING_SERVICES}/${processingServiceId}`,
2524
})
2625

2726
const processingService = useMemo(

ui/src/design-system/components/box/box.module.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
.box {
55
background: $color-generic-white;
66
width: 100%;
7-
border-radius: 4px;
7+
border-radius: 6px;
88
border: 1px solid $color-neutral-100;
99
overflow-x: auto;
1010
}

ui/src/design-system/components/pagination-bar/pagination-bar.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,28 @@ import { PageButton } from './page-button/page-button'
66
import styles from './pagination-bar.module.scss'
77

88
interface PaginationBarProps {
9+
compact?: boolean
910
pagination: {
1011
page: number
1112
perPage: number
1213
}
13-
total: number
1414
setPage: (page: number) => void
15+
total: number
1516
}
1617

1718
export const PaginationBar = ({
19+
compact,
1820
pagination,
19-
total,
2021
setPage,
22+
total,
2123
}: PaginationBarProps) => {
2224
const { page: currentPage, perPage } = pagination
2325
const numPages = Math.ceil(total / perPage)
26+
27+
if (compact && numPages === 1) {
28+
return null
29+
}
30+
2431
const firstPage = 0
2532
const lastPage = numPages - 1
2633
const pageWindow = getPageWindow(currentPage, numPages)

ui/src/design-system/components/plot-grid/plot-grid.module.scss

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
.grid {
77
display: grid;
88
grid-template-columns: 1fr 1fr 1fr;
9-
gap: 16px;
9+
gap: 24px;
1010
}
1111

1212
/* Adjust number of columns based on container width, to avoid horizontal scrolling.
@@ -15,13 +15,13 @@
1515
* maxWidth = plotWidth * numBoxes + gapSize * (numBoxes - 1)
1616
*/
1717

18-
@container container (max-width: calc(384px * 3 + 16px * 2)) {
18+
@container container (max-width: calc(384px * 3 + 24px * 2)) {
1919
.grid {
2020
grid-template-columns: 1fr 1fr;
2121
}
2222
}
2323

24-
@container container (max-width: calc(384px * 2 + 16px * 1)) {
24+
@container container (max-width: calc(384px * 2 + 24px * 1)) {
2525
.grid {
2626
grid-template-columns: 1fr;
2727
}

ui/src/design-system/map/styles.module.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
width: 100%;
88
height: 320px;
99
border: 1px solid $color-neutral-100;
10-
border-radius: 4px;
10+
border-radius: 6px;
1111
overflow: hidden;
1212
position: relative;
1313
z-index: 0;

ui/src/pages/collection-details/collection-details.tsx

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ import { PaginationBar } from 'design-system/components/pagination-bar/paginatio
77
import { Table } from 'design-system/components/table/table/table'
88
import { TableSortSettings } from 'design-system/components/table/types'
99
import { ToggleGroup } from 'design-system/components/toggle-group/toggle-group'
10-
import { useContext, useEffect, useState } from 'react'
10+
import { useState } from 'react'
1111
import { useParams } from 'react-router-dom'
12-
import { BreadcrumbContext } from 'utils/breadcrumbContext'
1312
import { STRING, translate } from 'utils/language'
1413
import { usePagination } from 'utils/usePagination'
1514
import { useSelectedView } from 'utils/useSelectedView'
@@ -21,17 +20,8 @@ export const CollectionDetails = () => {
2120
const { selectedView, setSelectedView } = useSelectedView('table')
2221

2322
// Collection details
24-
const { setDetailBreadcrumb } = useContext(BreadcrumbContext)
2523
const { collection } = useCollectionDetails(id as string)
2624

27-
useEffect(() => {
28-
setDetailBreadcrumb(collection ? { title: collection.name } : undefined)
29-
30-
return () => {
31-
setDetailBreadcrumb(undefined)
32-
}
33-
}, [collection])
34-
3525
// Collection captures
3626
const [sort, setSort] = useState<TableSortSettings>()
3727
const { pagination, setPage } = usePagination()

0 commit comments

Comments
 (0)