Add folder mapping status indicator to task list

Add account label to folder mapping modal

Update folder mapping logic to include destination login
This commit is contained in:
2026-07-06 19:15:30 +07:00
parent e84366eb0c
commit b076ac92d6
3 changed files with 39 additions and 3 deletions
+10
View File
@@ -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);
+8 -2
View File
@@ -7,6 +7,7 @@ type Props = {
dstFolders: string[]
initialMapping: Record<string, string>
initialExcluded: string[]
accountLabel?: string
onConfirm: (mapping: Record<string, string>, excluded: string[]) => void
onCancel: () => void
}
@@ -20,7 +21,7 @@ function defaultDst(src: string, dstFolders: string[], initial: Record<string, s
}
export function FolderMappingModal({
open, srcFolders, dstFolders, initialMapping, initialExcluded, onConfirm, onCancel,
open, srcFolders, dstFolders, initialMapping, initialExcluded, accountLabel, onConfirm, onCancel,
}: Props) {
const [choice, setChoice] = useState<Record<string, string>>({})
const [synced, setSynced] = useState<Record<string, boolean>>(() => {
@@ -58,7 +59,12 @@ export function FolderMappingModal({
}
return (
<Modal open={open} title="Map folders (source → destination)" onClose={onCancel} size="lg">
<Modal
open={open}
title={accountLabel ? `Map folders — ${accountLabel}` : 'Map folders (source → destination)'}
onClose={onCancel}
size="lg"
>
<div className="map-body">
<p className="map-hint">
Route each source folder to an existing destination folder. Leaving a folder mapped to its own name
+21 -1
View File
@@ -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<string, string>
@@ -214,7 +215,7 @@ export function TaskDetail({ id }: { id: number }) {
}
}
async function onEditFolders(a: { id: number; src_login: string; folder_mapping?: Record<string, string>; excluded_folders?: string[] }) {
async function onEditFolders(a: { id: number; src_login: string; dst_login: string; folder_mapping?: Record<string, string>; 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 }) {
</td>
<td className="num-cell">
<div className="row-actions">
{(() => {
const mapped =
Object.keys(a.folder_mapping ?? {}).length > 0 ||
(a.excluded_folders?.length ?? 0) > 0
return mapped ? (
<span
className="map-dot"
title="Folders already mapped for this account"
aria-label="Folders mapped"
/>
) : null
})()}
{a.status !== 'running' && data?.task.status !== 'running' && (
<button
type="button"
@@ -671,6 +685,11 @@ export function TaskDetail({ id }: { id: number }) {
dstFolders={mapState.dst}
initialMapping={task.folder_mapping ?? {}}
initialExcluded={[]}
accountLabel={
mapState.creds.dst_login && mapState.creds.dst_login !== mapState.creds.src_login
? `${mapState.creds.src_login}${mapState.creds.dst_login}`
: mapState.creds.src_login
}
onCancel={() => setMapState(null)}
onConfirm={confirmMapping}
/>
@@ -683,6 +702,7 @@ export function TaskDetail({ id }: { id: number }) {
dstFolders={editMap.dst}
initialMapping={editMap.mapping}
initialExcluded={editMap.excluded}
accountLabel={editMap.label}
onCancel={() => setEditMap(null)}
onConfirm={saveEditMapping}
/>