Skip to content

Commit

Permalink
Merge pull request #299 from achtzig20/dashboard-layout
Browse files Browse the repository at this point in the history
feat(supplier-dashboard): new dashboard layout
  • Loading branch information
tom-rm-meyer-ISST authored Mar 20, 2024
2 parents 29a20f9 + ecd6c72 commit 503a440
Show file tree
Hide file tree
Showing 10 changed files with 636 additions and 253 deletions.
46 changes: 46 additions & 0 deletions frontend/src/components/TableWithRowHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
Copyright (c) 2024 Volkswagen AG
Copyright (c) 2024 Contributors to the Eclipse Foundation
See the NOTICE file(s) distributed with this work for additional
information regarding copyright ownership.
This program and the accompanying materials are made available under the
terms of the Apache License, Version 2.0 which is available at
https://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
SPDX-License-Identifier: Apache-2.0
*/

import { Table, TableProps } from '@catena-x/portal-shared-components';
import { Box } from '@mui/material';

type TableWithRowHeaderProps = TableProps;

export const TableWithRowHeader = ({ rows, ...tableProps }: TableWithRowHeaderProps) => {
return (
<Box sx={{width: '100%'}}>
<div className="table-container">
<Table
title=''
columns={[{ field: 'name', headerName: '', width: 180 }]}
rows={rows}
rowSelection={false}
hideFooter={true}
disableColumnFilter
disableColumnMenu
sortingMode={'server'}
/>
<Box sx={{width: '100%', display: 'flex', overflowX: 'auto'}}>
<Table {...tableProps} rows={rows} disableColumnFilter disableColumnMenu sortingMode={'server'} showCellVerticalBorder showColumnVerticalBorder />
</Box>
</div>
</Box>
);
};
119 changes: 119 additions & 0 deletions frontend/src/features/dashboard/components/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
Copyright (c) 2024 Volkswagen AG
Copyright (c) 2024 Contributors to the Eclipse Foundation
See the NOTICE file(s) distributed with this work for additional
information regarding copyright ownership.
This program and the accompanying materials are made available under the
terms of the Apache License, Version 2.0 which is available at
https://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
SPDX-License-Identifier: Apache-2.0
*/

import { usePartnerStocks } from '@features/stock-view/hooks/usePartnerStocks';
import { useStocks } from '@features/stock-view/hooks/useStocks';
import { MaterialDescriptor } from '@models/types/data/material-descriptor';
import { Site } from '@models/types/edc/site';
import { useState } from 'react';
import { DashboardFilters } from './DashboardFilters';
import { DemandTable } from './DemandTable';
import { ProductionTable } from './ProductionTable';
import { Stack, Typography, capitalize } from '@mui/material';
import { Delivery } from '@models/types/data/delivery';
import { DeliveryInformationModal } from './DeliveryInformationModal';
import { getPartnerType } from '../util/helpers';

const NUMBER_OF_DAYS = 42;

export const Dashboard = ({ type }: { type: 'customer' | 'supplier' }) => {
const [selectedMaterial, setSelectedMaterial] = useState<MaterialDescriptor | null>(null);
const [selectedSite, setSelectedSite] = useState<Site | null>(null);
const [selectedPartnerSites, setSelectedPartnerSites] = useState<Site[] | null>(null);
const { stocks } = useStocks(type === 'customer' ? 'material' : 'product');
const { partnerStocks } = usePartnerStocks(type === 'customer' ? 'material' : 'product', selectedMaterial?.ownMaterialNumber ?? null);
const [open, setOpen] = useState(false);
const [delivery, setDelivery] = useState<Delivery | null>(null);
const openDeliveryDialog = (d: Delivery) => {
setDelivery(d);
setOpen(true);
};
const handleMaterialSelect = (material: MaterialDescriptor | null) => {
setSelectedMaterial(material);
setSelectedSite(null);
setSelectedPartnerSites(null);
};
return (
<>
<Stack spacing={3} alignItems={'center'}>
<DashboardFilters
type={type}
material={selectedMaterial}
site={selectedSite}
partnerSites={selectedPartnerSites}
onMaterialChange={handleMaterialSelect}
onSiteChange={setSelectedSite}
onPartnerSitesChange={setSelectedPartnerSites}
/>
<Typography variant="h5" component="h2" marginBottom={0}>
Our Stock Information {selectedMaterial && selectedSite && <>for {selectedMaterial.description}</>}
</Typography>
{selectedSite ? (
type === 'supplier' ? (
<ProductionTable
numberOfDays={NUMBER_OF_DAYS}
stocks={stocks}
site={selectedSite}
onDeliveryClick={openDeliveryDialog}
/>
) : (
<DemandTable
numberOfDays={NUMBER_OF_DAYS}
stocks={stocks}
site={selectedSite}
onDeliveryClick={openDeliveryDialog}
/>
)
) : (
<Typography variant="body1">Select a Site to show production data</Typography>
)}
{selectedSite && (
<>
<Typography variant="h5" component="h2">
{`${capitalize(getPartnerType(type))} Stocks ${selectedMaterial ? `for ${selectedMaterial?.description}` : ''}`}
</Typography>
{selectedPartnerSites ? (
selectedPartnerSites.map((ps) =>
type === 'supplier' ? (
<DemandTable
numberOfDays={NUMBER_OF_DAYS}
stocks={partnerStocks}
site={ps}
onDeliveryClick={openDeliveryDialog}
/>
) : (
<ProductionTable
numberOfDays={NUMBER_OF_DAYS}
stocks={partnerStocks}
site={ps}
onDeliveryClick={openDeliveryDialog}
/>
)
)
) : (
<Typography variant="body1">{`Select a ${getPartnerType(type)} site to show their stock information`}</Typography>
)}
</>
)}
</Stack>
<DeliveryInformationModal open={open} onClose={() => setOpen(false)} delivery={delivery} />
</>
);
};
96 changes: 96 additions & 0 deletions frontend/src/features/dashboard/components/DashboardFilters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
Copyright (c) 2024 Volkswagen AG
Copyright (c) 2024 Contributors to the Eclipse Foundation
See the NOTICE file(s) distributed with this work for additional
information regarding copyright ownership.
This program and the accompanying materials are made available under the
terms of the Apache License, Version 2.0 which is available at
https://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
SPDX-License-Identifier: Apache-2.0
*/

