From 5c2eb2c7ebe60f3fce762fba40f2f50975fe4b2c Mon Sep 17 00:00:00 2001 From: Rob Donigian Date: Tue, 29 Jul 2025 14:08:31 -0400 Subject: [PATCH 1/5] add field region to project grid --- .../ProjectDataGrid/ProjectColumns.tsx | 16 ++++++++++++++++ .../ProjectDataGrid/projectDataGridRow.graphql | 9 +++++++++ 2 files changed, 25 insertions(+) diff --git a/src/components/ProjectDataGrid/ProjectColumns.tsx b/src/components/ProjectDataGrid/ProjectColumns.tsx index 75d671653..c753e06b8 100644 --- a/src/components/ProjectDataGrid/ProjectColumns.tsx +++ b/src/components/ProjectDataGrid/ProjectColumns.tsx @@ -27,6 +27,7 @@ import { } from '../Grid'; import { ProjectNameColumn } from '../Grid/Columns/ProjectNameColumn'; import { SensitivityColumn } from '../Grid/Columns/SensitivityColumn'; +import { Link } from '../Routing'; import { ProjectDataGridRowFragment as Project } from './projectDataGridRow.graphql'; export const ProjectColumns: Array> = [ @@ -43,6 +44,21 @@ export const ProjectColumns: Array> = [ headerName: 'Country', width: 300, }, + { + field: 'fieldRegion.name', + headerName: 'Field Region', + ...textColumn(), + width: 250, + valueGetter: (_, { fieldRegion }) => fieldRegion.value?.name.value, + renderCell: ({ row: project }) => { + const { fieldRegion } = project; + return fieldRegion.value ? ( + + {fieldRegion.value.name.value} + + ) : null; + }, + }, { field: 'step', ...enumColumn(ProjectStepList, ProjectStepLabels, { diff --git a/src/components/ProjectDataGrid/projectDataGridRow.graphql b/src/components/ProjectDataGrid/projectDataGridRow.graphql index 8488cf5bb..f63ca60db 100644 --- a/src/components/ProjectDataGrid/projectDataGridRow.graphql +++ b/src/components/ProjectDataGrid/projectDataGridRow.graphql @@ -18,12 +18,21 @@ fragment projectDataGridRow on Project { } } } + fieldRegion { + value { + id + name { + value + } + } + } mouStart { value } mouEnd { value } + isMember pinned } From fee6719c3e01b098979aabca0a84c137e45be96e Mon Sep 17 00:00:00 2001 From: Rob Donigian Date: Tue, 29 Jul 2025 14:08:47 -0400 Subject: [PATCH 2/5] add field region to engagement grid --- .../EngagementDataGrid/EngagementColumns.tsx | 17 +++++++++++++++++ .../engagementDataGridRow.graphql | 9 ++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/components/EngagementDataGrid/EngagementColumns.tsx b/src/components/EngagementDataGrid/EngagementColumns.tsx index b4bb7231b..31cf30723 100644 --- a/src/components/EngagementDataGrid/EngagementColumns.tsx +++ b/src/components/EngagementDataGrid/EngagementColumns.tsx @@ -155,8 +155,25 @@ export const EngagementColumns: Array> = [ headerName: 'Country', field: 'project.primaryLocation.name', ...textColumn(), + width: 250, valueGetter: (_, row) => row.project.primaryLocation.value?.name.value, }, + { + field: 'project.fieldRegion.name', + headerName: 'Field Region', + ...textColumn(), + width: 250, + valueGetter: (_, { project }) => project.fieldRegion.value?.name.value, + renderCell: ({ row: engagement }) => { + const { fieldRegion } = engagement.project; + + return fieldRegion.value ? ( + + {fieldRegion.value.name.value} + + ) : null; + }, + }, { headerName: 'ISO', description: 'Ethnologue Code', diff --git a/src/components/EngagementDataGrid/engagementDataGridRow.graphql b/src/components/EngagementDataGrid/engagementDataGridRow.graphql index 8f4255c00..9f2e94b10 100644 --- a/src/components/EngagementDataGrid/engagementDataGridRow.graphql +++ b/src/components/EngagementDataGrid/engagementDataGridRow.graphql @@ -9,7 +9,6 @@ fragment engagementDataGridRow on Engagement { endDate { value } - project { id type @@ -27,6 +26,14 @@ fragment engagementDataGridRow on Engagement { } } } + fieldRegion { + value { + id + name { + value + } + } + } mouStart { value } From 13e4234bf4157cde0b4b02d857be0be9025aa837 Mon Sep 17 00:00:00 2001 From: Rob Donigian Date: Mon, 21 Jul 2025 10:29:08 -0400 Subject: [PATCH 3/5] Create Field Region tabs, Add Projects grid components and gql --- .../schema/typePolicies/typePolicies.base.ts | 5 + .../FieldRegions/Detail/FieldRegionDetail.tsx | 149 ++++++------------ .../Tabs/Profile/FieldRegionProfile.graphql | 4 + .../Tabs/Profile/FieldRegionProfile.tsx | 109 +++++++++++++ .../Tabs/Projects/FieldRegionProjects.graphql | 17 ++ .../Tabs/Projects/FieldRegionProjects.tsx | 65 ++++++++ 6 files changed, 247 insertions(+), 102 deletions(-) create mode 100644 src/scenes/FieldRegions/Detail/Tabs/Profile/FieldRegionProfile.graphql create mode 100644 src/scenes/FieldRegions/Detail/Tabs/Profile/FieldRegionProfile.tsx create mode 100644 src/scenes/FieldRegions/Detail/Tabs/Projects/FieldRegionProjects.graphql create mode 100644 src/scenes/FieldRegions/Detail/Tabs/Projects/FieldRegionProjects.tsx diff --git a/src/api/schema/typePolicies/typePolicies.base.ts b/src/api/schema/typePolicies/typePolicies.base.ts index 90b95800b..05495cf7b 100644 --- a/src/api/schema/typePolicies/typePolicies.base.ts +++ b/src/api/schema/typePolicies/typePolicies.base.ts @@ -85,6 +85,11 @@ export const typePolicies: TypePolicies = { engagements: {}, }, }, + FieldRegion: { + fields: { + projects: {}, // no page merging (infinite scroll) + }, + }, Query: { fields: { projects: {}, diff --git a/src/scenes/FieldRegions/Detail/FieldRegionDetail.tsx b/src/scenes/FieldRegions/Detail/FieldRegionDetail.tsx index 873bf4955..a81b76584 100644 --- a/src/scenes/FieldRegions/Detail/FieldRegionDetail.tsx +++ b/src/scenes/FieldRegions/Detail/FieldRegionDetail.tsx @@ -1,154 +1,99 @@ import { useQuery } from '@apollo/client'; -import { Edit } from '@mui/icons-material'; -import { Box, Skeleton, Typography } from '@mui/material'; +import { TabContext, TabList, TabPanel } from '@mui/lab'; +import { Box, Skeleton, Stack, Typography } from '@mui/material'; import { Helmet } from 'react-helmet-async'; import { useParams } from 'react-router-dom'; -import { canEditAny } from '~/common'; -import { useDialog } from '~/components/Dialog'; -import { - DisplaySimpleProperty, - DisplaySimplePropertyProps, -} from '~/components/DisplaySimpleProperty'; -import { EditFieldRegion } from '~/components/FieldRegion'; -import { Link } from '~/components/Routing'; +import { Tab, TabsContainer } from '~/components/Tabs'; +import { EnumParam, makeQueryHandler, withDefault } from '~/hooks'; import { Error } from '../../../components/Error'; -import { Fab } from '../../../components/Fab'; import { Redacted } from '../../../components/Redacted'; import { FieldRegionDetailDocument } from './FieldRegionDetail.graphql'; +import { FieldRegionProfile } from './Tabs/Profile/FieldRegionProfile'; +import { FieldRegionProjects } from './Tabs/Projects/FieldRegionProjects'; + +const useFieldRegionDetailsFilters = makeQueryHandler({ + tab: withDefault(EnumParam(['profile', 'projects']), 'profile'), +}); export const FieldRegionDetail = () => { const { fieldRegionId = '' } = useParams(); - const [editRegionState, editRegion] = useDialog(); - const { data, error } = useQuery(FieldRegionDetailDocument, { variables: { fieldRegionId }, }); + const [filters, setFilters] = useFieldRegionDetailsFilters(); + const fieldRegion = data?.fieldRegion; return ( - theme.breakpoints.values.xl, }} > - {{ NotFound: 'Could not find field region', Default: 'Error loading field region', }} + + {!error && ( - theme.breakpoints.values.md, - display: 'flex', - flexDirection: 'column', - gap: 3, - }} - > + <> {!fieldRegion ? ( - + ) : ( fieldRegion.name.value ?? ( ) )} - {canEditAny(fieldRegion, true) && ( - - - - )} - - - - {fieldRegion ? 'Field Region' : } - - - {fieldRegion?.fieldZone.value?.name.value} - - } - loading={!fieldRegion} - /> - - {fieldRegion?.director.value?.fullName} - - } - loading={!fieldRegion} - /> - - )} - {fieldRegion && ( - + + + setFilters({ ...filters, tab })} + aria-label="field region navigation tabs" + variant="scrollable" + > + + + + + {fieldRegion && ( + + )} + + + {fieldRegion && } + + + + )} - + ); }; - -const DisplayProperty = (props: DisplaySimplePropertyProps) => - !props.value && !props.loading ? null : ( - - - - - - - - - ) : null - } - LabelProps={{ - color: 'textSecondary', - variant: 'body2', - ...props.LabelProps, - }} - ValueProps={{ - color: 'textPrimary', - ...props.ValueProps, - }} - /> - ); diff --git a/src/scenes/FieldRegions/Detail/Tabs/Profile/FieldRegionProfile.graphql b/src/scenes/FieldRegions/Detail/Tabs/Profile/FieldRegionProfile.graphql new file mode 100644 index 000000000..d11506c8c --- /dev/null +++ b/src/scenes/FieldRegions/Detail/Tabs/Profile/FieldRegionProfile.graphql @@ -0,0 +1,4 @@ +fragment FieldRegionProfile on FieldRegion { + ...DisplayFieldRegion + ...FieldRegionForm +} diff --git a/src/scenes/FieldRegions/Detail/Tabs/Profile/FieldRegionProfile.tsx b/src/scenes/FieldRegions/Detail/Tabs/Profile/FieldRegionProfile.tsx new file mode 100644 index 000000000..4e6956289 --- /dev/null +++ b/src/scenes/FieldRegions/Detail/Tabs/Profile/FieldRegionProfile.tsx @@ -0,0 +1,109 @@ +import { Edit } from '@mui/icons-material'; +import { + Box, + IconButton, + Paper, + Skeleton, + Stack, + Tooltip, + Typography, +} from '@mui/material'; +import { canEditAny } from '~/common'; +import { useDialog } from '~/components/Dialog'; +import { + DisplaySimpleProperty, + DisplaySimplePropertyProps, +} from '~/components/DisplaySimpleProperty'; +import { EditFieldRegion } from '~/components/FieldRegion/EditFieldRegion/EditFieldRegion'; +import { Link } from '~/components/Routing'; +import { FieldRegionProfileFragment } from './FieldRegionProfile.graphql'; + +interface FieldRegionProfileProps { + fieldRegion: FieldRegionProfileFragment; +} + +export const FieldRegionProfile = ({ + fieldRegion, +}: FieldRegionProfileProps) => { + const [editRegionState, editRegion] = useDialog(); + + const canEditAnyFields = canEditAny(fieldRegion); + + return ( + ({ + display: 'flex', + justifyContent: 'space-between', + width: theme.breakpoints.values.md, + minHeight: 200, + })} + > + + + {fieldRegion.fieldZone.value?.name.value} + + } + loading={!fieldRegion} + /> + + {fieldRegion.director.value?.fullName} + + } + loading={!fieldRegion} + /> + + + {canEditAnyFields ? ( + + + + + + ) : null} + + + + ); +}; + +const DisplayProperty = (props: DisplaySimplePropertyProps) => + !props.value && !props.loading ? null : ( + + + + + + + + + ) : null + } + LabelProps={{ + color: 'textSecondary', + variant: 'body2', + ...props.LabelProps, + }} + ValueProps={{ + color: 'textPrimary', + ...props.ValueProps, + }} + /> + ); diff --git a/src/scenes/FieldRegions/Detail/Tabs/Projects/FieldRegionProjects.graphql b/src/scenes/FieldRegions/Detail/Tabs/Projects/FieldRegionProjects.graphql new file mode 100644 index 000000000..cc3dc42e8 --- /dev/null +++ b/src/scenes/FieldRegions/Detail/Tabs/Projects/FieldRegionProjects.graphql @@ -0,0 +1,17 @@ +query FieldRegionProjects($fieldRegionId: ID!, $input: ProjectListInput) { + fieldRegion(id: $fieldRegionId) { + id + projects(input: $input) { + canRead + hasMore + total + items { + ...fieldRegionProjectDataGridRow + } + } + } +} + +fragment fieldRegionProjectDataGridRow on Project { + ...projectDataGridRow +} diff --git a/src/scenes/FieldRegions/Detail/Tabs/Projects/FieldRegionProjects.tsx b/src/scenes/FieldRegions/Detail/Tabs/Projects/FieldRegionProjects.tsx new file mode 100644 index 000000000..5b0978d63 --- /dev/null +++ b/src/scenes/FieldRegions/Detail/Tabs/Projects/FieldRegionProjects.tsx @@ -0,0 +1,65 @@ +import { + DataGridPro as DataGrid, + DataGridProProps as DataGridProps, +} from '@mui/x-data-grid-pro'; +import { merge } from 'lodash'; +import { useMemo } from 'react'; +import { useParams } from 'react-router-dom'; +import { + DefaultDataGridStyles, + flexLayout, + noFooter, + noHeaderFilterButtons, + useDataGridSource, +} from '~/components/Grid'; +import { + ProjectColumns, + ProjectInitialState, + ProjectToolbar, +} from '~/components/ProjectDataGrid'; +import { TabPanelContent } from '~/components/Tabs'; +import { + FieldRegionProjectDataGridRowFragment as FieldRegionProject, + FieldRegionProjectsDocument, +} from './FieldRegionProjects.graphql'; + +export const FieldRegionProjects = () => { + const { fieldRegionId = '' } = useParams(); + + const [props] = useDataGridSource({ + query: FieldRegionProjectsDocument, + variables: { fieldRegionId }, + listAt: 'fieldRegion.projects', + initialInput: { + sort: 'name', + }, + }); + + const slots = useMemo( + () => + merge({}, DefaultDataGridStyles.slots, props.slots, { + toolbar: ProjectToolbar, + } satisfies DataGridProps['slots']), + [props.slots] + ); + const slotProps = useMemo( + () => merge({}, DefaultDataGridStyles.slotProps, props.slotProps), + [props.slotProps] + ); + + return ( + + + {...DefaultDataGridStyles} + {...props} + slots={slots} + slotProps={slotProps} + columns={ProjectColumns} + initialState={ProjectInitialState} + headerFilters + hideFooter + sx={[flexLayout, noHeaderFilterButtons, noFooter]} + /> + + ); +}; From 4638c9c3d9446eb07a017f0ad535a9458e7317f1 Mon Sep 17 00:00:00 2001 From: Rob Donigian Date: Mon, 21 Jul 2025 10:30:58 -0400 Subject: [PATCH 4/5] Create Field Zone tabs, Add Projects grid components and gql --- .../schema/typePolicies/typePolicies.base.ts | 6 +- .../FieldZones/Detail/FieldZoneDetail.tsx | 143 ++++++------------ .../Tabs/Profile/FieldZoneProfile.graphql | 4 + .../Detail/Tabs/Profile/FieldZoneProfile.tsx | 98 ++++++++++++ .../Tabs/Projects/FieldZoneProjects.graphql | 17 +++ .../Tabs/Projects/FieldZoneProjects.tsx | 65 ++++++++ 6 files changed, 236 insertions(+), 97 deletions(-) create mode 100644 src/scenes/FieldZones/Detail/Tabs/Profile/FieldZoneProfile.graphql create mode 100644 src/scenes/FieldZones/Detail/Tabs/Profile/FieldZoneProfile.tsx create mode 100644 src/scenes/FieldZones/Detail/Tabs/Projects/FieldZoneProjects.graphql create mode 100644 src/scenes/FieldZones/Detail/Tabs/Projects/FieldZoneProjects.tsx diff --git a/src/api/schema/typePolicies/typePolicies.base.ts b/src/api/schema/typePolicies/typePolicies.base.ts index 05495cf7b..d1cdbbec1 100644 --- a/src/api/schema/typePolicies/typePolicies.base.ts +++ b/src/api/schema/typePolicies/typePolicies.base.ts @@ -90,10 +90,14 @@ export const typePolicies: TypePolicies = { projects: {}, // no page merging (infinite scroll) }, }, + FieldZone: { + fields: { + projects: {}, // no page merging (infinite scroll) + }, + }, Query: { fields: { projects: {}, - engagements: {}, progressReports: {}, partners: {}, users: {}, diff --git a/src/scenes/FieldZones/Detail/FieldZoneDetail.tsx b/src/scenes/FieldZones/Detail/FieldZoneDetail.tsx index e729a82e5..be64ee5c2 100644 --- a/src/scenes/FieldZones/Detail/FieldZoneDetail.tsx +++ b/src/scenes/FieldZones/Detail/FieldZoneDetail.tsx @@ -1,146 +1,97 @@ import { useQuery } from '@apollo/client'; -import { Edit } from '@mui/icons-material'; -import { Box, Skeleton, Typography } from '@mui/material'; +import { TabContext, TabList, TabPanel } from '@mui/lab'; +import { Box, Skeleton, Stack, Typography } from '@mui/material'; import { Helmet } from 'react-helmet-async'; import { useParams } from 'react-router-dom'; -import { canEditAny } from '~/common'; -import { useDialog } from '~/components/Dialog'; -import { - DisplaySimpleProperty, - DisplaySimplePropertyProps, -} from '~/components/DisplaySimpleProperty'; -import { Link } from '~/components/Routing'; +import { Tab, TabsContainer } from '~/components/Tabs'; +import { EnumParam, makeQueryHandler, withDefault } from '~/hooks'; import { Error } from '../../../components/Error'; -import { Fab } from '../../../components/Fab'; -import { EditFieldZone } from '../../../components/FieldZone'; import { Redacted } from '../../../components/Redacted'; import { FieldZoneDetailDocument } from './FieldZoneDetail.graphql'; +import { FieldZoneProfile } from './Tabs/Profile/FieldZoneProfile'; +import { FieldZoneProjects } from './Tabs/Projects/FieldZoneProjects'; + +const useFieldZoneDetailsFilters = makeQueryHandler({ + tab: withDefault(EnumParam(['profile', 'projects']), 'profile'), +}); export const FieldZoneDetail = () => { const { fieldZoneId = '' } = useParams(); - const [editZoneState, editZone] = useDialog(); - const { data, error } = useQuery(FieldZoneDetailDocument, { variables: { fieldZoneId }, }); + const [filters, setFilters] = useFieldZoneDetailsFilters(); + const fieldZone = data?.fieldZone; return ( - theme.breakpoints.values.xl, }} > - {{ - NotFound: 'Could not find field zone', - Default: 'Error loading field zone', + NotFound: 'Could not find fieldZone', + Default: 'Error loading fieldZone', }} + + {!error && ( - theme.breakpoints.values.md, - display: 'flex', - flexDirection: 'column', - gap: 3, - }} - > + <> {!fieldZone ? ( - + ) : ( fieldZone.name.value ?? ( ) )} - {canEditAny(fieldZone, true) && ( - - - - )} - - - - {fieldZone ? 'Field Zone' : } - - - - {fieldZone.director.value.fullName} - - ) - } - loading={!fieldZone} - /> - + + + setFilters({ ...filters, tab })} + aria-label="field zone navigation tabs" + variant="scrollable" + > + + + + + {fieldZone && } + + + {fieldZone && } + + + + )} - {fieldZone && } - + ); }; - -const DisplayProperty = (props: DisplaySimplePropertyProps) => - !props.value && !props.loading ? null : ( - - - - - - - - - ) : null - } - LabelProps={{ - color: 'textSecondary', - variant: 'body2', - ...props.LabelProps, - }} - ValueProps={{ - color: 'textPrimary', - ...props.ValueProps, - }} - /> - ); diff --git a/src/scenes/FieldZones/Detail/Tabs/Profile/FieldZoneProfile.graphql b/src/scenes/FieldZones/Detail/Tabs/Profile/FieldZoneProfile.graphql new file mode 100644 index 000000000..58b3c8761 --- /dev/null +++ b/src/scenes/FieldZones/Detail/Tabs/Profile/FieldZoneProfile.graphql @@ -0,0 +1,4 @@ +fragment FieldZoneProfile on FieldZone { + ...DisplayFieldZone + ...FieldZoneForm +} diff --git a/src/scenes/FieldZones/Detail/Tabs/Profile/FieldZoneProfile.tsx b/src/scenes/FieldZones/Detail/Tabs/Profile/FieldZoneProfile.tsx new file mode 100644 index 000000000..cbd2aa7ac --- /dev/null +++ b/src/scenes/FieldZones/Detail/Tabs/Profile/FieldZoneProfile.tsx @@ -0,0 +1,98 @@ +import { Edit } from '@mui/icons-material'; +import { + Box, + IconButton, + Paper, + Skeleton, + Stack, + Tooltip, + Typography, +} from '@mui/material'; +import { canEditAny } from '~/common'; +import { useDialog } from '~/components/Dialog'; +import { + DisplaySimpleProperty, + DisplaySimplePropertyProps, +} from '~/components/DisplaySimpleProperty'; +import { EditFieldZone } from '~/components/FieldZone'; +import { Link } from '~/components/Routing'; +import { FieldZoneProfileFragment } from './FieldZoneProfile.graphql'; + +interface FieldZoneProfileProps { + fieldZone: FieldZoneProfileFragment; +} + +export const FieldZoneProfile = ({ fieldZone }: FieldZoneProfileProps) => { + const [editZoneState, editZone] = useDialog(); + + const canEditAnyFields = canEditAny(fieldZone); + + return ( + ({ + display: 'flex', + justifyContent: 'space-between', + width: theme.breakpoints.values.md, + minHeight: 200, + })} + > + + + {fieldZone.director.value?.fullName} + + } + loading={!fieldZone} + /> + + + {canEditAnyFields ? ( + + + + + + ) : null} + + + + ); +}; + +const DisplayProperty = (props: DisplaySimplePropertyProps) => + !props.value && !props.loading ? null : ( + + + + + + + + + ) : null + } + LabelProps={{ + color: 'textSecondary', + variant: 'body2', + ...props.LabelProps, + }} + ValueProps={{ + color: 'textPrimary', + ...props.ValueProps, + }} + /> + ); diff --git a/src/scenes/FieldZones/Detail/Tabs/Projects/FieldZoneProjects.graphql b/src/scenes/FieldZones/Detail/Tabs/Projects/FieldZoneProjects.graphql new file mode 100644 index 000000000..45ac23d5e --- /dev/null +++ b/src/scenes/FieldZones/Detail/Tabs/Projects/FieldZoneProjects.graphql @@ -0,0 +1,17 @@ +query FieldZoneProjects($fieldZoneId: ID!, $input: ProjectListInput) { + fieldZone(id: $fieldZoneId) { + id + projects(input: $input) { + canRead + hasMore + total + items { + ...fieldZoneProjectDataGridRow + } + } + } +} + +fragment fieldZoneProjectDataGridRow on Project { + ...projectDataGridRow +} diff --git a/src/scenes/FieldZones/Detail/Tabs/Projects/FieldZoneProjects.tsx b/src/scenes/FieldZones/Detail/Tabs/Projects/FieldZoneProjects.tsx new file mode 100644 index 000000000..3ce3f4cb4 --- /dev/null +++ b/src/scenes/FieldZones/Detail/Tabs/Projects/FieldZoneProjects.tsx @@ -0,0 +1,65 @@ +import { + DataGridPro as DataGrid, + DataGridProProps as DataGridProps, +} from '@mui/x-data-grid-pro'; +import { merge } from 'lodash'; +import { useMemo } from 'react'; +import { useParams } from 'react-router-dom'; +import { + DefaultDataGridStyles, + flexLayout, + noFooter, + noHeaderFilterButtons, + useDataGridSource, +} from '~/components/Grid'; +import { + ProjectColumns, + ProjectInitialState, + ProjectToolbar, +} from '~/components/ProjectDataGrid'; +import { TabPanelContent } from '~/components/Tabs'; +import { + FieldZoneProjectDataGridRowFragment as FieldZoneProject, + FieldZoneProjectsDocument, +} from './FieldZoneProjects.graphql'; + +export const FieldZoneProjects = () => { + const { fieldZoneId = '' } = useParams(); + + const [props] = useDataGridSource({ + query: FieldZoneProjectsDocument, + variables: { fieldZoneId }, + listAt: 'fieldZone.projects', + initialInput: { + sort: 'name', + }, + }); + + const slots = useMemo( + () => + merge({}, DefaultDataGridStyles.slots, props.slots, { + toolbar: ProjectToolbar, + } satisfies DataGridProps['slots']), + [props.slots] + ); + const slotProps = useMemo( + () => merge({}, DefaultDataGridStyles.slotProps, props.slotProps), + [props.slotProps] + ); + + return ( + + + {...DefaultDataGridStyles} + {...props} + slots={slots} + slotProps={slotProps} + columns={ProjectColumns} + initialState={ProjectInitialState} + headerFilters + hideFooter + sx={[flexLayout, noHeaderFilterButtons, noFooter]} + /> + + ); +}; From 561140726e00e2430d66fada1b32b0b04667c362 Mon Sep 17 00:00:00 2001 From: Rob Donigian Date: Wed, 30 Jul 2025 10:26:28 -0400 Subject: [PATCH 5/5] Add loading skeletons, remove field region col field region - projects table --- .../FieldRegions/Detail/FieldRegionDetail.tsx | 14 +++++------ .../Tabs/Profile/FieldRegionProfile.tsx | 25 ++++++++++++------- .../Tabs/Projects/FieldRegionProjects.tsx | 7 +++++- .../FieldZones/Detail/FieldZoneDetail.tsx | 6 ++--- .../Detail/Tabs/Profile/FieldZoneProfile.tsx | 14 ++++++----- 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/scenes/FieldRegions/Detail/FieldRegionDetail.tsx b/src/scenes/FieldRegions/Detail/FieldRegionDetail.tsx index a81b76584..a1caa2a94 100644 --- a/src/scenes/FieldRegions/Detail/FieldRegionDetail.tsx +++ b/src/scenes/FieldRegions/Detail/FieldRegionDetail.tsx @@ -18,7 +18,7 @@ const useFieldRegionDetailsFilters = makeQueryHandler({ export const FieldRegionDetail = () => { const { fieldRegionId = '' } = useParams(); - const { data, error } = useQuery(FieldRegionDetailDocument, { + const { data, error, loading } = useQuery(FieldRegionDetailDocument, { variables: { fieldRegionId }, }); @@ -60,8 +60,10 @@ export const FieldRegionDetail = () => { lineHeight: 'inherit', }} > - {!fieldRegion ? ( - + {loading ? ( + + ) : !fieldRegion ? ( + ) : ( fieldRegion.name.value ?? ( { - {fieldRegion && ( - - )} + - {fieldRegion && } + diff --git a/src/scenes/FieldRegions/Detail/Tabs/Profile/FieldRegionProfile.tsx b/src/scenes/FieldRegions/Detail/Tabs/Profile/FieldRegionProfile.tsx index 4e6956289..78dbdf49f 100644 --- a/src/scenes/FieldRegions/Detail/Tabs/Profile/FieldRegionProfile.tsx +++ b/src/scenes/FieldRegions/Detail/Tabs/Profile/FieldRegionProfile.tsx @@ -19,7 +19,7 @@ import { Link } from '~/components/Routing'; import { FieldRegionProfileFragment } from './FieldRegionProfile.graphql'; interface FieldRegionProfileProps { - fieldRegion: FieldRegionProfileFragment; + fieldRegion: FieldRegionProfileFragment | undefined; } export const FieldRegionProfile = ({ @@ -43,23 +43,28 @@ export const FieldRegionProfile = ({ sx={{ p: 2, gap: 2, + flex: 1, }} > - {fieldRegion.fieldZone.value?.name.value} - + fieldRegion?.fieldZone.value ? ( + + {fieldRegion.fieldZone.value.name.value} + + ) : null } loading={!fieldRegion} /> - {fieldRegion.director.value?.fullName} - + fieldRegion?.director.value ? ( + + {fieldRegion.director.value.fullName} + + ) : null } loading={!fieldRegion} /> @@ -67,13 +72,15 @@ export const FieldRegionProfile = ({ {canEditAnyFields ? ( - + ) : null} - + {fieldRegion && ( + + )} ); }; diff --git a/src/scenes/FieldRegions/Detail/Tabs/Projects/FieldRegionProjects.tsx b/src/scenes/FieldRegions/Detail/Tabs/Projects/FieldRegionProjects.tsx index 5b0978d63..a1fd47d37 100644 --- a/src/scenes/FieldRegions/Detail/Tabs/Projects/FieldRegionProjects.tsx +++ b/src/scenes/FieldRegions/Detail/Tabs/Projects/FieldRegionProjects.tsx @@ -54,7 +54,7 @@ export const FieldRegionProjects = () => { {...props} slots={slots} slotProps={slotProps} - columns={ProjectColumns} + columns={FieldRegionProjectColumns} initialState={ProjectInitialState} headerFilters hideFooter @@ -63,3 +63,8 @@ export const FieldRegionProjects = () => { ); }; + +// Remove the 'fieldRegion' column since this view is scoped to a specific field region +const FieldRegionProjectColumns = ProjectColumns.filter( + (col) => col.field !== 'fieldRegion.name' +); diff --git a/src/scenes/FieldZones/Detail/FieldZoneDetail.tsx b/src/scenes/FieldZones/Detail/FieldZoneDetail.tsx index be64ee5c2..fff78b01b 100644 --- a/src/scenes/FieldZones/Detail/FieldZoneDetail.tsx +++ b/src/scenes/FieldZones/Detail/FieldZoneDetail.tsx @@ -39,8 +39,8 @@ export const FieldZoneDetail = () => { > {{ - NotFound: 'Could not find fieldZone', - Default: 'Error loading fieldZone', + NotFound: 'Could not find field zone', + Default: 'Error loading field zone', }} @@ -83,7 +83,7 @@ export const FieldZoneDetail = () => { - {fieldZone && } + {fieldZone && } diff --git a/src/scenes/FieldZones/Detail/Tabs/Profile/FieldZoneProfile.tsx b/src/scenes/FieldZones/Detail/Tabs/Profile/FieldZoneProfile.tsx index cbd2aa7ac..b6b34529a 100644 --- a/src/scenes/FieldZones/Detail/Tabs/Profile/FieldZoneProfile.tsx +++ b/src/scenes/FieldZones/Detail/Tabs/Profile/FieldZoneProfile.tsx @@ -19,7 +19,7 @@ import { Link } from '~/components/Routing'; import { FieldZoneProfileFragment } from './FieldZoneProfile.graphql'; interface FieldZoneProfileProps { - fieldZone: FieldZoneProfileFragment; + fieldZone: FieldZoneProfileFragment | undefined; } export const FieldZoneProfile = ({ fieldZone }: FieldZoneProfileProps) => { @@ -46,9 +46,11 @@ export const FieldZoneProfile = ({ fieldZone }: FieldZoneProfileProps) => { - {fieldZone.director.value?.fullName} - + fieldZone?.director.value ? ( + + {fieldZone.director.value.fullName} + + ) : null } loading={!fieldZone} /> @@ -56,13 +58,13 @@ export const FieldZoneProfile = ({ fieldZone }: FieldZoneProfileProps) => { {canEditAnyFields ? ( - + ) : null} - + {fieldZone && } ); };