diff --git a/ichub-frontend/src/App.css b/ichub-frontend/src/App.css index 77f367b9..05503bb0 100644 --- a/ichub-frontend/src/App.css +++ b/ichub-frontend/src/App.css @@ -31,170 +31,98 @@ --brand-text: #f8f9fa; --brand-secondary-text: #d3d3d3; --brand-shadow: #ffffff80; + --brand-border-color: #5fb9ff42; font-family: "Manrope", sans-serif!important; font-optical-sizing: auto; font-style: normal; } -/****** Colors ******/ -.bg-brand-dark { - background-color: var(--brand-dark); -} - /****** General Styling ******/ -/* Padding */ -.p-1 { padding: 4px; } -.p-2 { padding: 8px; } -.p-3 { padding: 12px; } -.p-4 { padding: 16px; } -.p-5 { padding: 20px; } - -/* Margin */ -.m-1 { margin: 4px; } -.m-2 { margin: 8px; } -.m-3 { margin: 12px; } -.m-4 { margin: 16px; } -.m-5 { margin: 20px; } - -/* Padding individual */ -.pt-1 { padding-top: 4px; } -.pr-1 { padding-right: 4px; } -.pb-1 { padding-bottom: 4px; } -.pl-1 { padding-left: 4px; } - -.pt-2 { padding-top: 8px; } -.pr-2 { padding-right: 8px; } -.pb-2 { padding-bottom: 8px; } -.pl-2 { padding-left: 8px; } - -.pt-3 { padding-top: 12px; } -.pr-3 { padding-right: 12px; } -.pb-3 { padding-bottom: 12px; } -.pl-3 { padding-left: 12px; } - -.pt-4 { padding-top: 16px; } -.pr-4 { padding-right: 16px; } -.pb-4 { padding-bottom: 16px; } -.pl-4 { padding-left: 16px; } - -.pt-5 { padding-top: 20px; } -.pr-5 { padding-right: 20px; } -.pb-5 { padding-bottom: 20px; } -.pl-5 { padding-left: 20px; } - /* Margin individual */ -.mt-1 { margin-top: 4px; } .mr-1 { margin-right: 4px; } .mb-1 { margin-bottom: 4px; } -.ml-1 { margin-left: 4px; } - -.mt-2 { margin-top: 8px; } -.mr-2 { margin-right: 8px; } .mb-2 { margin-bottom: 8px; } -.ml-2 { margin-left: 8px; } - .mt-3 { margin-top: 12px; } .mr-3 { margin-right: 12px; } -.mb-3 { margin-bottom: 12px; } .ml-3 { margin-left: 12px; } - .mt-4 { margin-top: 16px; } -.mr-4 { margin-right: 16px; } -.mb-4 { margin-bottom: 16px; } -.ml-4 { margin-left: 16px; } - -.mt-5 { margin-top: 20px; } -.mr-5 { margin-right: 20px; } .mb-5 { margin-bottom: 20px; } -.ml-5 { margin-left: 20px; } +.my-4 { margin-top: 16px; margin-bottom: 16px; } /* Margin auto */ -.mt-auto { margin-top: auto; } -.mr-auto { margin-right: auto; } -.mb-auto { margin-bottom: auto; } -.ml-auto { margin-left: auto; } .my-auto { margin-bottom: auto; margin-top: auto; } .mx-auto { margin-left: auto; margin-right: auto; } -.m-auto { margin: auto; } - -/* Padding horizontal & vertical */ -.px-1 { padding-left: 4px; padding-right: 4px; } -.px-2 { padding-left: 8px; padding-right: 8px; } -.px-3 { padding-left: 12px; padding-right: 12px; } -.px-4 { padding-left: 16px; padding-right: 16px; } -.px-5 { padding-left: 20px; padding-right: 20px; } - -.py-1 { padding-top: 4px; padding-bottom: 4px; } -.py-2 { padding-top: 8px; padding-bottom: 8px; } -.py-3 { padding-top: 12px; padding-bottom: 12px; } -.py-4 { padding-top: 16px; padding-bottom: 16px; } -.py-5 { padding-top: 20px; padding-bottom: 20px; } - -/* Margin horizontal & vertical */ -.mx-1 { margin-left: 4px; margin-right: 4px; } -.mx-2 { margin-left: 8px; margin-right: 8px; } -.mx-3 { margin-left: 12px; margin-right: 12px; } -.mx-4 { margin-left: 16px; margin-right: 16px; } -.mx-5 { margin-left: 20px; margin-right: 20px; } - -.my-1 { margin-top: 4px; margin-bottom: 4px; } -.my-2 { margin-top: 8px; margin-bottom: 8px; } -.my-3 { margin-top: 12px; margin-bottom: 12px; } -.my-4 { margin-top: 16px; margin-bottom: 16px; } -.my-5 { margin-top: 20px; margin-bottom: 20px; } - - /* Flex */ .flex { display: flex; } .flex-content-center { justify-content: center; } -.flex-content-between { justify-content: space-between; } -.flex-content-around { justify-content: space-around; } -.flex-content-evenly { justify-content: space-evenly; } -.flex-items-center { align-items: center; } -.flex-items-start { align-items: flex-start; } -.flex-items-end { align-items: flex-end; } -.flex-column { flex-direction: column; } -.flex-row { flex-direction: row; } - -.flex-gap-1 { gap: 4px; } -.flex-gap-2 { gap: 8px; } -.flex-gap-3 { gap: 12px; } -.flex-gap-4 { gap: 16px; } -.flex-gap-5 { gap: 20px; } .text-center { text-align: center; } -.text-left { text-align: left; } -.text-right { text-align: right; } -/* Wrapper for sidebar and content area */ -.pageWrapper { +/* Sidebar styling */ +.sidebarContainer { display: flex; - min-height: 100vh; + flex-direction: row; + background: linear-gradient(180deg, rgba(1,32,96,0.8323704481792717) 0%, rgba(5,107,153,0.5690651260504201) 100%); + box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); + height: 100%; } -/* Sidebar styling */ -.sidebarArea { - width: 250px; +.iconBar { + width: 60px; + display: flex; + flex-direction: column; + align-items: center; + padding: 10px 0; + background: linear-gradient(180deg, rgba(0, 8, 24, 0.832) 0%, rgba(5, 60, 85, 0.569) 100%); +} + +.iconButton { + background: none; + border: none; + color: white; + font-size: 1.5rem; + margin: 10px 0; + cursor: pointer; + opacity: 0.6; + transition: opacity 0.3s; +} + +.iconButton.active, +.iconButton:hover { + opacity: 1; +} + +.sidebarContent { + flex-grow: 1; padding: 20px; - background: linear-gradient(180deg, rgba(1,32,96,0.8323704481792717) 0%, rgba(5,107,153,0.5690651260504201) 100%); - box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); } +.sidebarContent h2 { + margin-bottom: 10px; + font-size: 1.2rem; + color: var(--brand-text); +} -.sidebarArea a, .sidebarArea svg, .sidebarArea span { - color: #fff!important; +.sidebarContent ul { + list-style: none; + padding: 0; } +.sidebarContent li { + padding: 5px 0; + font-size: 1rem; + color: #666; +} -.sidebarArea .cx-menu a:hover { +.sidebarContent li a { + text-decoration: none; + color: #fff!important; +} +.sidebarContent li a:hover { color: var(--brand-dark); font-weight: bold; } -.sidebarArea .cx-menu .cx-menu a { - color: var(--brand-secondary-text)!important; - word-wrap: break-word; -} .cx-card__decision--button{ display: flex; @@ -212,9 +140,6 @@ padding: auto; } -.sidebarArea{ - min-width: 250px; -} .side-logo { height: 150px; margin-top: 20px; @@ -227,24 +152,9 @@ /* Content area styling */ .contentArea { - flex-grow: 1; padding: 20px; - height: 100 -} -/* Mobile responsiveness: stack layout vertically */ -@media (max-width: 992px) { - .pageWrapper { - flex-direction: column; - } - - .sidebarArea { - width: 100%; - box-shadow: none; - } } - - .productWrapper{ width: 100%; height: 100%; @@ -264,29 +174,20 @@ .cx-card__decision--item { border-radius: 10px!important; } -.product-table-wrapper { +.product-table-wrapper > div.MuiPaper-root { background: black!important; box-shadow: 0 0 10px rgb(1,32,96); border-radius: 8px!important; - border: 2px solid #5fb9ff42!important; + border: 2px solid var(--brand-border-color)!important; } -.cx-table .MuiBox-root{ - background: black!important; +.product-table-wrapper * { color: white!important; } -.cx-table .MuiDataGrid-main .MuiDataGrid-columnHeaders{ - background: black!important; - color: white!important; +.product-table-wrapper tbody td, .product-table-wrapper tbody th { + font-size: 14px; } -.product-table{ - color: white!important; - background: black!important; - border-radius: 0!important; - box-shadow: 0 0 10px rgb(1,32,96); - border: 2px solid #5fb9ff42!important; -} -.cx-table .cx-typography{ - color: white!important; +.product-table-wrapper tr.Mui-selected{ + background-color: var(--brand-border-color)!important; } .product-cards .product-card:hover { @@ -296,7 +197,7 @@ background: black !important; border-radius: 8px!important; box-shadow: 0 0 12px rgb(1, 40, 119); - border: 1px solid #5fb9ff42!important; + border: 1px solid var(--brand-border-color)!important; padding: 20px; } @@ -304,7 +205,7 @@ background: black !important; border-radius: 8px!important; box-shadow: 0 0 12px rgb(1, 40, 119); - border: 1px solid #5fb9ff42!important; + border: 1px solid var(--brand-border-color)!important; } .product-cards .MuiChip-icon{ color: black!important; @@ -313,7 +214,7 @@ background: black !important; border-radius: 8px!important; box-shadow: 0 0 10px rgb(1,32,96); - border: 2px solid #5fb9ff42!important; + border: 2px solid var(--brand-border-color)!important; } .MuiChip-icon{ color: black!important; @@ -338,11 +239,25 @@ color: var(--brand-text); border: 1px solid white; } - +.productDetail button.update-button { + background-color: rgba(77, 77, 77, 0.56); + height: 32px; + box-sizing: border-box; + border-radius: 6px; + font-size: 0.8125rem; +} .productDetail .cx-dropdown-menu .dropdown-buttons { background-color: var(--brand-dark); } - +.productDetail .title-subtitle { + margin: 20px 0; +} +.productDetail .product-card .MuiBox-root, .productDetail .product-card .MuiGrid2-root { + margin: 10px 0; +} +.productDetail .add-on-buttons button { + padding: 10px; +} div[role="presentation"] > div.MuiPaper-root.MuiPopover-paper.MuiMenu-paper { background-color: var(--brand-dark); box-shadow: 0 0 10px var(--brand-shadow) @@ -354,6 +269,51 @@ div[role="presentation"] > div.MuiPaper-root.MuiPopover-paper.MuiMenu-paper butt font-weight: 600; } +/* Navigation bar */ +header.ichub-header{ + background: rgb(1,32,96); + background: linear-gradient(206deg, rgba(1, 32, 96, 0.8323704481792717), rgba(0, 165, 255, 0.57))!important; + box-shadow: 0 2px 1px 3px rgba(71, 172, 255, 0.809); + border-bottom: 0; + padding: 0!important; + position: sticky!important; +} + +header.ichub-header button.user-button { + background-color: var(--brand-border-color); + color: var(--brand-text); + border: 1px solid white; + margin-left: 15px; +} +header.ichub-header button.user-button:hover { + color: var(--brand-text); + font-weight: bold; + background-color: var(--brand-dark)!important; + box-shadow: 0 0 15px var(--brand-orange)!important; +} + +/* Dropdown menu borders */ +#primary-search-account-menu > div.MuiPaper-root.MuiPopover-paper.MuiMenu-paper { + background-color: var(--brand-dark); + box-shadow: 0 0 10px var(--brand-shadow) +} + +#primary-search-account-menu > div.MuiPaper-root.MuiPopover-paper.MuiMenu-paper ul * { + color: #fff!important; +} +#primary-search-account-menu > div.MuiPaper-root.MuiPopover-paper.MuiMenu-paper li:hover { + color: var(--brand-text); + font-weight: bold; + background-color: var(--brand-dark); + box-shadow: 0 0 15px var(--brand-orange)!important; +} +#primary-search-account-menu > div.MuiPaper-root.MuiPopover-paper.MuiMenu-paper { + margin-top: 20px; +} +#primary-search-account-menu > div.MuiPaper-root.MuiPopover-paper.MuiMenu-paper hr{ + border-color: rgba(255, 255, 255, 0.486); +} + /* List view cards */ .cx-card__decision--button .cx-card__decision--chip span { color: var(--brand-dark)!important; @@ -371,6 +331,7 @@ div[role="presentation"] > div.MuiPaper-root.MuiPopover-paper.MuiMenu-paper butt max-width: 100%; height: auto; } + .share-dropdown-btn { border-radius: 0px!important; background-color: #000!important; @@ -390,33 +351,6 @@ div[role="presentation"] > div.MuiPaper-root.MuiPopover-paper.MuiMenu-paper butt background: linear-gradient(180deg, rgba(1,32,96,0.8323704481792717) 0%, rgba(5,107,153,0.5690651260504201) 100%); } -/* Navigation bar */ -.cx-main-navigation__wrapper { - background: rgb(1,32,96); - background: linear-gradient(206deg, rgba(1, 32, 96, 0.8323704481792717), rgba(0, 165, 255, 0.57))!important; - box-shadow: 0 2px 1px 3px rgba(71, 172, 255, 0.809); - border-bottom: 0; - padding: 0!important; - position: sticky!important; - } - -.cx-navigation-item a { - color: var(--brand-text); -} - -.cx-main-navigation { - height: 100%; - position: flex; - align-content: center; - justify-content: center; -} -.cx-main-navigation nav.cx-navigation a { - color: var(--brand-text); - font-weight: 900; - font-size: 25px!important; - padding: 0px!important; - cursor: default; -} .submodel-button-content{ padding-right: 5px; font-size: 15px; @@ -429,46 +363,19 @@ div[role="presentation"] > div.MuiPaper-root.MuiPopover-paper.MuiMenu-paper butt .submodel-button{ border-radius: 8px!important; -} - - -.cx-main-navigation__children-bottom button { - background-color: var(--brand-dark); - color: var(--brand-text); - border: 1px solid white; -} -.cx-main-navigation__children-bottom button:hover { - color: var(--brand-text); - font-weight: bold; - background-color: var(--brand-dark); - box-shadow: 0 0 15px var(--brand-orange)!important; -} - -.navbar-user-dropdown .MuiPaper-root { - margin-top: 20px; -} -.navbar-user-dropdown .MuiPaper-root *{ - color: #fff!important; -} -.navbar-user-dropdown .MuiPaper-root hr{ - border-color: rgba(255, 255, 255, 0.486); -} -.navbar-user-dropdown .MuiPaper-root li:hover{ - color: var(--brand-orange)!important; + border-color: #0f71cb!important; } .main-logo-link { height: 100%; width: 100%; } -.cx-main-navigation__children-top{ - width: auto!important; - display: flex; - justify-content: center; - align-self: center; - padding: 0!important; -} .main-logo { - height: 65px; + height: 30px; } +@media (min-width: 626px) { + .main-logo { + height: 50px; + } +} \ No newline at end of file diff --git a/ichub-frontend/src/components/general/Header.tsx b/ichub-frontend/src/components/general/Header.tsx index 0cad1afb..5fd8876e 100644 --- a/ichub-frontend/src/components/general/Header.tsx +++ b/ichub-frontend/src/components/general/Header.tsx @@ -21,83 +21,189 @@ ********************************************************************************/ import { useState } from 'react'; -import { MainNavigation, IconButton } from '@catena-x/portal-shared-components'; -import PersonIcon from '@mui/icons-material/Person'; -import { Menu, MenuItem, Typography, Divider, ListItemIcon } from '@mui/material'; -import AccountCircleIcon from '@mui/icons-material/AccountCircle'; -import SettingsIcon from '@mui/icons-material/Settings'; -import LogoutIcon from '@mui/icons-material/Logout'; +import AppBar from '@mui/material/AppBar'; +import Box from '@mui/material/Box'; +import Toolbar from '@mui/material/Toolbar'; +import IconButton from '@mui/material/IconButton'; +import Badge from '@mui/material/Badge'; +import MenuItem from '@mui/material/MenuItem'; +import Menu from '@mui/material/Menu'; +import AccountCircle from '@mui/icons-material/AccountCircle'; +import MailIcon from '@mui/icons-material/Mail'; +import NotificationsIcon from '@mui/icons-material/Notifications'; +import MoreIcon from '@mui/icons-material/MoreVert'; +import { Divider, ListItemIcon, Typography } from '@mui/material'; +import { Logout, Settings } from '@mui/icons-material'; -const Header = () => { - const [anchorEl, setAnchorEl] = useState(null); - const open = Boolean(anchorEl); +export default function PrimarySearchAppBar() { + const [anchorEl, setAnchorEl] = useState(null); + const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = useState(null); - const handleMenuOpen = (event) => { + const isMenuOpen = Boolean(anchorEl); + const isMobileMenuOpen = Boolean(mobileMoreAnchorEl); + + const handleProfileMenuOpen = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); }; + const handleMobileMenuClose = () => { + setMobileMoreAnchorEl(null); + }; + const handleMenuClose = () => { setAnchorEl(null); + handleMobileMenuClose(); }; - return ( - -
- - Eclipse Tractus-X logo - -
-
- - - - - {/* Encabezado con nombre y email */} - + const handleMobileMenuOpen = (event: React.MouseEvent) => { + setMobileMoreAnchorEl(event.currentTarget); + }; + + const menuId = 'primary-search-account-menu'; + const renderMenu = ( + + Mathias Brunkow Moser - - - CX-Operator - - + + + CX-Operator + + - {/* Opciones del menú */} - - - - - Profile - - - - - - Settings - - - - - - - Logout - - -
-
+ {/* Opciones del menú */} + + + + + Profile + + + + + + Settings + + + + + + + Logout + + ); -}; -export default Header; \ No newline at end of file + const mobileMenuId = 'primary-search-account-menu-mobile'; + const renderMobileMenu = ( + + + + + + + +

Messages

+
+ + + + + + +

Notifications

+
+ + + + +

Profile

+
+
+ ); + + return ( + + + + + Eclipse Tractus-X logo + + +

Industry Core Hub

+
+ + + + + + + + + + + + + + + + + + + + +
+
+ {renderMobileMenu} + {renderMenu} +
+ ); +} \ No newline at end of file diff --git a/ichub-frontend/src/components/general/PageNotification.tsx b/ichub-frontend/src/components/general/PageNotification.tsx new file mode 100644 index 00000000..30019f3f --- /dev/null +++ b/ichub-frontend/src/components/general/PageNotification.tsx @@ -0,0 +1,41 @@ +/******************************************************************************** + * Eclipse Tractus-X - Industry Core Hub Frontend + * + * Copyright (c) 2025 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 govern in permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ + + +import { Grid2 } from '@mui/material'; +import { PageNotifications } from '@catena-x/portal-shared-components'; + +interface PageNotificationProps { + notification: { open: boolean; severity: "success" | "error"; title: string } | null; +} + +const PageNotification = ({ notification }: PageNotificationProps) => { + return ( + notification && ( + + + + ) + ); +}; + +export default PageNotification; diff --git a/ichub-frontend/src/components/general/ProductCard.tsx b/ichub-frontend/src/components/general/ProductCard.tsx index 4dfa00e9..943cb604 100644 --- a/ichub-frontend/src/components/general/ProductCard.tsx +++ b/ichub-frontend/src/components/general/ProductCard.tsx @@ -177,9 +177,9 @@ export const ProductCard = ({ , + Category: , + Shared: , + Status: +}; + const Sidebar = () => { + const [activeIndex, setActiveIndex] = useState(0); + return ( - ({ - title, - href: '#', - children: subitems.map(({ name, link }) => ({ title: name, href: link })) - }))} - /> + + {/* Barra de Iconos */} + + {sidebarElements.map((item, index) => ( + + ))} + + + {/* Contenido del Sidebar */} + +

