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

Add Create tab into Digital Twins page preview #1005

Open
wants to merge 14 commits into
base: feature/distributed-demo
Choose a base branch
from
44 changes: 40 additions & 4 deletions client/src/preview/components/asset/AssetBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ import * as React from 'react';
import { Grid } from '@mui/material';
import { useSelector, useDispatch } from 'react-redux';
import { RootState } from 'store/store';
import { deleteAsset } from 'preview/store/assets.slice';
import { AssetCardExecute, AssetCardManage } from './AssetCard';
import { deleteAsset, setAssets } from 'preview/store/assets.slice';
import { setDigitalTwin } from 'preview/store/digitalTwin.slice';
import GitlabInstance from 'preview/util/gitlab';
import DigitalTwin from 'preview/util/gitlabDigitalTwin';
import { getAuthority } from 'util/envUtil';
import { Asset } from './Asset';
import { AssetCardExecute, AssetCardManage } from './AssetCard';

const outerGridContainerProps = {
container: true,
Expand All @@ -19,7 +23,6 @@ const outerGridContainerProps = {

interface AssetBoardProps {
tab: string;
error: string | null;
}

const AssetGridItem: React.FC<{
Expand All @@ -44,10 +47,43 @@ const AssetGridItem: React.FC<{
</Grid>
);

const AssetBoard: React.FC<AssetBoardProps> = ({ tab, error }) => {
const AssetBoard: React.FC<AssetBoardProps> = ({ tab }) => {
const assets = useSelector((state: RootState) => state.assets.items);
const [error, setError] = React.useState<string | null>(null);
const dispatch = useDispatch();

const gitlabInstance = new GitlabInstance(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code has been moved from DigitalTwinsPreview.tsx to here. The non-react part of code belongs to some file in preview/util module.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has been moved to init.ts in preview/util.

sessionStorage.getItem('username') || '',
getAuthority(),
sessionStorage.getItem('access_token') || '',
);

React.useEffect(() => {
const fetchAssetsAndCreateTwins = async () => {
try {
await gitlabInstance.init();
if (gitlabInstance.projectId) {
const subfolders = await gitlabInstance.getDTSubfolders(
gitlabInstance.projectId,
);
dispatch(setAssets(subfolders));

subfolders.forEach(async (asset) => {
const digitalTwin = new DigitalTwin(asset.name, gitlabInstance);
await digitalTwin.getDescription();
dispatch(setDigitalTwin({ assetName: asset.name, digitalTwin }));
});
} else {
dispatch(setAssets([]));
}
} catch (err) {
setError(`An error occurred while fetching assets: ${err}`);
}
};

fetchAssetsAndCreateTwins();
}, []);

const handleDelete = (deletedAssetPath: string) => {
dispatch(deleteAsset(deletedAssetPath));
};
Expand Down
92 changes: 31 additions & 61 deletions client/src/preview/route/digitaltwins/DigitalTwinsPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,85 +1,55 @@
import * as React from 'react';
import { useState, useEffect } from 'react';
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { Typography } from '@mui/material';
import Layout from 'page/Layout';
import TabComponent from 'components/tab/TabComponent';
import { TabData } from 'components/tab/subcomponents/TabRender';
import AssetBoard from 'preview/components/asset/AssetBoard';
import GitlabInstance from 'preview/util/gitlab';
import { getAuthority } from 'util/envUtil';
import { setAssets } from 'preview/store/assets.slice';
import { Asset } from 'preview/components/asset/Asset';
import DigitalTwin from 'preview/util/gitlabDigitalTwin';
import { setDigitalTwin } from 'preview/store/digitalTwin.slice';
import { addNewFile } from 'preview/store/file.slice';
import tabs from './DigitalTwinTabDataPreview';
import Editor from './editor/Editor';
import CustomSnackbar from './Snackbar';

export const createDTTab = (error: string | null): TabData[] =>
export const createDTTab = (): TabData[] =>
tabs
.filter((tab) => tab.label === 'Manage' || tab.label === 'Execute')
.filter(
(tab) =>
tab.label === 'Manage' ||
tab.label === 'Execute' ||
tab.label === 'Create',
)
.map((tab) => ({
label: tab.label,
body: (
<>
<Typography variant="body1">{tab.body}</Typography>
<AssetBoard tab={tab.label} error={error} />
{tab.label === 'Create' ? (
<>
<Typography variant="body1">{tab.body}</Typography>
<Editor tab={'create'} />
<CustomSnackbar />
</>
) : (
<>
<Typography variant="body1">{tab.body}</Typography>
<AssetBoard tab={tab.label} />
</>
)}
</>
),
}));

export const fetchSubfolders = async (
gitlabInstance: GitlabInstance,
dispatch: ReturnType<typeof useDispatch>,
setError: React.Dispatch<React.SetStateAction<string | null>>,
) => {
try {
await gitlabInstance.init();
if (gitlabInstance.projectId) {
const subfolders = await gitlabInstance.getDTSubfolders(
gitlabInstance.projectId,
);
dispatch(setAssets(subfolders));
return subfolders;
}
dispatch(setAssets([]));
return [];
} catch (error) {
setError('An error occurred');
return [];
}
};

export const createDigitalTwinsForAssets = async (
assets: Asset[],
dispatch: ReturnType<typeof useDispatch>,
) => {
assets.forEach(async (asset) => {
const gitlabInstance = new GitlabInstance(
sessionStorage.getItem('username') || '',
getAuthority(),
sessionStorage.getItem('access_token') || '',
);
await gitlabInstance.init();
const digitalTwin = new DigitalTwin(asset.name, gitlabInstance);
await digitalTwin.getDescription();
dispatch(setDigitalTwin({ assetName: asset.name, digitalTwin }));
});
};

export const DTContent = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only creating new DT. Can this intent be made more clear with a restructuring of code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of this file has remained unchanged, I have only added the necessary logic for the Create page (the useState newDigitalTwinName) while I have moved the pre-existing logic in the function fetchAssetsAndCreateTwins to the AssetBoard, as it no longer has to be called up only when the main component (DigitalTwinsPreview) is opened, but each time an AssetBoard is rendered, i.e. when switching to the Manage or Execute tabs. This is necessary in order to allow the new DT to be displayed immediately after creation by switching to one of the aforementioned tabs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AssetBoard is a react component. It is better to move the actual logic out of it.

const [error, setError] = useState<string | null>(null);
const dispatch = useDispatch();
const gitlabInstance = new GitlabInstance(
sessionStorage.getItem('username') || '',
getAuthority(),
sessionStorage.getItem('access_token') || '',
);

useEffect(() => {
fetchSubfolders(gitlabInstance, dispatch, setError).then((assets) => {
if (assets) {
createDigitalTwinsForAssets(assets, dispatch);
}
const defaultFiles = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This array and the method to iterate upon this can moved to either to gitlabDigitalTwin.ts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has been moved to file.ts in preview/util.

{ name: 'description.md', type: 'description' },
{ name: 'README.md', type: 'description' },
{ name: '.gitlab-ci.yml', type: 'config' },
];
defaultFiles.forEach((file) => {
dispatch(addNewFile(file));
});
}, [dispatch]);

Expand All @@ -90,7 +60,7 @@ export const DTContent = () => {
The feature is experimental and requires certain gitlab setup in order
for it to work.
</Typography>
<TabComponent assetType={createDTTab(error)} scope={[]} />
<TabComponent assetType={createDTTab()} scope={[]} />
</Layout>
);
};
Expand Down
104 changes: 104 additions & 0 deletions client/src/preview/route/digitaltwins/create/CreateDialogs.tsx
VanessaScherma marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import * as React from 'react';
import {
Dialog,
DialogActions,
DialogContent,
DialogTitle,
TextField,
Button,
} from '@mui/material';
import { useDispatch } from 'react-redux';
import { deleteFile, renameFile } from 'preview/store/file.slice';

interface CreateDialogsProps {
openChangeFileNameDialog: boolean;
onCloseChangeFileNameDialog: () => void;
fileName: string;
setFileName: (name: string) => void;
setFileContent: (content: string) => void;
setFileType: (type: string) => void;

openDeleteFileDialog: boolean;
onCloseDeleteFileDialog: () => void;
}

const CreateDialogs: React.FC<CreateDialogsProps> = ({
VanessaScherma marked this conversation as resolved.
Show resolved Hide resolved
openChangeFileNameDialog,
onCloseChangeFileNameDialog,
fileName,
setFileName,
setFileContent,
setFileType,

openDeleteFileDialog,
onCloseDeleteFileDialog,
}) => {
const [modifiedFileName, setModifiedFileName] = React.useState(fileName);
const dispatch = useDispatch();

React.useEffect(() => {
setModifiedFileName(fileName);
}, [fileName]);

const handleChangeFileName = () => {
dispatch(renameFile({ oldName: fileName, newName: modifiedFileName }));

setFileName(modifiedFileName);

const extension = modifiedFileName.split('.').pop();
setFileType(extension || '');

onCloseChangeFileNameDialog();
};

const handleDeleteFile = () => {
dispatch(deleteFile(fileName));
setFileName('');
setFileContent('');
onCloseDeleteFileDialog();
};

return (
<>
<Dialog open={openChangeFileNameDialog}>
<DialogTitle>Change the file name</DialogTitle>
<DialogContent>
<TextField
autoFocus
margin="dense"
label="New File Name"
fullWidth
variant="outlined"
value={modifiedFileName}
onChange={(e) => setModifiedFileName(e.target.value)}
/>
</DialogContent>
<DialogActions>
<Button onClick={onCloseChangeFileNameDialog} color="primary">
Cancel
</Button>
<Button onClick={handleChangeFileName} color="secondary">
Change
</Button>
</DialogActions>
</Dialog>

<Dialog open={openDeleteFileDialog}>
<DialogContent>
Are you sure you want to delete the <strong>{fileName}</strong> file
and its content?
</DialogContent>
<DialogActions>
<Button onClick={onCloseDeleteFileDialog} color="primary">
No
</Button>
<Button onClick={handleDeleteFile} color="secondary">
Yes
</Button>
</DialogActions>
</Dialog>
</>
);
};

export default CreateDialogs;
Loading
Loading