Skip to content

Commit c0bd6ab

Browse files
committed
Implement folder management modals and enhance prompts breadcrumb functionality
1 parent b70a480 commit c0bd6ab

File tree

4 files changed

+303
-15
lines changed

4 files changed

+303
-15
lines changed

web/oss/src/components/pages/prompts/PromptsPage.tsx

Lines changed: 113 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
11
import {useMemo, useState} from "react"
22

3-
import {Breadcrumb, Button, Dropdown, Input, Space, Table, Tag, Typography} from "antd"
3+
import {Button, Dropdown, Input, Space, Table, Typography} from "antd"
44
import {ColumnsType} from "antd/es/table"
5-
import {createUseStyles} from "react-jss"
65
import useSWR from "swr"
76

87
import {
98
FolderDashedIcon,
10-
FolderSimpleIcon,
9+
FolderIcon,
1110
GearSixIcon,
1211
NoteIcon,
1312
PencilSimpleIcon,
1413
PlusIcon,
14+
SquaresFourIcon,
1515
TrashIcon,
1616
} from "@phosphor-icons/react"
1717

1818
import {useBreadcrumbsEffect} from "@/oss/lib/hooks/useBreadcrumbs"
19-
import {JSSTheme} from "@/oss/lib/Types"
2019
import {useProjectData} from "@/oss/state/project"
2120
import {queryFolders} from "@/oss/services/folders"
2221
import PromptsBreadcrumb from "./components/PromptsBreadcrumb"
2322
import {buildFolderTree, FolderTreeNode} from "./assets/utils"
2423
import {MoreOutlined} from "@ant-design/icons"
2524
import {formatDay} from "@/oss/lib/helpers/dateTimeHelper"
25+
import {DataNode} from "antd/es/tree"
26+
import MoveFolderModal from "./modals/MoveFolderModal"
27+
import DeleteFolderModal from "./modals/DeleteFolderModal"
2628

2729
const {Title} = Typography
2830

29-
const useStyles = createUseStyles((theme: JSSTheme) => ({
30-
container: {},
31-
}))
32-
3331
const PromptsPage = () => {
34-
const classes = useStyles()
3532
const {project, projectId} = useProjectData()
3633
const [searchTerm, setSearchTerm] = useState("")
3734
const [currentFolderId, setCurrentFolderId] = useState<string | null>(null)
35+
const [renameModalOpen, setRenameModalOpen] = useState(false)
36+
const [moveModalOpen, setMoveModalOpen] = useState(false)
37+
const [newPromptModalOpen, setNewPromptModalOpen] = useState(false)
38+
const [newFolderModalOpen, setNewFolderModalOpen] = useState(false)
39+
const [setupWorkflowModalOpen, setSetupWorkflowModalOpen] = useState(false)
40+
const [deleteModalOpen, setDeleteModalOpen] = useState(false)
41+
const [renameValue, setRenameValue] = useState("")
42+
const [moveSelection, setMoveSelection] = useState<string | null>(null)
43+
const [newFolderName, setNewFolderName] = useState("")
3844

3945
useBreadcrumbsEffect({breadcrumbs: {prompts: {label: "prompts"}}}, [])
4046

@@ -47,15 +53,36 @@ const PromptsPage = () => {
4753
return buildFolderTree(folders)
4854
}, [foldersData])
4955

56+
const currentFolder = useMemo(
57+
() => (currentFolderId ? foldersById[currentFolderId] : null),
58+
[currentFolderId, foldersById],
59+
)
60+
61+
const treeData: DataNode[] = useMemo(() => {
62+
const buildNodes = (nodes: FolderTreeNode[]): DataNode[] =>
63+
nodes.map((node) => ({
64+
key: node.id!,
65+
title: node.name,
66+
children: buildNodes(node.children || []),
67+
disableCheckbox: node.id === currentFolderId,
68+
selectable: node.id !== currentFolderId,
69+
}))
70+
71+
return buildNodes(roots)
72+
}, [currentFolderId, roots])
73+
74+
const moveDestinationName = useMemo(
75+
() => (moveSelection ? (foldersById[moveSelection]?.name ?? moveSelection) : null),
76+
[moveSelection, foldersById],
77+
)
78+
5079
// what we show in the table
5180
const visibleRows: FolderTreeNode[] = useMemo(() => {
5281
if (!currentFolderId) return roots
5382
const current = foldersById[currentFolderId]
5483
return current?.children ?? roots
5584
}, [currentFolderId, roots, foldersById])
5685

