From e93332409cdd15a00b604baace5646388b635e3a Mon Sep 17 00:00:00 2001 From: LeBalz Date: Sun, 22 Dec 2024 14:39:44 +0100 Subject: [PATCH 1/7] add delete button --- src/components/shared/Button/Delete/index.tsx | 47 +++++++++++++++++++ .../shared/Button/Delete/styles.module.scss | 3 ++ 2 files changed, 50 insertions(+) create mode 100644 src/components/shared/Button/Delete/index.tsx create mode 100644 src/components/shared/Button/Delete/styles.module.scss diff --git a/src/components/shared/Button/Delete/index.tsx b/src/components/shared/Button/Delete/index.tsx new file mode 100644 index 00000000..4a3f7267 --- /dev/null +++ b/src/components/shared/Button/Delete/index.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import clsx from 'clsx'; + +import styles from './styles.module.scss'; +import Button from '..'; +import { mdiClose, mdiTrashCan, mdiTrashCanOutline } from '@mdi/js'; + +interface Props { + className?: string; + onDelete: () => void; +} +export const Delete = (props: Props) => { + const [confirmDelete, setConfirmDelete] = React.useState(false); + return ( +
+ {confirmDelete && ( +
+ ); +}; + +export default Button; diff --git a/src/components/shared/Button/Delete/styles.module.scss b/src/components/shared/Button/Delete/styles.module.scss new file mode 100644 index 00000000..464aef62 --- /dev/null +++ b/src/components/shared/Button/Delete/styles.module.scss @@ -0,0 +1,3 @@ +.delete { + display: flex; +} From a8e5dcb5bda8b5853341fd0a02fb7866933cc804 Mon Sep 17 00:00:00 2001 From: LeBalz Date: Sun, 22 Dec 2024 14:39:54 +0100 Subject: [PATCH 2/7] add selectInput --- src/components/shared/SelectInput/index.tsx | 33 +++++++++++++++++++ .../shared/SelectInput/styles.module.scss | 22 +++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/components/shared/SelectInput/index.tsx create mode 100644 src/components/shared/SelectInput/styles.module.scss diff --git a/src/components/shared/SelectInput/index.tsx b/src/components/shared/SelectInput/index.tsx new file mode 100644 index 00000000..7cfcd137 --- /dev/null +++ b/src/components/shared/SelectInput/index.tsx @@ -0,0 +1,33 @@ +import React, { HTMLInputTypeAttribute } from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.scss'; +import { observer } from 'mobx-react-lite'; + +interface Props { + defaultValue?: string; + placeholder?: string; + onChange: (text: string) => void; + options: string[]; + value: string; +} + +const SelectInput = observer((props: Props) => { + const { options, onChange, value } = props; + return ( + + ); +}); + +export default SelectInput; diff --git a/src/components/shared/SelectInput/styles.module.scss b/src/components/shared/SelectInput/styles.module.scss new file mode 100644 index 00000000..f7096436 --- /dev/null +++ b/src/components/shared/SelectInput/styles.module.scss @@ -0,0 +1,22 @@ +.dropdown { + appearance: none; + border: 1px solid #ccc; + border-radius: 4px; + padding: var(--ifm-button-padding-vertical); + font-size: var(--ifm-font-size-base); + color: #333; + cursor: pointer; + width: 100%; + max-width: 300px; + transition: all 0.3s ease; + + &:focus { + border-color: var(--ifm-link-hover-color); + outline: none; + box-shadow: 0 0 3px var(--ifm-link-hover-color); + } + + option { + padding: var(--ifm-button-padding-vertical); + } +} From ed1e9c5e613a85423cba7faae26ef21fa72d9126 Mon Sep 17 00:00:00 2001 From: LeBalz Date: Sun, 22 Dec 2024 14:41:10 +0100 Subject: [PATCH 3/7] add admin endpoint and store --- src/api/admin.ts | 24 ++++ src/components/Admin/AdminPanel/index.tsx | 4 + src/components/Admin/AllowedActions/index.tsx | 133 ++++++++++++++++++ .../Admin/AllowedActions/styles.module.scss | 37 +++++ src/stores/AdminStore.ts | 57 ++++++++ src/stores/rootStore.ts | 6 + 6 files changed, 261 insertions(+) create mode 100644 src/api/admin.ts create mode 100644 src/components/Admin/AllowedActions/index.tsx create mode 100644 src/components/Admin/AllowedActions/styles.module.scss create mode 100644 src/stores/AdminStore.ts diff --git a/src/api/admin.ts b/src/api/admin.ts new file mode 100644 index 00000000..f65a7fd4 --- /dev/null +++ b/src/api/admin.ts @@ -0,0 +1,24 @@ +import api from './base'; +import { AxiosPromise } from 'axios'; +import { DocumentType } from './document'; + +export interface AllowedAction { + id: string; + documentType: DocumentType; + action: `update@${string}`; +} + +export function deleteAllowedAction(id: string, signal: AbortSignal): AxiosPromise { + return api.delete(`/admin/allowedActions/${id}`, { signal }); +} + +export function createAllowedAction( + data: Omit, + signal: AbortSignal +): AxiosPromise { + return api.post('/admin/allowedActions', data, { signal }); +} + +export function allowedActions(signal: AbortSignal): AxiosPromise { + return api.get(`/admin/allowedActions`, { signal }); +} diff --git a/src/components/Admin/AdminPanel/index.tsx b/src/components/Admin/AdminPanel/index.tsx index 156e3f50..c900a71b 100644 --- a/src/components/Admin/AdminPanel/index.tsx +++ b/src/components/Admin/AdminPanel/index.tsx @@ -7,6 +7,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import StudentGroupPanel from '@tdev-components/Admin/StudentGroupPanel'; import UserTable from '@tdev-components/Admin/UserTable'; +import AllowedActions from '../AllowedActions'; const AdminPanel = observer(() => { const userStore = useStore('userStore'); @@ -29,6 +30,9 @@ const AdminPanel = observer(() => { + + + ); diff --git a/src/components/Admin/AllowedActions/index.tsx b/src/components/Admin/AllowedActions/index.tsx new file mode 100644 index 00000000..473cc572 --- /dev/null +++ b/src/components/Admin/AllowedActions/index.tsx @@ -0,0 +1,133 @@ +import React from 'react'; +import clsx from 'clsx'; + +import styles from './styles.module.scss'; +import { observer } from 'mobx-react-lite'; +import Button from '@tdev-components/shared/Button'; +import { mdiPlusCircle, mdiSortAscending, mdiSortDescending } from '@mdi/js'; +import { useStore } from '@tdev-hooks/useStore'; +import _ from 'lodash'; +import { Delete } from '@tdev-components/shared/Button/Delete'; +import { action } from 'mobx'; +import Details from '@theme/Details'; +import TextInput from '@tdev-components/shared/TextInput'; +import SelectInput from '@tdev-components/shared/SelectInput'; +import { DocumentType } from '@tdev-api/document'; + +const SIZE_S = 0.6; + +type SortColumn = 'id' | 'documentType' | 'action'; +interface Props { + className?: string; +} + +const AllowedActions = observer((props: Props) => { + const [sortDirection, setSortDirection] = React.useState<'asc' | 'desc'>('asc'); + const [sortColumn, _setSortColumn] = React.useState('documentType'); + const adminStore = useStore('adminStore'); + const [newAction, setNewAction] = React.useState(''); + const [newDocType, setNewDocType] = React.useState(undefined); + + const setSortColumn = (column: SortColumn) => { + if (column === sortColumn) { + setSortDirection((prev) => (prev === 'asc' ? 'desc' : 'asc')); + } else { + setSortDirection('asc'); + _setSortColumn(column); + } + }; + + const icon = sortDirection === 'asc' ? mdiSortAscending : mdiSortDescending; + return ( +
+
Neue Aktion Hinzufügen}> +
+ setNewAction(text)} + value={newAction} + placeholder="Aktion" + /> + setNewDocType(docType as DocumentType)} + options={['', ...Object.values(DocumentType)]} + value={newDocType || ''} + /> +
+
+
+ + + + + + + + + + + {_.orderBy(adminStore.allowedActions, [sortColumn], [sortDirection]).map((a, idx) => { + return ( + + + + + + + ); + })} + +
+ + +
{a.id} + {a.documentType} + + {a.action} + + { + adminStore.destroyAllowedAction(a.id); + })} + /> +
+
+
+ ); +}); + +export default AllowedActions; diff --git a/src/components/Admin/AllowedActions/styles.module.scss b/src/components/Admin/AllowedActions/styles.module.scss new file mode 100644 index 00000000..709cb83d --- /dev/null +++ b/src/components/Admin/AllowedActions/styles.module.scss @@ -0,0 +1,37 @@ +.userTable { + width: 100%; + .tableWrapper { + overflow-x: auto; + table.table { + width: 100%; + border-collapse: collapse; + display: table; + --ifm-table-cell-padding: 0.2em 0.5em; + .user { + .clients { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + } + .groupBadge:not(:last-child) { + display: inline-block; + margin-right: 0.5em; + } + } + td, + .nowrap { + white-space: nowrap; + } + } + } +} + +.newAction { + display: flex; + flex-direction: column; + gap: 0.5em; + .button { + align-self: flex-end; + } +} diff --git a/src/stores/AdminStore.ts b/src/stores/AdminStore.ts new file mode 100644 index 00000000..aeaf1637 --- /dev/null +++ b/src/stores/AdminStore.ts @@ -0,0 +1,57 @@ +import { action, observable } from 'mobx'; +import { RootStore } from '@tdev-stores/rootStore'; +import _ from 'lodash'; +import iStore from '@tdev-stores/iStore'; +import { + AllowedAction, + allowedActions as apiAllowedActions, + deleteAllowedAction as apiDeleteAllowedAction, + createAllowedAction as apiCreateAllowedAction +} from '@tdev-api/admin'; +import { DocumentType } from '@tdev-api/document'; + +export class AdminStore extends iStore { + readonly root: RootStore; + allowedActions = observable([]); + constructor(root: RootStore) { + super(); + this.root = root; + } + + @action + load() { + return this.withAbortController(`load-all`, async (ct) => { + return apiAllowedActions(ct.signal).then( + action((res) => { + this.allowedActions.replace(res.data); + }) + ); + }); + } + + @action + destroyAllowedAction(id: string) { + const allowedAction = this.allowedActions.find((a) => a.id === id); + if (allowedAction) { + return this.withAbortController(`destroy-${id}`, async (ct) => { + return apiDeleteAllowedAction(id, ct.signal).then( + action((res) => { + this.allowedActions.remove(allowedAction); + }) + ); + }); + } + return Promise.resolve(); + } + + @action + createAllowedAction(allowedAction: `update@${string}`, documentType: DocumentType) { + return this.withAbortController(`create-new`, async (ct) => { + return apiCreateAllowedAction({ action: allowedAction, documentType }, ct.signal).then( + action((res) => { + this.allowedActions.push(res.data); + }) + ); + }); + } +} diff --git a/src/stores/rootStore.ts b/src/stores/rootStore.ts index 9b0c060c..d4b023c2 100644 --- a/src/stores/rootStore.ts +++ b/src/stores/rootStore.ts @@ -8,6 +8,7 @@ import { StudentGroupStore } from '@tdev-stores/StudentGroupStore'; import PermissionStore from '@tdev-stores/PermissionStore'; import DocumentStore from '@tdev-stores/DocumentStore'; import { PageStore } from '@tdev-stores/PageStore'; +import { AdminStore } from './AdminStore'; export class RootStore { documentRootStore: DocumentRootStore; @@ -18,6 +19,7 @@ export class RootStore { permissionStore: PermissionStore; documentStore: DocumentStore; pageStore: PageStore; + adminStore: AdminStore; // @observable accessor initialized = false; constructor() { @@ -29,6 +31,7 @@ export class RootStore { this.permissionStore = new PermissionStore(this); this.documentStore = new DocumentStore(this); this.pageStore = new PageStore(this); + this.adminStore = new AdminStore(this); if (this.sessionStore.isLoggedIn) { this.load(); @@ -45,6 +48,9 @@ export class RootStore { */ this.userStore.load(); this.studentGroupStore.load(); + if (user.isAdmin) { + this.adminStore.load(); + } } }); } From 86ae18ef1f15645621de16e86c957c68a6baf22f Mon Sep 17 00:00:00 2001 From: LeBalz Date: Sun, 22 Dec 2024 14:41:21 +0100 Subject: [PATCH 4/7] basic implementation --- .../files-and-folders/index.mdx | 8 ++ .../Actions/MoveItem/DirTree/index.tsx | 108 ++++++++++++++++++ .../MoveItem/DirTree/styles.module.scss | 35 ++++++ .../FileSystem/Actions/MoveItem/index.tsx | 59 ++++++++++ .../Actions/MoveItem/styles.module.scss | 0 .../documents/FileSystem/Actions/index.tsx | 29 +++++ .../documents/FileSystem/Directory/index.tsx | 2 + .../documents/FileSystem/File/index.tsx | 2 +- src/components/shared/numberIcons.ts | 38 ++++++ .../documents/FileSystem/iFileSystem.ts | 13 ++- 10 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 src/components/documents/FileSystem/Actions/MoveItem/DirTree/index.tsx create mode 100644 src/components/documents/FileSystem/Actions/MoveItem/DirTree/styles.module.scss create mode 100644 src/components/documents/FileSystem/Actions/MoveItem/index.tsx create mode 100644 src/components/documents/FileSystem/Actions/MoveItem/styles.module.scss create mode 100644 src/components/shared/numberIcons.ts diff --git a/docs/gallery/dynamic-documents/files-and-folders/index.mdx b/docs/gallery/dynamic-documents/files-and-folders/index.mdx index 633740bd..3dac5704 100644 --- a/docs/gallery/dynamic-documents/files-and-folders/index.mdx +++ b/docs/gallery/dynamic-documents/files-and-folders/index.mdx @@ -2,11 +2,19 @@ page_id: 3139de4d-8bd6-4bed-978e-a342519d7c52 --- import Directory from '@tdev-components/documents/FileSystem/Directory'; +import BrowserWindow from '@tdev-components/BrowserWindow'; # Dateien und Ordner +```md +import Directory from '@tdev-components/documents/FileSystem/Directory'; +``` + + + + ## Installation diff --git a/src/components/documents/FileSystem/Actions/MoveItem/DirTree/index.tsx b/src/components/documents/FileSystem/Actions/MoveItem/DirTree/index.tsx new file mode 100644 index 00000000..ecfb6d03 --- /dev/null +++ b/src/components/documents/FileSystem/Actions/MoveItem/DirTree/index.tsx @@ -0,0 +1,108 @@ +import { + mdiCircle, + mdiClose, + mdiFileMove, + mdiFileMoveOutline, + mdiFolderMove, + mdiFolderMoveOutline, + mdiFolderOpen, + mdiFolderOutline +} from '@mdi/js'; +import styles from './styles.module.scss'; +import Button from '@tdev-components/shared/Button'; +import Directory from '@tdev-models/documents/FileSystem/Directory'; +import { observer } from 'mobx-react-lite'; +import clsx from 'clsx'; +import React from 'react'; +import { DocumentType } from '@tdev-api/document'; +import Icon, { Stack } from '@mdi/react'; +import { getNumericCircleIcon } from '@tdev-components/shared/numberIcons'; + +interface DirProps { + dir: Directory; + fileType: DocumentType; + moveTo: (dir: Directory) => void; + children?: React.ReactNode; +} + +const DirTree = observer((props: DirProps) => { + const [confirmMove, setConfirmMove] = React.useState(false); + const [isOpen, setIsOpen] = React.useState(props.dir.parent ? false : true); + const { dir } = props; + return ( + <> +
{ + e.preventDefault(); + e.stopPropagation(); + setIsOpen(!isOpen); + }} + > +
+ + + + + +
+
{dir.name}
+
+
+ {confirmMove && ( +
+
+ {isOpen && ( +
+ {dir.directories.map((c) => { + return ; + })} +
+ )} + + ); +}); + +export default DirTree; diff --git a/src/components/documents/FileSystem/Actions/MoveItem/DirTree/styles.module.scss b/src/components/documents/FileSystem/Actions/MoveItem/DirTree/styles.module.scss new file mode 100644 index 00000000..af0348cc --- /dev/null +++ b/src/components/documents/FileSystem/Actions/MoveItem/DirTree/styles.module.scss @@ -0,0 +1,35 @@ +.moveTo { + display: flex; + align-items: center; + justify-content: flex-start; + gap: 0.25em; + cursor: pointer; + border-radius: var(--ifm-global-radius); + -webkit-touch-callout: none; + /* Safari */ + -webkit-user-select: none; + /* Old versions of Firefox */ + -moz-user-select: none; + user-select: none; + + .spacer { + flex-grow: 1; + flex-basis: 0; + } + .stacked { + position: relative; + line-height: 1vh; + margin-right: 1em; + .topRight { + position: absolute; + top: -4px; + right: -4px; + } + } + &:hover { + box-shadow: var(--ifm-global-shadow-md); + } +} +.content { + margin-left: 1em; +} diff --git a/src/components/documents/FileSystem/Actions/MoveItem/index.tsx b/src/components/documents/FileSystem/Actions/MoveItem/index.tsx new file mode 100644 index 00000000..c06ec9c9 --- /dev/null +++ b/src/components/documents/FileSystem/Actions/MoveItem/index.tsx @@ -0,0 +1,59 @@ +import { + mdiCircle, + mdiClose, + mdiDotsHorizontalCircleOutline, + mdiFileMove, + mdiFileMoveOutline, + mdiFolder, + mdiFolderMove, + mdiFolderMoveOutline, + mdiFolderOpen, + mdiFolderOpenOutline, + mdiFolderOutline, + mdiNumeric0Circle, + mdiOneUp, + mdiRenameOutline, + mdiTrashCan, + mdiTrashCanOutline +} from '@mdi/js'; +import styles from './styles.module.scss'; +import Button from '@tdev-components/shared/Button'; +import Directory from '@tdev-models/documents/FileSystem/Directory'; +import File from '@tdev-models/documents/FileSystem/File'; +import { observer } from 'mobx-react-lite'; +import Popup from 'reactjs-popup'; +import clsx from 'clsx'; +import React from 'react'; +import { DocumentType } from '@tdev-api/document'; +import Icon, { Stack } from '@mdi/react'; +import { getNumericCircleIcon } from '@tdev-components/shared/numberIcons'; +import DirTree from './DirTree'; + +interface Props { + item: File | Directory; +} + +const MoveItem = observer((props: Props) => { + const { item } = props; + const root = item.path.filter((p) => p.type === DocumentType.Dir)[0]; + if (!root) { + return null; + } + return ( +
+
+

"{item.name}" Verschieben

+
+
+ { + item.parentId; + }} + /> +
+
+ ); +}); +export default MoveItem; diff --git a/src/components/documents/FileSystem/Actions/MoveItem/styles.module.scss b/src/components/documents/FileSystem/Actions/MoveItem/styles.module.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/components/documents/FileSystem/Actions/index.tsx b/src/components/documents/FileSystem/Actions/index.tsx index 9b3f0128..720834c2 100644 --- a/src/components/documents/FileSystem/Actions/index.tsx +++ b/src/components/documents/FileSystem/Actions/index.tsx @@ -1,6 +1,8 @@ import { mdiClose, mdiDotsHorizontalCircleOutline, + mdiFileMove, + mdiFolderMove, mdiRenameOutline, mdiTrashCan, mdiTrashCanOutline @@ -13,6 +15,8 @@ import { observer } from 'mobx-react-lite'; import Popup from 'reactjs-popup'; import clsx from 'clsx'; import React from 'react'; +import { DocumentType } from '@tdev-api/document'; +import MoveItem from './MoveItem'; interface Props { item: File | Directory; @@ -51,6 +55,7 @@ const Actions = observer((props: Props) => { offsetX={20} offsetY={5} repositionOnResize + nested >
@@ -83,6 +88,30 @@ const Actions = observer((props: Props) => { }} />
+
+ +
diff --git a/src/components/documents/FileSystem/Directory/index.tsx b/src/components/documents/FileSystem/Directory/index.tsx index e224f0bd..d4c77955 100644 --- a/src/components/documents/FileSystem/Directory/index.tsx +++ b/src/components/documents/FileSystem/Directory/index.tsx @@ -15,6 +15,7 @@ import { MetaInit } from '@tdev-models/documents/FileSystem/iFileSystem'; import { useFirstMainDocument } from '@tdev-hooks/useFirstMainDocument'; import Loader from '@tdev-components/Loader'; import FsDetails from '../FsDetails'; +import MoveItem from '../Actions/MoveItem'; interface Props extends MetaInit { id: string; @@ -72,6 +73,7 @@ export const DirectoryComponent = observer((props: DirectoryProps) => {
{dir.isOpen && ( <> + {dir.files.length > 0 && } {dir.files.map((file) => { return ; })} diff --git a/src/components/documents/FileSystem/File/index.tsx b/src/components/documents/FileSystem/File/index.tsx index 8d30ed59..535278bc 100644 --- a/src/components/documents/FileSystem/File/index.tsx +++ b/src/components/documents/FileSystem/File/index.tsx @@ -44,7 +44,7 @@ const getColor = (type?: DocumentType) => { } }; -const getIcon = (type?: DocumentType) => { +export const getIcon = (type?: DocumentType) => { switch (type) { case DocumentType.QuillV2: return mdiFileDocumentOutline; diff --git a/src/components/shared/numberIcons.ts b/src/components/shared/numberIcons.ts new file mode 100644 index 00000000..9dce8535 --- /dev/null +++ b/src/components/shared/numberIcons.ts @@ -0,0 +1,38 @@ +import { + mdiCircleOutline, + mdiMinusCircleOutline, + mdiNumeric0Circle, + mdiNumeric1Circle, + mdiNumeric2Circle, + mdiNumeric3Circle, + mdiNumeric4Circle, + mdiNumeric5Circle, + mdiNumeric6Circle, + mdiNumeric7Circle, + mdiNumeric8Circle, + mdiNumeric9Circle, + mdiNumeric9PlusCircle +} from '@mdi/js'; + +export const MdiNumericCircle = { + [0]: mdiNumeric0Circle, + [1]: mdiNumeric1Circle, + [2]: mdiNumeric2Circle, + [3]: mdiNumeric3Circle, + [4]: mdiNumeric4Circle, + [5]: mdiNumeric5Circle, + [6]: mdiNumeric6Circle, + [7]: mdiNumeric7Circle, + [8]: mdiNumeric8Circle, + [9]: mdiNumeric9Circle +}; + +export const getNumericCircleIcon = (num: number) => { + if (num < 0) { + return mdiMinusCircleOutline; + } + if (num > 9) { + return mdiNumeric9PlusCircle; + } + return MdiNumericCircle[num as keyof typeof MdiNumericCircle] || mdiCircleOutline; +}; diff --git a/src/models/documents/FileSystem/iFileSystem.ts b/src/models/documents/FileSystem/iFileSystem.ts index 18e6e402..25b19398 100644 --- a/src/models/documents/FileSystem/iFileSystem.ts +++ b/src/models/documents/FileSystem/iFileSystem.ts @@ -1,4 +1,4 @@ -import { action, observable } from 'mobx'; +import { action, computed, observable } from 'mobx'; import iDocument, { Source } from '@tdev-models/iDocument'; import { DocumentType, @@ -83,6 +83,17 @@ class iFileSystem extends iDocument { throw new Error('Not implemented'); } + @computed + get path() { + const path: DocumentTypes[] = []; + let parent = this.parent; + while (parent) { + path.unshift(parent); + parent = parent.parent; + } + return path; + } + @action setIsEditing(isEditing: boolean) { this.isEditing = isEditing; From c2e48b21e9bbc5912666c9d6d846a64e8b16cab5 Mon Sep 17 00:00:00 2001 From: LeBalz Date: Sun, 22 Dec 2024 18:10:25 +0100 Subject: [PATCH 5/7] introduce link api --- src/api/document.ts | 8 ++++ .../Actions/MoveItem/DirTree/index.tsx | 44 ++++++++++++------- .../FileSystem/Actions/MoveItem/index.tsx | 10 +++-- .../documents/FileSystem/Actions/index.tsx | 9 ++-- .../documents/FileSystem/Directory/index.tsx | 1 - src/stores/DocumentStore.ts | 17 ++++++- src/stores/SocketDataStore.ts | 3 ++ 7 files changed, 67 insertions(+), 25 deletions(-) diff --git a/src/api/document.ts b/src/api/document.ts index 505219ee..ef45c208 100644 --- a/src/api/document.ts +++ b/src/api/document.ts @@ -255,3 +255,11 @@ export function allDocuments(documentRootIds: string[], signal: AbortSignal): Ax signal }); } + +export function linkTo( + id: string, + linkToId: string, + signal: AbortSignal +): AxiosPromise> { + return api.put(`/documents/${id}/linkTo/${linkToId}`, { signal }); +} diff --git a/src/components/documents/FileSystem/Actions/MoveItem/DirTree/index.tsx b/src/components/documents/FileSystem/Actions/MoveItem/DirTree/index.tsx index ecfb6d03..b84bae8a 100644 --- a/src/components/documents/FileSystem/Actions/MoveItem/DirTree/index.tsx +++ b/src/components/documents/FileSystem/Actions/MoveItem/DirTree/index.tsx @@ -14,11 +14,13 @@ import Directory from '@tdev-models/documents/FileSystem/Directory'; import { observer } from 'mobx-react-lite'; import clsx from 'clsx'; import React from 'react'; -import { DocumentType } from '@tdev-api/document'; +import { Document, DocumentType } from '@tdev-api/document'; import Icon, { Stack } from '@mdi/react'; import { getNumericCircleIcon } from '@tdev-components/shared/numberIcons'; +import iFileSystem from '@tdev-models/documents/FileSystem/iFileSystem'; interface DirProps { + item: iFileSystem; dir: Directory; fileType: DocumentType; moveTo: (dir: Directory) => void; @@ -26,9 +28,10 @@ interface DirProps { } const DirTree = observer((props: DirProps) => { + const { dir, item } = props; const [confirmMove, setConfirmMove] = React.useState(false); - const [isOpen, setIsOpen] = React.useState(props.dir.parent ? false : true); - const { dir } = props; + const [isOpen, setIsOpen] = React.useState(item.path.some((p) => p.id === dir.id)); + const disabled = dir.id === item.id || dir.children.some((c) => c.id === item.id); return ( <>
{ - - - - + {dir.id !== item.id && ( + + + + + )}
-
{dir.name}
+
{dir.name}
{confirmMove && ( @@ -70,7 +75,7 @@ const DirTree = observer((props: DirProps) => { )}
- {isOpen && ( + {isOpen && dir.id !== item.id && (
{dir.directories.map((c) => { - return ; + return ( + + ); })}
)} diff --git a/src/components/documents/FileSystem/Actions/MoveItem/index.tsx b/src/components/documents/FileSystem/Actions/MoveItem/index.tsx index c06ec9c9..f6a44c4e 100644 --- a/src/components/documents/FileSystem/Actions/MoveItem/index.tsx +++ b/src/components/documents/FileSystem/Actions/MoveItem/index.tsx @@ -28,6 +28,8 @@ import { DocumentType } from '@tdev-api/document'; import Icon, { Stack } from '@mdi/react'; import { getNumericCircleIcon } from '@tdev-components/shared/numberIcons'; import DirTree from './DirTree'; +import { useStore } from '@tdev-hooks/useStore'; +import { action } from 'mobx'; interface Props { item: File | Directory; @@ -35,6 +37,7 @@ interface Props { const MoveItem = observer((props: Props) => { const { item } = props; + const documentStore = useStore('documentStore'); const root = item.path.filter((p) => p.type === DocumentType.Dir)[0]; if (!root) { return null; @@ -48,9 +51,10 @@ const MoveItem = observer((props: Props) => { { - item.parentId; - }} + moveTo={action((to) => { + documentStore.relinkParent(item, to); + })} + item={item} />
diff --git a/src/components/documents/FileSystem/Actions/index.tsx b/src/components/documents/FileSystem/Actions/index.tsx index 720834c2..17914437 100644 --- a/src/components/documents/FileSystem/Actions/index.tsx +++ b/src/components/documents/FileSystem/Actions/index.tsx @@ -94,20 +94,19 @@ const Actions = observer((props: Props) => {