Skip to content

Commit

Permalink
wip: Added manifest file structure select
Browse files Browse the repository at this point in the history
  • Loading branch information
JacobiClark committed Oct 25, 2024
1 parent eecf263 commit b6963ed
Show file tree
Hide file tree
Showing 4 changed files with 302 additions and 85 deletions.
177 changes: 118 additions & 59 deletions src/renderer/src/components/shared/DatasetTreeViewRenderer/index.jsx
Original file line number Diff line number Diff line change
@@ -1,110 +1,169 @@
import { useState } from "react";
import { Collapse, Text, Stack, UnstyledButton } from "@mantine/core";
import { useHover } from "@mantine/hooks";
import {
IconFolder,
IconFolderOpen,
IconFile,
IconPhoto,
IconFileTypeBmp,
IconFileTypeCsv,
IconFileTypeDoc,
IconFileTypeDocx,
IconFileTypeJpg,
IconFileTypePdf,
IconFileTypePng,
IconFileTypeSvg,
IconFileTypeTxt,
IconFileTypeXls,
IconFileTypeXml,
IconFileTypeZip,
} from "@tabler/icons-react";
import { getEntityForRelativePath } from "../../../stores/slices/manifestEntitySelectorSlice";

// Constants
const folderColor = "#ADD8E6";
const folderIconSize = 18;
const fileIconSize = 15;
const FOLDER_ICON_COLOR = "#ADD8E6";
const FOLDER_ICON_SIZE = 18;
const FILE_ICON_SIZE = 15;

