From 7a3980f1af4d4e8153ed984c7c8bd51273000576 Mon Sep 17 00:00:00 2001 From: ReneSchroederLJ Date: Tue, 19 Mar 2024 08:08:28 +0100 Subject: [PATCH 1/2] feat(supplier-dashboard): new dashboard layout --- .../src/components/TableWithRowHeader.tsx | 46 ++++ .../dashboard/components/Dashboard.tsx | 118 ++++++++ .../dashboard/components/DashboardFilters.tsx | 91 +++++++ .../components/DeliveryInformationModal.tsx | 63 +++++ .../dashboard/components/DemandTable.tsx | 98 +++++++ .../dashboard/components/ProductionTable.tsx | 92 +++++++ .../features/dashboard/util/table-helpers.tsx | 42 +++ frontend/src/index.css | 41 ++- frontend/src/models/types/data/delivery.ts | 34 +++ frontend/src/views/SupplierDashboardView.tsx | 255 ++---------------- 10 files changed, 627 insertions(+), 253 deletions(-) create mode 100644 frontend/src/components/TableWithRowHeader.tsx create mode 100644 frontend/src/features/dashboard/components/Dashboard.tsx create mode 100644 frontend/src/features/dashboard/components/DashboardFilters.tsx create mode 100644 frontend/src/features/dashboard/components/DeliveryInformationModal.tsx create mode 100644 frontend/src/features/dashboard/components/DemandTable.tsx create mode 100644 frontend/src/features/dashboard/components/ProductionTable.tsx create mode 100644 frontend/src/features/dashboard/util/table-helpers.tsx create mode 100644 frontend/src/models/types/data/delivery.ts diff --git a/frontend/src/components/TableWithRowHeader.tsx b/frontend/src/components/TableWithRowHeader.tsx new file mode 100644 index 00000000..e18e2c58 --- /dev/null +++ b/frontend/src/components/TableWithRowHeader.tsx @@ -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 ( + +
+ + +
+ + + + ); +}; diff --git a/frontend/src/features/dashboard/components/Dashboard.tsx b/frontend/src/features/dashboard/components/Dashboard.tsx new file mode 100644 index 00000000..9c6417de --- /dev/null +++ b/frontend/src/features/dashboard/components/Dashboard.tsx @@ -0,0 +1,118 @@ +/* +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'; + +const NUMBER_OF_DAYS = 42; + +export const Dashboard = ({ type }: { type: 'customer' | 'supplier' }) => { + const [selectedMaterial, setSelectedMaterial] = useState(null); + const [selectedSite, setSelectedSite] = useState(null); + const [selectedPartnerSites, setSelectedPartnerSites] = useState(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(null); + const openDeliveryDialog = (d: Delivery) => { + setDelivery(d); + setOpen(true); + }; + const handleMaterialSelect = (material: MaterialDescriptor | null) => { + setSelectedMaterial(material); + setSelectedSite(null); + setSelectedPartnerSites(null); + }; + return ( + <> + + + + Our Stock Information {selectedMaterial && selectedSite && <>for {selectedMaterial.description}} + + {selectedSite ? ( + type === 'customer' ? ( + + ) : ( + + ) + ) : ( + Select a Site to show production data + )} + {selectedSite && ( + <> + + {`${capitalize(type)} Stocks ${selectedMaterial ? `for ${selectedMaterial?.description}` : ''}`} + + {selectedPartnerSites ? ( + selectedPartnerSites.map((ps) => + type === 'customer' ? ( + + ) : ( + + ) + ) + ) : ( + {`Select a ${type} site to show their stock information`} + )} + + )} + + setOpen(false)} delivery={delivery} /> + + ); +}; diff --git a/frontend/src/features/dashboard/components/DashboardFilters.tsx b/frontend/src/features/dashboard/components/DashboardFilters.tsx new file mode 100644 index 00000000..b0a7dfbf --- /dev/null +++ b/frontend/src/features/dashboard/components/DashboardFilters.tsx @@ -0,0 +1,91 @@ +/* +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'; + +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 ( + + + option.ownMaterialNumber} + renderInput={(params) => } + onChange={(_, newValue) => onMaterialChange(newValue || null)} + /> + + + `${option.name} (${option.bpns})`} + disabled={!material} + renderInput={(params) => } + onChange={(_, newValue) => onSiteChange(newValue || null)} + /> + + + [...acc, ...p.sites], []) ?? []} + disabled={!site} + getOptionLabel={(option) => `${option.name} (${option.bpns})`} + isOptionEqualToValue={(option, value) => option.bpns === value.bpns} + renderInput={(params) => ( + + )} + onChange={(_, newValue) => onPartnerSitesChange(newValue?.length > 0 ? newValue : null)} + multiple={true} + /> + + + ); +}; diff --git a/frontend/src/features/dashboard/components/DeliveryInformationModal.tsx b/frontend/src/features/dashboard/components/DeliveryInformationModal.tsx new file mode 100644 index 00000000..6f522b67 --- /dev/null +++ b/frontend/src/features/dashboard/components/DeliveryInformationModal.tsx @@ -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 }) => ( + + + + {label}: + + + {value} + + + +); + +type DeliveryInformationModalProps = { + open: boolean; + onClose: () => void; + delivery: Delivery | null; +}; + +export const DeliveryInformationModal = ({ open, onClose, delivery }: DeliveryInformationModalProps) => { + return ( + + Delivery Information + + + + + + + + + + + + + + ); +}; diff --git a/frontend/src/features/dashboard/components/DemandTable.tsx b/frontend/src/features/dashboard/components/DemandTable.tsx new file mode 100644 index 00000000..c9092f67 --- /dev/null +++ b/frontend/src/features/dashboard/components/DemandTable.tsx @@ -0,0 +1,98 @@ +/* +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 { TableWithRowHeader } from '@components/TableWithRowHeader'; +import { Stock } from '@models/types/data/stock'; +import { Site } from '@models/types/edc/site'; +import { createDateColumnHeaders } from '../util/table-helpers'; +import { Box, Typography } from '@mui/material'; +import { Delivery } from '@models/types/data/delivery'; + +const createDemandRows = (numberOfDays: number, stocks: Stock[], site: Site) => { + const demands = { ...Object.keys(Array.from({ length: numberOfDays })).reduce((acc, _, index) => ({ ...acc, [index]: 30 }), {}) }; + const deliveries = { + ...Object.keys(Array.from({ length: numberOfDays })).reduce((acc, _, index) => ({ ...acc, [index]: index % 3 === 0 ? 45 : 0 }), {}), + }; + const currentStock = stocks.find((s) => s.stockLocationBpns === site.bpns)?.quantity ?? 0; + const itemStock = { + ...Object.keys(Array.from({ length: numberOfDays })).reduce( + (acc, _, index) => ({ + ...acc, + [index]: + (index === 0 ? currentStock : acc[(index - 1) as keyof typeof acc]) + + deliveries[index as keyof typeof deliveries] - + demands[index as keyof typeof demands], + }), + {} + ), + }; + const daysOfSupply = { + ...Object.keys(Array.from({ length: numberOfDays })).reduce( + (acc, _, index) => ({ + ...acc, + [index]: Math.max(itemStock[index as keyof typeof itemStock] / demands[index as keyof typeof demands], 0), + }), + {} + ), + }; + return [ + { id: 'demand', name: 'Demand', ...demands }, + { id: 'itemStock', name: 'Item Stock', ...itemStock }, + { id: 'daysOfSupply', name: 'Days of Supply', ...daysOfSupply }, + { id: 'delivery', name: 'Delivery', ...deliveries }, + ]; +}; + +type DemandTableProps = { numberOfDays: number; stocks: Stock[] | null; site: Site; onDeliveryClick: (delivery: Delivery) => void }; + +export const DemandTable = ({ numberOfDays, stocks, site, onDeliveryClick }: DemandTableProps) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const handleDeliveryClick = (cellData: any) => { + if (cellData.id !== 'delivery') return; + if (cellData.value === 0) return; + onDeliveryClick({ + quantity: cellData.value, + etd: cellData.colDef.headerName, + origin: { + bpns: site?.bpns, + }, + destination: { + bpns: site?.bpns, + }, + }); + }; + return ( + <> + + Site: + {site.name} ({site.bpns}) + + row.id} + hideFooter={true} + /> + + ); +}; diff --git a/frontend/src/features/dashboard/components/ProductionTable.tsx b/frontend/src/features/dashboard/components/ProductionTable.tsx new file mode 100644 index 00000000..99b6a7ad --- /dev/null +++ b/frontend/src/features/dashboard/components/ProductionTable.tsx @@ -0,0 +1,92 @@ +/* +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 { TableWithRowHeader } from '@components/TableWithRowHeader'; +import { Stock } from '@models/types/data/stock'; +import { Site } from '@models/types/edc/site'; +import { createDateColumnHeaders } from '../util/table-helpers'; +import { Box, Typography } from '@mui/material'; +import { Delivery } from '@models/types/data/delivery'; + +const createProductionRows = (numberOfDays: number, stocks: Stock[], site: Site) => { + const shipments = { + ...Object.keys(Array.from({ length: numberOfDays })).reduce((acc, _, index) => ({ ...acc, [index]: index % 3 === 1 ? 90 : 0 }), {}), + }; + const production = { ...Object.keys(Array.from({ length: numberOfDays })).reduce((acc, _, index) => ({ ...acc, [index]: 30 }), {}) }; + const stockQuantity = stocks.filter((stock) => stock.stockLocationBpns === site.bpns).reduce((acc, stock) => acc + stock.quantity, 0); + const allocatedStocks = { + ...Object.keys(Array.from({ length: numberOfDays })).reduce( + (acc, _, index) => ({ + ...acc, + [index]: + index === 0 + ? stockQuantity + : acc[(index - 1) as keyof typeof acc] - + shipments[index as keyof typeof shipments] + + production[(index - 1) as keyof typeof production], + }), + {} + ), + }; + return [ + { id: 'shipment', name: 'Shipments', ...shipments }, + { id: 'itemStock', name: 'Item Stock', ...allocatedStocks }, + { id: 'plannedProduction', name: 'Planned Production', ...production }, + ]; +}; + +type ProductionTableProps = { numberOfDays: number; stocks: Stock[] | null; site: Site, onDeliveryClick: (delivery: Delivery) => void }; + +export const ProductionTable = ({ numberOfDays, stocks, site, onDeliveryClick }: ProductionTableProps) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const handleDeliveryClick = (cellData: any) => { + if (cellData.id !== 'shipment') return; + if (cellData.value === 0) return; + onDeliveryClick({ + quantity: cellData.value, + etd: cellData.colDef.headerName, + origin: { + bpns: site?.bpns, + }, + destination: { + bpns: site?.bpns, + }, + }); + }; + return ( + <> + + + Site:{' '} + {' '} + {site.name} ({site.bpns}) + + row.id} + hideFooter={true} + /> + + ); +}; diff --git a/frontend/src/features/dashboard/util/table-helpers.tsx b/frontend/src/features/dashboard/util/table-helpers.tsx new file mode 100644 index 00000000..292ab876 --- /dev/null +++ b/frontend/src/features/dashboard/util/table-helpers.tsx @@ -0,0 +1,42 @@ +/* +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 { Info } from '@mui/icons-material'; +import { Box, Button } from '@mui/material'; + +export const createDateColumnHeaders = (numberOfDays: number) => { + return Object.keys(Array.from({ length: numberOfDays })).map((_, index) => { + const date = new Date(); + date.setDate(date.getDate() + index); + return { + field: `${index}`, + headerName: date.toLocaleDateString('en-US', { weekday: 'long', day: '2-digit', month: '2-digit', year: 'numeric' }), + width: 180, + renderCell: (data: { value?: number } & { row: {id: number | string }}) => { + return ( + + {(data.row.id === 'delivery' || data.row.id === 'shipment') && data.value !== 0 ? : data.value} + + ); + }, + type: 'number', + }; + }); +}; diff --git a/frontend/src/index.css b/frontend/src/index.css index 668e5af4..bb6f0c82 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -19,27 +19,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -/* - * Copyright (c) 2022,2024 Volkswagen AG - * Copyright (c) 2022,2024 Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. (represented by Fraunhofer ISST) - * Copyright (c) 2022,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 - */ - @tailwind base; @tailwind components; @tailwind utilities; @@ -48,7 +27,7 @@ body { @apply text-gray-700; } -[role="tabpanel"] { +[role='tabpanel'] { @apply w-full; } @@ -56,3 +35,21 @@ body { display: none; } +.table-container { + display: flex; + width: 100%; + border: 1px solid #e0e0e0; +} + +.table-container .MuiDataGrid-root > .MuiBox-root { + display: none; +} + +.table-container .MuiDataGrid-root { + border: none !important; + border-radius: 0; +} + +.table-container .MuiDataGrid-columnHeader button { + display: none; +} diff --git a/frontend/src/models/types/data/delivery.ts b/frontend/src/models/types/data/delivery.ts new file mode 100644 index 00000000..8b83aa70 --- /dev/null +++ b/frontend/src/models/types/data/delivery.ts @@ -0,0 +1,34 @@ +/* +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 { BPNA, BPNS } from '../edc/bpn'; + +export type Delivery = { + quantity?: number; + etd: string; + origin: { + bpns: BPNS; + bpna?: BPNA; + }, + destination: { + bpns: BPNS; + bpna?: BPNA; + }, +} diff --git a/frontend/src/views/SupplierDashboardView.tsx b/frontend/src/views/SupplierDashboardView.tsx index 5ab49cf3..eb046d6a 100644 --- a/frontend/src/views/SupplierDashboardView.tsx +++ b/frontend/src/views/SupplierDashboardView.tsx @@ -1,7 +1,7 @@ /* -Copyright (c) 2023-2024 Volkswagen AG -Copyright (c) 2023-2024 Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. (represented by Fraunhofer ISST) -Copyright (c) 2023-2024 Contributors to the Eclipse Foundation +Copyright (c) 2023,2024 Volkswagen AG +Copyright (c) 2023,2024 Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. (represented by Fraunhofer ISST) +Copyright (c) 2023,2024 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. @@ -19,237 +19,30 @@ under the License. SPDX-License-Identifier: Apache-2.0 */ -import { Input, Table } from '@catena-x/portal-shared-components'; -import { useState } from 'react'; +import { Tab, TabPanel, Tabs } from '@catena-x/portal-shared-components'; import { ConfidentialBanner } from '@components/ConfidentialBanner'; -import Autocomplete from '@mui/material/Autocomplete'; - -type Customer = { - name: string; - materials: Material[]; -}; - -type Material = { - name: string; - demandActual: number[]; - demandAdditional: number[]; - production: number[]; -}; - -const mockCustomerDemands: Customer[] = [ - { - name: 'Customer 1', - materials: [ - { - name: 'Central Control Unit', - demandActual: [ - 398, 23, 183, 53, 341, 492, 282, 80, 48, 199, 417, 223, 242, 263, 262, 185, 313, 78, 209, 405, 7, 134, 362, 196, 247, - 248, 336, 302, - ], - demandAdditional: [], - production: [ - 398, 23, 183, 53, 341, 492, 282, 80, 48, 199, 417, 223, 242, 263, 262, 185, 313, 78, 209, 405, 7, 134, 362, 196, 247, - 248, 336, 302, - ], - }, - { - name: 'Steering Wheel', - demandActual: [ - 342, 294, 48, 32, 243, 180, 113, 395, 5, 477, 223, 31, 193, 418, 472, 338, 45, 219, 149, 324, 92, 28, 129, 481, 235, - 348, 132, 259, - ], - demandAdditional: [], - production: [ - 342, 294, 48, 32, 243, 180, 113, 395, 5, 477, 223, 31, 193, 418, 472, 338, 45, 219, 149, 324, 92, 28, 129, 481, 235, - 348, 132, 259, - ], - }, - { - name: 'Wheel', - demandActual: [ - 311, 152, 173, 496, 418, 17, 79, 267, 22, 426, 103, 396, 469, 362, 299, 112, 105, 180, 141, 1, 133, 9, 476, 93, 118, - 373, 394, 376, - ], - demandAdditional: [], - production: [ - 311, 152, 173, 496, 418, 17, 79, 267, 22, 426, 103, 396, 469, 362, 299, 112, 105, 180, 141, 1, 133, 9, 476, 93, 118, - 373, 394, 376, - ], - }, - ], - }, - { - name: 'Customer 2', - materials: [ - { - name: 'Central Control Unit', - demandActual: [ - 399, 238, 16, 187, 317, 496, 134, 189, 264, 15, 357, 203, 322, 388, 1, 65, 423, 441, 119, 28, 417, 460, 218, 129, 217, - 5, 63, 198, - ], - demandAdditional: [ - 100, 50, 75, 0, 150, 500, 0, 20, 150, 0, 175, 200, 0, 40, 100, 50, 75, 0, 150, 500, 0, 20, 150, 0, 175, 200, 0, 40, - ], - production: [ - 499, 288, 66, 187, 467, 996, 134, 209, 314, 15, 532, 403, 322, 248, 101, 115, 498, 441, 269, 528, 417, 460, 218, 129, - 392, 205, 63, 198, - ], - }, - { - name: 'Steering Wheel', - demandActual: [ - 299, 252, 313, 63, 497, 35, 351, 426, 419, 86, 127, 374, 6, 66, 120, 82, 89, 286, 162, 327, 454, 500, 98, 10, 140, 415, - 368, 178, - ], - demandAdditional: [ - 100, 0, 50, 200, 150, 0, 75, 100, 0, 50, 200, 150, 0, 75, 100, 0, 50, 200, 150, 0, 75, 100, 0, 50, 200, 150, 0, 75, - ], - production: [ - 399, 252, 313, 263, 647, 35, 426, 526, 419, 136, 327, 374, 6, 141, 220, 82, 139, 486, 312, 327, 454, 600, 98, 60, 340, - 565, 368, 253, - ], - }, - { - name: 'Seats', - demandActual: [ - 100, 200, 834, 325, 989, 442, 121, 609, 964, 789, 331, 923, 22, 315, 947, 956, 732, 422, 878, 425, 562, 737, 370, 904, - 727, 706, 823, 459, - ], - demandAdditional: [ - 22, 300, 0, 200, 50, 350, 150, 100, 300, 400, 200, 50, 350, 150, 100, 300, 400, 200, 50, 350, 150, 100, 300, 400, 200, - 50, 350, 150, - ], - production: [ - 122, 500, 940, 237, 977, 626, 915, 196, 749, 382, 48, 982, 95, 14, 831, 23, 542, 142, 10, 664, 333, 731, 611, 797, 366, - 485, 732, 357, - ], - }, - ], - }, -]; - -const dateColumns = [ - { - field: 'name', - headerName: '', - width: 180, - }, - ...[ - 'Tue, 01.08.2023', - 'Wed, 02.08.2023', - 'Thu, 03.08.2023', - 'Fr, 04.08.2023', - 'Sa, 05.08.2023', - 'Su, 06.08.2023', - 'Mo, 07.08.2023', - 'Tue, 08.08.2023', - 'Wed, 09.08.202', - 'Thu, 10.08.2023', - 'Fr, 11.08.2023', - 'Sa, 12.08.2023', - 'Su, 13.08.2023', - 'Mo, 14.08.2023', - 'Tue, 15.08.2023', - 'Wed, 16.08.2023', - 'Thu, 17.08.2023', - 'Fr, 18.08.2023', - 'Sa, 19.08.2023', - 'So, 20.08.2023', - 'Mo, 21.08.2023', - 'Tue, 22.08.2023', - 'Wed, 23.08.2023', - 'Thu, 24.08.2023', - 'Fr, 25.08.2023', - 'Sa, 26.08.2023', - 'So, 27.08.2023', - 'Mo, 28.08.2023', - ].map((date, index) => ({ - field: index.toString(), - headerName: date, - renderCell: (data: { row: { name: string; material?: Material } & { [key: string]: number } }) => { - const isInsufficientProduction = - data.row.id === 4 && - data.row.material && - data.row[index] < data.row.material.demandActual[index] + (data.row.material.demandAdditional[index] ?? 0); - return ( - - {data.row[index]} - - ); - }, - })), -]; - -const createTableRows = (material: Material) => { - return [ - { - id: 1, - name: 'Demand (Actual)', - ...material.demandActual.reduce((acc, value, index) => ({ ...acc, [index]: value }), {}), - }, - { - id: 2, - name: 'Demand (Additional)', - ...material.demandAdditional.reduce((acc, value, index) => ({ ...acc, [index]: value }), {}), - }, - { - id: 3, - name: 'Demand (Total)', - ...material.demandActual.reduce( - (acc, value, index) => ({ ...acc, [index]: value + (material.demandAdditional[index] ?? 0) }), - {} - ), - }, - { - id: 4, - material, - name: 'Your Production', - ...material.production.reduce((acc, value, index) => ({ ...acc, [index]: value }), {}), - }, - ]; -}; +import { Dashboard } from '@features/dashboard/components/Dashboard'; +import { Box, Stack, Typography } from '@mui/material'; +import { useState } from 'react'; export const SupplierDashboardView = () => { - const [selectedCustomer, setSelectedCustomer] = useState(null); - const [selectedMaterial, setSelectedMaterial] = useState(null); - const handleCustomerSelect = (customer: Customer | null) => { - setSelectedCustomer(customer); - setSelectedMaterial(null); - }; + const [selectedTab, setSelectedTab] = useState(0); return ( -
+ -

Supplier Dashboard

-
- option.name} - renderInput={(params) => } - onChange={(_, newValue) => handleCustomerSelect(newValue)} - /> - option.name} - renderInput={(params) => } - onChange={(_, newValue) => setSelectedMaterial(newValue)} - /> -
-
-
row.id} - hideFooter={true} - /> - - + Dashboard + setSelectedTab(value)}> + + + + + + + + + + + + ); -} +}; From ecd6c720bf4da2ead083160a286b3f44138ede2f Mon Sep 17 00:00:00 2001 From: ReneSchroederLJ Date: Wed, 20 Mar 2024 08:05:39 +0100 Subject: [PATCH 2/2] fix: corrected the handling of views for supplier and customer --- frontend/src/features/dashboard/components/Dashboard.tsx | 9 +++++---- .../features/dashboard/components/DashboardFilters.tsx | 9 +++++++-- .../src/features/dashboard/components/DemandTable.tsx | 4 ++-- .../features/dashboard/components/ProductionTable.tsx | 2 +- .../dashboard/util/{table-helpers.tsx => helpers.tsx} | 3 +++ 5 files changed, 18 insertions(+), 9 deletions(-) rename frontend/src/features/dashboard/util/{table-helpers.tsx => helpers.tsx} (94%) diff --git a/frontend/src/features/dashboard/components/Dashboard.tsx b/frontend/src/features/dashboard/components/Dashboard.tsx index 9c6417de..9967634b 100644 --- a/frontend/src/features/dashboard/components/Dashboard.tsx +++ b/frontend/src/features/dashboard/components/Dashboard.tsx @@ -29,6 +29,7 @@ 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; @@ -65,7 +66,7 @@ export const Dashboard = ({ type }: { type: 'customer' | 'supplier' }) => { Our Stock Information {selectedMaterial && selectedSite && <>for {selectedMaterial.description}} {selectedSite ? ( - type === 'customer' ? ( + type === 'supplier' ? ( { {selectedSite && ( <> - {`${capitalize(type)} Stocks ${selectedMaterial ? `for ${selectedMaterial?.description}` : ''}`} + {`${capitalize(getPartnerType(type))} Stocks ${selectedMaterial ? `for ${selectedMaterial?.description}` : ''}`} {selectedPartnerSites ? ( selectedPartnerSites.map((ps) => - type === 'customer' ? ( + type === 'supplier' ? ( { ) ) ) : ( - {`Select a ${type} site to show their stock information`} + {`Select a ${getPartnerType(type)} site to show their stock information`} )} )} diff --git a/frontend/src/features/dashboard/components/DashboardFilters.tsx b/frontend/src/features/dashboard/components/DashboardFilters.tsx index b0a7dfbf..d639694b 100644 --- a/frontend/src/features/dashboard/components/DashboardFilters.tsx +++ b/frontend/src/features/dashboard/components/DashboardFilters.tsx @@ -25,6 +25,7 @@ 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'; @@ -73,14 +74,18 @@ export const DashboardFilters = ({ [...acc, ...p.sites], []) ?? []} disabled={!site} getOptionLabel={(option) => `${option.name} (${option.bpns})`} isOptionEqualToValue={(option, value) => option.bpns === value.bpns} renderInput={(params) => ( - + )} onChange={(_, newValue) => onPartnerSitesChange(newValue?.length > 0 ? newValue : null)} multiple={true} diff --git a/frontend/src/features/dashboard/components/DemandTable.tsx b/frontend/src/features/dashboard/components/DemandTable.tsx index c9092f67..ee75e35f 100644 --- a/frontend/src/features/dashboard/components/DemandTable.tsx +++ b/frontend/src/features/dashboard/components/DemandTable.tsx @@ -21,7 +21,7 @@ SPDX-License-Identifier: Apache-2.0 import { TableWithRowHeader } from '@components/TableWithRowHeader'; import { Stock } from '@models/types/data/stock'; import { Site } from '@models/types/edc/site'; -import { createDateColumnHeaders } from '../util/table-helpers'; +import { createDateColumnHeaders } from '../util/helpers'; import { Box, Typography } from '@mui/material'; import { Delivery } from '@models/types/data/delivery'; @@ -47,7 +47,7 @@ const createDemandRows = (numberOfDays: number, stocks: Stock[], site: Site) => ...Object.keys(Array.from({ length: numberOfDays })).reduce( (acc, _, index) => ({ ...acc, - [index]: Math.max(itemStock[index as keyof typeof itemStock] / demands[index as keyof typeof demands], 0), + [index]: Math.max(itemStock[index as keyof typeof itemStock] / demands[index as keyof typeof demands], 0).toFixed(2), }), {} ), diff --git a/frontend/src/features/dashboard/components/ProductionTable.tsx b/frontend/src/features/dashboard/components/ProductionTable.tsx index 99b6a7ad..f9b6b4e6 100644 --- a/frontend/src/features/dashboard/components/ProductionTable.tsx +++ b/frontend/src/features/dashboard/components/ProductionTable.tsx @@ -21,7 +21,7 @@ SPDX-License-Identifier: Apache-2.0 import { TableWithRowHeader } from '@components/TableWithRowHeader'; import { Stock } from '@models/types/data/stock'; import { Site } from '@models/types/edc/site'; -import { createDateColumnHeaders } from '../util/table-helpers'; +import { createDateColumnHeaders } from '../util/helpers'; import { Box, Typography } from '@mui/material'; import { Delivery } from '@models/types/data/delivery'; diff --git a/frontend/src/features/dashboard/util/table-helpers.tsx b/frontend/src/features/dashboard/util/helpers.tsx similarity index 94% rename from frontend/src/features/dashboard/util/table-helpers.tsx rename to frontend/src/features/dashboard/util/helpers.tsx index 292ab876..3b45fa14 100644 --- a/frontend/src/features/dashboard/util/table-helpers.tsx +++ b/frontend/src/features/dashboard/util/helpers.tsx @@ -40,3 +40,6 @@ export const createDateColumnHeaders = (numberOfDays: number) => { }; }); }; + +export const getPartnerType = (type: 'customer' | 'supplier') => (type === 'customer' ? 'supplier' : 'customer'); +