Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manage layout persistence via interface #40

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ef4d9fd
VUU-20: Define interface for layout persistence
pling-scottlogic Sep 7, 2023
59279f4
VUU-20: Adjust interface
pling-scottlogic Sep 8, 2023
1a74ae3
VUU-20: Implement local layout persistence via interface
pling-scottlogic Sep 8, 2023
72f676b
VUU-20: Access LayoutPersistenceManager via useLayoutManager
pling-scottlogic Sep 11, 2023
6debc78
VUU-20: Add LayoutPersistenceManager to hook props
pling-scottlogic Sep 11, 2023
dc107b7
VUU-20: Generate layout ID from persistence manager
pling-scottlogic Sep 12, 2023
904832e
VUU-20: Make tempLayouts a single layout
pling-scottlogic Sep 14, 2023
00a1850
VUU-20: Separate layout and metadata
pling-scottlogic Sep 14, 2023
13504b2
VUU-20: Refactor saveLayout
pling-scottlogic Sep 14, 2023
c896c71
VUU-20: Move ID out of metadata
pling-scottlogic Sep 14, 2023
566a0ab
VUU-20: Update documentation
pling-scottlogic Sep 15, 2023
065e007
VUU-20: Add PersistedLayoutMetadata type
pling-scottlogic Sep 15, 2023
597f9ce
VUU-20: Remove getLocalEntity from updateLayout
pling-scottlogic Sep 15, 2023
b859cfa
VUU-20: Remove loadMetadataByUser
pling-scottlogic Sep 15, 2023
ea956fc
VUU-20: Remove PersistedLayoutMetadata type
pling-scottlogic Sep 18, 2023
ec180bf
VUU-20: Remove metadata from Layout type
pling-scottlogic Sep 18, 2023
c6eb2db
VUU-20: Instantiate PersistenceManager in useLayoutManager
pling-scottlogic Sep 18, 2023
cee476f
VUU-20: Refactor LocalLayoutPersistenceManager
pling-scottlogic Sep 18, 2023
f7d0cfb
VUU-20: Remove unnecessary casting and import
pling-scottlogic Sep 19, 2023
38ce428
VUU-20: Remove layouts from state
pling-scottlogic Sep 19, 2023
4d9eeda
VUU-20: Remove loadLayouts from interface
pling-scottlogic Sep 19, 2023
ca04ca7
VUU-20: Remove unnecessary import
pling-scottlogic Sep 19, 2023
51abff6
VUU-20: Declare types in hook
pling-scottlogic Sep 19, 2023
c9bb4ee
VUU-20: Remove newLayout
pling-scottlogic Sep 19, 2023
ad3198c
VUU-20: Rename saveLayout to createLayout
pling-scottlogic Sep 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions vuu-ui/packages/vuu-layout/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from "./DraggableLayout";
export * from "./flexbox";
export { Action } from "./layout-action";
export * from "./layout-header";
export * from "./layout-persistence";
export * from "./layout-provider";
export * from "./layout-reducer";
export * from "./layout-view";
Expand Down
cfisher-scottlogic marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { LayoutJSON } from "@finos/vuu-layout";
import { LayoutMetadata } from "@finos/vuu-shell";