import { Input } from '@catena-x/portal-shared-components';
import { useMaterials } from '@features/stock-view/hooks/useMaterials';
import { usePartners } from '@features/stock-view/hooks/usePartners';
import { useSites } from '@features/stock-view/hooks/useSites';
import { MaterialDescriptor } from '@models/types/data/material-descriptor';
import { Site } from '@models/types/edc/site';
import { Autocomplete, Grid, capitalize } from '@mui/material';
import { getPartnerType } from '../util/helpers';

type DashboardFiltersProps = {
type: 'customer' | 'supplier';
material: MaterialDescriptor | null;
site: Site | null;
partnerSites: Site[] | null;
onMaterialChange: (material: MaterialDescriptor | null) => void;
onSiteChange: (site: Site | null) => void;
onPartnerSitesChange: (sites: Site[] | null) => void;
};

export const DashboardFilters = ({
type,
material,
site,
partnerSites,
onMaterialChange,
onSiteChange,
onPartnerSitesChange,
}: DashboardFiltersProps) => {
const { materials } = useMaterials(type === 'customer' ? 'material' : 'product');
const { partners } = usePartners(type === 'customer' ? 'material' : 'product', material?.ownMaterialNumber ?? null);
const { sites } = useSites();
return (
<Grid container spacing={2} maxWidth={1024}>
<Grid item md={6} paddingTop='0 !important'>
<Autocomplete
id="material"
value={material}
options={materials ?? []}
getOptionLabel={(option) => option.ownMaterialNumber}
renderInput={(params) => <Input {...params} label="Material*" placeholder="Select a Material" />}
onChange={(_, newValue) => onMaterialChange(newValue || null)}
/>
</Grid>
<Grid item md={6} paddingTop='0 !important'>
<Autocomplete
id="site"
value={site}
options={sites ?? []}
getOptionLabel={(option) => `${option.name} (${option.bpns})`}
disabled={!material}
renderInput={(params) => <Input {...params} label="Site*" placeholder="Select a Site" />}
onChange={(_, newValue) => onSiteChange(newValue || null)}
/>
</Grid>
<Grid item md={12} paddingTop='0 !important'>
<Autocomplete
id="partner-site"
value={partnerSites ?? []}
options={partners?.reduce((acc: Site[], p) => [...acc, ...p.sites], []) ?? []}
disabled={!site}
getOptionLabel={(option) => `${option.name} (${option.bpns})`}
isOptionEqualToValue={(option, value) => option.bpns === value.bpns}
renderInput={(params) => (
<Input
{...params}
label={`${capitalize(getPartnerType(type))} Sites*`}
placeholder={`Select a ${capitalize(getPartnerType(type))} site`}
/>
)}
onChange={(_, newValue) => onPartnerSitesChange(newValue?.length > 0 ? newValue : null)}
multiple={true}
/>
</Grid>
</Grid>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
Copyright (c) 2024 Volkswagen AG
Copyright (c) 2024 Contributors to the Eclipse Foundation

See the NOTICE file(s) distributed with this work for additional
information regarding copyright ownership.

This program and the accompanying materials are made available under the
terms of the Apache License, Version 2.0 which is available at
https://www.apache.org/licenses/LICENSE-2.0.

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.

SPDX-License-Identifier: Apache-2.0
*/

import { Delivery } from '@models/types/data/delivery';
import { Button, Dialog, DialogTitle, Grid, Stack, Typography } from '@mui/material';

const GridItem = ({ label, value }: { label: string; value: string }) => (
<Grid item xs={6}>
<Stack>
<Typography variant="caption1" fontWeight={500}>
{label}:
</Typography>
<Typography variant="body3" paddingLeft=".5rem">
{value}
</Typography>
</Stack>
</Grid>
);

type DeliveryInformationModalProps = {
open: boolean;
onClose: () => void;
delivery: Delivery | null;
};

export const DeliveryInformationModal = ({ open, onClose, delivery }: DeliveryInformationModalProps) => {
return (
<Dialog open={open && delivery !== null} onClose={onClose} title="Delivery Information">
<DialogTitle fontWeight={600} textAlign='center'>Delivery Information</DialogTitle>
<Stack padding="0 2rem 2rem">
<Grid container spacing={2} width="32rem" padding='.25rem'>
<GridItem label="Incoterm" value="EXW" />
<GridItem label="Tracking Number" value="1Z9829WDE02128" />
<GridItem label="Origin" value={delivery?.origin?.bpns ?? ''} />
<GridItem label="Destination" value={delivery?.destination?.bpns ?? ''} />
<GridItem label="ETD" value={delivery?.etd ?? ''} />
<GridItem label="ETA" value={new Date().toLocaleDateString('en-US', { weekday: 'long', day: '2-digit', month: '2-digit', year: 'numeric' })} />
<GridItem label="Delivery Quantity" value={`${delivery?.quantity} pieces`} />
</Grid>
<Button variant="contained" sx={{ marginTop: '2rem' }} onClick={onClose}>
Close
</Button>
</Stack>
</Dialog>
);
};
Loading

0 comments on commit 503a440

Please sign in to comment.