// File extension to icon map
const fileIconMap = {
csv: <IconFileTypeCsv size={fileIconSize} />,
xls: <IconFileTypeXls size={fileIconSize} />,
xlsx: <IconFileTypeXls size={fileIconSize} />,
txt: <IconFileTypeTxt size={fileIconSize} />,
doc: <IconFileTypeDoc size={fileIconSize} />,
docx: <IconFileTypeDocx size={fileIconSize} />,
pdf: <IconFileTypePdf size={fileIconSize} />,
png: <IconFileTypePng size={fileIconSize} />,
jpg: <IconFileTypeJpg size={fileIconSize} />,
jpeg: <IconFileTypeJpg size={fileIconSize} />,
bmp: <IconFileTypeBmp size={fileIconSize} />,
svg: <IconFileTypeSvg size={fileIconSize} />,
gif: <IconPhoto size={fileIconSize} />,
webp: <IconPhoto size={fileIconSize} />,
tiff: <IconPhoto size={fileIconSize} />,
heic: <IconPhoto size={fileIconSize} />,
heif: <IconPhoto size={fileIconSize} />,
avif: <IconPhoto size={fileIconSize} />,
jp2: <IconPhoto size={fileIconSize} />,
jxr: <IconPhoto size={fileIconSize} />,
wdp: <IconPhoto size={fileIconSize} />,
xml: <IconFileTypeXml size={fileIconSize} />,
zip: <IconFileTypeZip size={fileIconSize} />,
rar: <IconFileTypeZip size={fileIconSize} />,
csv: <IconFileTypeCsv size={FILE_ICON_SIZE} />,
xls: <IconFileTypeXls size={FILE_ICON_SIZE} />,
xlsx: <IconFileTypeXls size={FILE_ICON_SIZE} />,
txt: <IconFileTypeTxt size={FILE_ICON_SIZE} />,
doc: <IconFileTypeDoc size={FILE_ICON_SIZE} />,
docx: <IconFileTypeDocx size={FILE_ICON_SIZE} />,
pdf: <IconFileTypePdf size={FILE_ICON_SIZE} />,
png: <IconFileTypePng size={FILE_ICON_SIZE} />,
jpg: <IconFileTypeJpg size={FILE_ICON_SIZE} />,
jpeg: <IconFileTypeJpg size={FILE_ICON_SIZE} />,
xml: <IconFileTypeXml size={FILE_ICON_SIZE} />,
zip: <IconFileTypeZip size={FILE_ICON_SIZE} />,
rar: <IconFileTypeZip size={FILE_ICON_SIZE} />,
};
const getFileIcon = (fileName) => {

const getFileTypeIcon = (fileName) => {
const extension = fileName.split(".").pop().toLowerCase();
return fileIconMap[extension] || <IconFile size={fileIconSize} />;
return fileIconMap[extension] || <IconFile size={FILE_ICON_SIZE} />;
};

// FileItem component
const FileItem = ({ name, content, onFileClick, getFileBackgroundColor }) => {
const filesRelativePath = content.relativePath;
const filesEntity = getEntityForRelativePath("subjects", filesRelativePath);

const fileBackgroundColor = getFileBackgroundColor(filesRelativePath);
return (
<div
style={{
paddingLeft: 10,
display: "flex",
alignItems: "center",
gap: 5,
backgroundColor: fileBackgroundColor,
}}
onClick={() => onFileClick(name, content)}
>
{getFileTypeIcon(name)}
<Text>{name}</Text>
</div>
);
};
const FileView = ({ name }) => (
<div style={{ paddingLeft: 10, display: "flex", alignItems: "center", gap: 5 }}>
{getFileIcon(name)}
<Text>{name}</Text>
</div>
);
const FolderView = ({ name, content }) => {
const [folderIsOpen, setFolderIsOpen] = useState(false);

// FolderItem component
const FolderItem = ({
name,
content,
onFolderClick,
onFileClick,
getFolderBackgroundColor,
getFileBackgroundColor,
}) => {
const [isOpen, setIsOpen] = useState(false);
const { hovered, ref } = useHover();

const toggleFolder = () => {
setFolderIsOpen((prev) => !prev);
setIsOpen((prev) => !prev);
};

return (
<Stack gap={1}>
<UnstyledButton
onClick={toggleFolder}
style={{ display: "flex", alignItems: "center", gap: 3 }}
>
{folderIsOpen ? (
<IconFolderOpen size={folderIconSize} color={folderColor} />
<div style={{ display: "flex", alignItems: "center", gap: 3 }}>
{isOpen ? (
<IconFolderOpen
size={FOLDER_ICON_SIZE}
color={FOLDER_ICON_COLOR}
onClick={toggleFolder}
/>
) : (
<IconFolder size={folderIconSize} color={folderColor} />
<IconFolder size={FOLDER_ICON_SIZE} color={FOLDER_ICON_COLOR} onClick={toggleFolder} />
)}
<Text size="lg">{name}</Text>
</UnstyledButton>
<Collapse in={folderIsOpen} ml="sm">
<UnstyledButton
ref={ref}
style={{
backgroundColor: hovered ? "gray" : "transparent",
padding: "2px 5px",
borderRadius: "4px",
}}
onClick={() => onFolderClick(name, content)}
>
<Text size="lg">{name}</Text>
</UnstyledButton>
</div>
<Collapse in={isOpen} ml="xs">
{Object.keys(content.folders || {}).map((folderName) => (
<FolderView key={folderName} name={folderName} content={content.folders[folderName]} />
<FolderItem
key={folderName}
name={folderName}
content={content.folders[folderName]}
onFolderClick={onFolderClick}
onFileClick={onFileClick}
/>
))}
{Object.keys(content.files || {}).map((fileName) => (
<FileView key={fileName} name={fileName} />
<FileItem
key={fileName}
name={fileName}
content={content.files[fileName]}
onFileClick={onFileClick}
getFileBackgroundColor={getFileBackgroundColor}
/>
))}
</Collapse>
</Stack>
);
};

// Main component
const DatasetTreeViewRenderer = ({ datasetStructure }) => {
if (!datasetStructure?.["folders"] || !datasetStructure?.["files"]) return null;
return (
const DatasetTreeView = ({
datasetStructure,
onFolderClick,
onFileClick,
getFolderBackgroundColor,
getFileBackgroundColor,
}) =>
!datasetStructure?.folders && !datasetStructure?.files ? null : (
<Stack gap={1}>
{Object.keys(datasetStructure.files || {}).map((fileName) => (
<FileView key={fileName} name={fileName} />
<FileItem
key={fileName}
name={fileName}
content={datasetStructure.files[fileName]}
onFileClick={onFileClick}
getFileBackgroundColor={getFileBackgroundColor}
/>
))}
{Object.keys(datasetStructure.folders || {}).map((folderName) => (
<FolderView
<FolderItem
key={folderName}
name={folderName}
content={datasetStructure.folders[folderName]}
onFolderClick={onFolderClick}
onFileClick={onFileClick}
getFolderBackgroundColor={getFolderBackgroundColor}
getFileBackgroundColor={getFileBackgroundColor}
/>
))}
</Stack>
);
};

export default DatasetTreeViewRenderer;
export default DatasetTreeView;
Original file line number Diff line number Diff line change
@@ -1,27 +1,72 @@
import { Grid, Button, Stack } from "@mantine/core";
import { Grid, Button, Stack, Text, Paper, Divider, Group } from "@mantine/core";
import FullWidthContainer from "../../containers/FullWidthContainer";
import useGlobalStore from "../../../stores/globalStore";
import DatasetTreeViewRenderer from "../DatasetTreeViewRenderer";
import {
setActiveEntity,
toggleRelativeFilePathForManifestEntity,
} from "../../../stores/slices/manifestEntitySelectorSlice";

const ManifestEntitySelector = () => {
const setDatasetStructureJSONObj = useGlobalStore((state) => state.setDatasetStructureJSONObj);
const datasetStructureJSONObj = useGlobalStore((state) => state.datasetStructureJSONObj);
const entityList = useGlobalStore((state) => state.entityList);
const activeEntity = useGlobalStore((state) => state.activeEntity);
const entityType = useGlobalStore((state) => state.entityType);
const manifestEntityObj = useGlobalStore((state) => state.manifestEntityObj);
console.log("Manifest entity object:", JSON.stringify(manifestEntityObj, null, 2));
const handleEntityClick = (entity) => {
setActiveEntity(entity);
};

const handleFolderClick = (folderName, folderContents) => {
console.log("Folder clicked:", folderName);
console.log("Folder contents:", folderContents);
console.log("Entity type:", entityType);
toggleRelativeFilePathForManifestEntity(entityType, activeEntity, folderContents.relativePath);
};

const handleButtonClick = () => {
console.log("Button clicked!");
const datasetStructureJSONObjCopy = JSON.parse(JSON.stringify(window.datasetStructureJSONObj));
setDatasetStructureJSONObj(datasetStructureJSONObjCopy);
const handleFileClick = (fileName, fileContents) => {
console.log("File clicked:", fileName);
console.log("File contents:", fileContents);
console.log("Entity type:", entityType);
toggleRelativeFilePathForManifestEntity(entityType, activeEntity, fileContents.relativePath);
};

return (
<FullWidthContainer>
<Button onClick={handleButtonClick}>Set Name</Button>
<Grid>
<Grid.Col span={4}>
<Stack></Stack>
<Grid gutter="md">
<Grid.Col span={4} style={{ position: "sticky", top: "20px" }}>
<Paper shadow="lg" p="lg" radius="md" withBorder>
<Text size="xl" weight={700} align="center" color="dark">
Select Entity
</Text>
<Divider my="lg" />
<Stack gap="xs">
{entityList.map((entity) => (
<Button
key={entity}
variant={activeEntity === entity ? "filled" : "outline"}
color={activeEntity === entity ? "blue" : "gray"}
size="compact-md"
onClick={() => handleEntityClick(entity)}
>
<Text size="md" c={activeEntity === entity ? "white" : "black"}>
{entity}
</Text>
</Button>
))}
</Stack>
</Paper>
</Grid.Col>

<Grid.Col span={8}>
<DatasetTreeViewRenderer datasetStructure={datasetStructureJSONObj} />
<Paper shadow="sm" p="lg" radius="md">
<DatasetTreeViewRenderer
datasetStructure={datasetStructureJSONObj}
onFolderClick={handleFolderClick}
onFileClick={handleFileClick}
/>
</Paper>
</Grid.Col>
</Grid>
</FullWidthContainer>
Expand Down
12 changes: 9 additions & 3 deletions src/renderer/src/scripts/guided-mode/guided-curate-dataset.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import client from "../client";
import jQuery from "jquery";
import bootstrap from "bootstrap";
import * as select2 from "select2"; // TODO: select2()
// select2()
import {
swalConfirmAction,
swalShowError,
Expand All @@ -38,17 +37,20 @@ import {
} from "../utils/swal-utils";

// Import state management stores

import useGlobalStore from "../../stores/globalStore";
import { setDropdownState } from "../../stores/slices/dropDownSlice";
import {
setGuidedModePage,
setGuidedDatasetName,
setGuidedDatasetSubtitle,
} from "../../stores/slices/guidedModeSlice";
import {
setDatasetStructureJSONObj,
setEntityList,
setEntityType,
} from "../../stores/slices/manifestEntitySelectorSlice";

import "bootstrap-select";
// import DragSort from '@yaireo/dragsort'
import Cropper from "cropperjs";

import "jstree";
Expand Down Expand Up @@ -5463,6 +5465,10 @@ window.openPage = async (targetPageID) => {
renderManifestCards();
}
if (targetPageID === "guided-manifest-subject-entity-selector-tab") {
//
setEntityList(window.getExistingSubjectNames());
setDatasetStructureJSONObj(window.datasetStructureJSONObj);
setEntityType("subjects");
}

if (targetPageID === "guided-create-submission-metadata-tab") {
Expand Down
Loading

0 comments on commit b6963ed

Please sign in to comment.