57-
console.log("visibleRows: ", visibleRows)
58-
5986
const handleRowClick = (record: FolderTreeNode) => {
6087
// only drill into folders; later you’ll have non-folder rows
6188
setCurrentFolderId(record.id as string | null)
@@ -117,6 +144,7 @@ const PromptsPage = () => {
117144
icon: <FolderDashedIcon size={16} />,
118145
onClick: (e: any) => {
119146
e.domEvent.stopPropagation()
147+
setMoveModalOpen(true)
120148
},
121149
},
122150
{
@@ -129,6 +157,7 @@ const PromptsPage = () => {
129157
danger: true,
130158
onClick: (e: any) => {
131159
e.domEvent.stopPropagation()
160+
setDeleteModalOpen(true)
132161
},
133162
},
134163
],
@@ -174,9 +203,63 @@ const PromptsPage = () => {
174203
<Button icon={<TrashIcon />} danger>
175204
Delete
176205
</Button>
177-
<Button icon={<PlusIcon />} type="primary">
178-
Create new
179-
</Button>
206+
207+
<Dropdown
208+
trigger={["click"]}
209+
overlayStyle={{width: 200}}
210+
placement="bottomLeft"
211+
menu={{
212+
items: [
213+
{
214+
key: "new_prompt",
215+
icon: <SquaresFourIcon size={16} />,
216+
label: "New prompt",
217+
onClick: (event) => {
218+
event.domEvent.stopPropagation()
219+
// onNewPrompt?.()
220+
},
221+
},
222+
{
223+
key: "new_folder",
224+
icon: <FolderIcon size={16} />,
225+
label: "New folder",
226+
onClick: (event) => {
227+
event.domEvent.stopPropagation()
228+
// onNewFolder?.()
229+
},
230+
},
231+
{
232+
type: "divider",
233+
},
234+
{
235+
key: "setup_workflow",
236+
icon: (
237+
<svg
238+
xmlns="http://www.w3.org/2000/svg"
239+
width="16"
240+
height="16"
241+
viewBox="0 0 16 16"
242+
fill="none"
243+
>
244+
<path
245+
d="M13.5 2.5H2.5C2.23478 2.5 1.98043 2.60536 1.79289 2.79289C1.60536 2.98043 1.5 3.23478 1.5 3.5V12.5C1.5 12.7652 1.60536 13.0196 1.79289 13.2071C1.98043 13.3946 2.23478 13.5 2.5 13.5H13.5C13.7652 13.5 14.0196 13.3946 14.2071 13.2071C14.3946 13.0196 14.5 12.7652 14.5 12.5V3.5C14.5 3.23478 14.3946 2.98043 14.2071 2.79289C14.0196 2.60536 13.7652 2.5 13.5 2.5ZM5.8 9.1C5.90609 9.17957 5.97622 9.29801 5.99497 9.42929C6.01373 9.56056 5.97957 9.69391 5.9 9.8C5.82044 9.90609 5.70199 9.97622 5.57071 9.99498C5.43944 10.0137 5.30609 9.97956 5.2 9.9L3.2 8.4C3.1379 8.35343 3.0875 8.29303 3.05279 8.22361C3.01807 8.15418 3 8.07762 3 8C3 7.92238 3.01807 7.84582 3.05279 7.77639C3.0875 7.70697 3.1379 7.64657 3.2 7.6L5.2 6.1C5.30609 6.02043 5.43944 5.98627 5.57071 6.00503C5.70199 6.02378 5.82044 6.09391 5.9 6.2C5.97957 6.30609 6.01373 6.43944 5.99497 6.57071C5.97622 6.70199 5.90609 6.82044 5.8 6.9L4.33313 8L5.8 9.1ZM9.48063 4.6375L7.48063 11.6375C7.46358 11.7018 7.43389 11.762 7.3933 11.8146C7.35271 11.8672 7.30203 11.9113 7.24423 11.9441C7.18642 11.9769 7.12265 11.9979 7.05665 12.0058C6.99064 12.0136 6.92373 12.0083 6.85982 11.99C6.79591 11.9717 6.73628 11.9409 6.68444 11.8993C6.63259 11.8577 6.58956 11.8062 6.55786 11.7477C6.52616 11.6893 6.50643 11.6251 6.49982 11.559C6.49321 11.4928 6.49986 11.426 6.51937 11.3625L8.51937 4.3625C8.55781 4.23733 8.64382 4.13224 8.75891 4.0698C8.87399 4.00736 9.00898 3.99256 9.13487 4.02857C9.26075 4.06459 9.36749 4.14855 9.43214 4.26241C9.49679 4.37627 9.5142 4.51094 9.48063 4.6375ZM12.8 8.4L10.8 9.9C10.6939 9.97956 10.5606 10.0137 10.4293 9.99498C10.298 9.97622 10.1796 9.90609 10.1 9.8C10.0204 9.69391 9.98627 9.56056 10.005 9.42929C10.0238 9.29801 10.0939 9.17957 10.2 9.1L11.6669 8L10.2 6.9C10.1475 6.8606 10.1032 6.81125 10.0698 6.75475C10.0363 6.69825 10.0143 6.63571 10.005 6.57071C9.99574 6.50571 9.99935 6.43952 10.0156 6.37591C10.0319 6.3123 10.0606 6.25253 10.1 6.2C10.1394 6.14747 10.1888 6.10322 10.2453 6.06976C10.3018 6.03631 10.3643 6.01431 10.4293 6.00503C10.4943 5.99574 10.5605 5.99935 10.6241 6.01564C10.6877 6.03194 10.7475 6.0606 10.8 6.1L12.8 7.6C12.8621 7.64657 12.9125 7.70697 12.9472 7.77639C12.9819 7.84582 13 7.92238 13 8C13 8.07762 12.9819 8.15418 12.9472 8.22361C12.9125 8.29303 12.8621 8.35343 12.8 8.4Z"
246+
fill="#1C2C3D"
247+
/>
248+
</svg>
249+
),
250+
label: "Set up workflow",
251+
onClick: (event) => {
252+
event.domEvent.stopPropagation()
253+
// onSetupWorkflow?.()
254+
},
255+
},
256+
],
257+
}}
258+
>
259+
<Button icon={<PlusIcon />} type="primary">
260+
Create new
261+
</Button>
262+
</Dropdown>
180263
</Space>
181264
</div>
182265

@@ -194,6 +277,22 @@ const PromptsPage = () => {
194277
})}
195278
/>
196279
</div>
280+
281+
<MoveFolderModal
282+
foldername={currentFolder?.name}
283+
moveDestinationName={moveDestinationName}
284+
moveModalOpen={moveModalOpen}
285+
setMoveModalOpen={setMoveModalOpen}
286+
treeData={treeData}
287+
moveSelection={moveSelection}
288+
setMoveSelection={setMoveSelection}
289+
/>
290+
291+
<DeleteFolderModal
292+
deleteModalOpen={deleteModalOpen}
293+
setDeleteModalOpen={setDeleteModalOpen}
294+
folderName={currentFolder?.name}
295+
/>
197296
</div>
198297
)
199298
}

