diff --git a/package-lock.json b/package-lock.json index 887072551..128b00f71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,9 @@ "billboard.js": "^3.14.3", "js-yaml": "^4.1.0", "lodash": "^4.17.21", + "moment": "^2.30.1", "re-resizable": "^6.10.3", "react-draggable": "^4.4.6", - "moment": "^2.30.1", "react-share": "^5.1.0" }, "devDependencies": { diff --git a/src/custom/CatalogDesignTable/CatalogDesignTable.tsx b/src/custom/CatalogDesignTable/CatalogDesignTable.tsx index 552420b8e..431f9c24d 100644 --- a/src/custom/CatalogDesignTable/CatalogDesignTable.tsx +++ b/src/custom/CatalogDesignTable/CatalogDesignTable.tsx @@ -28,6 +28,7 @@ interface CatalogDesignsTableProps { rowsPerPageOptions?: number[]; handleBulkDeleteModal: (patterns: Pattern[], modalRef: React.RefObject) => void; setSearch?: (search: string) => void; + tableBackgroundColor?: string; handleBulkpatternsDataUnpublishModal: ( selected: any, patterns: Pattern[], @@ -51,6 +52,7 @@ export const CatalogDesignsTable: React.FC = ({ handleBulkDeleteModal, setSearch, rowsPerPageOptions = [10, 25, 50, 100], + tableBackgroundColor, handleBulkpatternsDataUnpublishModal }) => { const theme = useTheme(); @@ -203,7 +205,9 @@ export const CatalogDesignsTable: React.FC = ({ tableCols={processedColumns} columnVisibility={columnVisibility} backgroundColor={ - theme.palette.mode === 'light' + tableBackgroundColor + ? tableBackgroundColor + : theme.palette.mode === 'light' ? theme.palette.background.default : theme.palette.background.secondary } diff --git a/src/custom/CatalogDesignTable/DesignTableColumnConfig.tsx b/src/custom/CatalogDesignTable/DesignTableColumnConfig.tsx index 17c26b203..b4323e657 100644 --- a/src/custom/CatalogDesignTable/DesignTableColumnConfig.tsx +++ b/src/custom/CatalogDesignTable/DesignTableColumnConfig.tsx @@ -1,15 +1,15 @@ +import { Theme } from '@mui/material'; import { MUIDataTableColumn, MUIDataTableMeta } from 'mui-datatables'; import { PLAYGROUND_MODES } from '../../constants/constants'; import { ChainIcon, CopyIcon, KanvasIcon, PublishIcon } from '../../icons'; import Download from '../../icons/Download/Download'; -import { CHARCOAL } from '../../theme'; import { downloadPattern, slugify } from '../CatalogDetail/helper'; import { RESOURCE_TYPES } from '../CatalogDetail/types'; import { Pattern } from '../CustomCatalog/CustomCard'; import { ConditionalTooltip } from '../Helpers/CondtionalTooltip'; import { ColView } from '../Helpers/ResponsiveColumns/responsive-coulmns.tsx'; import { DataTableEllipsisMenu } from '../ResponsiveDataTable'; -import AuthorCell from './AuthorCell'; +import { UserTableAvatarInfo } from '../UsersTable'; import { getColumnValue } from './helper'; import { L5DeleteIcon, NameDiv } from './style'; @@ -25,7 +25,8 @@ interface ColumnConfigProps { handleCopyUrl: (type: string, name: string, id: string) => void; handleClone: (name: string, id: string) => void; handleShowDetails: (designId: string, designName: string) => void; - getDownloadUrl: (id: string) => string; + handleDownload?: (design: Pattern) => void; + getDownloadUrl?: (id: string) => string; isDownloadAllowed: boolean; isCopyLinkAllowed: boolean; isDeleteAllowed: boolean; @@ -34,6 +35,7 @@ interface ColumnConfigProps { // for workspace designs table page only isFromWorkspaceTable?: boolean; isRemoveAllowed?: boolean; + theme?: Theme; } export const colViews: ColView[] = [ @@ -55,12 +57,14 @@ export const createDesignsColumnsConfig = ({ handleClone, handleShowDetails, getDownloadUrl, + handleDownload, isUnpublishAllowed, isCopyLinkAllowed, isDeleteAllowed, isPublishAllowed, isDownloadAllowed, isRemoveAllowed, + theme, isFromWorkspaceTable = false }: ColumnConfigProps): MUIDataTableColumn[] => { return [ @@ -99,13 +103,14 @@ export const createDesignsColumnsConfig = ({ const lastName = getColumnValue(tableMeta as TableMeta, 'last_name'); const avatar_url = getColumnValue(tableMeta as TableMeta, 'avatar_url'); const user_id = getColumnValue(tableMeta as TableMeta, 'user_id'); + const userEmail = getColumnValue(tableMeta as TableMeta, 'email'); return ( - ); } @@ -153,6 +158,17 @@ export const createDesignsColumnsConfig = ({ searchable: false } }, + + { + name: 'email', + label: 'email', + options: { + filter: false, + sort: false, + searchable: false + } + }, + { name: 'actions', label: 'Actions', @@ -165,13 +181,14 @@ export const createDesignsColumnsConfig = ({ customBodyRender: function CustomBody(_, tableMeta: MUIDataTableMeta) { const rowIndex = (tableMeta as TableMeta).rowIndex; const rowData = (tableMeta as TableMeta).tableData[rowIndex]; - const actionsList = [ { title: 'Download', - onClick: () => downloadPattern(rowData.id, rowData.name, getDownloadUrl), + onClick: getDownloadUrl + ? () => downloadPattern(rowData.id, rowData.name, getDownloadUrl) + : () => handleDownload && handleDownload(rowData), disabled: !isDownloadAllowed, - icon: + icon: }, { title: 'Copy Link', @@ -179,7 +196,7 @@ export const createDesignsColumnsConfig = ({ onClick: () => { handleCopyUrl(RESOURCE_TYPES.DESIGN, rowData?.name, rowData?.id); }, - icon: + icon: }, { title: 'Open in playground', @@ -191,7 +208,9 @@ export const createDesignsColumnsConfig = ({ '_blank' ); }, - icon: + icon: ( + + ) }, { title: isFromWorkspaceTable ? 'Remove Design' : 'Delete', @@ -205,20 +224,20 @@ export const createDesignsColumnsConfig = ({ title: 'Publish', disabled: !isPublishAllowed, onClick: () => handlePublishModal(rowData), - icon: + icon: }; const unpublishAction = { title: 'Unpublish', onClick: () => handleUnpublishModal(rowData)(), disabled: !isUnpublishAllowed, - icon: + icon: }; const cloneAction = { title: 'Clone', onClick: () => handleClone(rowData?.name, rowData?.id), - icon: + icon: }; if (rowData.visibility === 'published') { @@ -228,7 +247,7 @@ export const createDesignsColumnsConfig = ({ actionsList.splice(1, 0, publishAction); } - return ; + return ; } } } diff --git a/src/custom/CatalogDesignTable/columnConfig.tsx b/src/custom/CatalogDesignTable/columnConfig.tsx index 06cca4e06..1979510b5 100644 --- a/src/custom/CatalogDesignTable/columnConfig.tsx +++ b/src/custom/CatalogDesignTable/columnConfig.tsx @@ -320,7 +320,7 @@ export const createDesignColumns = ({ }); } //@ts-ignore - return ; + return ; } } } diff --git a/src/custom/CatalogDesignTable/style.tsx b/src/custom/CatalogDesignTable/style.tsx index 2e01e64b9..b38e9c5d5 100644 --- a/src/custom/CatalogDesignTable/style.tsx +++ b/src/custom/CatalogDesignTable/style.tsx @@ -19,7 +19,7 @@ interface DeleteIconProps { } export const L5DeleteIcon = styled(DeleteIcon)(({ disabled, bulk, theme }) => ({ - color: disabled ? theme.palette.icon.disabled : theme.palette.text.secondary, + color: disabled ? theme.palette.icon.disabled : theme.palette.text.default, cursor: disabled ? 'not-allowed' : 'pointer', width: bulk ? '32' : '28.8', height: bulk ? '32' : '28.8', diff --git a/src/custom/CustomColumnVisibilityControl/CustomColumnVisibilityControl.tsx b/src/custom/CustomColumnVisibilityControl/CustomColumnVisibilityControl.tsx index bb420e38b..26bbc45d0 100644 --- a/src/custom/CustomColumnVisibilityControl/CustomColumnVisibilityControl.tsx +++ b/src/custom/CustomColumnVisibilityControl/CustomColumnVisibilityControl.tsx @@ -36,8 +36,13 @@ export function CustomColumnVisibilityControl({ const theme = useTheme(); const handleOpen = (event: React.MouseEvent) => { + event.stopPropagation(); + setOpen((prev) => !prev); + if (anchorEl) { + setAnchorEl(null); + return; + } setAnchorEl(event.currentTarget); - setOpen(true); }; const handleClose = () => { diff --git a/src/custom/FlipCard/FlipCard.tsx b/src/custom/FlipCard/FlipCard.tsx index 7ecceaa05..895964773 100644 --- a/src/custom/FlipCard/FlipCard.tsx +++ b/src/custom/FlipCard/FlipCard.tsx @@ -7,8 +7,17 @@ export type FlipCardProps = { onClick?: () => void; onShow?: () => void; children: [React.ReactNode, React.ReactNode]; + disableFlip?: boolean; + padding?: string; }; +/** + * Helper function to get the front or back child component from the children array + * @param children Array containing exactly two child components + * @param key Index to retrieve (0 for front, 1 for back) + * @throws Error if children is undefined or doesn't contain exactly two components + * @returns The selected child component + */ function GetChild(children: [React.ReactNode, React.ReactNode], key: number) { if (!children) throw Error('FlipCard requires exactly two child components'); if (children.length != 2) throw Error('FlipCard requires exactly two child components'); @@ -42,7 +51,32 @@ const BackContent = styled('div')({ wordBreak: 'break-word' }); -export function FlipCard({ duration = 500, onClick, onShow, children }: FlipCardProps) { +/** + * A card component that provides a flipping animation between two content faces + * + * @component + * @param props.duration - Animation duration in milliseconds (default: 500) + * @param props.onClick - Callback function triggered on card click + * @param props.onShow - Additional callback function triggered when card shows new face + * @param props.children - Array of exactly two child components (front and back) + * @param props.disableFlip - When true, prevents the card from flipping (default: false) + * + * @example + * ```tsx + * + *
Front Content
+ *
Back Content
+ *
+ * ``` + */ +export function FlipCard({ + duration = 500, + onClick, + onShow, + children, + disableFlip = false, + padding +}: FlipCardProps) { const [flipped, setFlipped] = React.useState(false); const [activeBack, setActiveBack] = React.useState(false); @@ -72,6 +106,7 @@ export function FlipCard({ duration = 500, onClick, onShow, children }: FlipCard return ( { + if (disableFlip) return; setFlipped((flipped) => !flipped); onClick && onClick(); onShow && onShow(); @@ -80,7 +115,8 @@ export function FlipCard({ duration = 500, onClick, onShow, children }: FlipCard {!activeBack ? ( diff --git a/src/custom/ResponsiveDataTable.tsx b/src/custom/ResponsiveDataTable.tsx index f59b55d22..0c281f102 100644 --- a/src/custom/ResponsiveDataTable.tsx +++ b/src/custom/ResponsiveDataTable.tsx @@ -48,7 +48,7 @@ export const DataTableEllipsisMenu: React.FC<{ } + icon={} arrow /> diff --git a/src/custom/TeamTable/TeamTable.tsx b/src/custom/TeamTable/TeamTable.tsx index cfa5de4ad..ba8dd6791 100644 --- a/src/custom/TeamTable/TeamTable.tsx +++ b/src/custom/TeamTable/TeamTable.tsx @@ -1,6 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Grid, TableCell } from '@mui/material'; +import { TableCell } from '@mui/material'; import { MUIDataTableColumn } from 'mui-datatables'; +import { Grid } from '../../base'; +import { styled, useTheme } from '../../theme'; import { ErrorBoundary } from '../ErrorBoundary/ErrorBoundary.js'; import { ColView } from '../Helpers/ResponsiveColumns/responsive-coulmns.tsx/index.js'; import ResponsiveDataTable from '../ResponsiveDataTable.js'; @@ -20,6 +22,14 @@ interface TeamTableProps { useNotificationHandlers: any; useRemoveUserFromTeamMutation: any; } +const StyledGrid = styled(Grid)(({ theme }) => ({ + display: 'grid', + margin: 'auto', + paddingLeft: '0.5rem', + borderRadius: '0.25rem', + width: 'inherit', + gap: theme.spacing(1) +})); const TeamTable: React.FC = ({ teams, @@ -35,6 +45,7 @@ const TeamTable: React.FC = ({ useNotificationHandlers, useRemoveUserFromTeamMutation }) => { + const theme = useTheme(); return ( = ({ - + = ({ useGetUsersForOrgQuery={useGetUsersForOrgQuery} useNotificationHandlers={useNotificationHandlers} useRemoveUserFromTeamMutation={useRemoveUserFromTeamMutation} + theme={theme} /> - + ); } diff --git a/src/custom/TeamTable/TeamTableConfiguration.tsx b/src/custom/TeamTable/TeamTableConfiguration.tsx index 80d584a99..36e8c3759 100644 --- a/src/custom/TeamTable/TeamTableConfiguration.tsx +++ b/src/custom/TeamTable/TeamTableConfiguration.tsx @@ -256,7 +256,7 @@ export default function TeamTableConfiguration({ }} iconType="delete" > - + ) : ( @@ -296,12 +296,6 @@ export default function TeamTableConfiguration({ download: false, elevation: 0, serverSide: true, - tableBody: { - style: { - backgroundColor: '#f3f1f1' - } - }, - viewColumns: false, search: false, rowsExpanded: [ExpandedRowIdx], @@ -392,7 +386,7 @@ export default function TeamTableConfiguration({ return { style: { - backgroundColor: theme.palette.background.paper + backgroundColor: theme.palette.background.constant?.table } }; } diff --git a/src/custom/UsersTable/UserTableAvatarInfo.tsx b/src/custom/UsersTable/UserTableAvatarInfo.tsx new file mode 100644 index 000000000..3f019cea0 --- /dev/null +++ b/src/custom/UsersTable/UserTableAvatarInfo.tsx @@ -0,0 +1,49 @@ +import { Avatar, Box, Grid, Typography } from '../../base'; +import { CLOUD_URL } from '../../constants/constants'; +import { PersonIcon } from '../../icons'; +import { useTheme } from '../../theme'; + +interface UserTableAvatarInfoProps { + userId: string; + userName: string; + userEmail: string; + profileUrl?: string; +} + +const UserTableAvatarInfo: React.FC = ({ + userId, + userName, + userEmail, + profileUrl +}): JSX.Element => { + const theme = useTheme(); + const handleProfileClick = (): void => { + window.open(`${CLOUD_URL}/user/${userId}`); + }; + + return ( + + + + + {profileUrl ? '' : } + + + + + {userName} + + {userEmail} + + + + ); +}; + +export default UserTableAvatarInfo; diff --git a/src/custom/UsersTable/UsersTable.tsx b/src/custom/UsersTable/UsersTable.tsx index ba389b1c4..69458f839 100644 --- a/src/custom/UsersTable/UsersTable.tsx +++ b/src/custom/UsersTable/UsersTable.tsx @@ -1,33 +1,36 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { Theme } from '@mui/material'; import { MUIDataTableColumn, MUIDataTableMeta } from 'mui-datatables'; import { useRef, useState } from 'react'; -import { Avatar, Box, Grid, Tooltip, Typography } from '../../base'; -import { EditIcon, PersonIcon } from '../../icons'; +import { Box, Tooltip } from '../../base'; +import { EditIcon } from '../../icons'; import Github from '../../icons/Github/GithubIcon'; import Google from '../../icons/Google/GoogleIcon'; import LogoutIcon from '../../icons/Logout/LogOutIcon'; -import { CHARCOAL, SistentThemeProvider } from '../../theme'; +import { CHARCOAL, SistentThemeProviderWithoutBaseLine } from '../../theme'; import { useWindowDimensions } from '../Helpers/Dimension'; import { ColView, updateVisibleColumns } from '../Helpers/ResponsiveColumns/responsive-coulmns.tsx/responsive-column'; -import PromptComponent from '../Prompt'; +import PromptComponent, { PROMPT_VARIANTS } from '../Prompt'; import ResponsiveDataTable from '../ResponsiveDataTable'; import { TooltipIcon } from '../TooltipIconButton'; import { parseDeletionTimestamp } from '../Workspaces/helper'; import { TableIconsContainer, TableIconsDisabledContainer } from '../Workspaces/styles'; - +import UserTableAvatarInfo from './UserTableAvatarInfo'; interface ActionButtonsProps { tableMeta: MUIDataTableMeta; isRemoveFromTeamAllowed: boolean; handleRemoveFromTeam: (data: any[]) => () => void; + theme?: Theme; } const ActionButtons: React.FC = ({ tableMeta, handleRemoveFromTeam, - isRemoveFromTeamAllowed + isRemoveFromTeamAllowed, + theme }) => { return (
@@ -39,12 +42,12 @@ const ActionButtons: React.FC = ({ title="Remove user membership from team" iconType="delete" > - + ) : ( - + )}
@@ -58,6 +61,7 @@ interface UsersTableProps { useRemoveUserFromTeamMutation: any; useNotificationHandlers: any; isRemoveFromTeamAllowed: boolean; + theme?: Theme; } const UsersTable: React.FC = ({ @@ -66,7 +70,8 @@ const UsersTable: React.FC = ({ org_id, useRemoveUserFromTeamMutation, useNotificationHandlers, - isRemoveFromTeamAllowed + isRemoveFromTeamAllowed, + theme }) => { const [page, setPage] = useState(0); const [pageSize, setPageSize] = useState(10); @@ -75,7 +80,6 @@ const UsersTable: React.FC = ({ const availableRoles: string[] = []; const { handleError, handleSuccess, handleInfo } = useNotificationHandlers(); const ref: any = useRef(null); - const { width } = useWindowDimensions(); const { data: userData } = useGetUsersForOrgQuery({ @@ -98,7 +102,8 @@ const UsersTable: React.FC = ({ const response = await ref.current?.show({ title: `Remove User From Team ?`, subtitle: removeUserFromTeamModalContent(data[3], data[2]), - primaryOption: 'Proceed' + primaryOption: 'Proceed', + variant: PROMPT_VARIANTS.DANGER }); if (response === 'Proceed') { removeUserFromTeam({ @@ -111,7 +116,6 @@ const UsersTable: React.FC = ({ handleSuccess(`${data[4] ? data[4] : ''} ${data[5] ? data[5] : ''} removed from team`); }) .catch((err: any) => { - console.log('heya err', err); const error = err.response?.data?.message || 'Failed to remove user from team'; if (err.response.status === 404) { handleInfo(error); @@ -127,20 +131,6 @@ const UsersTable: React.FC = ({ return rowData[columnIndex]; }; - // const fetchAvailableRoles = () => { - // axios - // .get(process.env.API_ENDPOINT_PREFIX + `/api/identity/orgs/${org_id}/roles?all=true`) - // .then((res) => { - // let roles = []; - // res?.data?.roles?.forEach((role) => roles.push(role?.role_name)); - // setAvailableRoles(roles); - // }) - // .catch((err) => { - // let error = err.response?.data?.message || 'Failed to fetch roles'; - // handleError(error); - // }); - // }; - const removeUserFromTeamModalContent = (user: string, email: string) => ( <>

Are you sure you want to remove this user? (This action is irreversible)

@@ -253,29 +243,12 @@ const UsersTable: React.FC = ({ searchable: false, customBodyRender: (value: string, tableMeta: MUIDataTableMeta) => ( img': { mr: 2, flexShrink: 0 } }}> - - - - { - window.open( - `/user/${getValidColumnValue(tableMeta.rowData, 'user_id', columns)}` - ); - }} - alt={getValidColumnValue(tableMeta.rowData, 'first_name', columns)} - src={value} - > - {value ? '' : } - - - - - {tableMeta.rowData[4]} {tableMeta.rowData[5]} - - {tableMeta.rowData[2]} - - - + ) } @@ -440,6 +413,7 @@ const UsersTable: React.FC = ({ tableMeta={tableMeta} handleRemoveFromTeam={handleRemoveFromTeam} isRemoveFromTeamAllowed={isRemoveFromTeamAllowed} + theme={theme} /> ) } @@ -457,9 +431,8 @@ const UsersTable: React.FC = ({ }); return initialVisibility; }); - return ( - +
= ({ tableCols={tableCols} updateCols={updateCols} columnVisibility={columnVisibility} + backgroundColor={theme?.palette.background.tabs} />
-
+ ); }; diff --git a/src/custom/UsersTable/index.ts b/src/custom/UsersTable/index.ts index ee80ef649..bab343d76 100644 --- a/src/custom/UsersTable/index.ts +++ b/src/custom/UsersTable/index.ts @@ -1,3 +1,3 @@ import UsersTable from './UsersTable'; - -export { UsersTable }; +import UserTableAvatarInfo from './UserTableAvatarInfo'; +export { UsersTable, UserTableAvatarInfo }; diff --git a/src/custom/Workspaces/DesignTable.tsx b/src/custom/Workspaces/DesignTable.tsx index 0ad01f4d9..c8c0a2f58 100644 --- a/src/custom/Workspaces/DesignTable.tsx +++ b/src/custom/Workspaces/DesignTable.tsx @@ -5,7 +5,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { Accordion, AccordionDetails, AccordionSummary, Typography } from '../../base'; import { DesignIcon } from '../../icons'; import { publishCatalogItemSchema } from '../../schemas'; -import { SistentThemeProvider } from '../../theme'; +import { useTheme } from '../../theme'; import { CatalogDesignsTable, createDesignsColumnsConfig, @@ -18,10 +18,8 @@ import { updateVisibleColumns } from '../Helpers/ResponsiveColumns/responsive-co import PromptComponent from '../Prompt'; import SearchBar from '../SearchBar'; import AssignmentModal from './AssignmentModal'; -import EditButton from './EditButton'; import useDesignAssignment from './hooks/useDesignAssignment'; -import { TableHeader, TableRightActionHeader } from './styles'; - +import { L5EditIcon, TableHeader, TableRightActionHeader } from './styles'; export interface DesignTableProps { workspaceId: string; workspaceName: string; @@ -39,16 +37,17 @@ export interface DesignTableProps { workspaceName: string, workspaceId: string ) => void; - getDownloadUrl: (id: string) => string; handlePublish: (publishModal: PublishModalState, data: any) => void; publishModalHandler: any; handleUnpublishModal: (design: Pattern, modalRef: React.RefObject) => void; + handleDownload?: (design: Pattern) => void; handleBulkUnpublishModal: ( selected: any, designs: Pattern[], modalRef: React.RefObject ) => void; handleShowDetails: (designId: string, designName: string) => void; + getDownloadUrl?: (id: string) => string; GenericRJSFModal: any; isDownloadAllowed: boolean; isCopyLinkAllowed: boolean; @@ -81,10 +80,11 @@ const DesignTable: React.FC = ({ handleClone, handleCopyUrl, handlePublish, + handleDownload, + getDownloadUrl, handleShowDetails, handleUnpublishModal, handleWorkspaceDesignDeleteModal, - getDownloadUrl, publishModalHandler, isCopyLinkAllowed, isDeleteAllowed, @@ -116,7 +116,7 @@ const DesignTable: React.FC = ({ pattern: result }); }; - + const theme = useTheme(); const columns = createDesignsColumnsConfig({ handleDeleteModal: (design) => () => handleWorkspaceDesignDeleteModal(design.id, workspaceId), handlePublishModal, @@ -124,6 +124,7 @@ const DesignTable: React.FC = ({ handleCopyUrl, handleClone, handleShowDetails, + handleDownload, getDownloadUrl, isCopyLinkAllowed, isDeleteAllowed, @@ -131,7 +132,8 @@ const DesignTable: React.FC = ({ isPublishAllowed, isUnpublishAllowed, isFromWorkspaceTable: true, - isRemoveAllowed + isRemoveAllowed, + theme }); const [publishSchema, setPublishSchema] = useState<{ @@ -152,7 +154,7 @@ const DesignTable: React.FC = ({ return initialVisibility; }); - const [expanded, setExpanded] = useState(true); + const [expanded, setExpanded] = useState(false); const handleAccordionChange = () => { setExpanded(!expanded); }; @@ -184,7 +186,7 @@ const DesignTable: React.FC = ({ const tableHeaderContent = ( - + Assigned Designs @@ -207,13 +209,17 @@ const DesignTable: React.FC = ({ }} id={'catalog-table'} /> - + ); return ( - + <> } @@ -242,6 +248,7 @@ const DesignTable: React.FC = ({ } filter={'my-designs'} setSearch={setDesignSearch} + tableBackgroundColor={theme.palette.background.constant?.table} /> @@ -276,7 +283,7 @@ const DesignTable: React.FC = ({ buttonTitle="Publish" /> - + ); }; diff --git a/src/custom/Workspaces/EditButton.tsx b/src/custom/Workspaces/EditButton.tsx deleted file mode 100644 index 7486ae159..000000000 --- a/src/custom/Workspaces/EditButton.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { IconButton } from '@mui/material'; -import React from 'react'; -import { CustomTooltip } from '../CustomTooltip'; -import { L5EditIcon } from './styles'; - -interface EditButtonProps { - onClick: (e: React.MouseEvent) => void; - disabled?: boolean; - title?: string; -} - -const EditButton: React.FC = ({ onClick, disabled, title = 'Edit' }) => { - return ( - -
- - - -
-
- ); -}; - -export default EditButton; diff --git a/src/custom/Workspaces/EnvironmentTable.tsx b/src/custom/Workspaces/EnvironmentTable.tsx index 79aa6739c..e79d6fa06 100644 --- a/src/custom/Workspaces/EnvironmentTable.tsx +++ b/src/custom/Workspaces/EnvironmentTable.tsx @@ -4,7 +4,7 @@ import { MUIDataTableColumn, MUIDataTableMeta } from 'mui-datatables'; import React, { useState } from 'react'; import { Accordion, AccordionDetails, AccordionSummary, Typography } from '../../base'; import { DeleteIcon, EnvironmentIcon } from '../../icons'; -import { CHARCOAL, SistentThemeProvider } from '../../theme'; +import { useTheme } from '../../theme'; import { CustomColumnVisibilityControl } from '../CustomColumnVisibilityControl'; import { CustomTooltip } from '../CustomTooltip'; import { ConditionalTooltip } from '../Helpers/CondtionalTooltip'; @@ -17,9 +17,14 @@ import ResponsiveDataTable, { IconWrapper } from '../ResponsiveDataTable'; import SearchBar from '../SearchBar'; import { TooltipIcon } from '../TooltipIconButton'; import AssignmentModal from './AssignmentModal'; -import EditButton from './EditButton'; import useEnvironmentAssignment from './hooks/useEnvironmentAssignment'; -import { CellStyle, CustomBodyRenderStyle, TableHeader, TableRightActionHeader } from './styles'; +import { + CellStyle, + CustomBodyRenderStyle, + L5EditIcon, + TableHeader, + TableRightActionHeader +} from './styles'; interface EnvironmentTableProps { workspaceId: string; @@ -62,8 +67,9 @@ const EnvironmentTable: React.FC = ({ useAssignEnvironmentToWorkspaceMutation, isAssignAllowed }) => { - const [expanded, setExpanded] = useState(true); - const handleAccordionChange = () => { + const [expanded, setExpanded] = useState(false); + const handleAccordionChange = (e: React.SyntheticEvent) => { + e.stopPropagation(); setExpanded(!expanded); }; const [search, setSearch] = useState(''); @@ -79,6 +85,7 @@ const EnvironmentTable: React.FC = ({ order: sortOrder }); const { width } = useWindowDimensions(); + const theme = useTheme(); const [unassignEnvironmentFromWorkspace] = useUnassignEnvironmentFromWorkspaceMutation(); const columns: MUIDataTableColumn[] = [ { @@ -164,7 +171,7 @@ const EnvironmentTable: React.FC = ({ }} iconType="delete" > - + ) @@ -236,7 +243,7 @@ const EnvironmentTable: React.FC = ({ const [tableCols, updateCols] = useState(columns); return ( - + <> } @@ -245,7 +252,7 @@ const EnvironmentTable: React.FC = ({ }} > - + Assigned Environments @@ -268,9 +275,10 @@ const EnvironmentTable: React.FC = ({ }} id={'environments-table'} /> - @@ -308,7 +316,7 @@ const EnvironmentTable: React.FC = ({ isAssignAllowed={isAssignAllowed} isRemoveAllowed={isRemoveAllowed} /> - + ); }; diff --git a/src/custom/Workspaces/WorkspaceCard.tsx b/src/custom/Workspaces/WorkspaceCard.tsx new file mode 100644 index 000000000..d06340cfc --- /dev/null +++ b/src/custom/Workspaces/WorkspaceCard.tsx @@ -0,0 +1,372 @@ +import { useTheme } from '@mui/material'; +import { Backdrop, CircularProgress, Grid } from '../../base'; +import { FlipCard } from '../FlipCard'; +import { RecordRow, RedirectButton, TransferButton } from './WorkspaceTransferButton'; +import { formattoLongDate } from './helper'; +import { + AllocationColumnGrid, + AllocationWorkspace, + BulkSelectCheckbox, + CardBackActionsGrid, + CardBackTitleGrid, + CardBackTopGrid, + CardBackWrapper, + CardFrontWrapper, + CardTitle, + DateColumnGrid, + DateGrid, + DateLabel, + DescriptionLabel, + EmptyDescription, + L5DeleteIcon, + L5EditIcon, + RecentActivityGrid, + RecentActivityTitle, + WorkspaceCardGrid +} from './styles'; + +interface WorkspaceDetails { + id: number; + name: string; + description: string; + deleted_at: { Valid: boolean }; + updated_at: string; + created_at: string; +} + +type Activity = { + description: string; + first_name: string; + created_at: string; +}; + +interface CardFrontProps { + onFlip: () => void; + name: string; + description: string; + environmentsCount: number; + onAssignEnvironment: () => void; + teamsCount: number; + onAssignTeam: () => void; + designAndViewOfWorkspaceCount: number; + onAssignDesign: () => void; + isEnvironmentAllowed: boolean; + isTeamAllowed: boolean; + isDesignAndViewAllowed: boolean; +} + +interface CardBackProps { + onFlipBack: () => void; + onSelect: () => void; + name: string; + onEdit: () => void; + onDelete: () => void; + selectedWorkspaces: number[]; + workspaceId: number; + loadingEvents: boolean; + recentActivities: Activity[]; + updatedDate: string; + createdDate: string; + deleted: boolean; + isDeleteWorkspaceAllowed: boolean; + isEditWorkspaceAllowed: boolean; +} + +interface WorkspaceCardProps { + workspaceDetails: WorkspaceDetails; + onDelete: () => void; + onEdit: () => void; + onSelect: () => void; + selectedWorkspaces: number[]; + onAssignTeam: () => void; + onAssignEnvironment: () => void; + onAssignDesign: () => void; + recentActivities: Activity[]; + onFlip: () => void; + onFlipBack: () => void; + loadingEvents: boolean; + teamsOfWorkspaceCount: number; + environmentsOfWorkspaceCount: number; + designAndViewOfWorkspaceCount: number; + isEnvironmentAllowed: boolean; + isTeamAllowed: boolean; + isDesignAndViewAllowed: boolean; + isDeleteWorkspaceAllowed: boolean; + isEditWorkspaceAllowed: boolean; +} + +/** + * Renders a Workspace card component. + * + * @param {Object} props - The component props. + * @param {Object} props.environmentDetails - The details of the workspace. + * @param {string} props.environmentDetails.name - The name of the workspace. + * @param {string} props.environmentDetails.description - The description of the workspace. + * @param {Function} props.onDelete - Function to delete the workspace. + * @param {Function} props.onEdit - Function to edit the workspace. + * @param {Function} props.onSelect - Function to select workspace for bulk actions. + * @param {Array} props.selectedWorkspaces - Selected workspace list for delete. + * @param {Function} props.onAssignTeam - Function to open team assignment modal open. + * @param {Function} props.onAssignDesign - Function to open design assignment modal open. + * @param {Array} props.latestActivity - List of latest activity. + * @param {Function} props.onFlip - Click event to trigger when card flip. + * @param {Function} props.onFlipBack - Click event to trigger when card flip back. + * @param {Boolean} props.loadingEvents - Loading state of the events. + * @param {Number} props.teamsOfWorkspaceCount - Count of teams assigned to the workspace. + * @param {Number} props.environmentsOfWorkspaceCount - Count of environments assigned to the workspace. + * @param {Number} props.designAndViewOfWorkspaceCount - Count of designs/views assigned to the workspace. + * @param {Boolean} props.isEnvironmentAllowed - Flag to check if environment assignment is allowed. + * @param {Boolean} props.isTeamAllowed - Flag to check if team assignment is allowed. + * @param {Boolean} props.isDesignAndViewAllowed - Flag to check if design assignment is allowed. + * @param {Boolean} props.isDeleteWorkspaceAllowed - Flag to check if workspace deletion is allowed. + * @param {Boolean} props.isEditWorkspaceAllowed - Flag to check if workspace edit is allowed. + * @returns {React.ReactElement} The Workspace card component. + * + */ + +const WorkspaceCard = ({ + workspaceDetails, + onDelete, + onEdit, + onSelect, + selectedWorkspaces, + onAssignTeam, + onAssignEnvironment, + onAssignDesign, + recentActivities, + onFlip, + onFlipBack, + loadingEvents, + teamsOfWorkspaceCount, + environmentsOfWorkspaceCount, + designAndViewOfWorkspaceCount, + isEnvironmentAllowed, + isTeamAllowed, + isDesignAndViewAllowed, + isDeleteWorkspaceAllowed, + isEditWorkspaceAllowed +}: WorkspaceCardProps) => { + const deleted = workspaceDetails.deleted_at.Valid; + return ( + + + + + + ); +}; + +export default WorkspaceCard; + +const CardFront = ({ + onFlip, + name, + description, + environmentsCount, + onAssignEnvironment, + teamsCount, + onAssignTeam, + designAndViewOfWorkspaceCount, + onAssignDesign, + isEnvironmentAllowed, + isTeamAllowed, + isDesignAndViewAllowed +}: CardFrontProps) => { + return ( + + + e.stopPropagation()}> + {name} + + + + {description ? ( + e.stopPropagation()} sx={{ maxHeight: '105px' }}> + {description} + + ) : ( + e.stopPropagation()}>No description + )} + + + + e.stopPropagation()}> + {isEnvironmentAllowed ? ( + + ) : ( + + )} + + + + + + e.stopPropagation()}> + {isTeamAllowed ? ( + + ) : ( + + )} + + + + + e.stopPropagation()}> + {isDesignAndViewAllowed ? ( + + ) : ( + + )} + + + + + + ); +}; + +const CardBack = ({ + onFlipBack, + onSelect, + name, + onEdit, + onDelete, + selectedWorkspaces, + workspaceId, + loadingEvents, + recentActivities, + updatedDate, + createdDate, + deleted, + isDeleteWorkspaceAllowed, + isEditWorkspaceAllowed +}: CardBackProps) => { + const isWorkspaceSelected = selectedWorkspaces?.includes(workspaceId); + const isEditButtonDisabled = isWorkspaceSelected ? true : !isEditWorkspaceAllowed; + const isDeleteButtonDisabled = isWorkspaceSelected ? true : !isDeleteWorkspaceAllowed; + + const theme = useTheme(); + return ( + + + + e.stopPropagation()} + onChange={onSelect} + disabled={deleted ? true : !isDeleteWorkspaceAllowed} + /> + e.stopPropagation()} + > + {name} + + + + + + + + + Recent Activity + + + {loadingEvents ? ( + + + + ) : ( + recentActivities?.map((activity, index) => { + return ( + + ); + }) + )} + + + + e.stopPropagation()}> + Updated At: {formattoLongDate(updatedDate)} + + + + e.stopPropagation()}> + Created At: {formattoLongDate(createdDate)} + + + + + ); +}; diff --git a/src/custom/Workspaces/WorkspaceTeamsTable.tsx b/src/custom/Workspaces/WorkspaceTeamsTable.tsx index 61475377b..dd4a366d6 100644 --- a/src/custom/Workspaces/WorkspaceTeamsTable.tsx +++ b/src/custom/Workspaces/WorkspaceTeamsTable.tsx @@ -3,15 +3,14 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { useState } from 'react'; import { Accordion, AccordionDetails, AccordionSummary, Typography } from '../../base'; import { TeamsIcon } from '../../icons'; -import { SistentThemeProvider } from '../../theme'; +import { useTheme } from '../../theme'; import { CustomColumnVisibilityControl } from '../CustomColumnVisibilityControl'; import SearchBar from '../SearchBar'; import { TeamTableConfiguration } from '../TeamTable'; import TeamTable from '../TeamTable/TeamTable'; import AssignmentModal from './AssignmentModal'; -import EditButton from './EditButton'; import useTeamAssignment from './hooks/useTeamAssignment'; -import { TableHeader, TableRightActionHeader } from './styles'; +import { L5EditIcon, TableHeader, TableRightActionHeader } from './styles'; export interface TeamsTableProps { workspaceId: string; @@ -51,7 +50,7 @@ const TeamsTable: React.FC = ({ const [pageSize, setPageSize] = useState(10); const [sortOrder, setSortOrder] = useState('updated_at desc'); const [bulkSelect, setBulkSelect] = useState(false); - const [expanded, setExpanded] = useState(true); + const [expanded, setExpanded] = useState(false); const handleAccordionChange = () => { setExpanded(!expanded); }; @@ -104,16 +103,16 @@ const TeamsTable: React.FC = ({ isDeleteTeamAllowed: isDeleteTeamAllowed, setSearch }); - + const theme = useTheme(); return ( - + <> } sx={{ backgroundColor: 'background.paper' }} > - + Assigned Teams @@ -136,9 +135,10 @@ const TeamsTable: React.FC = ({ }} id={'teams-table'} /> - @@ -165,7 +165,14 @@ const TeamsTable: React.FC = ({ open={teamAssignment.assignModal} onClose={teamAssignment.handleAssignModalClose} title={`Assign Teams to ${workspaceName}`} - headerIcon={} + headerIcon={ + + } name="Teams" assignableData={teamAssignment.data} handleAssignedData={teamAssignment.handleAssignData} @@ -175,7 +182,7 @@ const TeamsTable: React.FC = ({ height="5rem" width="5rem" primaryFill={'#808080'} - secondaryFill={'gray'} + secondaryFill={theme.palette.icon.disabled} fill={'#808080'} /> } @@ -189,7 +196,7 @@ const TeamsTable: React.FC = ({ isAssignAllowed={isAssignTeamAllowed} isRemoveAllowed={isRemoveTeamFromWorkspaceAllowed} /> - + ); }; diff --git a/src/custom/Workspaces/WorkspaceTransferButton.tsx b/src/custom/Workspaces/WorkspaceTransferButton.tsx new file mode 100644 index 000000000..7fb1cfeae --- /dev/null +++ b/src/custom/Workspaces/WorkspaceTransferButton.tsx @@ -0,0 +1,114 @@ +import { SyncAlt as SyncAltIcon } from '@mui/icons-material'; +import { Grid, Tooltip, Typography } from '../../base'; +import { useTheme } from '../../theme'; +import { formatShortDate, formatShortDateTime } from './helper'; +import { PopupButton, Record, TabCount, TabTitle } from './styles'; + +interface TransferButtonProps { + title: string; + count: number; + onAssign: () => void; + disabled: boolean; +} + +interface RedirectButtonProps { + title: string; + count: number; + disabled?: boolean; +} + +export const TransferButton: React.FC = ({ + title, + count, + onAssign, + disabled +}) => { + const theme = useTheme(); + return ( + + + {count} + {title} + + + + ); +}; + +export const RedirectButton: React.FC = ({ + title, + count, + disabled = true +}) => { + return ( + + + {count} + {title} + {/* */} + + + ); +}; + +interface RecordRowProps { + title: string; + name: string; + date?: string | Date; +} + +export const RecordRow: React.FC = ({ title, name, date }) => { + const theme = useTheme(); + + return ( + + + + {title} + + + {name} + + + + + + {date ? formatShortDate(date) : '-'} + + + + + ); +}; diff --git a/src/custom/Workspaces/WorkspaceViewsTable.tsx b/src/custom/Workspaces/WorkspaceViewsTable.tsx index 2335dcf00..af1cdc70d 100644 --- a/src/custom/Workspaces/WorkspaceViewsTable.tsx +++ b/src/custom/Workspaces/WorkspaceViewsTable.tsx @@ -2,9 +2,9 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { MUIDataTableColumn, MUIDataTableMeta } from 'mui-datatables'; import React, { useState } from 'react'; -import { Accordion, AccordionDetails, AccordionSummary, Typography } from '../../base'; +import { Accordion, AccordionDetails, AccordionSummary, Box, Typography } from '../../base'; import { DeleteIcon, EnvironmentIcon } from '../../icons'; -import { CHARCOAL, SistentThemeProvider } from '../../theme'; +import { useTheme } from '../../theme'; import { NameDiv } from '../CatalogDesignTable/style'; import { RESOURCE_TYPES } from '../CatalogDetail/types'; import { CustomColumnVisibilityControl } from '../CustomColumnVisibilityControl'; @@ -18,10 +18,16 @@ import { import ResponsiveDataTable, { IconWrapper } from '../ResponsiveDataTable'; import SearchBar from '../SearchBar'; import { TooltipIcon } from '../TooltipIconButton'; +import { UserTableAvatarInfo } from '../UsersTable'; import AssignmentModal from './AssignmentModal'; -import EditButton from './EditButton'; import useViewAssignment from './hooks/useViewsAssignment'; -import { CellStyle, CustomBodyRenderStyle, TableHeader, TableRightActionHeader } from './styles'; +import { + CellStyle, + CustomBodyRenderStyle, + L5EditIcon, + TableHeader, + TableRightActionHeader +} from './styles'; interface ViewsTableProps { workspaceId: string; @@ -36,10 +42,13 @@ interface ViewsTableProps { const colViews: ColView[] = [ ['id', 'na'], + ['avatar_url', 'xs'], + ['email', 'na'], ['name', 'xs'], - ['description', 'm'], - ['organization_id', 'l'], - ['created_at', 'xl'], + ['first_name', 'na'], + ['last_name', 'na'], + ['organization_id', 'xl'], + ['created_at', 'na'], ['updated_at', 'xl'], ['visibility', 'l'], ['actions', 'xs'] @@ -67,7 +76,8 @@ const WorkspaceViewsTable: React.FC = ({ isAssignAllowed, handleShowDetails }) => { - const [expanded, setExpanded] = useState(true); + const theme = useTheme(); + const [expanded, setExpanded] = useState(false); const handleAccordionChange = () => { setExpanded(!expanded); }; @@ -81,7 +91,8 @@ const WorkspaceViewsTable: React.FC = ({ page: page, pageSize: pageSize, search: search, - order: sortOrder + order: sortOrder, + expandUser: true }); const { width } = useWindowDimensions(); const [unassignviewFromWorkspace] = useUnassignViewFromWorkspaceMutation(); @@ -112,6 +123,62 @@ const WorkspaceViewsTable: React.FC = ({ } } }, + { + name: 'avatar_url', + label: 'Owner', + options: { + filter: false, + sort: false, + searchable: false, + customBodyRender: (value: string, tableMeta: MUIDataTableMeta) => { + const getValidColumnValue = ( + rowData: any, + columnName: string, + columns: MUIDataTableColumn[] + ) => { + const columnIndex = columns.findIndex((column: any) => column.name === columnName); + return rowData[columnIndex]; + }; + return ( + img': { mr: 2, flexShrink: 0 } }}> + + + ); + } + } + }, + { + name: 'email', + label: 'Email', + options: { + filter: false, + sort: true, + searchable: true + } + }, + { + name: 'first_name', + label: 'First Name', + options: { + filter: false, + sort: true, + searchable: true + } + }, + { + name: 'last_name', + label: 'Last Name', + options: { + filter: false, + sort: true, + searchable: true + } + }, { name: 'created_at', label: 'Created At', @@ -169,7 +236,7 @@ const WorkspaceViewsTable: React.FC = ({ }} iconType="delete" > - + ) @@ -236,7 +303,7 @@ const WorkspaceViewsTable: React.FC = ({ const [tableCols, updateCols] = useState(columns); return ( - + <> } @@ -245,7 +312,7 @@ const WorkspaceViewsTable: React.FC = ({ }} > - + Assigned Views @@ -268,7 +335,11 @@ const WorkspaceViewsTable: React.FC = ({ }} id={'views-table'} /> - + @@ -305,7 +376,7 @@ const WorkspaceViewsTable: React.FC = ({ isAssignAllowed={isAssignAllowed} isRemoveAllowed={isRemoveAllowed} /> - + ); }; diff --git a/src/custom/Workspaces/helper.ts b/src/custom/Workspaces/helper.ts index 553442794..23e613b2f 100644 --- a/src/custom/Workspaces/helper.ts +++ b/src/custom/Workspaces/helper.ts @@ -17,3 +17,65 @@ export const parseDeletionTimestamp = (data: { return DEFAULT_DATE; } }; + +/** + * Formats a date into a short date-time string (e.g., "Jan 1, 2024, 09:30 AM") + * + * @param {Date | string} date - The date to format. Can be a Date object or date string + * @returns {string} Formatted date string in the format "MMM D, YYYY, HH:MM AM/PM" + * + * @example + * formatShortDateTime("2024-01-01T09:30:00") // Returns "Jan 1, 2024, 09:30 AM" + * formatShortDateTime(new Date()) // Returns current date-time in short format + * + * Generated by Copilot + */ +export const formatShortDateTime = (date: Date | string): string => { + return new Date(date).toLocaleDateString('en-US', { + day: 'numeric', + month: 'short', + year: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); +}; + +/** + * Formats a date into a short date string (e.g., "Jan 1, 2024") + * + * @param {Date | string} date - The date to format. Can be a Date object or date string + * @returns {string} Formatted date string in the format "MMM D, YYYY" + * + * @example + * formatShortDate("2024-01-01") // Returns "Jan 1, 2024" + * formatShortDate(new Date()) // Returns current date in short format + * + * Generated by Copilot + */ +export const formatShortDate = (date: Date | string): string => { + return new Date(date).toLocaleDateString('en-US', { + day: 'numeric', + month: 'short', + year: 'numeric' + }); +}; + +/** + * Formats a date into a long date string (e.g., "January 1, 2024") + * + * @param {Date | string} date - The date to format. Can be a Date object or date string + * @returns {string} Formatted date string in the format "MMMM D, YYYY" + * + * @example + * formattoLongDate("2024-01-01") // Returns "January 1, 2024" + * formattoLongDate(new Date()) // Returns current date in long format + * + * Generated by Copilot + */ +export const formattoLongDate = (date: Date | string): string => { + return new Date(date).toLocaleDateString('en-US', { + day: 'numeric', + month: 'long', + year: 'numeric' + }); +}; diff --git a/src/custom/Workspaces/hooks/useDesignAssignment.tsx b/src/custom/Workspaces/hooks/useDesignAssignment.tsx index 31f268ff9..4f6f5fb33 100644 --- a/src/custom/Workspaces/hooks/useDesignAssignment.tsx +++ b/src/custom/Workspaces/hooks/useDesignAssignment.tsx @@ -95,9 +95,6 @@ const useDesignAssignment = ({ }; const getAddedAndRemovedDesigns = (allAssignedDesigns: Pattern[]): AddedAndRemovedDesigns => { - if (Array.isArray(workspaceDesignsData) && workspaceDesignsData.length === 0) { - return { addedDesignsIds: [], removedDesignsIds: [] }; - } const originalDesignsIds = workspaceDesignsData.map((design) => design.id); const updatedDesignsIds = allAssignedDesigns.map((design) => design.id); diff --git a/src/custom/Workspaces/index.ts b/src/custom/Workspaces/index.ts index 82cd1eacd..7215852a7 100644 --- a/src/custom/Workspaces/index.ts +++ b/src/custom/Workspaces/index.ts @@ -1,17 +1,22 @@ import AssignmentModal from './AssignmentModal'; import DesignTable from './DesignTable'; import EnvironmentTable from './EnvironmentTable'; +import WorkspaceCard from './WorkspaceCard'; import WorkspaceTeamsTable from './WorkspaceTeamsTable'; import WorkspaceViewsTable from './WorkspaceViewsTable'; import useDesignAssignment from './hooks/useDesignAssignment'; import useEnvironmentAssignment from './hooks/useEnvironmentAssignment'; import useTeamAssignment from './hooks/useTeamAssignment'; import useViewAssignment from './hooks/useViewsAssignment'; +import { L5DeleteIcon, L5EditIcon } from './styles'; export { AssignmentModal, DesignTable, EnvironmentTable, + L5DeleteIcon, + L5EditIcon, + WorkspaceCard, WorkspaceTeamsTable, WorkspaceViewsTable, useDesignAssignment, diff --git a/src/custom/Workspaces/styles.ts b/src/custom/Workspaces/styles.ts deleted file mode 100644 index c26b225ab..000000000 --- a/src/custom/Workspaces/styles.ts +++ /dev/null @@ -1,112 +0,0 @@ -import EditIcon from '@mui/icons-material/Edit'; -import { buttonDisabled, styled } from '../../theme'; -import { KEPPEL } from '../../theme/colors/colors'; - -export const ModalActionDiv = styled('div')({ - display: 'flex', - gap: '1rem' -}); - -interface ExtendedEditIconProps { - disabled?: boolean; - bulk?: boolean; - style?: React.CSSProperties; -} - -export const L5EditIcon = styled(EditIcon)( - ({ disabled, bulk, style, theme }) => ({ - color: disabled ? theme.palette.icon.disabled : theme.palette.text.secondary, - cursor: disabled ? 'not-allowed' : 'pointer', - width: bulk ? '32' : '28.8', - height: bulk ? '32' : '28.8', - '&:hover': { - color: disabled ? buttonDisabled : KEPPEL, - '& svg': { - color: disabled ? buttonDisabled : KEPPEL - } - }, - '& svg': { - color: theme.palette.error.main, - cursor: disabled ? 'not-allowed' : 'pointer' - }, - ...style - }) -); - -export const TableHeader = styled('div')({ - display: 'flex', - justifyContent: 'space-between', - width: '100%', - alignItems: 'center' -}); - -export const TableRightActionHeader = styled('div')({ - display: 'flex', - alignItems: 'center', - marginRight: '1rem' -}); - -export const CellStyle = styled('div')({ - boxSizing: 'border-box', - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap' -}); - -export const CustomBodyRenderStyle = styled('div')({ - position: 'absolute', - top: 0, - right: 0, - bottom: 0, - left: 0, - boxSizing: 'border-box', - display: 'block', - width: '100%' -}); - -export const TableTopIcon = styled('span')(() => ({ - '& svg': { - cursor: 'pointer', - width: '2rem', - height: '2rem' - } -})); - -export const DisabledTableTopIcon = styled('span')(() => ({ - '& svg': { - width: '2rem', - height: '2rem' - } -})); - -export const MesheryDeleteIcon = styled('span')(({ theme }) => ({ - '& svg': { - color: '#3C494F', - '&:hover': { - color: theme.palette.error.error - } - } -})); - -export const TableIconsDisabledContainer = styled('span')(() => ({ - color: '#455a64', - opacity: '0.5', - '& svg': { - cursor: 'not-allowed' - } -})); - -export const TableTopIconsWrapper = styled('div')(() => ({ - display: 'flex', - justifyContent: 'space-between', - paddingRight: '26px' -})); - -export const TableIconsContainer = styled('div')(() => ({ - color: '#455a64', - display: 'flex', - cursor: 'not-allowed', - '& svg': { - cursor: 'pointer' - } -})); diff --git a/src/custom/Workspaces/styles.tsx b/src/custom/Workspaces/styles.tsx new file mode 100644 index 000000000..0fdea72a3 --- /dev/null +++ b/src/custom/Workspaces/styles.tsx @@ -0,0 +1,415 @@ +import { Box, Button, Card, Checkbox, Grid, IconButton, Typography } from '../../base'; +import { DeleteIcon, EditIcon } from '../../icons'; +import { styled, useTheme } from '../../theme'; +import { charcoal } from '../../theme/colors/colors'; +import { CustomTooltip } from '../CustomTooltip'; + +export const ModalActionDiv = styled('div')({ + display: 'flex', + gap: '1rem' +}); + +interface ExtendedEditIconProps { + onClick: () => void; + disabled?: boolean; + bulk?: boolean; + style?: React.CSSProperties; + title?: string; +} + +export const TableHeader = styled('div')({ + display: 'flex', + justifyContent: 'space-between', + width: '100%', + alignItems: 'center' +}); + +export const TableRightActionHeader = styled('div')({ + display: 'flex', + alignItems: 'center', + marginRight: '1rem' +}); + +export const CellStyle = styled('div')({ + boxSizing: 'border-box', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap' +}); + +export const CustomBodyRenderStyle = styled('div')({ + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0, + boxSizing: 'border-box', + display: 'block', + width: '100%' +}); + +export const TableTopIcon = styled('span')(() => ({ + '& svg': { + cursor: 'pointer', + width: '2rem', + height: '2rem' + } +})); + +export const DisabledTableTopIcon = styled('span')(() => ({ + '& svg': { + width: '2rem', + height: '2rem' + } +})); + +export const MesheryDeleteIcon = styled('span')(({ theme }) => ({ + '& svg': { + color: '#3C494F', + '&:hover': { + color: theme.palette.error.error + } + } +})); + +export const TableIconsDisabledContainer = styled('span')(() => ({ + color: '#455a64', + opacity: '0.5', + '& svg': { + cursor: 'not-allowed' + } +})); + +export const TableTopIconsWrapper = styled('div')(() => ({ + display: 'flex', + justifyContent: 'space-between', + paddingRight: '26px' +})); + +export const TableIconsContainer = styled('div')(() => ({ + color: '#455a64', + display: 'flex', + cursor: 'not-allowed', + '& svg': { + cursor: 'pointer' + } +})); + +export const BulkSelectCheckbox = styled(Checkbox)({ + padding: 0, + marginRight: '0.5rem', + height: '28px', + '& .MuiSvgIcon-root': { + borderColor: 'white' + }, + color: 'white', + '&:hover': { + color: 'white', + cursor: 'pointer' + }, + '&.Mui-checked': { + color: 'white' + } +}); + +export const CardTitle = styled(Typography)({ + fontSize: '1.25rem', + fontWeight: 800, + '&:hover': { + cursor: 'default' + } +}); + +export const OrganizationName = styled(Typography)({ + fontSize: '0.9rem', + display: 'flex', + alignItems: 'end', + padding: '0 5px', + '&:hover': { + cursor: 'default' + } +}); + +export const StyledIconButton = styled('button')({ + background: 'transparent', + border: 'none', + '&:hover': { + cursor: 'default' + } +}); + +export const DateLabel = styled(Typography)({ + fontStyle: 'italic', + fontSize: '12px', + '&:hover': { + cursor: 'default' + } +}); + +export const EmptyDescription = styled(Typography)({ + fontSize: '0.9rem', + textAlign: 'left', + fontStyle: 'italic' +}); + +export const DescriptionLabel = styled(EmptyDescription)({ + height: 'fit-content', + fontStyle: 'normal', + '&:hover': { + cursor: 'default' + } +}); + +export const AllocationButton = styled(Box)(({ theme }) => ({ + background: theme.palette.background.brand?.default, + padding: '10px 10px 1px 10px', + borderRadius: '4px', + height: '100%', + display: 'flex', + width: '100%' +})); + +export const AllocationWorkspace = styled(AllocationButton)({ + display: 'flex', + width: '100%', + gap: '10px', + ['@media (min-width : 600px)']: { + flexDirection: 'column', + gap: '0' + } +}); + +export const PopupButton = styled(Button)(({ theme }) => ({ + width: '100%', + borderRadius: '4px', + background: theme.palette.background.brand?.default, + boxShadow: '0px 4px 4px 0px rgba(0, 0, 0, 0.25)', + display: 'flex', + flexDirection: 'column', + marginBottom: '10px', + color: theme.palette.text.default, + '&:hover': { + background: theme.palette.text.default + }, + padding: '15px 10px' +})); + +interface TabStyleProps { + textColor?: string; +} + +export const TabTitle = styled('p')(({ theme, textColor }) => ({ + margin: '0', + fontSize: '14px', + fontWeight: '400', + display: 'flex', + color: textColor || theme.palette.text.constant?.white +})); + +export const TabCount = styled('p')(({ theme, textColor }) => ({ + margin: '0', + fontSize: '60px', + fontWeight: '500', + lineHeight: 1, + marginBottom: '5px', + color: textColor || theme.palette.text.constant?.white +})); + +export const ViewButton = styled(Button)(({ theme }) => ({ + width: '100%', + borderRadius: '4px', + background: theme.palette.text.default, + boxShadow: '0px 4px 4px 0px rgba(0, 0, 0, 0.25)', + display: 'flex', + flexDirection: 'column', + marginBottom: '10px', + color: `${charcoal[40]}30 !important`, + '&:hover': { + background: theme.palette.text.default + }, + padding: '15px 10px' +})); + +interface IconWrapperProps { + disabled?: boolean; +} + +export const IconWrapper = styled('div')(({ disabled = false }) => ({ + cursor: disabled ? 'not-allowed' : 'pointer', + opacity: disabled ? '0.5' : '1', + display: 'flex', + '& svg': { + cursor: disabled ? 'not-allowed' : 'pointer' + } +})); + +export const Record = styled(Grid)(({ theme }) => ({ + borderBottom: `1px solid ${theme.palette.divider}`, + display: 'flex', + flexDirection: 'row', + padding: '5px 0' +})); + +export const L5DeleteIcon = ({ + onClick, + bulk, + disabled, + style, + key, + title = 'Delete' +}: { + onClick: () => void; + bulk?: boolean; + disabled?: boolean; + style?: React.CSSProperties; + key?: string; + title?: string; +}) => { + const theme = useTheme(); + return ( + +
+ + + +
+
+ ); +}; + +export const L5EditIcon = ({ + onClick, + disabled, + bulk, + style, + title = 'Edit' +}: ExtendedEditIconProps) => { + const theme = useTheme(); + return ( + +
+ + + +
+
+ ); +}; + +export const WorkspaceCardGrid = styled(Grid)({ + display: 'flex', + flexDirection: 'row' +}); + +export const DescriptionGrid = styled(Grid)({ + display: 'flex', + alignItems: 'center', + marginTop: 1 +}); + +export const AllocationColumnGrid = styled(Grid)({ + width: '-moz-available' +}); + +export const CardWrapper = styled(Card)(({ theme }) => ({ + width: '100%', + display: 'flex', + flexDirection: 'column', + backgroundColor: theme.palette.background.paper, + padding: theme.spacing(2.5), + cursor: 'pointer' +})); + +export const CardBackWrapper = styled(CardWrapper)(({ theme }) => ({ + minHeight: theme.spacing(50), + background: 'linear-gradient(180deg, #007366 0%, #000 100%)' +})); + +export const CardFrontWrapper = styled(CardWrapper)(({ theme }) => ({ + minHeight: theme.spacing(50), + + backgroundColor: theme.palette.background.paper, + boxShadow: 'none' +})); + +export const CardBackTopGrid = styled(Grid)({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between' +}); + +export const CardBackTitleGrid = styled(Grid)({ + display: 'flex', + alignItems: 'flex-start' +}); + +export const CardBackActionsGrid = styled(Grid)({ + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-end' +}); + +export const RecentActivityTitle = styled(Typography)(({ theme }) => ({ + fontSize: '1.25rem', + fontWeight: 600, + padding: '0.5rem 0', + color: theme.palette.background.constant?.white +})); + +export const RecentActivityGrid = styled(Grid)({ + display: 'flex', + flexDirection: 'column', + maxHeight: '14.5rem', + overflowY: 'scroll' +}); + +export const DateGrid = styled(Grid)(({ theme }) => ({ + display: 'flex', + flexDirection: 'row', + position: 'absolute', + bottom: '20px', + width: '100%', + color: `${theme.palette.background.constant?.white}99`, + justifyContent: 'space-between', + paddingRight: '40px' +})); + +export const DateColumnGrid = styled(Grid)({ + textAlign: 'left' +}); diff --git a/src/custom/index.tsx b/src/custom/index.tsx index cc47f9dd8..4aeb3fdb2 100644 --- a/src/custom/index.tsx +++ b/src/custom/index.tsx @@ -47,7 +47,7 @@ import { TooltipIcon } from './TooltipIconButton'; import { TransferList } from './TransferModal/TransferList'; import { TransferListProps } from './TransferModal/TransferList/TransferList'; import UniversalFilter, { UniversalFilterProps } from './UniversalFilter'; -import { UsersTable } from './UsersTable'; +import { UserTableAvatarInfo, UsersTable } from './UsersTable'; import { VisibilityChipMenu } from './VisibilityChipMenu'; export { CatalogCard } from './CatalogCard'; export { CatalogFilterSidebar } from './CatalogFilterSection'; @@ -104,6 +104,7 @@ export { TooltipIcon, TransferList, UniversalFilter, + UserTableAvatarInfo, UsersTable, VisibilityChipMenu, updateVisibleColumns, diff --git a/src/icons/Delete/DeleteIcon.tsx b/src/icons/Delete/DeleteIcon.tsx index 31d3a5500..0877592e3 100644 --- a/src/icons/Delete/DeleteIcon.tsx +++ b/src/icons/Delete/DeleteIcon.tsx @@ -8,14 +8,15 @@ export const DeleteIcon = ({ style, ...props }: IconProps): JSX.Element => { + const _finalFill = style?.fill || fill; + return ( diff --git a/src/icons/View/ViewIcon.tsx b/src/icons/View/ViewIcon.tsx new file mode 100644 index 000000000..0ccc69c28 --- /dev/null +++ b/src/icons/View/ViewIcon.tsx @@ -0,0 +1,34 @@ +import { FC } from 'react'; +import { IconProps } from '../types'; + +const ViewsIcon: FC = ({ width, height, style = {}, fill }) => ( + + + + + + + + + + + + +); + +export default ViewsIcon; diff --git a/src/icons/View/index.ts b/src/icons/View/index.ts new file mode 100644 index 000000000..07e933a55 --- /dev/null +++ b/src/icons/View/index.ts @@ -0,0 +1 @@ +export { default as ViewIcon } from './ViewIcon'; diff --git a/src/icons/index.ts b/src/icons/index.ts index 518f9d688..e162d2c1f 100644 --- a/src/icons/index.ts +++ b/src/icons/index.ts @@ -104,6 +104,7 @@ export * from './Tropy'; export * from './Undeploy'; export * from './Undo'; export * from './Validate'; +export * from './View'; export * from './Visibility'; export * from './Visualizer'; export * from './Workspace';