From b076ac92d6d09984c6a3a3c35da33f8d0b95b3ff Mon Sep 17 00:00:00 2001 From: Vassiliy Yegorov Date: Mon, 6 Jul 2026 19:15:30 +0700 Subject: [PATCH] Add folder mapping status indicator to task list Add account label to folder mapping modal Update folder mapping logic to include destination login --- web/src/app.css | 10 ++++++++++ web/src/components/FolderMappingModal.tsx | 10 ++++++++-- web/src/pages/TaskDetail.tsx | 22 +++++++++++++++++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/web/src/app.css b/web/src/app.css index 7d8d622..caf9ef0 100644 --- a/web/src/app.css +++ b/web/src/app.css @@ -797,6 +797,16 @@ table.tbl a.rowlink:hover { justify-content: flex-end; } +/* Marks accounts whose folders have already been mapped/excluded. */ +.map-dot { + width: 8px; + height: 8px; + border-radius: 50%; + flex: none; + background: var(--ok); + box-shadow: 0 0 5px var(--ok); +} + .empty-row td { text-align: center; color: var(--fg-faint); diff --git a/web/src/components/FolderMappingModal.tsx b/web/src/components/FolderMappingModal.tsx index f5addaa..ac2aba6 100644 --- a/web/src/components/FolderMappingModal.tsx +++ b/web/src/components/FolderMappingModal.tsx @@ -7,6 +7,7 @@ type Props = { dstFolders: string[] initialMapping: Record initialExcluded: string[] + accountLabel?: string onConfirm: (mapping: Record, excluded: string[]) => void onCancel: () => void } @@ -20,7 +21,7 @@ function defaultDst(src: string, dstFolders: string[], initial: Record>({}) const [synced, setSynced] = useState>(() => { @@ -58,7 +59,12 @@ export function FolderMappingModal({ } return ( - +

Route each source folder to an existing destination folder. Leaving a folder mapped to its own name diff --git a/web/src/pages/TaskDetail.tsx b/web/src/pages/TaskDetail.tsx index ff7bf3f..a5291d6 100644 --- a/web/src/pages/TaskDetail.tsx +++ b/web/src/pages/TaskDetail.tsx @@ -79,6 +79,7 @@ export function TaskDetail({ id }: { id: number }) { const [mapState, setMapState] = useState<{ src: string[]; dst: string[]; creds: typeof emptyAccount } | null>(null) const [editMap, setEditMap] = useState<{ accId: number + label: string src: string[] dst: string[] mapping: Record @@ -214,7 +215,7 @@ export function TaskDetail({ id }: { id: number }) { } } - async function onEditFolders(a: { id: number; src_login: string; folder_mapping?: Record; excluded_folders?: string[] }) { + async function onEditFolders(a: { id: number; src_login: string; dst_login: string; folder_mapping?: Record; excluded_folders?: string[] }) { setBusy('probe') setError(null) try { @@ -228,6 +229,7 @@ export function TaskDetail({ id }: { id: number }) { } setEditMap({ accId: a.id, + label: a.dst_login && a.dst_login !== a.src_login ? `${a.src_login} → ${a.dst_login}` : a.src_login, src: res.src.folders ?? [], dst: res.dst.folders ?? [], mapping: a.folder_mapping ?? {}, @@ -629,6 +631,18 @@ export function TaskDetail({ id }: { id: number }) {

+ {(() => { + const mapped = + Object.keys(a.folder_mapping ?? {}).length > 0 || + (a.excluded_folders?.length ?? 0) > 0 + return mapped ? ( + + ) : null + })()} {a.status !== 'running' && data?.task.status !== 'running' && (