web/oss/src/components/pages/prompts/components/PromptsBreadcrumb.tsx

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,52 @@ import {Breadcrumb, BreadcrumbProps} from "antd"
22
import React, {useMemo} from "react"
33
import {useProjectData} from "@/oss/state/project"
44
import {FolderTreeNode} from "../assets/utils"
5+
import {createUseStyles} from "react-jss"
6+
import {JSSTheme} from "@/oss/lib/Types"
7+
import {
8+
FolderDashedIcon,
9+
FolderIcon,
10+
PencilSimpleLineIcon,
11+
SquaresFourIcon,
12+
TrashIcon,
13+
} from "@phosphor-icons/react"
514

615
interface PromptsBreadcrumbProps {
716
foldersById: Record<string, FolderTreeNode>
817
currentFolderId: string | null
918
onFolderChange?: (folderId: string | null) => void
1019
}
1120

21+
const useStyles = createUseStyles((theme: JSSTheme) => ({
22+
container: {
23+
width: "fit-content",
24+
backgroundColor: theme.colorPrimaryBg,
25+
borderRadius: theme.borderRadiusLG,
26+
paddingLeft: theme.paddingXS,
27+
paddingRight: theme.paddingXS,
28+
"& :not(.ant-breadcrumb-separator)": {
29+
cursor: "pointer",
30+
},
31+
"& .ant-dropdown-trigger": {
32+
display: "flex",
33+
alignItems: "center",
34+
gap: 4,
35+
"&:hover": {
36+
backgroundColor: "inherit",
37+
},
38+
"& .anticon-down": {
39+
fontSize: "10px !important",
40+
},
41+
},
42+
},
43+
}))
44+
1245
const PromptsBreadcrumb = ({
1346
foldersById,
1447
currentFolderId,
1548
onFolderChange,
1649
}: PromptsBreadcrumbProps) => {
50+
const classes = useStyles()
1751
const {project} = useProjectData()
1852

1953
const folderChain = useMemo(() => {
@@ -41,19 +75,89 @@ const PromptsBreadcrumb = ({
4175
})
4276
}
4377

78+
const actionItems = [
79+
{
80+
key: "rename_folder",
81+
icon: <PencilSimpleLineIcon size={16} />,
82+
label: "Rename",
83+
onClick: () => {},
84+
// disabled: disableFolderActions,
85+
},
86+
{
87+
key: "move_folder",
88+
icon: <FolderDashedIcon size={16} />,
89+
label: "Move",
90+
onClick: () => {},
91+
// disabled: disableFolderActions,
92+
},
93+
{
94+
type: "divider",
95+
},
96+
{
97+
key: "new_prompt",
98+
icon: <SquaresFourIcon size={16} />,
99+
label: "New prompt",
100+
onClick: () => {},
101+
},
102+
{
103+
key: "new_folder",
104+
icon: <FolderIcon size={16} />,
105+
label: "New folder",
106+
onClick: () => {},
107+
},
108+
{
109+
type: "divider",
110+
},
111+
{
112+
key: "setup_workflow",
113+
icon: (
114+
<svg
115+
xmlns="http://www.w3.org/2000/svg"
116+
width="16"
117+
height="16"
118+
viewBox="0 0 16 16"
119+
fill="none"
120+
>
121+
<path
122+
d="M13.5 2.5H2.5C2.23478 2.5 1.98043 2.60536 1.79289 2.79289C1.60536 2.98043 1.5 3.23478 1.5 3.5V12.5C1.5 12.7652 1.60536 13.0196 1.79289 13.2071C1.98043 13.3946 2.23478 13.5 2.5 13.5H13.5C13.7652 13.5 14.0196 13.3946 14.2071 13.2071C14.3946 13.0196 14.5 12.7652 14.5 12.5V3.5C14.5 3.23478 14.3946 2.98043 14.2071 2.79289C14.0196 2.60536 13.7652 2.5 13.5 2.5ZM5.8 9.1C5.90609 9.17957 5.97622 9.29801 5.99497 9.42929C6.01373 9.56056 5.97957 9.69391 5.9 9.8C5.82044 9.90609 5.70199 9.97622 5.57071 9.99498C5.43944 10.0137 5.30609 9.97956 5.2 9.9L3.2 8.4C3.1379 8.35343 3.0875 8.29303 3.05279 8.22361C3.01807 8.15418 3 8.07762 3 8C3 7.92238 3.01807 7.84582 3.05279 7.77639C3.0875 7.70697 3.1379 7.64657 3.2 7.6L5.2 6.1C5.30609 6.02043 5.43944 5.98627 5.57071 6.00503C5.70199 6.02378 5.82044 6.09391 5.9 6.2C5.97957 6.30609 6.01373 6.43944 5.99497 6.57071C5.97622 6.70199 5.90609 6.82044 5.8 6.9L4.33313 8L5.8 9.1ZM9.48063 4.6375L7.48063 11.6375C7.46358 11.7018 7.43389 11.762 7.3933 11.8146C7.35271 11.8672 7.30203 11.9113 7.24423 11.9441C7.18642 11.9769 7.12265 11.9979 7.05665 12.0058C6.99064 12.0136 6.92373 12.0083 6.85982 11.99C6.79591 11.9717 6.73628 11.9409 6.68444 11.8993C6.63259 11.8577 6.58956 11.8062 6.55786 11.7477C6.52616 11.6893 6.50643 11.6251 6.49982 11.559C6.49321 11.4928 6.49986 11.426 6.51937 11.3625L8.51937 4.3625C8.55781 4.23733 8.64382 4.13224 8.75891 4.0698C8.87399 4.00736 9.00898 3.99256 9.13487 4.02857C9.26075 4.06459 9.36749 4.14855 9.43214 4.26241C9.49679 4.37627 9.5142 4.51094 9.48063 4.6375ZM12.8 8.4L10.8 9.9C10.6939 9.97956 10.5606 10.0137 10.4293 9.99498C10.298 9.97622 10.1796 9.90609 10.1 9.8C10.0204 9.69391 9.98627 9.56056 10.005 9.42929C10.0238 9.29801 10.0939 9.17957 10.2 9.1L11.6669 8L10.2 6.9C10.1475 6.8606 10.1032 6.81125 10.0698 6.75475C10.0363 6.69825 10.0143 6.63571 10.005 6.57071C9.99574 6.50571 9.99935 6.43952 10.0156 6.37591C10.0319 6.3123 10.0606 6.25253 10.1 6.2C10.1394 6.14747 10.1888 6.10322 10.2453 6.06976C10.3018 6.03631 10.3643 6.01431 10.4293 6.00503C10.4943 5.99574 10.5605 5.99935 10.6241 6.01564C10.6877 6.03194 10.7475 6.0606 10.8 6.1L12.8 7.6C12.8621 7.64657 12.9125 7.70697 12.9472 7.77639C12.9819 7.84582 13 7.92238 13 8C13 8.07762 12.9819 8.15418 12.9472 8.22361C12.9125 8.29303 12.8621 8.35343 12.8 8.4Z"
123+
fill="#1C2C3D"
124+
/>
125+
</svg>
126+
),
127+
label: "Set up workflow",
128+
onClick: () => {},
129+
},
130+
{
131+
key: "delete_folder",
132+
icon: <TrashIcon size={16} />,
133+
label: "Delete",
134+
danger: true,
135+
onClick: () => {},
136+
// disabled: disableFolderActions,
137+
},
138+
]
139+
44140
folderChain.forEach((folder, index) => {
45141
const isLast = index === folderChain.length - 1
46142

47143
base.push({
48144
title: folder.name,
49145
onClick: !isLast ? () => onFolderChange?.(folder.id!) : undefined,
146+
...(isLast
147+
? {
148+
menu: {
149+
items: actionItems,
150+
className: "w-[200px]",
151+
},
152+
}
153+
: {}),
50154
})
51155
})
52156

53157
return base
54158
}, [project, folderChain, onFolderChange])
55159