{sidebarElements[activeIndex].title}

+
    + {sidebarElements[activeIndex].subitems.map((subitem, idx) => ( +
  • + {subitem.name} +
  • + ))} +
+
+
); }; diff --git a/ichub-frontend/src/components/product-detail/InstanceProductsTable.tsx b/ichub-frontend/src/components/product-detail/InstanceProductsTable.tsx new file mode 100644 index 00000000..9aedb95a --- /dev/null +++ b/ichub-frontend/src/components/product-detail/InstanceProductsTable.tsx @@ -0,0 +1,385 @@ +/******************************************************************************** + * Eclipse Tractus-X - Industry Core Hub Frontend + * + * Copyright (c) 2025 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 govern in permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ + +import { useMemo, useState } from 'react'; +import { alpha } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import Table from '@mui/material/Table'; +import TableBody from '@mui/material/TableBody'; +import TableCell from '@mui/material/TableCell'; +import TableContainer from '@mui/material/TableContainer'; +import TableHead from '@mui/material/TableHead'; +import TablePagination from '@mui/material/TablePagination'; +import TableRow from '@mui/material/TableRow'; +import TableSortLabel from '@mui/material/TableSortLabel'; +import Toolbar from '@mui/material/Toolbar'; +import Typography from '@mui/material/Typography'; +import Paper from '@mui/material/Paper'; +import Checkbox from '@mui/material/Checkbox'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import DeleteIcon from '@mui/icons-material/Delete'; +import FilterListIcon from '@mui/icons-material/FilterList'; +import { visuallyHidden } from '@mui/utils'; + +import instanceData from "../../tests/payloads/instance-data.json"; +import { InstanceProduct } from '../../types/instanceProduct'; + +const rows = instanceData; + +function descendingComparator(a: T, b: T, orderBy: keyof T) { + if (b[orderBy] < a[orderBy]) { + return -1; + } + if (b[orderBy] > a[orderBy]) { + return 1; + } + return 0; +} + +type Order = 'asc' | 'desc'; + +function getComparator( + order: Order, + orderBy: Key, +): ( + a: { [key in Key]: number | string }, + b: { [key in Key]: number | string }, +) => number { + return order === 'desc' + ? (a, b) => descendingComparator(a, b, orderBy) + : (a, b) => -descendingComparator(a, b, orderBy); +} + +interface HeadCell { + disablePadding: boolean; + id: keyof InstanceProduct; + label: string; + numeric: boolean; +} + +const headCells: readonly HeadCell[] = [ + { + id: 'uuid', + numeric: false, + disablePadding: true, + label: 'UUID', + }, + { + id: 'partInstanceId', + numeric: false, + disablePadding: false, + label: 'Part Instance ID', + }, + { + id: 'submodels', + numeric: true, + disablePadding: false, + label: 'Submodels', + }, + { + id: 'status', + numeric: false, + disablePadding: false, + label: 'Status', + }, + { + id: 'type', + numeric: false, + disablePadding: false, + label: 'Type', + }, + { + id: 'created', + numeric: false, + disablePadding: false, + label: 'Created at', + }, + { + id: 'updated', + numeric: false, + disablePadding: false, + label: 'Updated at', + }, + { + id: 'manufacturer', + numeric: false, + disablePadding: false, + label: 'Manufacturer', + }, +]; + +interface EnhancedTableProps { + numSelected: number; + onRequestSort: (event: React.MouseEvent, property: keyof InstanceProduct) => void; + onSelectAllClick: (event: React.ChangeEvent) => void; + order: Order; + orderBy: string; + rowCount: number; +} + +function EnhancedTableHead(props: EnhancedTableProps) { + const { onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort } = + props; + const createSortHandler = + (property: keyof InstanceProduct) => (event: React.MouseEvent) => { + onRequestSort(event, property); + }; + + return ( + + + + 0 && numSelected < rowCount} + checked={rowCount > 0 && numSelected === rowCount} + onChange={onSelectAllClick} + inputProps={{ + 'aria-label': 'select all instances', + }} + /> + + {headCells.map((headCell) => ( + + + {headCell.label} + {orderBy === headCell.id ? ( + + {order === 'desc' ? 'sorted descending' : 'sorted ascending'} + + ) : null} + + + ))} + + + ); +} +interface EnhancedTableToolbarProps { + numSelected: number; +} +function EnhancedTableToolbar(props: EnhancedTableToolbarProps) { + const { numSelected } = props; + return ( + 0 && { + bgcolor: '#5fb9ff42', + }, + ]} + > + {numSelected > 0 ? ( + + {numSelected} selected + + ) : ( + + Instance Products(5 of 52.0213 Digital Twins) + + )} + {numSelected > 0 ? ( + + + + + + ) : ( + + + + + + )} + + ); +} +export default function EnhancedTable() { + const [order, setOrder] = useState('asc'); + const [orderBy, setOrderBy] = useState('uuid'); + const [selected, setSelected] = useState([]); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(5); + + const handleRequestSort = ( + event: React.MouseEvent, + property: keyof InstanceProduct, + ) => { + const isAsc = orderBy === property && order === 'asc'; + setOrder(isAsc ? 'desc' : 'asc'); + setOrderBy(property); + }; + + const handleSelectAllClick = (event: React.ChangeEvent) => { + if (event.target.checked) { + const newSelected = rows.map((n) => n.id); + setSelected(newSelected); + return; + } + setSelected([]); + }; + + const handleClick = (event: React.MouseEvent, id: number) => { + const selectedIndex = selected.indexOf(id); + let newSelected: readonly number[] = []; + + if (selectedIndex === -1) { + newSelected = newSelected.concat(selected, id); + } else if (selectedIndex === 0) { + newSelected = newSelected.concat(selected.slice(1)); + } else if (selectedIndex === selected.length - 1) { + newSelected = newSelected.concat(selected.slice(0, -1)); + } else if (selectedIndex > 0) { + newSelected = newSelected.concat( + selected.slice(0, selectedIndex), + selected.slice(selectedIndex + 1), + ); + } + setSelected(newSelected); + }; + + const handleChangePage = (event: unknown, newPage: number) => { + setPage(newPage); + }; + + const handleChangeRowsPerPage = (event: React.ChangeEvent) => { + setRowsPerPage(parseInt(event.target.value, 10)); + setPage(0); + }; + + // Avoid a layout jump when reaching the last page with empty rows. + const emptyRows = + page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0; + + const visibleRows = useMemo( + () => + [...rows] + .sort(getComparator(order, orderBy)) + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage), + [order, orderBy, page, rowsPerPage], + ); + + return ( + + + + + + + + {visibleRows.map((row, index) => { + const isItemSelected = selected.includes(row.id); + const labelId = `enhanced-table-checkbox-${index}`; + + return ( + handleClick(event, row.id)} + role="checkbox" + aria-checked={isItemSelected} + tabIndex={-1} + key={row.id} + selected={isItemSelected} + sx={{ cursor: 'pointer' }} + > + + + + + {row.uuid} + + {row.partInstanceId} + {row.submodels} + {row.status} + {row.type} + {row.created} + {row.updated} + {row.manufacturer} + + ); + })} + {emptyRows > 0 && ( + + + + )} + +
+
+ +
+
+ ); +} diff --git a/ichub-frontend/src/components/product-detail/ProductButton.tsx b/ichub-frontend/src/components/product-detail/ProductButton.tsx new file mode 100644 index 00000000..faa0a59e --- /dev/null +++ b/ichub-frontend/src/components/product-detail/ProductButton.tsx @@ -0,0 +1,43 @@ +/******************************************************************************** + * Eclipse Tractus-X - Industry Core Hub Frontend + * + * Copyright (c) 2025 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 govern in permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ + +import { Grid2, Button } from '@mui/material'; +import { Icon } from '@catena-x/portal-shared-components'; + +interface ProductButtonProps { + gridSize?: { md?: number; xs?: number; lg?: number; sm?: number }; + buttonText: string; + onClick: () => void; +} + +const ProductButton = ({ gridSize, buttonText, onClick }: ProductButtonProps) => { + return ( + + + + ); +}; + +export default ProductButton; diff --git a/ichub-frontend/src/components/product-detail/ProductData.tsx b/ichub-frontend/src/components/product-detail/ProductData.tsx new file mode 100644 index 00000000..a800f4b0 --- /dev/null +++ b/ichub-frontend/src/components/product-detail/ProductData.tsx @@ -0,0 +1,97 @@ +/******************************************************************************** + * Eclipse Tractus-X - Industry Core Hub Frontend + * + * Copyright (c) 2025 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 govern in permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ + +import { Box, Grid2 } from '@mui/material' +import { Icon, Typography } from '@catena-x/portal-shared-components'; +import { PartInstance } from '../../types/product'; + +interface ProductDataProps { + part: PartInstance; + } + +const ProductData = ({ part }: ProductDataProps) => { + return ( + + + + {part.name} + {part.class} + + + + + Manufacturer + {part.manufacturer} + + + Manufacturer Part Id + {part.manufacturerPartId} + + + Description + {part.description} + + + + Created + {part.created} + + + Updated + {part.created} + + + + + + {part.name} + + {part.uuid} + +

Shared With:

+
    +
  • + + Volkswagen AG - + BPNL42621500AS61 +
  • +
  • + + BMW Racing Gmbh - + BPNL3A4T8A5621S3 +
  • +
  • + + John the Recycler KG - + BPNL5ASD5428800A +
  • +
  • + + 512 more +
  • +
+
+
+ ) +} + +export default ProductData \ No newline at end of file diff --git a/ichub-frontend/src/components/product-detail/ShareDropdown.tsx b/ichub-frontend/src/components/product-detail/ShareDropdown.tsx new file mode 100644 index 00000000..97e76ea1 --- /dev/null +++ b/ichub-frontend/src/components/product-detail/ShareDropdown.tsx @@ -0,0 +1,59 @@ +/******************************************************************************** + * Eclipse Tractus-X - Industry Core Hub Frontend + * + * Copyright (c) 2025 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 govern in permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ + +import { DropdownMenu, Button, Icon } from '@catena-x/portal-shared-components'; +import { PRODUCT_OPTIONS } from "../../types/common"; +import { Grid2 } from '@mui/material'; + +interface ShareDropdownProps { + handleCopy: () => void; + handleDownload: () => void; + handleShare: () => void; + } + +const ShareDropdown = ({ handleCopy, handleDownload, handleShare }: ShareDropdownProps) => { + return ( + } + > + + + + + + + ); +}; + +export default ShareDropdown; diff --git a/ichub-frontend/src/layout/MainLayout.tsx b/ichub-frontend/src/layout/MainLayout.tsx index 3227977f..ea4fef97 100644 --- a/ichub-frontend/src/layout/MainLayout.tsx +++ b/ichub-frontend/src/layout/MainLayout.tsx @@ -21,23 +21,26 @@ ********************************************************************************/ import { Outlet } from "react-router-dom"; +import { Grid2 } from '@mui/material'; import Header from '../components/general/Header'; import Sidebar from '../components/general/Sidebar'; function MainLayout() { return ( -
-
-
- -
+ + -
-
-
+ + + ); }; diff --git a/ichub-frontend/src/pages/ProductsDetails.tsx b/ichub-frontend/src/pages/ProductsDetails.tsx index 5f0b3156..691a1941 100644 --- a/ichub-frontend/src/pages/ProductsDetails.tsx +++ b/ichub-frontend/src/pages/ProductsDetails.tsx @@ -23,11 +23,15 @@ import React from "react"; import { useParams } from "react-router-dom"; import carPartsData from "../tests/payloads/sample-data.json"; -import instanceData from "../tests/payloads/instance-data.json"; -import { DropdownMenu, StatusTag, Button, Icon, Typography, PageNotifications, Table } from '@catena-x/portal-shared-components'; -import { PRODUCT_STATUS, PRODUCT_OPTIONS } from "../types/common"; +import { StatusTag, Button, Icon } from '@catena-x/portal-shared-components'; +import { PRODUCT_STATUS } from "../types/common"; import JsonViewerDialog from "../components/general/JsonViewerDialog"; import { Grid2 } from '@mui/material'; +import InstanceProductsTable from "../components/product-detail/InstanceProductsTable"; +import PageNotification from "../components/general/PageNotification"; +import ShareDropdown from "../components/product-detail/ShareDropdown"; +import ProductButton from "../components/product-detail/ProductButton"; +import ProductData from "../components/product-detail/ProductData"; const ProductsDetails = () => { const { id } = useParams<{ id: string }>(); @@ -55,7 +59,7 @@ const ProductsDetails = () => { severity: "success", title: "PartInstanceID copied to clipboard", }); - setTimeout(() => setNotification(null), 15500); // Cierra la notificación después de 3 segundos + setTimeout(() => setNotification(null), 3000); }) .catch((error) => { setNotification({ @@ -63,7 +67,7 @@ const ProductsDetails = () => { severity: "error", title: "Failed to copy PartInstanceID", }); - setTimeout(() => setNotification(null), 15500); // Cierra la notificación después de 3 segundos + setTimeout(() => setNotification(null), 3000); console.error("Failed to copy text: ", error); }); }; @@ -112,214 +116,49 @@ const ProductsDetails = () => { }; return ( -
+ <> + - {notification && ( -
- -
- )} -
-
- {getStatusTag(part.status)} - + + + + + + + + + - - } - > -
- - - -
-
-
- - - -
- {part.name} - {part.class} -
+ + + + console.log("PCF v2.0 button")} /> + console.log("TRANSMISSION PASS v2.0.0")} /> + console.log("DPP v2.0 button")} /> - -
- Manufacturer - {part.manufacturer} -
-
- Manufacturer Part Id - {part.manufacturerPartId} -
-
- Description - {part.description} -
-
-
- Created - {part.created} -
-
- Updated - {part.created} -
-
+ + -
- - {part.name} -
-
- {part.uuid} -
-

