diff --git a/example/docker-compose.yaml b/example/docker-compose.yaml index a00456086..f04a4f0cc 100644 --- a/example/docker-compose.yaml +++ b/example/docker-compose.yaml @@ -10,8 +10,8 @@ services: ENVIRONMENT: ${ENVIRONMENT:-local} RESET_DATA_SOURCE: "off" SECRET_KEY: sg9aeUM5i1JO4gNN8fQadokJa3_gXQMLBjSGGYcfscs= # Don't reuse this in production... -# volumes: -# - ../../data-modelling-storage-service/src:/code/src + volumes: + - ../../data-modelling-storage-service/src:/code/src ports: - '5000:5000' depends_on: diff --git a/example/src/ViewPage.tsx b/example/src/ViewPage.tsx index 76abb78f7..b628ee8dd 100644 --- a/example/src/ViewPage.tsx +++ b/example/src/ViewPage.tsx @@ -36,7 +36,13 @@ function ViewPage() { ) } - return + return ( + + ) } export default ViewPage diff --git a/example/src/index.tsx b/example/src/index.tsx index b0f4639ca..2a832dac3 100644 --- a/example/src/index.tsx +++ b/example/src/index.tsx @@ -1,5 +1,4 @@ import '@development-framework/dm-core-plugins/dist/main.css' -import React from 'react' import 'react-toastify/dist/ReactToastify.min.css' import './main.css' @@ -26,7 +25,7 @@ const authConfig = { const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) root.render( - +
{authEnabled ? ( @@ -34,5 +33,5 @@ root.render( ) : ( )} - +
) diff --git a/example/src/plugins/marmo-ui/containers/views/SignalPlot.tsx b/example/src/plugins/marmo-ui/containers/views/SignalPlot.tsx index ed4f7a678..4228c249d 100644 --- a/example/src/plugins/marmo-ui/containers/views/SignalPlot.tsx +++ b/example/src/plugins/marmo-ui/containers/views/SignalPlot.tsx @@ -57,10 +57,12 @@ const ESSPlotPlugin = (props: { document: TGenericObject }) => { } const SignalPlot_Component = (props: IUIPlugin) => { - const { idReference } = props + const { idReference, entity } = props const { document, isLoading, error } = useDocument( idReference, - 1 + 1, + true, + entity ) if (isLoading) return diff --git a/packages/dm-core-plugins/src/form/FormPlugin.tsx b/packages/dm-core-plugins/src/form/FormPlugin.tsx index f40291e32..579284f85 100644 --- a/packages/dm-core-plugins/src/form/FormPlugin.tsx +++ b/packages/dm-core-plugins/src/form/FormPlugin.tsx @@ -9,7 +9,9 @@ import { Form } from './components/Form' export const FormPlugin = (props: IUIPlugin) => { const { document, isLoading, updateDocument, error } = useDocument( props.idReference, - 0 + 0, + true, + props.entity ) // react-hook-form is unable to rerender when the document is updated. diff --git a/packages/dm-core-plugins/src/form/components/OpenObjectButton.tsx b/packages/dm-core-plugins/src/form/components/OpenObjectButton.tsx index 098e9481d..eececd1fa 100644 --- a/packages/dm-core-plugins/src/form/components/OpenObjectButton.tsx +++ b/packages/dm-core-plugins/src/form/components/OpenObjectButton.tsx @@ -1,6 +1,7 @@ import { TInlineRecipeViewConfig, TReferenceViewConfig, + TValidEntity, TViewConfig, } from '@development-framework/dm-core' import { external_link } from '@equinor/eds-icons' @@ -11,19 +12,22 @@ export const OpenObjectButton = ({ viewId, viewConfig, idReference, + entity, }: { viewId: string viewConfig: TViewConfig | TReferenceViewConfig | TInlineRecipeViewConfig idReference?: string + entity?: TValidEntity }) => { const { onOpen } = useRegistryContext() + console.log(entity) return ( onOpen?.(viewId, viewConfig, idReference)} + button-onClick={() => onOpen?.(viewId, viewConfig, entity, idReference)} icon={external_link} /> ) diff --git a/packages/dm-core-plugins/src/form/fields/ArrayField/templates/ArrayComplexTemplate.tsx b/packages/dm-core-plugins/src/form/fields/ArrayField/templates/ArrayComplexTemplate.tsx index ac30ba76a..865e0ef9e 100644 --- a/packages/dm-core-plugins/src/form/fields/ArrayField/templates/ArrayComplexTemplate.tsx +++ b/packages/dm-core-plugins/src/form/fields/ArrayField/templates/ArrayComplexTemplate.tsx @@ -43,6 +43,7 @@ export const ArrayComplexTemplate = (props: TArrayTemplate) => { onOpen?.( namePath, getOpenViewConfig(uiAttribute, namePath), + getValues(namePath), idReference ) } @@ -56,6 +57,7 @@ export const ArrayComplexTemplate = (props: TArrayTemplate) => { )} {attribute.optional && @@ -88,6 +90,7 @@ export const ArrayComplexTemplate = (props: TArrayTemplate) => { idReference={`${idReference}.${namePath}`} onOpen={onOpen} viewConfig={getExpandViewConfig(uiAttribute)} + entity={getValues(namePath)} /> diff --git a/packages/dm-core-plugins/src/form/fields/ObjectField/templates/ObjectModelContainedTemplate.tsx b/packages/dm-core-plugins/src/form/fields/ObjectField/templates/ObjectModelContainedTemplate.tsx index 7575e7714..8d2ab176e 100644 --- a/packages/dm-core-plugins/src/form/fields/ObjectField/templates/ObjectModelContainedTemplate.tsx +++ b/packages/dm-core-plugins/src/form/fields/ObjectField/templates/ObjectModelContainedTemplate.tsx @@ -49,6 +49,7 @@ export const ObjectModelContainedTemplate = ( onOpen?.( namePath, getOpenViewConfig(uiAttribute, namePath), + value, idReference ) } @@ -61,6 +62,7 @@ export const ObjectModelContainedTemplate = ( viewId={namePath} idReference={idReference} viewConfig={getOpenViewConfig(uiAttribute, namePath)} + entity={value} /> )} {attribute.optional && @@ -103,6 +105,7 @@ export const ObjectModelContainedTemplate = ( idReference={`${idReference}.${attribute.name}`} onOpen={onOpen} viewConfig={getExpandViewConfig(uiAttribute)} + entity={value} onChange={(data: Record) => setValue( namePath, diff --git a/packages/dm-core-plugins/src/form/fields/ObjectField/templates/ObjectModelUncontainedTemplate.tsx b/packages/dm-core-plugins/src/form/fields/ObjectField/templates/ObjectModelUncontainedTemplate.tsx index a907aacd8..89597db74 100644 --- a/packages/dm-core-plugins/src/form/fields/ObjectField/templates/ObjectModelUncontainedTemplate.tsx +++ b/packages/dm-core-plugins/src/form/fields/ObjectField/templates/ObjectModelUncontainedTemplate.tsx @@ -58,7 +58,7 @@ export const ObjectModelUncontainedTemplate = ( objectIsNotEmpty={referenceExists} setIsExpanded={setIsExpanded} onOpen={() => - onOpen?.(namePath, getOpenViewConfig(uiAttribute), address) + onOpen?.(namePath, getOpenViewConfig(uiAttribute), value, address) } icon={link} uiAttribute={uiAttribute} @@ -98,6 +98,7 @@ export const ObjectModelUncontainedTemplate = ( idReference={address ?? ''} onOpen={onOpen} viewConfig={getExpandViewConfig(uiAttribute)} + entity={value} /> diff --git a/packages/dm-core-plugins/src/grid/GridElement.tsx b/packages/dm-core-plugins/src/grid/GridElement.tsx index e7f86d157..c71ecaa4f 100644 --- a/packages/dm-core-plugins/src/grid/GridElement.tsx +++ b/packages/dm-core-plugins/src/grid/GridElement.tsx @@ -1,4 +1,4 @@ -import { ViewCreator } from '@development-framework/dm-core' +import { TValidEntity, ViewCreator } from '@development-framework/dm-core' import { Typography } from '@equinor/eds-core-react' import React from 'react' import styled from 'styled-components' @@ -32,11 +32,19 @@ type TGridItemProps = { showItemBorders: boolean onSubmit?: (data: any) => void onChange?: (data: any) => void + entity?: TValidEntity } export const GridElement = (props: TGridItemProps): React.ReactElement => { - const { idReference, item, itemBorder, showItemBorders, onSubmit, onChange } = - props + const { + idReference, + item, + itemBorder, + showItemBorders, + onSubmit, + onChange, + entity, + } = props return ( { {item?.title && {item.title}}
void onChange?: (data: any) => void + entity?: TValidEntity } export const GridItems = (props: GridItemsProps) => { @@ -20,10 +22,12 @@ export const GridItems = (props: GridItemsProps) => { showItemBorders, onChange, onSubmit, + entity, } = props const elements = items.map((item: TGridItem, index) => { return ( { - const { config, idReference, type, onSubmit, onChange } = props + const { config, idReference, type, onSubmit, onChange, entity } = props const internalConfig: TGridPluginConfig = { ...defaultConfig, @@ -54,6 +54,7 @@ export const GridPlugin = ( return ( { - const { idReference, config: passedConfig, type } = props + const { + idReference, + config: passedConfig, + type, + entity: passedEntity, + } = props const config: THeaderPluginConfig & { adminRole: string } = { ...defaultHeaderPluginConfig, ...passedConfig, } const [isMenuOpen, setIsMenuOpen] = useState(false) const [anchorEl, setAnchorEl] = useState(null) - const { document: entity, isLoading } = useDocument(idReference) + const { document: entity, isLoading } = useDocument( + idReference, + 0, + true, + passedEntity + ) const { uiRecipes, isLoading: isBlueprintLoading } = useBlueprint(type) const { roles, role, dmssAPI, name } = useApplication() const [aboutOpen, setAboutOpen] = useState(false) @@ -216,6 +226,7 @@ export default (props: IUIPlugin): React.ReactElement => { />
{ - const { - config, - idReference, - }: { config: TJobPluginConfig; idReference: string } = props + const { config, idReference, entity } = props const { dmssAPI } = useApplication() const { tokenData } = useContext(AuthContext) const username = tokenData?.preferred_username ?? 'unknown user' @@ -85,7 +82,7 @@ export const JobCreate = (props: IUIPlugin & { config: TJobPluginConfig }) => { const { document: jobDocument, updateDocument } = useDocument< TJob | TRecurringJob - >(jobTargetAddress, 0, false) + >(jobTargetAddress, 0, false, entity) const { start, diff --git a/packages/dm-core-plugins/src/list/ListPlugin.tsx b/packages/dm-core-plugins/src/list/ListPlugin.tsx index 3ec1c1fe9..173260500 100644 --- a/packages/dm-core-plugins/src/list/ListPlugin.tsx +++ b/packages/dm-core-plugins/src/list/ListPlugin.tsx @@ -76,7 +76,7 @@ const defaultConfig: TListConfig = { } export const ListPlugin = (props: IUIPlugin & { config?: TListConfig }) => { - const { idReference, config, type, onOpen } = props + const { idReference, config, type, onOpen, entity } = props const internalConfig: TListConfig = { ...defaultConfig, ...config, @@ -96,7 +96,11 @@ export const ListPlugin = (props: IUIPlugin & { config?: TListConfig }) => { moveItem, reloadData, updateItem, - } = useList(idReference, internalConfig.resolveReferences) + } = useList( + idReference, + internalConfig.resolveReferences, + entity + ) const { currentItems, @@ -149,6 +153,7 @@ export const ListPlugin = (props: IUIPlugin & { config?: TListConfig }) => { onOpen( item.key, view, + item.data, item.idReference, false, config.saveExpanded diff --git a/packages/dm-core-plugins/src/table/Table/Table.tsx b/packages/dm-core-plugins/src/table/Table/Table.tsx index d8284b624..818c547e9 100644 --- a/packages/dm-core-plugins/src/table/Table/Table.tsx +++ b/packages/dm-core-plugins/src/table/Table/Table.tsx @@ -1,6 +1,6 @@ import { - TGenericObject, TItem, + TValidEntity, usePagination, } from '@development-framework/dm-core' import { @@ -54,7 +54,7 @@ export function Table(props: TableProps) { const [tableVariant, setTableVariant] = useState( config.variant[0].name ) - const [sortedItems, setSortedItems] = useState[]>([]) + const [sortedItems, setSortedItems] = useState[]>([]) const [sortColumn, setSortColumn] = useState(undefined) const [sortDirection, setSortDirection] = useState('ascending') @@ -79,7 +79,7 @@ export function Table(props: TableProps) { } const handleRemoveItem = async ( - itemToDelete: TItem, + itemToDelete: TItem, saveOnRemove?: boolean | undefined ) => { setDeletingRow(itemToDelete.key) @@ -130,7 +130,7 @@ export function Table(props: TableProps) { setPage(0) } - function reorderItems(reorderedItems: TItem[]) { + function reorderItems(reorderedItems: TItem[]) { setItems(reorderedItems) setDirtyState(true) } @@ -196,7 +196,7 @@ export function Table(props: TableProps) { functionalityConfig={functionalityConfig} idReference={props.idReference} index={items.findIndex( - (it: TItem) => it.key === item.key + (it: TItem) => it.key === item.key )} item={item} items={items} diff --git a/packages/dm-core-plugins/src/table/Table/TableRow/TableCell/TableCell.tsx b/packages/dm-core-plugins/src/table/Table/TableRow/TableCell/TableCell.tsx index 67d6cdf8b..cd18536aa 100644 --- a/packages/dm-core-plugins/src/table/Table/TableRow/TableCell/TableCell.tsx +++ b/packages/dm-core-plugins/src/table/Table/TableRow/TableCell/TableCell.tsx @@ -26,7 +26,7 @@ export function TableCell(props: TableCellProps) { // TODO: Add more logic for creating better default values. // If pointing to complex optional object, it has to be created. - const value = resolvePath(item.data || {}, column?.data, '') + const value = resolvePath(item.data || { type: 'unknown' }, column?.data, '') if (typeof value === 'object') { throw new Error( diff --git a/packages/dm-core-plugins/src/table/Table/TableRow/TableRow.tsx b/packages/dm-core-plugins/src/table/Table/TableRow/TableRow.tsx index d8573695f..1a870b93f 100644 --- a/packages/dm-core-plugins/src/table/Table/TableRow/TableRow.tsx +++ b/packages/dm-core-plugins/src/table/Table/TableRow/TableRow.tsx @@ -52,17 +52,17 @@ export function TableRow(props: TableRowProps) { ) return } - const label = - config.label || + const label = (config.label || item.data?.label || item.data?.name || - `${idReference.split('.').slice(-1)}` + `${idReference.split('.').slice(-1)}`) as string props.onOpen( item.key, { label: config.labelByIndex ? `${label} #${index + 1}` : label, type: 'ViewConfig', }, + item.data ?? undefined, item.idReference, false, (data: any) => handleItemUpdate(item, data) diff --git a/packages/dm-core-plugins/src/table/Table/types.ts b/packages/dm-core-plugins/src/table/Table/types.ts index 12d1ac672..89994707d 100644 --- a/packages/dm-core-plugins/src/table/Table/types.ts +++ b/packages/dm-core-plugins/src/table/Table/types.ts @@ -1,10 +1,10 @@ import { IUIPlugin, - TGenericObject, TInlineRecipeViewConfig, TItem, TOnOpen, TReferenceViewConfig, + TValidEntity, TViewConfig, } from '@development-framework/dm-core' import { TSortableItem, TTemplate } from '../../common' @@ -64,18 +64,18 @@ export type TableProps = { ) => void config: TTableConfig dirtyState: boolean - items: TItem[] + items: TItem[] isLoading: boolean reloadData?: () => void removeItem: ( - itemToDelete: TItem, + itemToDelete: TItem, saveOnRemove?: boolean ) => Promise - saveTable: (items: TItem[]) => void - setItems: React.Dispatch[]>> + saveTable: (items: TItem[]) => void + setItems: React.Dispatch[]>> updateItem: ( - itemToUpdate: TItem, - newDocument: TGenericObject, + itemToUpdate: TItem, + newDocument: TValidEntity, saveOnUpdate?: boolean ) => Promise setDirtyState: React.Dispatch> @@ -100,24 +100,24 @@ export type TableRowProps = { ) => void config: TTableConfig removeItem: ( - itemToDelete: TItem, + itemToDelete: TItem, saveOnRemove?: boolean ) => Promise editMode: boolean functionalityConfig: TTableFunctionalityConfig - item: TItem + item: TItem index: number idReference: string - items: TItem[] + items: TItem[] onOpen?: TOnOpen rowsPerPage: number disableActions: boolean setDirtyState: React.Dispatch> - setItems: React.Dispatch[]>> + setItems: React.Dispatch[]>> showActionsCell: boolean updateItem: ( - itemToUpdate: TItem, - newDocument: TGenericObject, + itemToUpdate: TItem, + newDocument: TValidEntity, saveOnUpdate?: boolean ) => Promise tableVariant: TableVariantNameEnum @@ -125,9 +125,9 @@ export type TableRowProps = { export type TableRowActionsProps = { editMode: boolean - item: TItem + item: TItem removeItem: ( - itemToDelete: TItem, + itemToDelete: TItem, saveOnRemove?: boolean ) => Promise functionalityConfig: TTableFunctionalityConfig @@ -138,7 +138,7 @@ export type TableCellProps = { column: TTableColumnConfig editMode: boolean isExpanded: boolean - item: TItem + item: TItem openItemAsTab: () => void setIsExpanded: React.Dispatch> updateItem: ( diff --git a/packages/dm-core-plugins/src/table/Table/utils.ts b/packages/dm-core-plugins/src/table/Table/utils.ts index c3f7d87b5..3e7751f95 100644 --- a/packages/dm-core-plugins/src/table/Table/utils.ts +++ b/packages/dm-core-plugins/src/table/Table/utils.ts @@ -1,4 +1,4 @@ -import { TGenericObject, TItem } from '@development-framework/dm-core' +import { TItem, TValidEntity } from '@development-framework/dm-core' import { isObject } from 'lodash' import { TTableConfig, @@ -7,14 +7,16 @@ import { TableVariantNameEnum, } from './types' -function setValue(object: TGenericObject, attribute: string, value: any) { +function setValue(object: TValidEntity, attribute: string, value: any) { const properties = attribute.split('.') const lastProperty = properties.pop() const lastObject = properties.reduce( + // @ts-ignore (a, prop) => (isObject(a) ? a[prop] : null), object ) if (isObject(lastObject) && lastProperty) { + // @ts-ignore lastObject[lastProperty] = value return true } else { @@ -23,25 +25,22 @@ function setValue(object: TGenericObject, attribute: string, value: any) { } export function updateItemAttribute( - items: TItem[], + items: TItem[], key: string, attribute: string, newValue: string | number | boolean ) { const itemsCopy = [...items] const index = itemsCopy.findIndex((item) => item.key === key) - const itemData = itemsCopy[index].data || {} + const itemData = itemsCopy[index].data || { type: 'unknown' } const success = setValue(itemData, attribute, newValue) if (success && itemsCopy[index].data) { - ;(itemsCopy[index].data as TGenericObject) = itemData + ;(itemsCopy[index].data as TValidEntity) = itemData } return itemsCopy } -export function createNewItemObject( - data: TGenericObject, - newItemIndex: number -) { +export function createNewItemObject(data: TValidEntity, newItemIndex: number) { const id: string = crypto.randomUUID() return { key: id, @@ -52,9 +51,9 @@ export function createNewItemObject( } export function removeItemFromList( - items: TItem[], + items: TItem[], key: string -): TItem[] { +): TItem[] { const itemIndex = items.findIndex((item) => item.key === key) const itemsCopy = [...items] itemsCopy.splice(itemIndex, 1) @@ -87,7 +86,7 @@ export function dynamicSort(direction: TTableSortDirection, property: string) { } export const resolvePath = ( - object: TGenericObject, + object: TValidEntity, path: string, defaultValue: any ) => diff --git a/packages/dm-core-plugins/src/table/TablePlugin.tsx b/packages/dm-core-plugins/src/table/TablePlugin.tsx index def8e7b8b..a03f9af27 100644 --- a/packages/dm-core-plugins/src/table/TablePlugin.tsx +++ b/packages/dm-core-plugins/src/table/TablePlugin.tsx @@ -1,13 +1,13 @@ import { IUIPlugin, - TGenericObject, + TValidEntity, useList, } from '@development-framework/dm-core' import { TTableConfig, Table } from './Table/Table' import * as utils from './utils' export const TablePlugin = (props: IUIPlugin) => { - const { idReference } = props + const { idReference, entity } = props const config: TTableConfig = utils.mergeConfigs(props.config) const { @@ -22,7 +22,7 @@ export const TablePlugin = (props: IUIPlugin) => { removeItem, save, reloadData, - } = useList(idReference, true) + } = useList(idReference, true, entity) if (error) throw new Error(JSON.stringify(error, null, 2)) diff --git a/packages/dm-core-plugins/src/view_selector/SidebarPlugin.tsx b/packages/dm-core-plugins/src/view_selector/SidebarPlugin.tsx index 85206af60..36755db96 100644 --- a/packages/dm-core-plugins/src/view_selector/SidebarPlugin.tsx +++ b/packages/dm-core-plugins/src/view_selector/SidebarPlugin.tsx @@ -12,7 +12,7 @@ import { useViewSelector } from './useViewSelector' export const SidebarPlugin = ( props: IUIPlugin & { config?: TViewSelectorConfig } ): React.ReactElement => { - const { idReference, config, type } = props + const { idReference, config, type, entity } = props const { addView, @@ -23,7 +23,7 @@ export const SidebarPlugin = ( formData, setSelectedViewId, setFormData, - } = useViewSelector(idReference, config) + } = useViewSelector(idReference, config, entity) if (error) { throw new Error(JSON.stringify(error, null, 2)) @@ -47,6 +47,7 @@ export const SidebarPlugin = (
{viewItem.viewConfig ? ( + entity?: TValidEntity }): React.ReactElement => { const { selectedViewId, viewSelectorItems, setFormData, formData, onOpen } = props @@ -29,6 +31,7 @@ export const TabsContent = (props: { {config.viewConfig ? ( { diff --git a/packages/dm-core-plugins/src/view_selector/TabsPlugin.tsx b/packages/dm-core-plugins/src/view_selector/TabsPlugin.tsx index 0b5ee0a84..3223ca2fb 100644 --- a/packages/dm-core-plugins/src/view_selector/TabsPlugin.tsx +++ b/packages/dm-core-plugins/src/view_selector/TabsPlugin.tsx @@ -8,8 +8,7 @@ import { useViewSelector } from './useViewSelector' export const TabsPlugin = ( props: IUIPlugin & { config?: TViewSelectorConfig } ): React.ReactElement | null => { - const { idReference, config, type, onSubmit, onChange } = props - + const { idReference, config, type, onSubmit, onChange, entity } = props const { addView, removeView, @@ -20,7 +19,7 @@ export const TabsPlugin = ( formData, setSelectedViewId, setFormData, - } = useViewSelector(idReference, config, onSubmit, onChange) + } = useViewSelector(idReference, config, entity, onSubmit, onChange) if (error) { throw new Error(JSON.stringify(error, null, 2)) @@ -39,6 +38,7 @@ export const TabsPlugin = ( /> void onChange?: (data: any) => void diff --git a/packages/dm-core-plugins/src/view_selector/useViewSelector.tsx b/packages/dm-core-plugins/src/view_selector/useViewSelector.tsx index ec92dffbe..f3974946d 100644 --- a/packages/dm-core-plugins/src/view_selector/useViewSelector.tsx +++ b/packages/dm-core-plugins/src/view_selector/useViewSelector.tsx @@ -4,7 +4,9 @@ import { TInlineRecipeViewConfig, TOnOpen, TReferenceViewConfig, + TValidEntity, TViewConfig, + getScopedEntity, useDocument, } from '@development-framework/dm-core' import * as React from 'react' @@ -27,6 +29,7 @@ interface IUseViewSelector { export function useViewSelector( idReference: string, config: Record, + passedEntity?: TValidEntity | undefined, onSubmit?: (data: any) => void, onChange?: (data: any) => void ): IUseViewSelector { @@ -34,7 +37,7 @@ export function useViewSelector( document: entity, isLoading, error, - } = useDocument(idReference) + } = useDocument(idReference, 0, true, passedEntity) const [selectedViewId, setSelectedViewId] = useState() const [viewSelectorItems, setViewSelectorItems] = useState([]) const [formData, setFormData] = useState({}) @@ -47,6 +50,7 @@ export function useViewSelector( const addView: TOnOpen = ( viewId: string, viewConfig: TViewConfig | TReferenceViewConfig | TInlineRecipeViewConfig, + entity?: TValidEntity, rootId?: string, isSubItem?: boolean, onSubmitAdded?: (data: any) => void, @@ -57,6 +61,7 @@ export function useViewSelector( ) const newView: TItemData = { viewId: viewId, + entity: entity, viewConfig: viewConfig, label: viewConfig.label ?? viewId, rootEntityId: rootId || idReference, @@ -101,6 +106,7 @@ export function useViewSelector( const backupKey: string = viewItem.viewConfig?.scope ?? 'self' // If the view does not have a scope, the scope is 'self' const viewId = crypto.randomUUID() newViews.push({ + entity: getScopedEntity(entity, viewItem.viewConfig?.scope), viewConfig: viewItem.viewConfig, subItems: viewItem.subItems, eds_icon: viewItem.eds_icon, @@ -117,6 +123,7 @@ export function useViewSelector( if (!selectedViewId && viewItem.viewConfig) selectedViewId = viewId if (!selectedViewId) selectedViewId = subViewId newViews.push({ + entity: getScopedEntity(entity, viewItem.viewConfig?.scope), viewConfig: subItem.viewConfig, subItems: subItem.subItems, eds_icon: subItem.eds_icon, @@ -135,6 +142,7 @@ export function useViewSelector( } else { // No views where passed. Create default for all complex attributes and "self" newViews.push({ + entity: entity, label: 'self', viewId: 'self', eds_icon: 'home', @@ -155,6 +163,7 @@ export function useViewSelector( ([key, attributeEntity]: [string, any]) => { if (typeof attributeEntity === 'object') { newViews.push({ + entity: getScopedEntity(entity, key), viewId: key, label: key, onSubmit: onSubmit, diff --git a/packages/dm-core-plugins/src/yaml/YamlPlugin.tsx b/packages/dm-core-plugins/src/yaml/YamlPlugin.tsx index 845a57a77..23db1831a 100644 --- a/packages/dm-core-plugins/src/yaml/YamlPlugin.tsx +++ b/packages/dm-core-plugins/src/yaml/YamlPlugin.tsx @@ -15,7 +15,7 @@ import { ActionButton, ActionsWrapper, CodeContainer } from './styles' import { YamlPluginProps, defaultConfig } from './types' export const YamlPlugin = (props: YamlPluginProps) => { - const { idReference, config: userConfig } = props + const { idReference, config: userConfig, entity } = props const config = { ...defaultConfig, ...userConfig } const [depth, setDepth] = useState(0) const [isEditMode, setIsEditMode] = useState(false) @@ -28,7 +28,7 @@ export const YamlPlugin = (props: YamlPluginProps) => { const depthPopoverTrigger = useRef(null) const { document, isLoading, error, setError, updateDocument } = - useDocument(idReference, depth, false) + useDocument(idReference, depth, false, entity) const asYAML = useMemo(() => YAML.stringify(document), [document]) const asJSON = useMemo(() => JSON.stringify(document, null, 2), [document]) diff --git a/packages/dm-core/src/ApplicationContext.tsx b/packages/dm-core/src/ApplicationContext.tsx index be0d81b4f..f88920339 100644 --- a/packages/dm-core/src/ApplicationContext.tsx +++ b/packages/dm-core/src/ApplicationContext.tsx @@ -17,6 +17,7 @@ import { IUIPlugin, TApplication, TRole, TUiPluginMap } from './types' import { ErrorGroup } from './utils/ErrorBoundary' const DEFAULT_ROLE: TRole = { + type: 'role', name: 'anonymous', authServerRoleName: 'anonymous', label: 'Anonymous', @@ -61,6 +62,7 @@ function generateFallbackRoles(roles: string[] | undefined): TRole[] { name: r, label: capitalizeFirstLetter(r), authServerRoleName: r, + type: 'role', })) } diff --git a/packages/dm-core/src/components/EntityView.tsx b/packages/dm-core/src/components/EntityView.tsx index e30b5d359..635101c72 100644 --- a/packages/dm-core/src/components/EntityView.tsx +++ b/packages/dm-core/src/components/EntityView.tsx @@ -25,6 +25,7 @@ const MemoizedUiPlugin = memo(function UiPlugin( export const EntityView = (props: IEntityView): React.ReactElement => { const { idReference, + entity, type, onSubmit, onOpen, @@ -99,6 +100,7 @@ export const EntityView = (props: IEntityView): React.ReactElement => { }} > & { * @param props */ export const ViewCreator = (props: TViewCreator): React.ReactElement => { - const { idReference, viewConfig, onOpen, onSubmit, onChange } = props + const { idReference, viewConfig, onOpen, onSubmit, onChange, entity } = props const { dmssAPI } = useApplication() const [error, setError] = useState() @@ -53,7 +53,7 @@ export const ViewCreator = (props: TViewCreator): React.ReactElement => { () => getTarget(idReference, viewConfig), [idReference, viewConfig] ) - const queryKeys = ['attributes', reference, viewConfig.resolve] + const queryKeys = ['attributes', reference, viewConfig.resolve ?? false] const { isPending, data } = useQuery<{ address: string @@ -69,10 +69,13 @@ export const ViewCreator = (props: TViewCreator): React.ReactElement => { resolve: props.viewConfig.resolve, }) .then((response: any) => response.data) - .catch((error: AxiosError) => setError(error)), + .catch((error: AxiosError) => { + setError(error) + return null + }), }) - if (isPending || !data?.address) return + if (isPending) return if (error) return ( @@ -80,18 +83,24 @@ export const ViewCreator = (props: TViewCreator): React.ReactElement => { {error.message}) ) - if (data.attribute === undefined) + if (!data || data.attribute === undefined) { throw new Error('Unable to find type and dimensions for view') + } if (viewConfig === undefined) throw new Error( 'Cannot create a View without a "viewConfig". Sure the attribute is properly named?' ) + if (entity === undefined) { + console.log('UNDEFINED ENTITY') + console.log(viewConfig.scope) + } if (isInlineRecipeViewConfig(viewConfig)) { return ( { { return ( { document: T | null isLoading: boolean @@ -54,7 +55,8 @@ interface IUseDocumentReturnType { export function useDocument( idReference: string, depth?: number | undefined, - notify: boolean = true + notify: boolean = true, + entity?: TValidEntity | undefined ): IUseDocumentReturnType { const { dmssAPI } = useApplication() const [errorResponse, setErrorResponse] = useState(null) @@ -68,8 +70,13 @@ export function useDocument( staleTime: 5 * 1000, refetchOnMount: true, queryKey: queryKeys, - queryFn: () => - dmssAPI + queryFn: () => { + if (entity && depth === 0) { + console.log(`Recived entity for ${idReference}. Will not fetch`) + return entity + } + console.log(`UseDocument did not recieve entity for ${idReference}`) + return dmssAPI .documentGet({ address: idReference, depth: documentDepth, @@ -85,7 +92,8 @@ export function useDocument( setErrorResponse( error.response?.data || { message: error.name, data: error } ) - }), + }) + }, }) const mutation = useMutation({ diff --git a/packages/dm-core/src/hooks/useList/useList.tsx b/packages/dm-core/src/hooks/useList/useList.tsx index f987f1c9b..91bca14c6 100644 --- a/packages/dm-core/src/hooks/useList/useList.tsx +++ b/packages/dm-core/src/hooks/useList/useList.tsx @@ -12,7 +12,8 @@ export type { TItem } export function useList( idReference: string, - resolveReferences: boolean = true + resolveReferences: boolean = true, + entity?: T ): IUseListReturnType { const [attribute, setAttribute] = useState(null) const [items, setItems] = useState[]>([]) @@ -39,6 +40,23 @@ export function useList( if (!attribute) return setLoading(true) setDirtyState(false) + if (entity) { + const items: TItem[] = Object.values(entity).map((data, index) => ({ + key: crypto.randomUUID() as string, + index: index, + idReference: `${idReference}${ + data._id ? `(_id=${data._id})` : `[${index}]` + }`, + data: data, + reference: null, + isSaved: true, + })) + setItems(items) + setError(null) + setLoading(false) + return + } + console.log(`Did not recive entity for ${idReference}`) dmssAPI .documentGet({ address: idReference, diff --git a/packages/dm-core/src/types.ts b/packages/dm-core/src/types.ts index 7c09d80bb..b21a5ba51 100644 --- a/packages/dm-core/src/types.ts +++ b/packages/dm-core/src/types.ts @@ -10,6 +10,7 @@ export type TDataSource = { export type TRole = { name: string + type: string label: string authServerRoleName: string } @@ -173,6 +174,7 @@ export type TPackage = { export interface IUIPlugin { type: string idReference: string + entity?: TValidEntity onSubmit?: (data: any) => void // Listen to submits onChange?: (data: any) => void // Listen to changes onOpen?: TOnOpen @@ -183,6 +185,7 @@ export interface IUIPlugin { export type TOnOpen = ( viewId: string, view: TViewConfig | TReferenceViewConfig | TInlineRecipeViewConfig, + entity?: TValidEntity, rootId?: string, isSubItem?: boolean, onSubmit?: (data: any) => void, @@ -221,6 +224,7 @@ export type TUserIdMapping = { userId: string; username: string } export type TViewConfig = { type: string + entity?: TValidEntity scope?: string resolve?: boolean label?: string diff --git a/packages/dm-core/src/utils/addressUtilities.ts b/packages/dm-core/src/utils/addressUtilities.ts index 578443ea7..0f59cb51e 100644 --- a/packages/dm-core/src/utils/addressUtilities.ts +++ b/packages/dm-core/src/utils/addressUtilities.ts @@ -1,3 +1,4 @@ +import { TValidEntity } from '../types' import { splitString } from './stringUtilities' /** @@ -126,3 +127,44 @@ export const resolveRelativeAddressSimplified = ( `Invalid format for relative address '${relativeAddress}' at location '${location}'. Check format and update recipes.` ) } + +export function getScopedEntity( + entity?: TValidEntity, + scope?: string +): TValidEntity | undefined { + if (!entity) { + return undefined + } + if (!scope) return entity + + if (['self', '.'].includes(scope)) return entity + + let subEntity = entity + const scopeParts = scope.split('.') + + for (const part of scopeParts) { + if (part.endsWith(']')) { + const leftBracketIndex = part.indexOf('[') + const rightBracketIndex = part.indexOf(']') + const beforeBracket = part.slice(0, leftBracketIndex) + const listIndex = parseInt( + part.slice(leftBracketIndex + 1, rightBracketIndex) + ) + // @ts-ignore + subEntity = subEntity[beforeBracket][listIndex] + } else { + // @ts-ignore + subEntity = subEntity[part] + } + } + if (!subEntity) { + console.error( + `Could not find scoped entity for scope ${scope} in entity ${JSON.stringify( + entity + )}` + ) + console.error(entity) + console.error(scope) + } + return subEntity +}