Skip to content
Open
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
0141a13
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 21, 2026
967671a
fix(data): scope document loading feedback per action
seonghobae Aug 24, 2026
c01b5e4
fix(data): show WebDAV source loading feedback
seonghobae Aug 24, 2026
230446a
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 24, 2026
56e15b2
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 24, 2026
142def4
fix(data): scope document action loading feedback
seonghobae Aug 24, 2026
5dc4b5a
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 24, 2026
8f85f9b
fix(data): key document action loading state per action (#1449)
seonghobae Aug 24, 2026
a5699ac
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 24, 2026
6191d8c
fix(data): scope document action loading state
seonghobae Aug 24, 2026
5663547
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 24, 2026
e264e0b
fix(data): scope document action loading state
seonghobae Aug 24, 2026
1d08dd7
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 24, 2026
2d28c95
fix(data): scope document action loading state
seonghobae Aug 24, 2026
9748011
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 24, 2026
207544e
fix(data): scope document action loading state
seonghobae Aug 24, 2026
765809d
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 24, 2026
92f0ffb
fix(data): scope document action loading state
seonghobae Aug 24, 2026
a868cbf
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 24, 2026
af734ff
fix(data): remove stale retrigger note and scope action state
seonghobae Aug 24, 2026
9a036fc
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 24, 2026
a5d0092
fix(data): show loading state for active document action
seonghobae Aug 24, 2026
68d679b
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 24, 2026
6e90f43
fix(data): lock document picker during action
seonghobae Aug 24, 2026
766001d
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 24, 2026
90248b2
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 24, 2026
8aab5d3
Merge branch 'develop' into palette-ux-add-aria-busy-and-loader-to-do…
seonghobae Aug 25, 2026
44190cc
fix(data): make document actions explicit
seonghobae Aug 31, 2026
9f99464
Merge remote-tracking branch 'origin/develop' into codex/pr1449-fix
seonghobae Aug 31, 2026
dee604d
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 31, 2026
9fdb5b3
fix(data): contain WebDAV confirmation focus
seonghobae Aug 31, 2026
be29bc9
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 31, 2026
be339b9
Revert "🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선"
seonghobae Aug 31, 2026
f8fa900
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Aug 31, 2026
31076c0
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 1, 2026
8071931
test(data): follow confirmed WebDAV write flow
seonghobae Sep 5, 2026
254780c
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 5, 2026
63318d9
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 5, 2026
2bccde0
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 5, 2026
416eff3
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 5, 2026
27c8f64
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 5, 2026
22ab290
chore(data): remove CI workaround and parallel gap receipt
seonghobae Sep 5, 2026
f3520f4
fix(data): restore WebDAV confirmation keyboard contract
seonghobae Sep 5, 2026
c5f10e4
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 5, 2026
190fb90
test(data): restore WebDAV modal focus contract
seonghobae Sep 5, 2026
364895b
fix(data): preserve WebDAV modal keyboard and focus semantics
seonghobae Sep 5, 2026
2e68813
chore(data): remove parallel CI and gap receipts
seonghobae Sep 5, 2026
e29f7e5
test(data): align WebDAV modal fixture with typed contracts
seonghobae Sep 5, 2026
dfd8ec7
test(data): require true modal WebDAV confirmation boundary
seonghobae Sep 5, 2026
43cf974
fix(data): make WebDAV confirmation truly modal
seonghobae Sep 5, 2026
72f4532
docs(data): trace WebDAV modal accessibility decision
seonghobae Sep 5, 2026
78c690d
test(data): lock repository selection during document actions
seonghobae Sep 5, 2026
517c8e7
fix(data): freeze repository selection during document actions
seonghobae Sep 5, 2026
e2f8187
docs(data): trace pending-action selection invariant
seonghobae Sep 5, 2026
e875707
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 5, 2026
8332dd0
repair(data): adopt concurrent UI delta without regressions
seonghobae Sep 5, 2026
53cbc6c
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 6, 2026
cef0d99
repair(data): preserve reviewed WebDAV contract after concurrent regr…
seonghobae Sep 6, 2026
aadf787
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 6, 2026
6833bd1
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 6, 2026
22fd626
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 6, 2026
e25a399
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 7, 2026
a0862a8
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 7, 2026
ab9cee0
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 7, 2026
fe59ef9
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 7, 2026
36bbd9b
fix(data-ui): restore reviewed WebDAV interaction boundary
seonghobae Sep 7, 2026
3760f65
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 7, 2026
0cd6f17
fix(data-ui): restore reviewed WebDAV contract after palette regression
seonghobae Sep 7, 2026
f53f50c
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 7, 2026
ce5ea43
fix(data-ui): keep WebDAV write confirmation focus trap
seonghobae Sep 7, 2026
3eb1465
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 7, 2026
34e618e
fix(data-ui): keep WebDAV confirmation keyboard boundary
seonghobae Sep 7, 2026
0bafaa7
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 7, 2026
3c246a8
fix(data-ui): restore WebDAV confirmation after palette overlay
seonghobae Sep 7, 2026
c64e753
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 7, 2026
2120c8b
fix(data-ui): restore WebDAV confirmation after palette overlay
seonghobae Sep 7, 2026
f38796a
🎨 Palette: 데이터 저장소 버튼들의 로딩 상태 및 접근성 개선
seonghobae Sep 7, 2026
31ecc63
fix(data-ui): restore WebDAV confirmation after palette overlay
seonghobae Sep 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/product-technical-gap-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
**Baseline version:** 1.2
**Observed on:** 2026-08-26 (Asia/Seoul)
**Observed protected branch (current scan; row Base-SHA values remain historical):** `develop@e5e99b4e3bb081b92c602358878856536030e2ca`
**Observed product version:** `0.14.4`
**Observed product version:** `0.14.4`
**Canonical completion issue:** [#1428](https://github.com/ContextualWisdomLab/naruon/issues/1428)

**Inventory observation:** the 106-PR open surface below is a fresh live
Expand Down
174 changes: 172 additions & 2 deletions frontend/src/app/data/page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1903,7 +1903,7 @@ describe("DataPage", () => {
expect(container.textContent).toContain("decision-note.md");
expect(container.textContent).toContain("의도만 기록");

for (const buttonLabel of ["재파싱 실행", "임베딩 재생성 의도", "HWP 변환 의도"]) {
for (const buttonLabel of ["파싱 완료로 표시", "임베딩 재생성 요청 등록", "HWP 변환 요청 등록"]) {
const button = Array.from(container.querySelectorAll("button")).find((candidate) =>
candidate.textContent?.includes(buttonLabel),
);
Expand All @@ -1919,12 +1919,28 @@ describe("DataPage", () => {
expect(container.textContent).toContain("HWP conversion intent recorded");

const materializeButton = Array.from(container.querySelectorAll("button")).find((candidate) =>
candidate.textContent?.includes("WebDAV 문서 실행 요청"),
candidate.textContent?.includes("고객 WebDAV에 문서 쓰기"),
);
expect(materializeButton).toBeDefined();
await act(async () => {
materializeButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
expect(fetchMock.mock.calls.some(([input]) => String(input).includes("webdav-materialization-intent"))).toBe(false);
expect(container.textContent).toContain("운영 문서 원본에 현재 문서를 기록합니다");

const cancelWriteButton = Array.from(container.querySelectorAll("button")).find((candidate) => candidate.textContent === "취소");
await act(async () => {
cancelWriteButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
expect(fetchMock.mock.calls.some(([input]) => String(input).includes("webdav-materialization-intent"))).toBe(false);

await act(async () => {
materializeButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
const confirmWriteButton = Array.from(container.querySelectorAll("button")).find((candidate) => candidate.textContent === "WebDAV 쓰기 확인");
await act(async () => {
confirmWriteButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});

const materializeCall = fetchMock.mock.calls.find(([input]) => (
String(input) === "/api/data/documents/doc_repository_ready/webdav-materialization-intent"
Expand Down Expand Up @@ -1965,6 +1981,160 @@ describe("DataPage", () => {
expect(container.textContent).not.toContain("runner_req_data_doc_1");
});

it("shows loading feedback only on the document action that is pending", async () => {
const fetchMock = mockWebdavFetch();
vi.stubGlobal("fetch", fetchMock);
container = document.createElement("div");
document.body.appendChild(container);
root = createRoot(container);

await act(async () => {
root?.render(<DataPage />);
});

const originalFetch = fetchMock.getMockImplementation();
const reparsePath = "/api/data/documents/doc_repository_ready/reparse";
let releaseReparse: (() => void) | null = null;
const pendingReparse = new Promise<ReturnType<typeof jsonResponse>>((resolve) => {
releaseReparse = () => resolve(jsonResponse({
document_id: "doc_repository_ready",
workspace_id: "workspace-org-acme",
document_name: "roadmap.md",
document_type: "text/markdown",
document_status: "parsed",
content_chars: 128,
provider_write_executed: false,
provenance: "server-authoritative",
audit_event: "data.document.reparsed",
message: "Document parse metadata refreshed in the signed workspace scope.",
}));
});
fetchMock.mockImplementation((input, init) => {
if (String(input) === reparsePath) return pendingReparse;
return originalFetch?.(input, init) ?? Promise.reject(new Error("missing fetch implementation"));
});

const findButton = (label: string) => Array.from(container?.querySelectorAll("button") ?? []).find((candidate) => (
candidate.textContent?.includes(label)
));
const reparseButton = findButton("파싱 완료로 표시");
const embeddingButton = findButton("임베딩 재생성 요청 등록");
const hwpButton = findButton("HWP 변환 요청 등록");
const materializeButton = findButton("고객 WebDAV에 문서 쓰기");
expect(reparseButton).toBeDefined();
expect(embeddingButton).toBeDefined();
expect(hwpButton).toBeDefined();
expect(materializeButton).toBeDefined();

await act(async () => {
reparseButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});

expect(reparseButton?.getAttribute("aria-busy")).toBe("true");
expect(reparseButton?.textContent).toContain("상태 변경 중");
const documentFileInput = container?.querySelector('input[accept=".txt,.md,.markdown,text/plain,text/markdown"]');
expect(documentFileInput).toHaveProperty("disabled", true);
for (const button of [embeddingButton, hwpButton, materializeButton]) {
expect(button?.getAttribute("aria-busy")).toBe("false");
expect(button?.disabled).toBe(true);
expect(button?.textContent).not.toContain("요청 등록 중");
expect(button?.textContent).not.toContain("WebDAV에 쓰는 중");
}

await act(async () => {
releaseReparse?.();
await pendingReparse;
await Promise.resolve();
await Promise.resolve();
});
});

it.each([
[409, "문서 처리가 아직 끝나지 않았습니다. 처리가 완료된 후 재시도하세요."],
[422, "쓸 수 있는 문서 내용 또는 WebDAV 저장소를 확인한 후 재시도하세요."],
[401, "로그인이 만료되었습니다. 다시 로그인한 후 재시도하세요."],
[500, "문서 작업을 완료하지 못했습니다. 잠시 후 재시도하세요."],
])("shows a bounded actionable document error for HTTP %s and allows retry", async (status, message) => {
const fetchMock = mockWebdavFetch();
const originalFetch = fetchMock.getMockImplementation();
let attempts = 0;
fetchMock.mockImplementation((input, init) => {
if (String(input).endsWith("/doc_repository_ready/reparse")) {
attempts += 1;
return Promise.resolve(jsonResponse({}, false, status));
}
return originalFetch?.(input, init) ?? Promise.reject(new Error("missing fetch implementation"));
});
vi.stubGlobal("fetch", fetchMock);
container = document.createElement("div");
document.body.appendChild(container);
root = createRoot(container);

await act(async () => {
root?.render(<DataPage />);
await Promise.resolve();
await Promise.resolve();
});
const findReparseButton = () => Array.from(container?.querySelectorAll("button") ?? []).find((candidate) => candidate.textContent?.includes("파싱 완료로 표시"));
await act(async () => {
findReparseButton()?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
await Promise.resolve();
await Promise.resolve();
});
expect(container.textContent).toContain(message);
expect(findReparseButton()?.disabled).toBe(false);

await act(async () => {
findReparseButton()?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
await Promise.resolve();
await Promise.resolve();
});
expect(attempts).toBe(2);
});

it("reports a connector failure after explicit WebDAV write confirmation", async () => {
const fetchMock = mockWebdavFetch();
const originalFetch = fetchMock.getMockImplementation();
fetchMock.mockImplementation((input, init) => {
if (String(input).endsWith("/doc_repository_ready/webdav-materialization-intent")) {
return Promise.resolve(jsonResponse({
document_id: "doc_repository_ready",
workspace_id: "workspace-org-acme",
document_name: "roadmap.md",
document_type: "text/markdown",
document_status: "parsed",
content_chars: 128,
provider_write_executed: false,
provenance: "server-authoritative",
audit_event: "data.document.webdav_materialization.dispatch_failed",
error_code: "connector_unavailable",
message: "dispatch failed",
}));
}
return originalFetch?.(input, init) ?? Promise.reject(new Error("missing fetch implementation"));
});
vi.stubGlobal("fetch", fetchMock);
container = document.createElement("div");
document.body.appendChild(container);
root = createRoot(container);

await act(async () => {
root?.render(<DataPage />);
await Promise.resolve();
await Promise.resolve();
});
const writeButton = Array.from(container.querySelectorAll("button")).find((candidate) => candidate.textContent?.includes("고객 WebDAV에 문서 쓰기"));
await act(async () => writeButton?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
const confirmButton = Array.from(container.querySelectorAll("button")).find((candidate) => candidate.textContent === "WebDAV 쓰기 확인");
await act(async () => {
confirmButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
await Promise.resolve();
await Promise.resolve();
});
expect(container.textContent).toContain("WebDAV 쓰기를 완료하지 못했습니다. 연결 상태를 확인한 후 재시도하거나 관리자에게 문의하세요.");
expect(container.textContent).not.toContain("connector_unavailable");
});

it("loads signed data quality surface without public identity headers", async () => {
const fetchMock = mockWebdavFetch();
vi.stubGlobal("fetch", fetchMock);
Expand Down
32 changes: 28 additions & 4 deletions frontend/src/components/DataLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
DataQualitySurfaceResponse,
EmailFileImportResponse,
DataDocumentActionResponse,
DocumentActionKind,
duplicateImportCandidates
} from './data-layout/types';
import {
Expand Down Expand Up @@ -65,6 +66,7 @@ export function DataLayout() {
const [emailImportResult, setEmailImportResult] = useState<EmailFileImportResponse | null>(null);
const [emailImportFiles, setEmailImportFiles] = useState<File[]>([]);
const [documentActionStatus, setDocumentActionStatus] = useState<DocumentActionStatus>('idle');
const [documentActionPendingAction, setDocumentActionPendingAction] = useState<DocumentActionKind | null>(null);
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.
const [documentActionResult, setDocumentActionResult] = useState<DataDocumentActionResponse | null>(null);
const [documentUploadFiles, setDocumentUploadFiles] = useState<File[]>([]);
const [dataSurfaceStatus, setDataSurfaceStatus] = useState<DataSurfaceStatus>('loading');
Expand Down Expand Up @@ -229,21 +231,25 @@ export function DataLayout() {
const requestDocumentUpload = useCallback(async () => {
const [file] = documentUploadFiles;
if (!file) {
setDocumentActionPendingAction(null);
setDocumentActionStatus('error');
return;
}

// 50MB file size limit
if (file.size > 50 * 1024 * 1024) {
setDocumentActionPendingAction(null);
setDocumentActionStatus('error');
return;
}

setDocumentActionPendingAction('upload');
setDocumentActionStatus('loading');
setDocumentActionResult(null);
try {
const documentType = getDocumentTypeForFile(file);
if (!isTextDocumentUploadType(documentType)) {
setDocumentActionPendingAction(null);
setDocumentActionStatus('error');
return;
}
Expand All @@ -257,33 +263,38 @@ export function DataLayout() {
},
);
setDocumentActionResult(result);
setDocumentActionPendingAction(null);
setDocumentActionStatus('success');
setDataSurfaceStatus('loading');
await loadDataQualitySurface();
} catch (error: unknown) {
const status = getApiErrorStatus(error);
setDocumentActionPendingAction(null);
setDocumentActionStatus(status === 401 || status === 403 ? 'auth' : 'error');
}
}, [documentUploadFiles, loadDataQualitySurface]);

const requestDocumentAction = useCallback(async (
action: 'reparse' | 'embedding-regeneration-intent' | 'hwp-conversion-intent' | 'webdav-materialization-intent',
action: Exclude<DocumentActionKind, 'upload'>,
) => {
const asset = dataQualitySurface?.repository_assets.find((candidate) => (
candidate.asset_key === selectedRepositoryAssetKey
)) ?? dataQualitySurface?.repository_assets[0] ?? null;
if (!asset || asset.asset_type !== 'workspace_document') {
setDocumentActionPendingAction(null);
setDocumentActionStatus('error');
return;
}
const targetSourceId = webdavAccounts.find((account) => (
account.source_id === selectedWebdavSourceId && account.writeback_enabled
))?.source_id ?? webdavAccounts.find((account) => account.writeback_enabled)?.source_id;
if (action === 'webdav-materialization-intent' && (webdavAccountStatus !== 'ready' || !targetSourceId)) {
setDocumentActionPendingAction(null);
setDocumentActionStatus('error');
return;
}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.

setDocumentActionPendingAction(action);
setDocumentActionStatus('loading');
setDocumentActionResult(null);
try {
Expand All @@ -294,12 +305,26 @@ export function DataLayout() {
: {},
);
setDocumentActionResult(result);
setDocumentActionPendingAction(null);
if (action === 'webdav-materialization-intent' && !result.provider_write_executed) {
setDocumentActionStatus('connector_error');
return;
}
setDocumentActionStatus('success');
setDataSurfaceStatus('loading');
await loadDataQualitySurface();
} catch (error: unknown) {
const status = getApiErrorStatus(error);
setDocumentActionStatus(status === 401 || status === 403 ? 'auth' : 'error');
setDocumentActionPendingAction(null);
setDocumentActionStatus(
status === 401 || status === 403
? 'auth'
: status === 409
? 'conflict'
: status === 422
? 'invalid'
: 'error',
);
}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
}, [
dataQualitySurface,
Expand All @@ -315,7 +340,6 @@ export function DataLayout() {
const canRequestWebdavWriteback = webdavAccountStatus === 'ready';
const isUniqueThreadLoading = uniqueThreadStatus === 'loading';
const isEmailImportLoading = emailImportStatus === 'loading';
const isDocumentActionLoading = documentActionStatus === 'loading';
const selectedWebdavAccount = webdavAccounts.find((account) => (
account.source_id === selectedWebdavSourceId && account.writeback_enabled
)) ?? webdavAccounts.find((account) => account.writeback_enabled) ?? null;
Expand Down Expand Up @@ -440,7 +464,7 @@ export function DataLayout() {
emailImportResult={emailImportResult}
handleDocumentFileChange={handleDocumentFileChange}
requestDocumentUpload={requestDocumentUpload}
isDocumentActionLoading={isDocumentActionLoading}
documentActionPendingAction={documentActionPendingAction}
documentUploadFiles={documentUploadFiles}
documentActionStatus={documentActionStatus}
documentActionResult={documentActionResult}
Expand Down
Loading
Loading