Shared With:

-
    -
  • - - Volkswagen AG - - BPNL42621500AS61 -
  • -
  • - - BMW Racing Gmbh - - BPNL3A4T8A5621S3 -
  • -
  • - - John the Recycler KG - - BPNL5ASD5428800A -
  • -
  • - - 512 more - -
  • -
-
-
-
-
- -
-
- - - -
-
- -
+
- -
-
- row.uuid} + + - rowsCount="5 of 52.0213 Digital Twins" - columns={[ - { - field: 'uuid', - flex: 3, - headerName: 'uuid' - }, - { - field: 'partInstanceId', - flex: 3, - headerName: 'Part Instance ID' - }, - { - field: 'submodels', - flex: 1, - headerName: 'Submodels' - }, - { - field: 'status', - flex: 1, - headerName: 'Status' - }, - { - field: 'type', - flex: 1, - headerName: 'Type' - }, - { - field: 'created', - flex: 1, - headerName: 'Created' - }, - { - field: 'updated', - flex: 2, - headerName: 'Updated' - }, - { - field: 'manufacturer', - flex: 2, - headerName: 'Manufacturer' - } - ]} - disableColumnMenu - disableColumnSelector - disableDensitySelector - hasBorder - hideFooter - noRowsMsg="No rows" - rowHeight={50} - rows={instanceData} - - searchPlaceholder="" - title="Instance Products" - toolbarVariant="basic" - /> - - + + ); } diff --git a/ichub-frontend/src/pages/ProductsList.tsx b/ichub-frontend/src/pages/ProductsList.tsx index fd91d817..8baddbce 100644 --- a/ichub-frontend/src/pages/ProductsList.tsx +++ b/ichub-frontend/src/pages/ProductsList.tsx @@ -37,7 +37,6 @@ const ProductsList = () => { }, []); const handleButtonClick = (itemId: string) => { - console.log(itemId); navigate(`/product/${itemId}`); // Navigate to the details page }; diff --git a/ichub-frontend/src/tests/payloads/instance-data.json b/ichub-frontend/src/tests/payloads/instance-data.json index 0a3c84b1..58e955d5 100644 --- a/ichub-frontend/src/tests/payloads/instance-data.json +++ b/ichub-frontend/src/tests/payloads/instance-data.json @@ -1,5 +1,6 @@ [ { + "id": 1, "uuid": "urn:uuid:a3f91b6e-4d6f-45d5-b2a5-8e74298fbe01", "partInstanceId": "PGA00000000000000002321450BXYZ", "submodels": 3, @@ -10,6 +11,7 @@ "manufacturer": "BPNL501TG00870AS0" }, { + "id": 2, "uuid": "urn:uuid:c1e2f3a4-5678-49d0-81a2-b3456789cdef", "partInstanceId": "PGA00000000000000002321450CD21", "submodels": 1, @@ -20,6 +22,7 @@ "manufacturer": "BPNL501TG00870AS0" }, { + "id": 3, "uuid": "urn:uuid:7d8a9b0c-1234-4d5e-98f7-6b54321cde90", "partInstanceId": "PGA00000000000000002321450EFG3", "submodels": 2, @@ -30,6 +33,7 @@ "manufacturer": "BPNL501TG00870AS0" }, { + "id": 4, "uuid": "urn:uuid:45a67bc8-d90e-4321-8765-1f2e3d4c5b6a", "partInstanceId": "PGA00000000000000002321450HIJ4", "submodels": 0, @@ -40,6 +44,7 @@ "manufacturer": "BPNL501TG00870AS0" }, { + "id": 5, "uuid": "urn:uuid:23b4a5c6-d7e8-490f-9102-3d4c5b6a7f8e", "partInstanceId": "PGA00000000000000002321450KLM5", "submodels": 4, @@ -48,5 +53,16 @@ "created": "25-02-2025 12:34:56.7891", "updated": "28-03-2025 14:20:30.9876", "manufacturer": "BPNL501TG00870AS0" + }, + { + "id": 6, + "uuid": "urn:uuid:23b4a543-d7e8-490f-9102-3d4c5b6a7f8e", + "partInstanceId": "PGA00000000000000002387850YHT5", + "submodels": 4, + "status": "Built", + "type": "Batch", + "created": "25-02-2025 12:34:56.7891", + "updated": "28-03-2025 14:20:30.9876", + "manufacturer": "BPNL501TG00870AS0" } ] \ No newline at end of file diff --git a/ichub-frontend/src/tests/payloads/sidebar-elements.json b/ichub-frontend/src/tests/payloads/sidebar-elements.json index ea0c6c06..7b7c4a7f 100644 --- a/ichub-frontend/src/tests/payloads/sidebar-elements.json +++ b/ichub-frontend/src/tests/payloads/sidebar-elements.json @@ -1,6 +1,7 @@ [ { "title": "Brands", + "icon": "Storefront", "subitems": [ { "name": "Bosch", "link": "/" }, { "name": "MagnaFlow", "link": "/" }, @@ -12,6 +13,7 @@ }, { "title": "Categories", + "icon": "Category", "subitems": [ { "name": "Turbochargers", "link": "/" }, { "name": "Suspension Parts", "link": "/" }, @@ -21,6 +23,7 @@ }, { "title": "Shared with", + "icon": "Shared", "subitems": [ { "name": "BASF", "link": "/" }, { "name": "BMW", "link": "/" }, @@ -31,6 +34,7 @@ }, { "title": "Status", + "icon": "Status", "subitems": [ { "name": "Draft", "link": "/" }, { "name": "Pending", "link": "/" }, diff --git a/ichub-frontend/src/types/instanceProduct.ts b/ichub-frontend/src/types/instanceProduct.ts new file mode 100644 index 00000000..35bd0e5d --- /dev/null +++ b/ichub-frontend/src/types/instanceProduct.ts @@ -0,0 +1,33 @@ +/******************************************************************************** + * Eclipse Tractus-X - Industry Core Hub Frontend + * + * Copyright (c) 2025 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 govern in permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ + +export interface InstanceProduct { + id: number, + uuid: string, + partInstanceId: string, + submodels: number, + status: string, + type: string, + created: string, + updated: string, + manufacturer: string +} \ No newline at end of file diff --git a/ichub-frontend/src/types/product.ts b/ichub-frontend/src/types/product.ts index 1ee39918..510e9bde 100644 --- a/ichub-frontend/src/types/product.ts +++ b/ichub-frontend/src/types/product.ts @@ -29,4 +29,6 @@ export interface PartInstance { class: string, description: string, image: string + created: string + updated: string } \ No newline at end of file