|
| 1 | +# UiPath Document Review Workspace (Validation Station subcomponents) |
| 2 | + |
| 3 | +A sample React + TypeScript + Vite application that builds a **custom** human-in-the-loop document review screen by composing the individual **subcomponents** exported by `@uipath/ui-widgets-validation-station` — rather than the all-in-one `ValidationStation` component. |
| 4 | + |
| 5 | +It lists pending Document Validation tasks from UiPath Action Center and, for the selected task, lays out five compact subcomponents in a grid: the document viewer, a document-type field, the extraction fields form, a line-items table editor, and a business-rules panel. All five share a single `instanceId`, so they mirror one store — selecting a field in the form highlights it in the viewer, picking a table field opens the table editor, and clicking a rule focuses the offending field, with no cross-wiring. |
| 6 | + |
| 7 | +> **Monolithic vs. compose-your-own.** The sibling [`document-validation-app`](../document-validation-app) renders the same task with the single `ValidationStation` component — the fastest path when you want the standard layout. **This** sample shows the other path: drop the subcomponents into your own layout when you need to rearrange, hide, or embed individual panels. Both talk to the same tasks and the same bucket artifacts. |
| 8 | +
|
| 9 | +## Preview |
| 10 | + |
| 11 | + |
| 12 | + |
| 13 | +## What this sample demonstrates |
| 14 | + |
| 15 | +- OAuth 2.0 authorization code + PKCE login against UiPath Cloud using the `@uipath/uipath-typescript` SDK |
| 16 | +- Listing Document Validation tasks (`Tasks.getAll` with an OData filter) and hydrating one with its full validation payload (`Tasks.getById` with `TaskType.DocumentValidation`) |
| 17 | +- Fetching the document artifacts **once** with the `useBucketArtifacts` hook and sharing them across subcomponents |
| 18 | +- Composing `DocumentViewer`, `CompactDocTypeField`, `CompactFieldsForm`, `CompactTableEditor`, and `CompactBusinessRules` into a custom layout linked by a shared `instanceId` |
| 19 | +- Using `options` flags (`hideBusinessRules`, `hideDocumentTypeField`, `emitDtoStateChanges`) so the fields form drops the panels that are rendered standalone |
| 20 | +- Submitting the completed task (`Task.complete`), saving in-progress edits (save as draft), and reporting a document as an exception via `OrchestratorDuModule.submitExceptionReport` |
| 21 | + |
| 22 | +## Prerequisites |
| 23 | + |
| 24 | +- Node.js 20+ and npm |
| 25 | +- A UiPath Cloud organization and tenant with Action Center enabled |
| 26 | +- At least one pending Document Validation task in the tenant (produced by a Document Understanding process) |
| 27 | +- An OAuth External Application registered in the UiPath Admin Center (see below) |
| 28 | + |
| 29 | +## Configure the OAuth External Application |
| 30 | + |
| 31 | +1. In UiPath Cloud: **Admin → External Applications → Add Application**. |
| 32 | +2. Choose **Non Confidential Application** (this is a browser SPA — no client secret is used or stored). |
| 33 | +3. Set: |
| 34 | + - **Name**: e.g., `Document Review Subcomponents Sample` |
| 35 | + - **Redirect URI**: the exact URL the app runs on, including scheme, host, port, and path. For local development this is `http://localhost:5173/`. The redirect URI is matched **exactly** by UiPath — a trailing-slash or port mismatch will fail the callback. |
| 36 | + - **Scopes** (least-privilege set used by this sample): |
| 37 | + - `OR.Tasks` — list and complete validation tasks |
| 38 | + - `OR.Buckets` — read the source document and write the validated/draft result back to the bucket on submit and save-as-draft |
| 39 | + - `OR.Folders.Read` — resolve the folder a task belongs to |
| 40 | +4. Save and copy the generated **Application ID** — this is the `clientId` value below. |
| 41 | + |
| 42 | +> Add a separate Redirect URI entry for any other environment (e.g., a staging URL). Do not use wildcards. |
| 43 | +
|
| 44 | +## Configure `uipath.json` |
| 45 | + |
| 46 | +Copy the template and fill in the values: |
| 47 | + |
| 48 | +```bash |
| 49 | +cp uipath.json.example uipath.json |
| 50 | +``` |
| 51 | + |
| 52 | +| Field | Where to find it | Example | |
| 53 | +|-------|------------------|---------| |
| 54 | +| `clientId` | Application ID from the External Application you just created | `12345678-aaaa-bbbb-cccc-1234567890ab` | |
| 55 | +| `orgName` | The organization slug in your UiPath Cloud URL (`cloud.uipath.com/<org>/<tenant>/...`) | `acme` | |
| 56 | +| `tenantName` | The tenant slug, in the same URL | `DefaultTenant` | |
| 57 | +| `baseUrl` | UiPath Cloud API host. Leave as the default unless you use a regional endpoint | `https://api.uipath.com` | |
| 58 | +| `redirectUri` | Must match the Redirect URI registered on the External Application **exactly** | `http://localhost:5173/` | |
| 59 | +| `scope` | Space-separated scopes — must be a subset of the scopes granted to the External Application | `OR.Tasks OR.Buckets OR.Folders.Read` | |
| 60 | + |
| 61 | +The client ID is not a secret, but `uipath.json` is gitignored to keep environment-specific values out of source control. The `@uipath/coded-apps-dev` Vite plugin reads `uipath.json` and injects the values as `<meta>` tags during local dev; in production, the UiPath platform injects them at deploy time. |
| 62 | + |
| 63 | +## Install, run, and build |
| 64 | + |
| 65 | +```bash |
| 66 | +npm install # install dependencies |
| 67 | +npm run dev # start Vite dev server at http://localhost:5173 |
| 68 | +npm run build # type-check and produce a production bundle in dist/ |
| 69 | +npm run preview # serve the built bundle locally for verification |
| 70 | +``` |
| 71 | + |
| 72 | +On first load the app shows a **Sign in with UiPath** button. After returning from UiPath Cloud, pick a pending task from the left to open the review workspace. Edit fields, select a table field to open the line-items editor, then **Submit**, **Save as draft**, or **Report exception** from the fields form's built-in actions. The status bar at the bottom of the workspace shows the shared store id and the last cross-component interaction. |
| 73 | + |
| 74 | +## Project layout |
| 75 | + |
| 76 | +``` |
| 77 | +src/ |
| 78 | +├── components/ |
| 79 | +│ ├── TaskList.tsx # Left-pane list of pending tasks |
| 80 | +│ ├── ReviewInbox.tsx # Owns task fetch/selection + the SDK mutations |
| 81 | +│ ├── ReviewWorkspace.tsx # The 5-subcomponent composition (shared instance-id) |
| 82 | +│ ├── Panel.tsx # Grid-area panel wrapper |
| 83 | +│ └── CenteredMessage.tsx # Empty / loading / error placeholder |
| 84 | +├── hooks/ |
| 85 | +│ └── useAuth.tsx # AuthProvider wrapping the UiPath SDK + OAuth flow |
| 86 | +├── App.tsx # Top-level layout, sign-in / sign-out |
| 87 | +└── main.tsx # Entry point |
| 88 | +``` |
| 89 | + |
| 90 | +### Validation station runtime assets |
| 91 | + |
| 92 | +The validation station web component resolves several files at runtime (relative to its own bundle): the `du-assets/` folder (PDF.js worker, cmaps, wasm, i18n) plus `styles.css`, `fonts.css`, and `media/`. `vite.config.ts` handles these in two ways: |
| 93 | + |
| 94 | +- **Build** — a `closeBundle` plugin copies `du-assets/`, `media/`, `styles.css`, and `fonts.css` from `@uipath/du-validation-station-wc` next to the emitted JS chunks so `import.meta.url` resolution finds them. |
| 95 | +- **Dev** — Vite rewrites `.css` requests into JS modules, which would break the component's runtime `fetch("styles.css")`. A dev-only middleware detects that raw fetch (`Sec-Fetch-Dest: empty`) and returns the real CSS instead. |
| 96 | + |
| 97 | +The web component bundle is also excluded from Vite's dependency pre-bundling (`optimizeDeps.exclude`), since pre-bundling rewrites `import.meta.url` and breaks the runtime asset resolution. If these steps are missing, the component silently 404s at runtime — PDFs fail to render and icons fall back to empty boxes. |
| 98 | + |
| 99 | +> **Use `persistent: false`.** With `persistent` on, StrictMode's throwaway unmount triggers `forceDestroy()` on the underlying element, so it never re-renders (a blank panel). These panels live in a static grid and are never re-parented, so `false` is safe. |
| 100 | +
|
| 101 | +## Troubleshooting |
| 102 | + |
| 103 | +- **Callback fails with `redirect_uri_mismatch`** — the `redirectUri` in `uipath.json` and the URL you opened in the browser must both match the External Application's Redirect URI character-for-character (scheme, host, port, path, trailing slash). |
| 104 | +- **`insufficient_scope` when loading tasks** — the External Application is missing one of `OR.Tasks`, `OR.Buckets`, or `OR.Folders.Read`. Update the app, then sign out and sign back in to get a new token. |
| 105 | +- **The list is empty** — the signed-in user has no pending / unassigned Document Validation tasks, or no access to the folder the tasks live in. Verify in Action Center first. |
| 106 | +- **A panel is blank** — either the asset copy step in `vite.config.ts` did not run (production), the dev raw-CSS middleware is not registered (dev), or `persistent` was left `true` (see the note above). |
| 107 | + |
| 108 | +## Further reading |
| 109 | + |
| 110 | +- [UiPath TypeScript SDK docs](https://uipath.github.io/uipath-typescript/) |
| 111 | +- [OAuth scopes reference](https://uipath.github.io/uipath-typescript/oauth-scopes/) |
| 112 | +- [Action Center Tasks](https://docs.uipath.com/action-center/) |
0 commit comments