56-
return <Breadcrumb items={items} />
160+
return <Breadcrumb items={items} className={classes.container} />
57161
}
58162

59163
export default PromptsBreadcrumb
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import {FolderSimpleIcon} from "@phosphor-icons/react"
2+
import {Modal} from "antd"
3+
import React, {Dispatch, SetStateAction} from "react"
4+
5+
interface DeleteFolderModalProps {
6+
deleteModalOpen: boolean
7+
setDeleteModalOpen: Dispatch<SetStateAction<boolean>>
8+
folderName?: string | null | undefined
9+
}
10+
11+
const DeleteFolderModal = ({
12+
deleteModalOpen,
13+
setDeleteModalOpen,
14+
folderName,
15+
}: DeleteFolderModalProps) => {
16+
return (
17+
<Modal
18+
title="Delete folder"
19+
open={deleteModalOpen}
20+
onOk={() => setDeleteModalOpen(false)}
21+
onCancel={() => setDeleteModalOpen(false)}
22+
okText="Delete"
23+
okButtonProps={{danger: true}}
24+
destroyOnClose
25+
>
26+
<div className="flex flex-col gap-3">
27+
<div className="text-gray-500">Are you sure you want to delete?</div>
28+
<div className="flex items-center gap-2">
29+
<FolderSimpleIcon size={20} />
30+
<span className="font-medium">{folderName ?? "Folder"}</span>
31+
</div>
32+
<div className="text-gray-500">This action is not reversible.</div>
33+
</div>
34+
</Modal>
35+
)
36+
}
37+
38+
export default DeleteFolderModal

0 commit comments

Comments
 (0)