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:
@@ -797,6 +797,16 @@ table.tbl a.rowlink:hover {
|
|||||||
justify-content: flex-end;
|
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 {
|
.empty-row td {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: var(--fg-faint);
|
color: var(--fg-faint);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ type Props = {
|
|||||||
dstFolders: string[]
|
dstFolders: string[]
|
||||||
initialMapping: Record<string, string>
|
initialMapping: Record<string, string>
|
||||||
initialExcluded: string[]
|
initialExcluded: string[]
|
||||||
|
accountLabel?: string
|
||||||
onConfirm: (mapping: Record<string, string>, excluded: string[]) => void
|
onConfirm: (mapping: Record<string, string>, excluded: string[]) => void
|
||||||
onCancel: () => void
|
onCancel: () => void
|
||||||
}
|
}
|
||||||
@@ -20,7 +21,7 @@ function defaultDst(src: string, dstFolders: string[], initial: Record<string, s
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function FolderMappingModal({
|
export function FolderMappingModal({
|
||||||
open, srcFolders, dstFolders, initialMapping, initialExcluded, onConfirm, onCancel,
|
open, srcFolders, dstFolders, initialMapping, initialExcluded, accountLabel, onConfirm, onCancel,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const [choice, setChoice] = useState<Record<string, string>>({})
|
const [choice, setChoice] = useState<Record<string, string>>({})
|
||||||
const [synced, setSynced] = useState<Record<string, boolean>>(() => {
|
const [synced, setSynced] = useState<Record<string, boolean>>(() => {
|
||||||
@@ -58,7 +59,12 @@ export function FolderMappingModal({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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">
|
<div className="map-body">
|
||||||
<p className="map-hint">
|
<p className="map-hint">
|
||||||
Route each source folder to an existing destination folder. Leaving a folder mapped to its own name
|
Route each source folder to an existing destination folder. Leaving a folder mapped to its own name
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export function TaskDetail({ id }: { id: number }) {
|
|||||||
const [mapState, setMapState] = useState<{ src: string[]; dst: string[]; creds: typeof emptyAccount } | null>(null)
|
const [mapState, setMapState] = useState<{ src: string[]; dst: string[]; creds: typeof emptyAccount } | null>(null)
|
||||||
const [editMap, setEditMap] = useState<{
|
const [editMap, setEditMap] = useState<{
|
||||||
accId: number
|
accId: number
|
||||||
|
label: string
|
||||||
src: string[]
|
src: string[]
|
||||||
dst: string[]
|
dst: string[]
|
||||||
mapping: Record<string, 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')
|
setBusy('probe')
|
||||||
setError(null)
|
setError(null)
|
||||||
try {
|
try {
|
||||||
@@ -228,6 +229,7 @@ export function TaskDetail({ id }: { id: number }) {
|
|||||||
}
|
}
|
||||||
setEditMap({
|
setEditMap({
|
||||||
accId: a.id,
|
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 ?? [],
|
src: res.src.folders ?? [],
|
||||||
dst: res.dst.folders ?? [],
|
dst: res.dst.folders ?? [],
|
||||||
mapping: a.folder_mapping ?? {},
|
mapping: a.folder_mapping ?? {},
|
||||||
@@ -629,6 +631,18 @@ export function TaskDetail({ id }: { id: number }) {
|
|||||||
</td>
|
</td>
|
||||||
<td className="num-cell">
|
<td className="num-cell">
|
||||||
<div className="row-actions">
|
<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' && (
|
{a.status !== 'running' && data?.task.status !== 'running' && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -671,6 +685,11 @@ export function TaskDetail({ id }: { id: number }) {
|
|||||||
dstFolders={mapState.dst}
|
dstFolders={mapState.dst}
|
||||||
initialMapping={task.folder_mapping ?? {}}
|
initialMapping={task.folder_mapping ?? {}}
|
||||||
initialExcluded={[]}
|
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)}
|
onCancel={() => setMapState(null)}
|
||||||
onConfirm={confirmMapping}
|
onConfirm={confirmMapping}
|
||||||
/>
|
/>
|
||||||
@@ -683,6 +702,7 @@ export function TaskDetail({ id }: { id: number }) {
|
|||||||
dstFolders={editMap.dst}
|
dstFolders={editMap.dst}
|
||||||
initialMapping={editMap.mapping}
|
initialMapping={editMap.mapping}
|
||||||
initialExcluded={editMap.excluded}
|
initialExcluded={editMap.excluded}
|
||||||
|
accountLabel={editMap.label}
|
||||||
onCancel={() => setEditMap(null)}
|
onCancel={() => setEditMap(null)}
|
||||||
onConfirm={saveEditMapping}
|
onConfirm={saveEditMapping}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user