export interface LayoutPersistenceManager {
/**
* Saves a new layout
*
* @param metadata - Metadata about the layout to be saved
* @param layout - Full JSON representation of the layout to be saved
*
* @returns ID assigned to the saved layout
*/
createLayout: (metadata: Omit<LayoutMetadata, "id">, layout: LayoutJSON) => string;

/**
* Overwrites an existing layout with a new one
cfisher-scottlogic marked this conversation as resolved.
Show resolved Hide resolved
*
* @param id - Unique identifier of the existing layout to be updated
* @param metadata - Metadata describing the new layout to overwrite with
* @param layout - Full JSON representation of the new layout to overwrite with
*/
updateLayout: (id: string, metadata: Omit<LayoutMetadata, "id">, layout: LayoutJSON) => void;
vferraro-scottlogic marked this conversation as resolved.
Show resolved Hide resolved

/**
* Deletes an existing layout
cfisher-scottlogic marked this conversation as resolved.
Show resolved Hide resolved
*
* @param id - Unique identifier of the existing layout to be deleted
*/
deleteLayout: (id: string) => void;

/**
* Retrieves an existing layout
*
* @param id - Unique identifier of the existing layout to be retrieved
*
* @returns the layout corresponding to provided metadata
*/
loadLayout: (id: string) => LayoutJSON;

/**
* Retrieves metadata for all existing layouts
*
* @returns an array of all persisted layout metadata
*/
loadMetadata: () => LayoutMetadata[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Layout, LayoutMetadata } from "@finos/vuu-shell";
import { LayoutJSON, LayoutPersistenceManager } from "@finos/vuu-layout";

import { getLocalEntity, saveLocalEntity } from "@finos/vuu-filters";
import { getUniqueId } from "@finos/vuu-utils";

const metadataSaveLocation = "layouts/metadata";
const layoutsSaveLocation = "layouts/layouts";

export class LocalLayoutPersistenceManager implements LayoutPersistenceManager {
createLayout(metadata: Omit<LayoutMetadata, "id">, layout: LayoutJSON): string {
console.log(`Saving layout as ${metadata.name} to group ${metadata.group}...`);

const existingLayouts = this.loadLayouts();
const existingMetadata = this.loadMetadata();

const id = getUniqueId();

this.appendAndPersist(id, metadata, layout, existingLayouts, existingMetadata);

return id;
}

updateLayout(id: string, metadata: Omit<LayoutMetadata, "id">, newLayoutJson: LayoutJSON): void {
const existingLayouts = this.loadLayouts().filter(layout => layout.id !== id);
const existingMetadata = this.loadMetadata().filter(metadata => metadata.id !== id);
cfisher-scottlogic marked this conversation as resolved.
Show resolved Hide resolved

this.appendAndPersist(id, metadata, newLayoutJson, existingLayouts, existingMetadata);
}
cfisher-scottlogic marked this conversation as resolved.
Show resolved Hide resolved

deleteLayout(id: string): void {
const layouts = this.loadLayouts().filter(layout => layout.id !== id);
const metadata = this.loadMetadata().filter(metadata => metadata.id !== id);

this.saveLayoutsWithMetadata(layouts, metadata);
}

loadLayout(id: string): LayoutJSON {
const layout = this.loadLayouts().filter(layout => layout.id === id);

switch (layout.length) {
case 1: {
return layout[0].json;
}
case 0: {
console.log(`WARNING: no layout exists for ID "${id}"; returning empty layout`);
return {} as LayoutJSON;
}
default: {
console.log(`WARNING: multiple layouts exist for ID "${id}"; returning first instance`)
return layout[0].json;
}
}
}

loadMetadata(): LayoutMetadata[] {
return getLocalEntity<LayoutMetadata[]>(metadataSaveLocation) || [];
}

private loadLayouts(): Layout[] {
return getLocalEntity<Layout[]>(layoutsSaveLocation) || [];
}

private appendAndPersist(newId: string,
newMetadata: Omit<LayoutMetadata, "id">,
newLayout: LayoutJSON,
existingLayouts: Layout[],
existingMetadata: LayoutMetadata[]) {
existingLayouts.push({id: newId, json: newLayout});
existingMetadata.push({id: newId, ...newMetadata});

this.saveLayoutsWithMetadata(existingLayouts, existingMetadata);
}

private saveLayoutsWithMetadata(layouts: Layout[], metadata: LayoutMetadata[]): void {
saveLocalEntity<Layout[]>(layoutsSaveLocation, layouts);
saveLocalEntity<LayoutMetadata[]>(metadataSaveLocation, metadata);
}
}
2 changes: 2 additions & 0 deletions vuu-ui/packages/vuu-layout/src/layout-persistence/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './LayoutPersistenceManager';
export * from './LocalLayoutPersistenceManager';
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ type LayoutGroups = {
const classBase = "vuuLayoutList";

export const LayoutsList = (props: HTMLAttributes<HTMLDivElement>) => {
const { layouts } = useLayoutManager();

const layoutMetadata = layouts.map(layout => layout.metadata)
const { layoutMetadata } = useLayoutManager();

const handleLoadLayout = (layoutId?: string) => {
// TODO load layout
console.log("loading layout with id", layoutId)
console.log("json:", layouts.find(layout => layout.metadata.id === layoutId))
console.log("json:", layoutMetadata.find(metadata => metadata.id === layoutId))
}

const layoutsByGroup = layoutMetadata.reduce((acc: LayoutGroups, cur) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { LayoutJSON } from "@finos/vuu-layout";

export type LayoutMetadata = {
id: string;
name: string;
group: string;
screenshot: string;
user: string;
date: string;
id: string;
};

export type Layout = {
id: string,
json: LayoutJSON;
metadata: LayoutMetadata;
};
vferraro-scottlogic marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,47 +1,46 @@
import React, { useState, useCallback, useContext, useEffect } from "react";
import { getLocalEntity, saveLocalEntity } from "@finos/vuu-filters";
import { LayoutJSON } from "@finos/vuu-layout";
import { getUniqueId } from "@finos/vuu-utils";
import { LayoutMetadata, Layout } from "./layoutTypes";
import { getLocalEntity } from "@finos/vuu-filters";
import { LayoutJSON, LocalLayoutPersistenceManager } from "@finos/vuu-layout";
import { LayoutMetadata } from "./layoutTypes";

const persistenceManager = new LocalLayoutPersistenceManager();

export const LayoutManagementContext = React.createContext<{
layouts: Layout[],
layoutMetadata: LayoutMetadata[],
saveLayout: (n: Omit<LayoutMetadata, "id">) => void
}>({ layouts: [], saveLayout: () => { } })

export const LayoutManagementProvider = (props: { children: JSX.Element | JSX.Element[] }) => {
}>({ layoutMetadata: [], saveLayout: () => { } })

const [layouts, setLayouts] = useState<Layout[]>([])
export const LayoutManagementProvider = (props: {
children: JSX.Element | JSX.Element[]
}) => {
const [layoutMetadata, setLayoutMetadata] = useState<LayoutMetadata[]>([]);

useEffect(() => {
const layouts = getLocalEntity<Layout[]>("layouts")
setLayouts(layouts || [])
const loadedMetadata = persistenceManager.loadMetadata();
setLayoutMetadata(loadedMetadata || [])
}, [])

useEffect(() => {
saveLocalEntity<Layout[]>("layouts", layouts)
}, [layouts])

const saveLayout = useCallback((metadata: Omit<LayoutMetadata, "id">) => {
const json = getLocalEntity<LayoutJSON>("api/vui")
const json = getLocalEntity<LayoutJSON>("api/vui");

if (json) {
setLayouts(prev =>
[
...prev,
{
metadata: {
...metadata,
id: getUniqueId()
},
json
}
]
)
// Persist layouts
const generatedId = persistenceManager.createLayout(metadata, json);

// Update state
const newMetadata: LayoutMetadata = {
...metadata,
id: generatedId
};

setLayoutMetadata(prev => [...prev, newMetadata]);
}
}, [])

// TODO: add loadLayout function

return (
<LayoutManagementContext.Provider value={{ layouts, saveLayout }} >
<LayoutManagementContext.Provider value={{ layoutMetadata, saveLayout }} >
{props.children}
</LayoutManagementContext.Provider>
)
Expand Down
2 changes: 1 addition & 1 deletion vuu-ui/packages/vuu-shell/src/left-nav/LeftNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export const LeftNav = ({
Layout Templates
</div>
<div className="vuuLeftNav-drawer">
<LayoutsList layouts={[]} />
<LayoutsList/>
</div>
</Stack>
</div>
Expand Down
2 changes: 0 additions & 2 deletions vuu-ui/showcase/src/examples/Apps/NewTheme.examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const ShellWithNewTheme = () => {
const { saveLayout } = useLayoutManager();

const handleSave = useCallback((layoutMetadata: Omit<LayoutMetadata, "id">) => {
console.log(`Save layout as ${layoutMetadata.name} to group ${layoutMetadata.group}`);
saveLayout(layoutMetadata)
setDialogContent(undefined)
}, []);
Expand Down Expand Up @@ -176,7 +175,6 @@ const ShellWithNewTheme = () => {
style={{ maxHeight: 500, borderColor: "#6d188b" }}
title={"Save Layout"}
hideCloseButton
headerProps={{ className: "dialogHeader" }}
>
{dialogContent}
</Dialog>
Expand Down