diff --git a/.changeset/pr-900-2239364209.md b/.changeset/pr-900-2239364209.md new file mode 100644 index 00000000..03ba7199 --- /dev/null +++ b/.changeset/pr-900-2239364209.md @@ -0,0 +1,9 @@ + +--- +"fusion-project-portal": patch +--- +Better styling for better layout +- Displaying deleted apps better +- Displaying version information +- Not allowing context app connections on non context portals +- Better AG grid configuration diff --git a/client/apps/portal-administration/package.json b/client/apps/portal-administration/package.json index d293e2b2..13b9d1b5 100644 --- a/client/apps/portal-administration/package.json +++ b/client/apps/portal-administration/package.json @@ -1,6 +1,6 @@ { "name": "portal-administration", - "version": "1.1.2", + "version": "1.1.3", "description": "", "private": true, "type": "module", diff --git a/client/apps/portal-administration/src/components/Ag-Grid/defaultColDef.ts b/client/apps/portal-administration/src/components/Ag-Grid/defaultColDef.ts new file mode 100644 index 00000000..fa8c5754 --- /dev/null +++ b/client/apps/portal-administration/src/components/Ag-Grid/defaultColDef.ts @@ -0,0 +1,67 @@ +import { ColDef } from '@equinor/workspace-ag-grid'; +import { format } from 'date-fns/format'; + +export const defaultColDef: ColDef = { + valueFormatter: (a) => tryFormat(a.value), + resizable: true, + filter: true, + sortable: true, +}; + +function tryFormat(value: unknown): string { + switch (typeof value) { + case 'string': { + const maybeDate = new Date(value); + if (Number.isNaN(maybeDate.valueOf())) { + return value; + } + return formatDate(maybeDate); + } + + case 'number': { + return formatNumber(value); + } + + case 'object': { + if (value === null) { + return formatNull(); + } + + if (value instanceof Date) { + return formatDate(value); + } + } + + case 'undefined': { + return formatUndefined(); + } + + case 'boolean': { + return formatBoolean(value); + } + + default: { + return '-'; + } + } +} + +function formatNumber(val: number) { + return val.toLocaleString('no'); +} + +function formatNull() { + return '-'; +} + +function formatUndefined() { + return '-'; +} + +function formatBoolean(val: boolean) { + return val ? 'yes' : 'no'; +} + +function formatDate(date: Date) { + return format(date, 'dd/MM/yy').toString(); +} diff --git a/client/apps/portal-administration/src/components/Ag-Grid/defaultGridOptions.ts b/client/apps/portal-administration/src/components/Ag-Grid/defaultGridOptions.ts new file mode 100644 index 00000000..6da1b301 --- /dev/null +++ b/client/apps/portal-administration/src/components/Ag-Grid/defaultGridOptions.ts @@ -0,0 +1,24 @@ +import { GridOptions } from '@equinor/workspace-ag-grid'; +import { defaultColDef } from './defaultColDef'; + +export const defaultGridOptions: GridOptions = { + defaultColDef: defaultColDef, + serverSideInitialRowCount: 100, + enableBrowserTooltips: true, + enableCellTextSelection: true, + ensureDomOrder: true, + onFirstDataRendered: (event) => { + const api = event.api; + api.sizeColumnsToFit(); + }, + suppressColumnVirtualisation: true, + autoSizePadding: 10, + rowHeight: 36, +}; + +export const bottomPadding = 70; + +export function capitalizeFirstLetter(str?: string) { + if (!str) return ''; // Handle empty string + return str.charAt(0).toUpperCase() + str.slice(1); +} diff --git a/client/apps/portal-administration/src/components/AgStyle.tsx b/client/apps/portal-administration/src/components/AgStyle.tsx index 9bdbe38c..2a993ef5 100644 --- a/client/apps/portal-administration/src/components/AgStyle.tsx +++ b/client/apps/portal-administration/src/components/AgStyle.tsx @@ -24,7 +24,7 @@ export const AgStyles = { TableContent: styled.div` padding-top: 1rem; position: relative; - height: calc(100% - 3rem); + height: calc(100% - 1rem); width: 100%; `, @@ -68,5 +68,9 @@ export const AgStyles = { font-weight: bold; background-color: ${tokens.colors.ui.background__danger.hex} !important; } + .noBuilds { + font-weight: bold; + background-color: ${tokens.colors.ui.background__warning.hex} !important; + } `, }; diff --git a/client/apps/portal-administration/src/components/OnboardedApps/AppsTable.tsx b/client/apps/portal-administration/src/components/OnboardedApps/AppsTable.tsx index d7237f2f..84db8964 100644 --- a/client/apps/portal-administration/src/components/OnboardedApps/AppsTable.tsx +++ b/client/apps/portal-administration/src/components/OnboardedApps/AppsTable.tsx @@ -1,19 +1,16 @@ -import { CustomCellRendererProps } from '@ag-grid-community/react'; -import { Button, Icon } from '@equinor/eds-core-react'; import { ClientGrid } from '@equinor/workspace-ag-grid'; -import { useRef, useState } from 'react'; +import { useMemo, useRef, useState } from 'react'; import { useDeleteOnboardedApp } from '../../hooks/use-onboarded-apps'; import { PortalApp } from '../../types'; -import { delete_to_trash, edit } from '@equinor/eds-icons'; -import { AgStyles } from '../AgStyle'; import { OnboardedAppsActionBar } from './OnboardedAppsActionBar'; import { useResizeObserver } from '../../hooks/use-resise-observer'; import { Message } from '../Message'; - -const FAIL_MESSAGE = 'Application Error!'; +import { AgStyles } from '../AgStyle'; +import { appsColDefs } from './appsColDef'; +import { defaultGridOptions } from '../Ag-Grid/defaultGridOptions'; export const AppsTable = ({ onboardedApps }: { onboardedApps: PortalApp[] | undefined }) => { const [selectedApps, setSelectedApps] = useState([]); @@ -22,33 +19,20 @@ export const AppsTable = ({ onboardedApps }: { onboardedApps: PortalApp[] | unde const ref = useRef(null); const [_, height] = useResizeObserver(ref); + const coldDef = useMemo(() => appsColDefs(deleteAppByAppKey), [deleteAppByAppKey]); + return ( height={selectedApps.length === 0 ? height : height - 150} + {...defaultGridOptions} rowData={onboardedApps || []} noRowsOverlayComponent={() => } rowSelection="multiple" - rowHeight={36} - defaultColDef={{ - filter: true, - flex: 1, - sortable: true, - resizable: true, - }} rowClassRules={{ notActive: (params) => !Boolean(params.data?.displayName || params.data?.description), }} - autoSizeStrategy={{ - type: 'fitGridWidth', - defaultMinWidth: 80, - defaultMaxWidth: 700, - }} - onGridReady={(event) => { - const api = event.api; - api.sizeColumnsToFit(); - }} onRowDataUpdated={() => { setSelectedApps([]); }} @@ -56,80 +40,7 @@ export const AppsTable = ({ onboardedApps }: { onboardedApps: PortalApp[] | unde const selectedRows = event.api!.getSelectedRows(); setSelectedApps(selectedRows); }} - colDefs={[ - { - field: 'id', - headerName: 'Id', - hide: true, - }, - { - field: 'displayName', - headerName: 'Display Name', - filter: true, - valueFormatter: (params) => { - return params.value ? params.value : FAIL_MESSAGE; - }, - }, - { - field: 'appKey', - headerName: 'App Key', - filter: true, - }, - { - field: 'description', - headerName: 'Description', - width: 700, - }, - - { - field: 'contexts', - headerName: 'Contexts Types', - filter: true, - - cellRenderer: ( - params: CustomCellRendererProps<{ - contextTypes: string[]; - appKey: string; - }> - ) => { - return ( - - {params?.data?.contextTypes?.map((type) => { - return ( - - {type} - - ); - })} - - ); - }, - }, - { - field: 'appKey', - headerName: 'Action', - maxWidth: 100, - resizable: false, - cellRenderer: (params: CustomCellRendererProps) => { - return ( - - - - ); - }, - }, - ]} + colDefs={coldDef} /> diff --git a/client/apps/portal-administration/src/components/OnboardedApps/OnboardApp.tsx b/client/apps/portal-administration/src/components/OnboardedApps/OnboardApp.tsx index 12f7e2c5..e2a5b081 100644 --- a/client/apps/portal-administration/src/components/OnboardedApps/OnboardApp.tsx +++ b/client/apps/portal-administration/src/components/OnboardedApps/OnboardApp.tsx @@ -10,8 +10,7 @@ import { OnboardAppInputs, onboardAppInput } from '../../schema/app'; import { useAddOnboardedApp, useOnboardedApps } from '../../hooks/use-onboarded-apps'; import { useGetContextTypes } from '../../hooks/use-context-type-query'; import { AppSelector } from './AppSelector'; -import { Row } from '@equinor/eds-core-react/dist/types/components/Table/Row'; -import { arrow_back, arrow_drop_left, chevron_down, chevron_left } from '@equinor/eds-icons'; +import { chevron_down, chevron_left } from '@equinor/eds-icons'; import { InfoPopover } from '../InfoPopover'; const Style = { @@ -89,7 +88,7 @@ export const OnboardApp = () => { return ( - setActive((s) => !s)}> + Onboard App diff --git a/client/apps/portal-administration/src/components/OnboardedApps/OnboardedAppsActionBar.tsx b/client/apps/portal-administration/src/components/OnboardedApps/OnboardedAppsActionBar.tsx index ef020edb..52972447 100644 --- a/client/apps/portal-administration/src/components/OnboardedApps/OnboardedAppsActionBar.tsx +++ b/client/apps/portal-administration/src/components/OnboardedApps/OnboardedAppsActionBar.tsx @@ -43,7 +43,9 @@ export const OnboardedAppsActionBar = ({ selection }: { selection: PortalApp[] } } }, [selection]); const { data: contextTypes } = useGetContextTypes(); + if (selection.length === 0) return null; + return ( @@ -59,7 +61,6 @@ export const OnboardedAppsActionBar = ({ selection }: { selection: PortalApp[] } return item === compare; }} onOptionsChange={(change) => { - console.log(change.selectedItems); setSelected(change.selectedItems); editSelected( selection.map((s) => ({ appKey: s.appKey, contextTypes: change.selectedItems })) diff --git a/client/apps/portal-administration/src/components/OnboardedApps/appsColDef.tsx b/client/apps/portal-administration/src/components/OnboardedApps/appsColDef.tsx new file mode 100644 index 00000000..baae4538 --- /dev/null +++ b/client/apps/portal-administration/src/components/OnboardedApps/appsColDef.tsx @@ -0,0 +1,98 @@ +import { CustomCellRendererProps } from '@ag-grid-community/react'; +import { Button, Icon } from '@equinor/eds-core-react'; +import { delete_to_trash } from '@equinor/eds-icons'; + +import { FormattedError, PortalApp } from '../../types'; +import { AgStyles } from '../AgStyle'; +import { UseMutateAsyncFunction } from '@tanstack/react-query'; +import { ValueFormatterParams } from '@ag-grid-community/core'; +import { ColDef } from '@equinor/workspace-ag-grid'; +import { capitalizeFirstLetter } from '../Ag-Grid/defaultGridOptions'; + +export const appsColDefs = ( + deleteAppByAppKey: UseMutateAsyncFunction< + boolean, + FormattedError, + string, + { + appKey: string; + contextTypes: string[]; + } + > +): ColDef[] => [ + { + field: 'id', + headerName: 'Id', + hide: true, + }, + { + field: 'displayName', + headerName: 'Display Name', + filter: true, + valueFormatter: (params) => { + return !params.data?.doesNotExistInFusion ? params.value : `${capitalizeFirstLetter(params.data.appKey)}`; + }, + }, + { + field: 'appKey', + headerName: 'App Key', + filter: true, + }, + { + field: 'description', + headerName: 'Description', + width: 700, + valueFormatter: (params) => { + return !params.data?.doesNotExistInFusion ? params.value : "This app doesn't exist in Fusion"; + }, + }, + + { + field: 'contexts', + headerName: 'Contexts Types', + filter: true, + + cellRenderer: ( + params: CustomCellRendererProps<{ + contextTypes: string[]; + appKey: string; + }> + ) => { + return ( + + {params?.data?.contextTypes?.map((type) => { + return ( + + {type} + + ); + })} + + ); + }, + }, + { + field: 'appKey', + headerName: 'Action', + maxWidth: 100, + resizable: false, + cellRenderer: (params: CustomCellRendererProps) => { + return ( + + + + ); + }, + }, +]; diff --git a/client/apps/portal-administration/src/components/OnboardedContexts/AddContext.tsx b/client/apps/portal-administration/src/components/OnboardedContexts/AddContext.tsx index d62cc767..0ed9e704 100644 --- a/client/apps/portal-administration/src/components/OnboardedContexts/AddContext.tsx +++ b/client/apps/portal-administration/src/components/OnboardedContexts/AddContext.tsx @@ -66,7 +66,7 @@ export const AddContext = () => { return ( - setActive((s) => !s)}> + Add Context diff --git a/client/apps/portal-administration/src/components/OnboardedContexts/ContextType.tsx b/client/apps/portal-administration/src/components/OnboardedContexts/ContextType.tsx index dcd0a698..2716b7a4 100644 --- a/client/apps/portal-administration/src/components/OnboardedContexts/ContextType.tsx +++ b/client/apps/portal-administration/src/components/OnboardedContexts/ContextType.tsx @@ -80,7 +80,7 @@ export const EditContextTypeForm = () => { {isAdmin && ( - setActive((s) => !s)}> + Add Context Type diff --git a/client/apps/portal-administration/src/components/OnboardedContexts/OnboardedContextsTable.tsx b/client/apps/portal-administration/src/components/OnboardedContexts/OnboardedContextsTable.tsx index 6e493e55..35b0e638 100644 --- a/client/apps/portal-administration/src/components/OnboardedContexts/OnboardedContextsTable.tsx +++ b/client/apps/portal-administration/src/components/OnboardedContexts/OnboardedContextsTable.tsx @@ -3,9 +3,11 @@ import { ClientGrid } from '@equinor/workspace-ag-grid'; import { useEditOnboardContext } from '../../hooks/use-onboarded-context'; import { OnboardedContext } from '../../types'; -import { AgStyles } from '../AgStyle'; + import { useRef } from 'react'; import { useResizeObserver } from '../../hooks/use-resise-observer'; +import { AgStyles } from '../AgStyle'; +import { bottomPadding, defaultGridOptions } from '../Ag-Grid/defaultGridOptions'; export function OnboardedContextsTable({ onboardedContexts }: { onboardedContexts?: OnboardedContext[] }) { const { mutateAsync } = useEditOnboardContext(); @@ -17,25 +19,9 @@ export function OnboardedContextsTable({ onboardedContexts }: { onboardedContext - height={height} + height={height - bottomPadding} + {...defaultGridOptions} rowData={onboardedContexts || []} - enableCellTextSelection - ensureDomOrder - defaultColDef={{ - filter: true, - flex: 1, - sortable: true, - resizable: true, - }} - autoSizeStrategy={{ - type: 'fitGridWidth', - defaultMinWidth: 80, - defaultMaxWidth: 300, - }} - onGridReady={(event) => { - const api = event.api; - api.sizeColumnsToFit(); - }} onCellValueChanged={(event) => { if (event.data) mutateAsync(event.data); }} diff --git a/client/apps/portal-administration/src/components/PortalApps/ActionBar.tsx b/client/apps/portal-administration/src/components/PortalApps/ActionBar.tsx index c93dbe45..f9cc580e 100644 --- a/client/apps/portal-administration/src/components/PortalApps/ActionBar.tsx +++ b/client/apps/portal-administration/src/components/PortalApps/ActionBar.tsx @@ -4,13 +4,14 @@ import { Typography } from '@equinor/eds-core-react'; import { useAddPortalApps, useRemovePortalApps } from '../../hooks/use-portal-apps'; import { usePortalContext } from '../../context/PortalContext'; import { ContextAppSideSheet } from './ContextAppSideSheet'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import { MakeSelectionGlobalButton } from '../Actions/MakeGlobalAppsButton'; import { ActivateSelectedButton } from '../Actions/ActivateSelectedButton'; import { RemoveAppsButton } from '../Actions/RemoveAppsButton'; import { EditSelectedButton } from '../Actions/EditSelectedButton'; import { ActivateSelectedWithContextButton } from '../Actions/ActivateSelectedWithContextButton'; import { useQueryClient } from '@tanstack/react-query'; +import { Message } from '../Message'; const Styles = { Wrapper: styled.div` @@ -39,7 +40,7 @@ const Styles = { }; export const ActionBar = ({ selection }: { selection: PortalApplication[] }) => { - const { activePortalId } = usePortalContext(); + const { activePortalId, contexts } = usePortalContext(); const queryClient = useQueryClient(); const { mutateAsync: activateSelected } = useAddPortalApps(activePortalId); @@ -47,6 +48,22 @@ export const ActionBar = ({ selection }: { selection: PortalApplication[] }) => const [isOpen, setIsOpen] = useState(false); + /** + * Determines if any of the selected apps do not have a version. + * @returns {boolean} `true` if any app in the selection has a `null` build version or no `appManifest`; otherwise, `false`. + */ + const hadNoVersion = useMemo( + () => selection.some((app) => app.appManifest?.build === null || !app.appManifest), + [selection] + ); + + /** + * Determines if the current portal is app driven or context driven portal. If the portal has contexts, it is a context driven portal. + * And the user can activate the selected apps with context. + * @returns {boolean} True if there are contexts and the length of contexts is greater than 0, otherwise false. + */ + const isContextPortal = useMemo(() => contexts && contexts.length > 0, [contexts]); + if (selection.length === 0) return null; return ( @@ -65,21 +82,30 @@ export const ActionBar = ({ selection }: { selection: PortalApplication[] }) => Portal Application Actions - - - setIsOpen(true)} - /> - { - setIsOpen(true); - }} - selection={selection} - /> - - - + {hadNoVersion ? ( + + + + + ) : ( + + + {isContextPortal && ( + setIsOpen(true)} + /> + )} + { + setIsOpen(true); + }} + selection={selection} + /> + + + + )} Selected applications ({selection.length}) diff --git a/client/apps/portal-administration/src/components/PortalApps/ContextAppSideSheet.tsx b/client/apps/portal-administration/src/components/PortalApps/ContextAppSideSheet.tsx index 6e6b0a25..03096618 100644 --- a/client/apps/portal-administration/src/components/PortalApps/ContextAppSideSheet.tsx +++ b/client/apps/portal-administration/src/components/PortalApps/ContextAppSideSheet.tsx @@ -81,7 +81,6 @@ export function ContextAppSideSheet({ const { data: activeApp, isLoading } = useGetPortalApp(activePortalId, app.appManifest.appKey); const contexts: OnboardedContext[] = useMemo(() => { - if (!activeApp || !activeContexts) return []; return activeContexts.map((context) => ({ ...context, @@ -125,7 +124,6 @@ export function ContextAppSideSheet({ rowHeight={36} onRowSelected={(event) => { const selectedRows = event.api!.getSelectedRows(); - setSelectedContexts(selectedRows); }} noRowsOverlayComponent={() => ( @@ -164,10 +162,15 @@ export function ContextAppSideSheet({ editable: true, onCellValueChanged: (event) => { if (event.newValue) { - console.log('Activate context', event.data); - add({ appKey: app.appManifest.appKey, contextIds: [event.data.contextId] }); + add({ + appKey: app.appManifest.appKey, + contextIds: [event.data.contextId], + }); } else { - remove({ appKey: app.appManifest.appKey, contextIds: [event.data.contextId] }); + remove({ + appKey: app.appManifest.appKey, + contextIds: [event.data.contextId], + }); } }, }, diff --git a/client/apps/portal-administration/src/components/PortalApps/PortalAppTable.tsx b/client/apps/portal-administration/src/components/PortalApps/PortalAppTable.tsx index 71b419b1..81936d04 100644 --- a/client/apps/portal-administration/src/components/PortalApps/PortalAppTable.tsx +++ b/client/apps/portal-administration/src/components/PortalApps/PortalAppTable.tsx @@ -1,14 +1,16 @@ import { ClientGrid } from '@equinor/workspace-ag-grid'; import { useRef, useState } from 'react'; -import { CustomCellRendererProps } from '@ag-grid-community/react'; + import { useResizeObserver } from '../../hooks/use-resise-observer'; -import { ContextType, PortalApplication } from '../../types'; +import { PortalApplication } from '../../types'; import { ActionBar } from './ActionBar'; import { AgStyles } from '../AgStyle'; import { Message } from '../Message'; -const FAIL_MESSAGE = 'Application Error!'; +import { colDefs } from './portalAppsColDef'; + +import { defaultGridOptions } from '../Ag-Grid/defaultGridOptions'; export const PortalAppTable = ({ portalApps, canEdit }: { portalApps: PortalApplication[]; canEdit?: boolean }) => { const ref = useRef(null); @@ -20,31 +22,15 @@ export const PortalAppTable = ({ portalApps, canEdit }: { portalApps: PortalAppl + {...defaultGridOptions} height={selectedApps.length === 0 ? height : height - 150} rowData={portalApps || []} noRowsOverlayComponent={() => } - enableCellTextSelection - defaultColDef={{ - filter: true, - flex: 1, - sortable: true, - resizable: true, - }} rowClassRules={{ - notActive: (params) => !Boolean(params.data?.appManifest), - }} - ensureDomOrder - onGridReady={(event) => { - const api = event.api; - api.sizeColumnsToFit(); + notActive: (params) => Boolean(params.data?.doesNotExistInFusion), + noBuilds: (params) => Boolean(params.data?.appManifest?.build === null), }} rowSelection="multiple" - rowHeight={36} - autoSizeStrategy={{ - type: 'fitGridWidth', - defaultMinWidth: 80, - defaultMaxWidth: 300, - }} onRowSelected={(event) => { const selectedRows = event.api!.getSelectedRows(); canEdit && setSelectedApps(selectedRows); @@ -52,145 +38,7 @@ export const PortalAppTable = ({ portalApps, canEdit }: { portalApps: PortalAppl onRowDataUpdated={() => { canEdit && setSelectedApps([]); }} - colDefs={[ - { - field: 'isActive', - headerName: 'Status', - maxWidth: 110, - cellRenderer: ( - params: CustomCellRendererProps<{ - isActive?: boolean; - isContextual?: boolean; - appKey: string; - appManifest?: { name: string }; - }> - ) => { - return ( - - {params.data?.isContextual ? ( - - ) : ( - - )} - - ); - }, - }, - { - field: 'isContextual', - headerName: 'Is Contextual', - maxWidth: 130, - hide: true, - cellRenderer: ( - params: CustomCellRendererProps<{ - isContextual?: boolean; - appKey: string; - }> - ) => { - return ( - - - - ); - }, - }, - { - field: 'appManifest.displayName', - headerName: 'Name', - filterParams: { - filterOptions: ['contains', 'startsWith', 'endsWith'], - defaultOption: 'startsWith', - }, - cellRenderer: ( - params: CustomCellRendererProps<{ - appManifest?: { displayName: string }; - }> - ) => { - return ( - - {params.data?.appManifest ? params.data?.appManifest.displayName : FAIL_MESSAGE} - - ); - }, - }, - { - field: 'appKey', - maxWidth: 350, - headerName: 'Application key', - filterParams: { - filterOptions: ['contains', 'startsWith', 'endsWith'], - defaultOption: 'startsWith', - }, - }, - { - field: 'appManifest.category.displayName', - maxWidth: 350, - headerName: 'Category', - filterParams: { - filterOptions: ['contains', 'startsWith', 'endsWith'], - defaultOption: 'startsWith', - }, - cellRenderer: ( - params: CustomCellRendererProps<{ - appManifest?: { category: { displayName: string } }; - }> - ) => { - return ( - - {params.data?.appManifest - ? params.data?.appManifest.category.displayName - : FAIL_MESSAGE} - - ); - }, - }, - - { - field: 'appManifest', - headerName: 'Description', - filterParams: { - filterOptions: ['contains', 'startsWith', 'endsWith'], - defaultOption: 'startsWith', - }, - width: 500, - valueFormatter: (params) => { - return params.data?.appManifest - ? params.data?.appManifest.description - : 'Application Missing application manifest! Application may have been deleted from Fusion app service.'; - }, - }, - - { - field: 'contexts', - headerName: 'Contexts Types', - filter: false, - cellRenderer: ( - params: CustomCellRendererProps<{ - contexts: ContextType[]; - contextTypes: string[]; - appKey: string; - }> - ) => { - return ( - - {params?.data?.contextTypes?.map((type) => { - return ( - - {type} - - ); - })} - - ); - }, - }, - ]} + colDefs={colDefs} /> {canEdit && } diff --git a/client/apps/portal-administration/src/components/PortalApps/portalAppsColDef.tsx b/client/apps/portal-administration/src/components/PortalApps/portalAppsColDef.tsx new file mode 100644 index 00000000..865b5e67 --- /dev/null +++ b/client/apps/portal-administration/src/components/PortalApps/portalAppsColDef.tsx @@ -0,0 +1,150 @@ +import { CustomCellRendererProps } from '@ag-grid-community/react'; + +import { AgStyles } from '../AgStyle'; +import { ColDef } from '@equinor/workspace-ag-grid'; +import { capitalizeFirstLetter } from '../Ag-Grid/defaultGridOptions'; + +export const colDefs: ColDef[] = [ + { + field: 'isActive', + headerName: 'Status', + maxWidth: 110, + cellRenderer: ( + params: CustomCellRendererProps<{ + isActive?: boolean; + isContextual?: boolean; + appKey: string; + appManifest?: { name: string }; + }> + ) => { + return ( + + {params.data?.isContextual ? ( + + ) : ( + + )} + + ); + }, + }, + { + field: 'isContextual', + headerName: 'Is Contextual', + + width: 110, + hide: true, + cellRenderer: ( + params: CustomCellRendererProps<{ + isContextual?: boolean; + appKey: string; + }> + ) => { + return ( + + + + ); + }, + }, + { + field: 'appManifest.displayName', + headerName: 'Name', + width: 110, + filterParams: { + filterOptions: ['contains', 'startsWith', 'endsWith'], + defaultOption: 'startsWith', + }, + cellRenderer: ( + params: CustomCellRendererProps<{ + appManifest?: { displayName: string }; + appKey: string; + }> + ) => { + return ( + + {params.data?.appManifest + ? params.data?.appManifest.displayName + : capitalizeFirstLetter(params.data?.appKey)} + + ); + }, + }, + { + field: 'appKey', + maxWidth: 350, + width: 200, + headerName: 'Application key', + filterParams: { + filterOptions: ['contains', 'startsWith', 'endsWith'], + defaultOption: 'startsWith', + }, + }, + { + field: 'appManifest.category.displayName', + maxWidth: 350, + width: 200, + headerName: 'Category', + filterParams: { + filterOptions: ['contains', 'startsWith', 'endsWith'], + defaultOption: 'startsWith', + }, + }, + { + field: 'appManifest.build.version', + headerName: 'Build Version', + width: 200, + filterParams: { + filterOptions: ['contains', 'startsWith', 'endsWith'], + defaultOption: 'startsWith', + }, + maxWidth: 200, + valueFormatter: (params) => { + return params.data?.appManifest?.build?.version || 'No version available!'; + }, + }, + { + field: 'appManifest', + headerName: 'Description', + filterParams: { + filterOptions: ['contains', 'startsWith', 'endsWith'], + defaultOption: 'startsWith', + }, + width: 500, + valueFormatter: (params) => { + return params.data?.appManifest + ? params.data?.appManifest.description + : 'Application Missing application manifest! Application may have been deleted from Fusion app service.'; + }, + }, + { + field: 'contextTypes', + headerName: 'Contexts Types', + filter: false, + width: 200, + cellRenderer: ( + params: CustomCellRendererProps<{ + contextTypes: string[]; + appKey: string; + }> + ) => { + return ( + + {params?.data?.contextTypes?.map((type) => { + return ( + + {type} + + ); + })} + + ); + }, + }, +]; diff --git a/client/apps/portal-administration/src/components/Portals/PortalTable.tsx b/client/apps/portal-administration/src/components/Portals/PortalTable.tsx index f5f004a9..bd4c8831 100644 --- a/client/apps/portal-administration/src/components/Portals/PortalTable.tsx +++ b/client/apps/portal-administration/src/components/Portals/PortalTable.tsx @@ -14,6 +14,7 @@ import { AgStyles } from '../AgStyle'; import { Message } from '../Message'; import { usePortalContext } from '../../context/PortalContext'; import { tokens } from '@equinor/eds-tokens'; +import { defaultGridOptions } from '../Ag-Grid/defaultGridOptions'; export function PortalTable({ portalsData }: { portalsData?: Portal[] }) { const { mutateAsync: deletePortal } = useDeletePortal(); @@ -30,22 +31,9 @@ export function PortalTable({ portalsData }: { portalsData?: Portal[] }) { height={height} + {...defaultGridOptions} rowData={portalsData || []} noRowsOverlayComponent={() => } - rowHeight={36} - autoSizeStrategy={{ - type: 'fitGridWidth', - defaultMinWidth: 80, - defaultMaxWidth: 300, - }} - onGridReady={(event) => { - const api = event.api; - api.sizeColumnsToFit(); - }} - onGridSizeChanged={(event) => { - const api = event.api; - api.sizeColumnsToFit(); - }} getRowStyle={(params) => { if (params.data?.id === activePortalId) { return { backgroundColor: tokens.colors.ui.background__info.rgba }; @@ -55,8 +43,8 @@ export function PortalTable({ portalsData }: { portalsData?: Portal[] }) { { field: 'icon', headerName: 'Icon', - maxWidth: 70, - minWidth: 70, + maxWidth: 80, + minWidth: 80, cellRenderer: ( params: CustomCellRendererProps<{ icon: string; diff --git a/client/apps/portal-administration/src/components/Router/RouterTree.tsx b/client/apps/portal-administration/src/components/Router/RouterTree.tsx index 5f6c6542..a31b1e4f 100644 --- a/client/apps/portal-administration/src/components/Router/RouterTree.tsx +++ b/client/apps/portal-administration/src/components/Router/RouterTree.tsx @@ -9,7 +9,7 @@ import { TreeRoot } from '../Tree/TreeRoot'; const Style = { Router: styled.span` - margin: 1rem 0 2rem 0; + margin-top: 1rem; padding: 1rem; background-color: ${tokens.colors.ui.background__medium.hex}; diff --git a/client/apps/portal-administration/src/pages/Portal.tsx b/client/apps/portal-administration/src/pages/Portal.tsx index 6c077aef..ee3bb4d9 100644 --- a/client/apps/portal-administration/src/pages/Portal.tsx +++ b/client/apps/portal-administration/src/pages/Portal.tsx @@ -16,7 +16,7 @@ const Styles = { flex-direction: column; padding: 1rem; display: flex; - height: 100%; + height: calc(100% - 2rem); `, Wrapper: styled.div` display: block; @@ -31,7 +31,7 @@ export const Portal = () => { useEffect(() => { if (!portalId || portalId === 'undefined') { - navigate('portals'); + navigate('/portals'); } else { activePortalId !== portalId && setActivePortalById(portalId); } diff --git a/client/apps/portal-administration/src/types/index.ts b/client/apps/portal-administration/src/types/index.ts index 78b37863..9c3feaa3 100644 --- a/client/apps/portal-administration/src/types/index.ts +++ b/client/apps/portal-administration/src/types/index.ts @@ -68,6 +68,7 @@ export type PortalApp = { appInformation: { icon: string }; isActive?: boolean; isGlobal?: boolean; + doesNotExistInFusion: boolean; isContextual?: boolean; }; @@ -79,6 +80,7 @@ export type PortalApplication = { isActive?: boolean; isGlobal?: boolean; isContextual?: boolean; + doesNotExistInFusion: boolean; contextIds?: string[]; }; diff --git a/client/apps/portal-administration/src/utils/syntaxHighlightJson.tsx b/client/apps/portal-administration/src/utils/syntaxHighlightJson.tsx index 5f04d075..a4b9d41e 100644 --- a/client/apps/portal-administration/src/utils/syntaxHighlightJson.tsx +++ b/client/apps/portal-administration/src/utils/syntaxHighlightJson.tsx @@ -5,11 +5,12 @@ export const CodeStyle = styled.pre` background-color: #242a2d; border: 2px solid #3a3d3e; color: #ffffff; - padding: 1.5rem; + padding: 2rem; font-family: monospace; max-height: -webkit-fill-available; - margin-bottom: 4rem; + margin-bottom: 0.5rem; overflow: auto; + max-height: calc(100vh - 350px); .key { color: rgb(156, 220, 254); } diff --git a/client/packages/core/src/modules/portal-config/portal.ts b/client/packages/core/src/modules/portal-config/portal.ts index 8d754a52..5ab2f8b9 100644 --- a/client/packages/core/src/modules/portal-config/portal.ts +++ b/client/packages/core/src/modules/portal-config/portal.ts @@ -32,7 +32,9 @@ export type CurrentPortal = IPortal; export class Portal implements IPortal { #state: FlowSubject; + portalAppConfig: PortalConfig; + base: BaseConfig; constructor(args: { provider: PortalConfigProvider; base: BaseConfig; initialPortalConfig?: PortalState }) {