enh: create subfolder
Co-Authored-By: Colin Chen <1207878+silenceroom@users.noreply.github.com>
This commit is contained in:
co-authored by
Colin Chen
parent
80b5896b70
commit
8913f37c3d
@@ -71,6 +71,7 @@ class FolderForm(BaseModel):
|
|||||||
name: str
|
name: str
|
||||||
data: Optional[dict] = None
|
data: Optional[dict] = None
|
||||||
meta: Optional[dict] = None
|
meta: Optional[dict] = None
|
||||||
|
parent_id: Optional[str] = None
|
||||||
model_config = ConfigDict(extra="allow")
|
model_config = ConfigDict(extra="allow")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ def create_folder(
|
|||||||
db: Session = Depends(get_session),
|
db: Session = Depends(get_session),
|
||||||
):
|
):
|
||||||
folder = Folders.get_folder_by_parent_id_and_user_id_and_name(
|
folder = Folders.get_folder_by_parent_id_and_user_id_and_name(
|
||||||
None, user.id, form_data.name, db=db
|
form_data.parent_id, user.id, form_data.name, db=db
|
||||||
)
|
)
|
||||||
|
|
||||||
if folder:
|
if folder:
|
||||||
@@ -129,7 +129,7 @@ def create_folder(
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
folder = Folders.insert_new_folder(user.id, form_data, db=db)
|
folder = Folders.insert_new_folder(user.id, form_data, form_data.parent_id, db=db)
|
||||||
return folder
|
return folder
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
@@ -317,7 +317,7 @@ async def delete_folder_by_id(
|
|||||||
folder = folders.pop()
|
folder = folders.pop()
|
||||||
if folder:
|
if folder:
|
||||||
try:
|
try:
|
||||||
folder_ids = Folders.delete_folder_by_id_and_user_id(id, user.id, db=db)
|
folder_ids = Folders.delete_folder_by_id_and_user_id(folder.id, user.id, db=db)
|
||||||
|
|
||||||
for folder_id in folder_ids:
|
for folder_id in folder_ids:
|
||||||
if delete_contents:
|
if delete_contents:
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ type FolderForm = {
|
|||||||
name?: string;
|
name?: string;
|
||||||
data?: Record<string, any>;
|
data?: Record<string, any>;
|
||||||
meta?: Record<string, any>;
|
meta?: Record<string, any>;
|
||||||
|
parent_id?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createNewFolder = async (token: string, folderForm: FolderForm) => {
|
export const createNewFolder = async (token: string, folderForm: FolderForm) => {
|
||||||
|
|||||||
@@ -142,19 +142,24 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const createFolder = async ({ name, data }) => {
|
const createFolder = async ({ name, data, parent_id }) => {
|
||||||
name = name?.trim();
|
name = name?.trim();
|
||||||
if (!name) {
|
if (!name) {
|
||||||
toast.error($i18n.t('Folder name cannot be empty.'));
|
toast.error($i18n.t('Folder name cannot be empty.'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootFolders = Object.values(folders).filter((folder) => folder.parent_id === null);
|
// Check for duplicate names in the same parent
|
||||||
if (rootFolders.find((folder) => folder.name.toLowerCase() === name.toLowerCase())) {
|
const siblings = Object.values(folders).filter(
|
||||||
|
(folder) => folder.parent_id === parent_id
|
||||||
|
);
|
||||||
|
if (siblings.find((folder) => folder.name.toLowerCase() === name.toLowerCase())) {
|
||||||
// If a folder with the same name already exists, append a number to the name
|
// If a folder with the same name already exists, append a number to the name
|
||||||
let i = 1;
|
let i = 1;
|
||||||
while (
|
while (
|
||||||
rootFolders.find((folder) => folder.name.toLowerCase() === `${name} ${i}`.toLowerCase())
|
siblings.find(
|
||||||
|
(folder) => folder.name.toLowerCase() === `${name} ${i}`.toLowerCase()
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -166,9 +171,10 @@
|
|||||||
const tempId = uuidv4();
|
const tempId = uuidv4();
|
||||||
folders = {
|
folders = {
|
||||||
...folders,
|
...folders,
|
||||||
tempId: {
|
[tempId]: {
|
||||||
id: tempId,
|
id: tempId,
|
||||||
name: name,
|
name: name,
|
||||||
|
parent_id: parent_id,
|
||||||
created_at: Date.now(),
|
created_at: Date.now(),
|
||||||
updated_at: Date.now()
|
updated_at: Date.now()
|
||||||
}
|
}
|
||||||
@@ -176,7 +182,8 @@
|
|||||||
|
|
||||||
const res = await createNewFolder(localStorage.token, {
|
const res = await createNewFolder(localStorage.token, {
|
||||||
name,
|
name,
|
||||||
data
|
data,
|
||||||
|
parent_id
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
toast.error(`${error}`);
|
toast.error(`${error}`);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -11,11 +11,13 @@
|
|||||||
import Pencil from '$lib/components/icons/Pencil.svelte';
|
import Pencil from '$lib/components/icons/Pencil.svelte';
|
||||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||||
import Download from '$lib/components/icons/Download.svelte';
|
import Download from '$lib/components/icons/Download.svelte';
|
||||||
|
import Folder from '$lib/components/icons/Folder.svelte';
|
||||||
|
|
||||||
export let align: 'start' | 'end' = 'start';
|
export let align: 'start' | 'end' = 'start';
|
||||||
export let onEdit = () => {};
|
export let onEdit = () => {};
|
||||||
export let onExport = () => {};
|
export let onExport = () => {};
|
||||||
export let onDelete = () => {};
|
export let onDelete = () => {};
|
||||||
|
export let onCreateSub = () => {};
|
||||||
|
|
||||||
let show = false;
|
let show = false;
|
||||||
</script>
|
</script>
|
||||||
@@ -47,6 +49,18 @@
|
|||||||
{align}
|
{align}
|
||||||
transition={flyAndScale}
|
transition={flyAndScale}
|
||||||
>
|
>
|
||||||
|
<DropdownMenu.Item
|
||||||
|
class="flex gap-2 items-center px-3 py-1.5 text-sm select-none cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl"
|
||||||
|
on:click={() => {
|
||||||
|
onCreateSub();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Folder />
|
||||||
|
<div class="flex items-center">{$i18n.t('Create Folder')}</div>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
|
||||||
|
<hr class="border-gray-50/30 dark:border-gray-800/30 my-1" />
|
||||||
|
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
class="flex gap-2 items-center px-3 py-1.5 text-sm select-none cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl"
|
class="flex gap-2 items-center px-3 py-1.5 text-sm select-none cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
export let onSubmit: Function = (e) => {};
|
export let onSubmit: Function = (e) => {};
|
||||||
|
|
||||||
export let folderId = null;
|
export let folderId = null;
|
||||||
|
export let parentId = null;
|
||||||
export let edit = false;
|
export let edit = false;
|
||||||
|
|
||||||
let folder = null;
|
let folder = null;
|
||||||
@@ -55,7 +56,8 @@
|
|||||||
await onSubmit({
|
await onSubmit({
|
||||||
name,
|
name,
|
||||||
meta,
|
meta,
|
||||||
data
|
data,
|
||||||
|
parent_id: edit ? undefined : parentId
|
||||||
});
|
});
|
||||||
show = false;
|
show = false;
|
||||||
loading = false;
|
loading = false;
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
updateFolderIsExpandedById,
|
updateFolderIsExpandedById,
|
||||||
updateFolderById,
|
updateFolderById,
|
||||||
updateFolderParentIdById,
|
updateFolderParentIdById,
|
||||||
getFolderById
|
getFolderById,
|
||||||
|
createNewFolder
|
||||||
} from '$lib/apis/folders';
|
} from '$lib/apis/folders';
|
||||||
import {
|
import {
|
||||||
getChatById,
|
getChatById,
|
||||||
@@ -64,6 +65,9 @@
|
|||||||
let showFolderModal = false;
|
let showFolderModal = false;
|
||||||
let edit = false;
|
let edit = false;
|
||||||
|
|
||||||
|
let showCreateSubFolderModal = false;
|
||||||
|
let createSubFolderParentId = null;
|
||||||
|
|
||||||
let draggedOver = false;
|
let draggedOver = false;
|
||||||
let dragged = false;
|
let dragged = false;
|
||||||
|
|
||||||
@@ -414,6 +418,30 @@
|
|||||||
|
|
||||||
saveAs(blob, `folder-${folders[folderId].name}-export-${Date.now()}.json`);
|
saveAs(blob, `folder-${folders[folderId].name}-export-${Date.now()}.json`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createSubFolderHandler = async ({ name, meta, data, parent_id }) => {
|
||||||
|
if (name === '') {
|
||||||
|
toast.error($i18n.t('Folder name cannot be empty.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = name.trim();
|
||||||
|
|
||||||
|
const res = await createNewFolder(localStorage.token, {
|
||||||
|
name,
|
||||||
|
data,
|
||||||
|
meta,
|
||||||
|
parent_id
|
||||||
|
}).catch((error) => {
|
||||||
|
toast.error(`${error}`);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
toast.success($i18n.t('Folder created successfully'));
|
||||||
|
dispatch('update');
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<DeleteConfirmDialog
|
<DeleteConfirmDialog
|
||||||
@@ -444,6 +472,12 @@
|
|||||||
|
|
||||||
<FolderModal bind:show={showFolderModal} edit={true} {folderId} onSubmit={updateHandler} />
|
<FolderModal bind:show={showFolderModal} edit={true} {folderId} onSubmit={updateHandler} />
|
||||||
|
|
||||||
|
<FolderModal
|
||||||
|
bind:show={showCreateSubFolderModal}
|
||||||
|
parentId={createSubFolderParentId}
|
||||||
|
onSubmit={createSubFolderHandler}
|
||||||
|
/>
|
||||||
|
|
||||||
{#if dragged && x && y}
|
{#if dragged && x && y}
|
||||||
<DragGhost {x} {y}>
|
<DragGhost {x} {y}>
|
||||||
<div class=" bg-black/80 backdrop-blur-2xl px-2 py-1 rounded-lg w-fit max-w-40">
|
<div class=" bg-black/80 backdrop-blur-2xl px-2 py-1 rounded-lg w-fit max-w-40">
|
||||||
@@ -593,6 +627,10 @@
|
|||||||
onExport={() => {
|
onExport={() => {
|
||||||
exportHandler();
|
exportHandler();
|
||||||
}}
|
}}
|
||||||
|
onCreateSub={() => {
|
||||||
|
createSubFolderParentId = folderId;
|
||||||
|
showCreateSubFolderModal = true;
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div class="p-1 dark:hover:bg-gray-850 rounded-lg touch-auto">
|
<div class="p-1 dark:hover:bg-gray-850 rounded-lg touch-auto">
|
||||||
<EllipsisHorizontal className="size-4" strokeWidth="2.5" />
|
<EllipsisHorizontal className="size-4" strokeWidth="2.5" />
|
||||||
|
|||||||
@@ -897,6 +897,7 @@
|
|||||||
"Focus Chat Input": "",
|
"Focus Chat Input": "",
|
||||||
"Folder": "",
|
"Folder": "",
|
||||||
"Folder Background Image": "",
|
"Folder Background Image": "",
|
||||||
|
"Folder created successfully": "",
|
||||||
"Folder deleted successfully": "",
|
"Folder deleted successfully": "",
|
||||||
"Folder Max File Count": "",
|
"Folder Max File Count": "",
|
||||||
"Folder name": "",
|
"Folder name": "",
|
||||||
@@ -2135,4 +2136,4 @@
|
|||||||
"YouTube": "",
|
"YouTube": "",
|
||||||
"Youtube Language": "",
|
"Youtube Language": "",
|
||||||
"Youtube Proxy URL": ""
|
"Youtube Proxy URL": ""
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user