Skip to content

Commit 8a5cb19

Browse files
committed
feat: version manager
1 parent eda853d commit 8a5cb19

File tree

18 files changed

+370
-8
lines changed

18 files changed

+370
-8
lines changed

package-lock.json

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"react": "^18.3.1",
7373
"react-apexcharts": "^1.7.0",
7474
"react-avatar": "^5.0.3",
75+
"react-complex-tree": "^2.6.1",
7576
"react-dom": "^18.3.1",
7677
"react-ga4": "^2.1.0",
7778
"react-hook-form": "^7.54.2",

src/components/molecules/drawer.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,33 +19,37 @@ export const Drawer = ({
1919
width,
2020
divId,
2121
isScreenHeight = true,
22+
position = "right",
2223
}: DrawerProps) => {
2324
const { isOpen, onClose } = useDrawerStore((state) => ({
2425
isOpen: state.drawers[name] || isForcedOpen,
2526
onClose: state.closeDrawer,
2627
}));
2728

2829
const baseClass = cn(
29-
"size-full border-l border-gray-950 bg-white p-5 text-black shadow-lg",
30+
"size-full bg-white p-5 text-black shadow-lg",
3031
{
32+
"border-l border-gray-950": position === "right",
33+
"border-r border-gray-950": position === "left",
3134
"bg-gray-1100 text-white": variant === "dark",
3235
},
3336
className
3437
);
3538

3639
const wrapperClass = cn(
37-
"fixed right-0 top-0 z-drawer h-full",
40+
"fixed top-0 z-drawer h-full",
3841
{
42+
"right-0": position === "right",
43+
"left-0": position === "left",
3944
"w-550": !width,
40-
},
41-
{
4245
"h-full": isScreenHeight,
4346
},
4447
wrapperClassName
4548
);
4649

4750
const wrapperStyle = width ? { width: `${width}vw` } : {};
4851
const animationDistance = width && typeof window !== "undefined" ? window.innerWidth * (width / 100) : 500;
52+
const animationX = position === "left" ? -animationDistance : animationDistance;
4953

5054
const bgClass = cn("fixed left-0 top-0 z-overlay flex size-full items-center justify-center backdrop-blur-sm", {
5155
"backdrop-blur-none": bgTransparent,
@@ -64,10 +68,10 @@ export const Drawer = ({
6468
className={baseClass}
6569
data-drawer-name={name}
6670
exit={{
67-
x: animationDistance,
71+
x: animationX,
6872
transition: { duration: 0.25 },
6973
}}
70-
initial={{ x: animationDistance }}
74+
initial={{ x: animationX }}
7175
>
7276
{children}
7377
</motion.aside>

src/components/organisms/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export { TitleTopbar } from "@components/organisms/topbar";
1616
export { UserFeedbackForm } from "@components/organisms/userFeedbackForm";
1717
export { ProtectedRoute } from "@components/organisms/protectedRoute";
1818
export { ProjectSettingsViewDrawer } from "@components/organisms/projectSettingsView";
19+
export { ProjectFilesDrawer } from "@components/organisms/projectFilesDrawer";
1920
export { TemplateStart } from "@components/organisms/templateStart";
2021
export { TourManager, TourPopover } from "@components/organisms/tour";
2122
export { CreateNewProject } from "@components/organisms/createNewProject";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { ProjectFilesDrawer } from "./projectFilesDrawer";
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
.rct-tree-root {
2+
color: #e8e8eb;
3+
}
4+
5+
.rct-tree-item-li {
6+
color: #e8e8eb;
7+
position: relative;
8+
}
9+
10+
.rct-tree-item-title-container {
11+
padding: 0.5rem;
12+
border-radius: 0.5rem;
13+
transition: background-color 0.2s;
14+
display: flex;
15+
align-items: center;
16+
justify-content: space-between;
17+
}
18+
19+
.rct-tree-item-title-container:hover {
20+
background-color:rgb(24, 24, 42);
21+
}
22+
23+
.rct-tree-item-title-container-selected {
24+
background-color: #2c2c2c !important;
25+
color: #ffffff;
26+
}
27+
28+
.rct-tree-item-title-container-dragging-over {
29+
background-color: transparent !important;
30+
}
31+
32+
.rct-tree-item-arrow {
33+
color: #e8e8eb;
34+
min-width: 1rem;
35+
}
36+
37+
.rct-tree-item-arrow:hover {
38+
color: #ffffff;
39+
}
40+
41+
.rct-tree-item-li[aria-expanded="true"] > .rct-tree-item-title-container > .rct-tree-item-arrow {
42+
color: #ffffff;
43+
}
44+
45+
.rct-tree-root ul {
46+
padding-left: 0.75rem;
47+
}
48+
49+
.rct-tree-item-button {
50+
background: transparent;
51+
border: none;
52+
padding: 0;
53+
margin: 0;
54+
cursor: pointer;
55+
}
56+
57+
.rct-tree-item-title-container:hover [data-delete-btn] {
58+
opacity: 1 !important;
59+
}
60+
61+
.rct-tree-item-button:hover {
62+
background-color: #BCF870;
63+
color: #2d2d2d;
64+
65+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import React, { useCallback, useMemo } from "react";
2+
3+
import { UncontrolledTreeEnvironment, Tree, TreeItemIndex } from "react-complex-tree";
4+
import { useParams } from "react-router-dom";
5+
import "react-complex-tree/lib/style-modern.css";
6+
import "./projectFilesDrawer.css";
7+
8+
import { defaultProjectFilesWidth } from "@src/constants";
9+
import { EventListenerName } from "@src/enums";
10+
import { DrawerName, ModalName } from "@src/enums/components";
11+
import { useEventListener, useResize } from "@src/hooks";
12+
import { useCacheStore, useDrawerStore, useFileStore, useModalStore, useSharedBetweenProjectsStore } from "@src/store";
13+
import { buildFileTree } from "@src/utilities";
14+
15+
import { IconSvg, ResizeButton } from "@components/atoms";
16+
import { Drawer } from "@components/molecules";
17+
18+
import { TrashIcon } from "@assets/image/icons";
19+
20+
export const ProjectFilesDrawer = () => {
21+
const { projectId } = useParams();
22+
const { openDrawer, closeDrawer } = useDrawerStore();
23+
const { setProjectFilesWidth, projectFilesWidth } = useSharedBetweenProjectsStore();
24+
const currentProjectFilesWidth = projectFilesWidth[projectId!] || defaultProjectFilesWidth.initial;
25+
const { resources } = useCacheStore();
26+
const { openFileAsActive, openFiles } = useFileStore();
27+
const { openModal } = useModalStore();
28+
29+
const activeFile = openFiles[projectId!]?.find((f: { isActive: boolean }) => f.isActive);
30+
const activeFileName = activeFile?.name || "";
31+
32+
const [drawerWidth] = useResize({
33+
direction: "horizontal",
34+
min: defaultProjectFilesWidth.min,
35+
max: defaultProjectFilesWidth.max,
36+
initial: currentProjectFilesWidth,
37+
value: currentProjectFilesWidth,
38+
id: "project-files-drawer-resize",
39+
onChange: (width) => {
40+
if (projectId) {
41+
setProjectFilesWidth(projectId, width);
42+
}
43+
},
44+
invertDirection: false,
45+
});
46+
47+
const open = () => {
48+
if (!projectId) return;
49+
openDrawer(DrawerName.projectFiles);
50+
};
51+
52+
const close = () => {
53+
if (!projectId) return;
54+
closeDrawer(DrawerName.projectFiles);
55+
};
56+
57+
useEventListener(EventListenerName.displayProjectFilesSidebar, () => open());
58+
useEventListener(EventListenerName.hideProjectFilesSidebar, () => close());
59+
60+
const files = Object.keys(resources || {}).filter((name) => name !== "README.md");
61+
62+
const treeData = useMemo(() => buildFileTree(files), [files]);
63+
64+
const handleFileClick = (fileName: string) => {
65+
openFileAsActive(fileName);
66+
};
67+
68+
const handleDeleteFile = useCallback(
69+
(fileName: string, e: React.MouseEvent) => {
70+
e.stopPropagation();
71+
openModal(ModalName.deleteFile, fileName);
72+
},
73+
[openModal]
74+
);
75+
76+
const renderItemTitle = useCallback(
77+
(context: { item: any; title: string }) => {
78+
const { item, title } = context;
79+
const isFolder = item.isFolder;
80+
const itemPath = item.index as string;
81+
82+
return (
83+
<div className="flex w-full items-center justify-between">
84+
<span>{title}</span>
85+
{!isFolder && itemPath !== "root" ? (
86+
<button
87+
className="flex items-center justify-center rounded bg-transparent p-1 opacity-0 transition-opacity hover:bg-gray-1250 group-hover:opacity-100"
88+
data-delete-btn
89+
onClick={(e) => handleDeleteFile(itemPath, e)}
90+
type="button"
91+
>
92+
<IconSvg className="size-4 stroke-white hover:stroke-red-500" src={TrashIcon} />
93+
</button>
94+
) : null}
95+
</div>
96+
);
97+
},
98+
[handleDeleteFile]
99+
);
100+
101+
return (
102+
<Drawer
103+
bgTransparent
104+
className="rounded-r-lg bg-gray-1100 pt-4"
105+
divId="project-sidebar-files"
106+
isScreenHeight={false}
107+
name={DrawerName.projectFiles}
108+
onCloseCallback={close}
109+
position="left"
110+
width={drawerWidth}
111+
wrapperClassName="p-0 relative absolute"
112+
>
113+
<div className="flex h-full flex-col px-4 pt-4">
114+
<div className="mb-4 text-sm font-semibold uppercase text-gray-400">Files</div>
115+
116+
<div className="scrollbar flex-1 overflow-y-auto">
117+
{files.length === 0 ? (
118+
<div className="flex items-center justify-center py-8 text-sm text-gray-500">
119+
No files available
120+
</div>
121+
) : (
122+
<UncontrolledTreeEnvironment
123+
canDragAndDrop={false}
124+
canDropOnFolder={false}
125+
canRename={false}
126+
canReorderItems={false}
127+
dataProvider={{
128+
async getTreeItem(itemId: TreeItemIndex) {
129+
return treeData[itemId];
130+
},
131+
}}
132+
getItemTitle={(item) => item.data as string}
133+
onSelectItems={(items) => {
134+
const selectedItem = items[0] as string;
135+
if (selectedItem && !treeData[selectedItem]?.isFolder) {
136+
handleFileClick(selectedItem);
137+
}
138+
}}
139+
renderItemTitle={renderItemTitle}
140+
viewState={{
141+
"project-files-tree": {
142+
expandedItems: Object.keys(treeData).filter((key) => treeData[key].isFolder),
143+
selectedItems: [activeFileName],
144+
},
145+
}}
146+
>
147+
<Tree rootItem="root" treeId="project-files-tree" treeLabel="Project Files" />
148+
</UncontrolledTreeEnvironment>
149+
)}
150+
</div>
151+
</div>
152+
153+
<ResizeButton
154+
className="absolute right-0 top-1/2 z-[125] w-2 -translate-y-1/2 cursor-ew-resize px-1 hover:bg-white"
155+
direction="horizontal"
156+
id="project-files-drawer-resize-button"
157+
resizeId="project-files-drawer-resize"
158+
/>
159+
</Drawer>
160+
);
161+
};

src/components/pages/project.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Outlet, useLocation, useNavigate, useParams } from "react-router-dom";
44

55
import { defaultProjectTab, projectTabs } from "@constants/project.constants";
66
import { EventListenerName, TourId } from "@src/enums";
7-
import { useEventListener } from "@src/hooks";
7+
import { triggerEvent, useEventListener } from "@src/hooks";
88
import {
99
useCacheStore,
1010
useManualRunStore,
@@ -31,6 +31,10 @@ export const Project = () => {
3131
const { setExpandedProjectNavigation, expandedProjectNavigation } = useSharedBetweenProjectsStore();
3232
const [isConnectionLoadingFromChatbot, setIsConnectionLoadingFromChatbot] = useState(false);
3333

34+
useEffect(() => {
35+
triggerEvent(EventListenerName.displayProjectFilesSidebar);
36+
}, []);
37+
3438
useEffect(() => {
3539
if (expandedProjectNavigation[projectId!] === undefined) {
3640
setExpandedProjectNavigation(projectId!, true);

src/components/templates/projectWrapper.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { DrawerName } from "@src/enums/components";
77
import { triggerEvent } from "@src/hooks";
88
import { useDrawerStore, useSharedBetweenProjectsStore } from "@src/store";
99

10-
import { ChatbotDrawer, ProjectSettingsViewDrawer } from "@components/organisms";
10+
import { ChatbotDrawer, ProjectFilesDrawer, ProjectSettingsViewDrawer } from "@components/organisms";
1111

1212
export const ProjectWrapper = () => {
1313
const { projectId } = useParams();
@@ -39,6 +39,7 @@ export const ProjectWrapper = () => {
3939
return (
4040
<div className="relative mt-1.5 h-full overflow-hidden">
4141
<Outlet />
42+
<ProjectFilesDrawer />
4243
<ChatbotDrawer />
4344
<ProjectSettingsViewDrawer />
4445
</div>

src/constants/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export {
7777
defaultSplitFrameSize,
7878
defaultChatbotWidth,
7979
defaultProjectSettingsWidth,
80+
defaultProjectFilesWidth,
8081
} from "@constants/resize.constants";
8182
export {
8283
sessionTabs,

0 commit comments

Comments
 (0)