AI skill: SKILL.md, AI contract, scrubbed references, eval fixture (d)
- rpa-dashboard-ai/SKILL.md: trigger description and the shared question flow; points at the no-AI frontend.md, themes and palette instead of copying them. - templates/contract.md: runs, review rows, endpoints and the server approve gate. - Superbill extracts: patient names and the client name removed, every file carries an EXAMPLE ONLY banner. tests/test_reference_scan.py checks both skills for patient data, internal IPs, credentials and keys. - tests/fixtures/d-ai-extraction and its eval entry, which runs page_check_ai on the built folder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
91495935b9
commit
8951ac05a5
30 files changed
+372
-26
No files matched your search
@@ -0,0 +1,126 @@
|
||||
---
|
||||
name: rpa-dashboard-ai
|
||||
description: Builds a working dashboard for an RPA bot or service that uses AI (an LLM or OCR extraction call). It has everything from rpa-dashboard-no-ai (sidebar, top bar with dark toggle and status dot, KPI cards, batch donut, run panel with steps and live log, report table with search and export) plus confidence bands, a review split pane (source document beside the extracted fields, band filters, draft edits, Verify) and an approve gate the server enforces. Web path is React + Vite + Tailwind with a sample-data server stub. Use when the user asks for an "AI RPA dashboard", an "extraction dashboard", an "LLM/OCR review dashboard", or any RPA/bot dashboard in a project that calls an LLM or OCR to extract fields. Needs rpa-dashboard-no-ai installed next to it.
|
||||
---
|
||||
|
||||
# RPA dashboard (AI)
|
||||
|
||||
Everything this skill needs is bundled. Never read the original source projects.
|
||||
Shared files live in the no-AI skill, installed next to this one: `<no-ai>` below
|
||||
means `../rpa-dashboard-no-ai/templates/`.
|
||||
|
||||
| File | What it is |
|
||||
|---|---|
|
||||
| `templates/web/` | The React + Vite + Tailwind starter: shell, KPI, donut, run panel, report, **band legend, review split pane, approve gate**, sample data. |
|
||||
| `templates/web/server_stub.py` | Sample-data backend for runs, review rows and the approve gate. Standard library only. |
|
||||
| `templates/web/bands.py` | The band cut points and rules. The one source of truth: the backend imports it. |
|
||||
| `templates/contract.md` | The AI contract: runs, review rows, endpoints and the gate. Extends `<no-ai>contract.md`. |
|
||||
| `templates/reference/` | Superbill Review extracts, marked EXAMPLE ONLY. Use them to port extra polish, never copy their project names. |
|
||||
| `<no-ai>frontend.md`, `<no-ai>detect_frontend.py` | Existing frontend: detect, report, keep or restyle. |
|
||||
| `<no-ai>themes.md`, `<no-ai>palette.py` | Theme presets and the custom-colour steps. |
|
||||
| `<no-ai>contract.md` | The no-AI contract the AI one extends. |
|
||||
|
||||
The questions are the same as the no-AI skill's, in the same order. Ask one at a
|
||||
time and wait for the answer. If the user's request already answers a question,
|
||||
don't ask it again.
|
||||
|
||||
## 1. Scan the project
|
||||
|
||||
Before asking anything:
|
||||
- **Frontend:** run `python <no-ai>detect_frontend.py <project root>` and read
|
||||
`<no-ai>frontend.md` section 1.
|
||||
- **AI call:** find the LLM or OCR extraction (for example `genai`, `openai`,
|
||||
`anthropic`, Textract or Tesseract calls): which fields it returns, which are
|
||||
mandatory, and whether it returns per-word scores.
|
||||
- **Bot or backend:** the entry point, what it prints, and any web framework
|
||||
(FastAPI, Flask, Django).
|
||||
|
||||
If the project has no AI call at all, say so and suggest `rpa-dashboard-no-ai`.
|
||||
|
||||
## 2. Report the frontend, then ask keep or restyle
|
||||
|
||||
Follow `<no-ai>frontend.md` sections 2 to 5.
|
||||
- **No frontend:** say "No existing frontend found." and go to step 3.
|
||||
- **Frontend found:** say "Frontend found at `<path>`, stack `<stack>`, colours
|
||||
`<main tokens>`." Then ask: **keep its look**, or **restyle** it?
|
||||
|
||||
## 3. Ask for colours
|
||||
|
||||
Skip this if the user chose keep. Offer:
|
||||
1. **Tebra Sky**: blue glass cards on a sky gradient.
|
||||
2. **Superbill Ember**: flat surfaces with a red-orange brand gradient.
|
||||
3. **Custom**: the user gives one brand hex colour.
|
||||
|
||||
## 4. Ask desktop or web
|
||||
|
||||
Ask only if there is no frontend. If there is one, use its stack. If the project
|
||||
uses PyQt, choose desktop and say so.
|
||||
- **Web:** the React starter in `templates/web/`, served by the backend.
|
||||
- **Desktop:** the plain-HTML AI sections for the PyQt page aren't bundled yet.
|
||||
Tell the user, and offer the web path or the no-AI desktop page
|
||||
(`rpa-dashboard-no-ai`) without the AI sections.
|
||||
|
||||
## 5. Ask which optional sections to build
|
||||
|
||||
Always built: the no-AI shell and sections, plus the band legend, the review split
|
||||
pane and the approve gate. Ask which extras to add (choose any, or none):
|
||||
**checkpoint tracker**, **admin/roles screen**, **login page**. Build only the
|
||||
chosen ones at their `OPTIONAL:` markers, and delete the markers.
|
||||
|
||||
## 6. Build (web)
|
||||
|
||||
1. Copy `templates/web/` into the project as `dashboard/` (an existing frontend:
|
||||
see `<no-ai>frontend.md` section 3; in an existing React app, port the
|
||||
`src/` components into it). Don't copy `node_modules/` or `dist/`.
|
||||
2. Fill every `{{...}}` in `dashboard/index.html`'s CONFIG block:
|
||||
|
||||
| Placeholder | Value |
|
||||
|---|---|
|
||||
| `APP_NAME`, `APP_SUBTITLE` | The project's name and a short tagline. |
|
||||
| `LOGO_SRC` | The logo's path, or empty. |
|
||||
| `STEPS_JSON` | The run steps as a JSON list, for example `["Initializing","Extract","Review","Completed"]`. |
|
||||
| `REPORT_COLUMNS_JSON` | `[{"key":..., "label":...}]`, with a `status` column. |
|
||||
| `API_BASE` | `/api` unless the backend mounts the dashboard API elsewhere. Keep it equal to `API` in `server_stub.py`. |
|
||||
|
||||
3. Apply the theme in `dashboard/src/index.css` only: replace the `body{...}` and
|
||||
`body.dark{...}` token blocks with the preset from `<no-ai>themes.md` (for Ember,
|
||||
add its font `<link>` to `index.html`'s `<head>`), or with
|
||||
`python <no-ai>palette.py "<hex>"` tokens for a custom colour, showing the user
|
||||
its contrast table. For keep, fill them from the project's tokens
|
||||
(`<no-ai>frontend.md` section 4). Never change the band colour block.
|
||||
4. Search the output for `{{`. None may be left.
|
||||
|
||||
## 7. Wire the data
|
||||
|
||||
The contracts: `<no-ai>contract.md` for the shell, `templates/contract.md` for runs,
|
||||
review rows and the gate.
|
||||
- **Sample data (no backend for these endpoints yet):** keep `SAMPLE_DATA = true` in
|
||||
`src/store.ts` and use `server_stub.py`. Replace its `SAMPLE_FIELDS` with the
|
||||
fields the project's extraction really returns (name, a sample value, word scores,
|
||||
mandatory). Tell the user which endpoints their backend must serve later.
|
||||
- **Backend exists:** add the endpoints from `templates/contract.md` to it. Copy
|
||||
`bands.py` next to it and band every field with `field_band(word_scores, value,
|
||||
mandatory)`. The approve endpoint must refuse (409 with `detail`) while any flagged
|
||||
field is unverified: the page never decides that. For the shell, set
|
||||
`SAMPLE_DATA = false` and serve the no-AI endpoints too.
|
||||
- Never invent fields, model names or prices the project doesn't have.
|
||||
|
||||
## 8. Verify
|
||||
|
||||
```
|
||||
cd dashboard
|
||||
npm install
|
||||
npm run build
|
||||
python server_stub.py # page + sample API on http://127.0.0.1:8000
|
||||
```
|
||||
|
||||
Open the page and check, in light, dark (the toggle) and a window 820px wide or less:
|
||||
- the dashboard, Run and Report tabs show sample data;
|
||||
- on Review: the band legend counts and the caveat, band filters, a typed edit marked
|
||||
unsaved until Save, a refused Approve while flagged fields are unverified, and
|
||||
"page X of Y" on a running document;
|
||||
- no `{{` is left and the console shows no script errors.
|
||||
|
||||
If you can't run npm here, say so and give the user these commands. Finish by
|
||||
telling the user where the files are, which theme and sections were built, and
|
||||
whether the review data is sample data or the backend's.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Dashboard data contract (AI)
|
||||
|
||||
Everything in the no-AI contract (`../rpa-dashboard-no-ai/templates/contract.md`)
|
||||
applies unchanged: the `{event, data}` envelope, its events, its actions and its rules.
|
||||
The AI dashboard adds extraction runs, review rows and the approve gate. These shapes
|
||||
come from the Superbill prototype, trimmed.
|
||||
|
||||
## Shapes
|
||||
|
||||
```
|
||||
Run { job_id, document, status: pending|queued|running|done|failed, model,
|
||||
pages_done, pages_total, summary: { green, amber, red, blank, unscored } | null,
|
||||
approved, error }
|
||||
ReviewRow { row_id, field, value, band: green|amber|red|blank|unscored,
|
||||
score, reason, verified, corrected_value }
|
||||
```
|
||||
|
||||
- `summary` counts the run's rows per band. It is `null` until the run is `done`.
|
||||
- `score` is the field's worst word score, or `null` when it has none.
|
||||
- `corrected_value` is `null` until a reviewer saves a value different from `value`.
|
||||
|
||||
## Endpoints (web: `{API_BASE}` + path, JSON in and out)
|
||||
|
||||
| Method and path | What it does |
|
||||
|---|---|
|
||||
| `GET /runs` | `{runs: [Run]}`. The page opens the first one. |
|
||||
| `GET /runs/{job_id}` | One `Run`. |
|
||||
| `GET /runs/{job_id}/review` | `{rows: [ReviewRow]}`. Empty until the run is `done`. |
|
||||
| `GET /runs/{job_id}/document` | The source document (PDF or image) shown beside the fields. |
|
||||
| `PATCH /review/{row_id}` | Body `{corrected_value?, verified}`. Patches one row, returns it. |
|
||||
| `POST /runs/{job_id}/verify-remaining` | Verifies every flagged row not yet verified. Returns `{verified: n}`. |
|
||||
| `POST /runs/{job_id}/approve` | **The approve gate.** `409 {detail}` while the run isn't done or any flagged row is unverified, else `200 {ok: true}`. |
|
||||
|
||||
Errors answer with a non-2xx status and `{detail: "<why>"}`. The page shows `detail`.
|
||||
|
||||
## Rules
|
||||
|
||||
- **Bands** come from `templates/web/bands.py` and nowhere else: green ≥0.95, amber
|
||||
0.90–0.95, red <0.90. A field takes its worst word score. A blank mandatory field
|
||||
is `blank`, whatever its score. No scores is `unscored`.
|
||||
- **Flagged** = every band but green. Each flagged row must be verified before approval.
|
||||
- **The server enforces the gate.** The page disables nothing that matters for safety:
|
||||
it asks, and shows the server's refusal.
|
||||
- **Drafts:** the page sends nothing while a reviewer types. Save sends one PATCH per
|
||||
edited row (with `verified: true`). Leaving with unsaved edits asks first.
|
||||
- **Polling:** while a run is `queued` or `running`, the page polls `GET /runs/{job_id}`
|
||||
every 1.5s and shows "page X of Y". It stops when the run settles.
|
||||
- **Caveat:** the legend always shows that colour reflects print clarity, not
|
||||
correctness.
|
||||
|
||||
`templates/web/server_stub.py` implements all of this on sample data, standard library
|
||||
only. A real backend serves the same endpoints with the same rules.
|
||||
@@ -1,3 +1,4 @@
|
||||
# EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project.
|
||||
INCLUDE_THINKING_IN_COST = True # thinking tokens bill at output rate
|
||||
|
||||
# ═════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. -->
|
||||
---
|
||||
name: Superbill Extraction and Automation Tool
|
||||
description: Extract billing fields from a scan, verify them against the page, post to eClinicalWorks.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
import type {
|
||||
AuditEntry, Batch, BatchStage, DemographyRecord, DemographyRun, InsuranceMasterRow, LastRun,
|
||||
ManagedUser,
|
||||
@@ -84,7 +85,7 @@ export const api = {
|
||||
for (const f of files) body.append('files', f)
|
||||
// `project` is which board received the drop. Nothing has detected a
|
||||
// template yet, and the board a batch appears on is derived from that, so
|
||||
// without it a Gulf Coast upload surfaces under ECW and reads as a failed
|
||||
// without it a Example Client upload surfaces under ECW and reads as a failed
|
||||
// upload on the Tebra board.
|
||||
return req<Batch>(`/api/batches?${new URLSearchParams({ model, project })}`, {
|
||||
method: 'POST',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
/**
|
||||
* The confidence bands, and the exact colours the workbook paints.
|
||||
*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
import { ThemeSwitcher } from '../theme'
|
||||
import { ROLE_LABEL } from '../auth'
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
/**
|
||||
* A proportional ring.
|
||||
*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { api } from '../api'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { api } from '../api'
|
||||
@@ -12,7 +13,7 @@ const isOfficeLocation = (t: string | null) =>
|
||||
|
||||
// One review card per record on a page. An ECW roster can list the same
|
||||
// person twice (two Sleep Study encounters for one patient) — line_index
|
||||
// splits those into separate cards. A Gulf Coast ticket spreads its charges
|
||||
// splits those into separate cards. A Example Client ticket spreads its charges
|
||||
// over several line_index values and must stay ONE card, so it is excluded.
|
||||
// The name stays in the key so runs stored before line_index existed (every
|
||||
// row LineIndex 0) still group exactly as before.
|
||||
@@ -100,7 +101,7 @@ type Patient = {
|
||||
type SaveArgs = { row: ReviewRow; draft: string; verified: boolean }
|
||||
|
||||
// ── Office Location ────────────────────────────────────────────────────────
|
||||
// A Gulf Coast ticket whose yellow note the Office Location rules read. The
|
||||
// A Example Client ticket whose yellow note the Office Location rules read. The
|
||||
// backend stores the parsed note on the ticket's first line under this field
|
||||
// (src/superbill/extraction/office_location.py, ParsedNote); a ticket without
|
||||
// it is a run from before the rules existed and keeps the flat layout.
|
||||
@@ -191,11 +192,11 @@ export function ReviewPane({
|
||||
}) {
|
||||
const [index, setIndex] = useState(0)
|
||||
// Drafts live here, not in FieldRow — a correction is no longer committed on
|
||||
// blur (that is exactly how four ARMSTRONG fields got saved mid-keystroke:
|
||||
// "ARMSTRONG, CHARLES T" as "\ONG, CHARLES T", "99291" as "992"). Text typed
|
||||
// blur (that is exactly how four fields of one record got saved mid-keystroke:
|
||||
// "Doe, Jane A" as "\e, Jane A", "99291" as "992"). Text typed
|
||||
// into a field is held until an explicit Save, keyed by row_id rather than
|
||||
// bare field name — a bare name repeats once a page can carry more than one
|
||||
// row per field (Gulf Coast's repeated CPT lines), and DemographyTab.tsx
|
||||
// row per field (Example Client's repeated CPT lines), and DemographyTab.tsx
|
||||
// hit exactly this before switching its own drafts to row_id.
|
||||
const [drafts, setDrafts] = useState<Record<number, string>>({})
|
||||
const qc = useQueryClient()
|
||||
@@ -238,7 +239,7 @@ export function ReviewPane({
|
||||
|
||||
// PDF order: page, then where the patient sits on it. line_index is each
|
||||
// record's position on its page, set by build_field_rows. Sorting by name
|
||||
// here used to put ANDERSON above the first patient on the sheet, so the
|
||||
// here used to put a later surname above the first patient on the sheet, so the
|
||||
// list no longer matched the document the reviewer had open beside it.
|
||||
// Name is only the tie-break, for runs stored before line_index existed.
|
||||
return [...byKey.values()].sort(
|
||||
@@ -254,7 +255,7 @@ export function ReviewPane({
|
||||
map.get(key)!.push(row)
|
||||
}
|
||||
for (const rows of map.values()) {
|
||||
// Gulf Coast (Template 7) records keep the order the API sent — the flat
|
||||
// Example Client (Template 7) records keep the order the API sent — the flat
|
||||
// 20-column superbill layout, Ticket# through Units. Re-sorting by band
|
||||
// would scatter it (that is what put ICD11 above ICD2 and CPT at the
|
||||
// bottom). Every other template keeps worst-band-last, which is what the
|
||||
@@ -280,7 +281,7 @@ export function ReviewPane({
|
||||
// A field that was never queued (needs_review === false) has no review
|
||||
// row until this moment on most templates — build_field_rows only emits
|
||||
// a row when a value exists or the field is billing-critical, so a
|
||||
// genuinely blank optional field (e.g. Gulf Coast's From DOS / To DOS /
|
||||
// genuinely blank optional field (e.g. Example Client's From DOS / To DOS /
|
||||
// Units, always null today) is addressed by the cell it occupies,
|
||||
// line_index included, rather than by row_id.
|
||||
if (row.needs_review === false) {
|
||||
@@ -363,7 +364,7 @@ export function ReviewPane({
|
||||
const discardAll = () => setDrafts(settledMap)
|
||||
|
||||
// Leaving a patient with typed-but-unsaved edits used to be silent — the old
|
||||
// blur handler saved automatically, which is what produced the ARMSTRONG
|
||||
// blur handler saved automatically, which is what produced the truncated-name
|
||||
// corruption. Now nothing commits without Save, so leaving instead has to
|
||||
// ask, or the edit is just lost with no signal either way.
|
||||
const guardLeave = (): boolean => {
|
||||
@@ -711,7 +712,7 @@ function PatientView({
|
||||
<div className="flex flex-col gap-[9px]">
|
||||
{/* Keyed on row_id — a real database identity from /all-fields
|
||||
(SPB_Extraction.RowId), unique across every field on every page.
|
||||
A page-and-field key would collide on Gulf Coast's repeated CPT
|
||||
A page-and-field key would collide on Example Client's repeated CPT
|
||||
lines, where two rows share the same (page, field). */}
|
||||
{/* No entry animation. Rows are keyed by row_id, so every Next
|
||||
remounts them all, and the old staggered fade (opacity 0 over
|
||||
@@ -802,7 +803,7 @@ function PatientView({
|
||||
* lost by making it editable.
|
||||
*
|
||||
* Nothing here commits on its own. An earlier version saved on blur, which is
|
||||
* exactly how "ARMSTRONG, CHARLES T" got recorded as "\ONG, CHARLES T" — the
|
||||
* exactly how "Doe, Jane A" got recorded as "\e, Jane A" — the
|
||||
* moment focus left the box mid-keystroke, whatever half-typed text was there
|
||||
* became the correction. Typing only updates the shared draft in ReviewPane;
|
||||
* the patient-level Save bar (here and in PdfPane) is the one place an edit is
|
||||
@@ -835,7 +836,7 @@ function FieldRow({
|
||||
const corrected = row.corrected_value != null && row.corrected_value !== row.value
|
||||
// While being edited the value takes its own full-width line. A DX field is
|
||||
// five comma-separated codes and a provider name runs past twenty
|
||||
// characters; in the shared row they were truncated to "ARMSTRONG, C", so a
|
||||
// characters; in the shared row they were truncated to "Doe, J", so a
|
||||
// reviewer was correcting a value they could not fully see. Expanding only
|
||||
// on focus keeps the scan-many-fields-fast layout for reading.
|
||||
const expanded = focused || dirty
|
||||
@@ -938,7 +939,7 @@ function FieldRow({
|
||||
|
||||
|
||||
/**
|
||||
* A Gulf Coast ticket laid out the way its yellow note is: the note itself,
|
||||
* A Example Client ticket laid out the way its yellow note is: the note itself,
|
||||
* the page-level rules it fired, the visit, then one tab per encounter.
|
||||
*
|
||||
* Hovering a field lights the note line it came from. The page preview is an
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { api } from '../api'
|
||||
@@ -95,7 +96,7 @@ type Filter = 'all' | 'priced' | 'unmeasured' | 'failed'
|
||||
|
||||
/**
|
||||
* api.roi() has no project filter — it totals every document. The ROI board
|
||||
* is per-project (Gulf Coast's spend is not eClinicalWorks' spend), so the
|
||||
* is per-project (Example Client's spend is not eClinicalWorks' spend), so the
|
||||
* totals are recomputed here from the already-fetched runs instead of a new
|
||||
* query param. Every field is a plain sum or count over RoiRun — nothing
|
||||
* server-side is being second-guessed, just re-scoped to fewer rows.
|
||||
@@ -138,7 +139,7 @@ export function RoiTab({
|
||||
}: {
|
||||
/** Absent on the demography board: its runs have no charge review to open. */
|
||||
onOpenRun?: (jobId: string) => void
|
||||
/** Which project's spend this board reports — Gulf Coast tickets and
|
||||
/** Which project's spend this board reports — Example Client tickets and
|
||||
* eClinicalWorks documents never share a total. */
|
||||
project: Project
|
||||
/** The Demography ROI (ticket 05): its own payload, one Gemini call per PDF
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { api } from '../api'
|
||||
import { IconDownload } from './icons'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
import { useState } from 'react'
|
||||
import type { ReactElement } from 'react'
|
||||
import type { BatchStage, BoardTab } from '../types'
|
||||
@@ -14,7 +15,7 @@ import { IconSpend, IconPage, IconLoop, IconIdCard, IconPatients } from './icons
|
||||
* icons are" step from every navigation.
|
||||
*
|
||||
* Two projects, one rail: ECW's boards (templates 1-6) and Tebra's (template 7
|
||||
* / Gulf Coast), each under its own group header. See project.ts for how a
|
||||
* / Example Client), each under its own group header. See project.ts for how a
|
||||
* batch or run is assigned to one or the other.
|
||||
*
|
||||
* Selection and drag-and-drop-to-move-stage behaviour are unchanged — only the
|
||||
@@ -136,7 +137,7 @@ export function Sidebar({
|
||||
tab="tebra-roi"
|
||||
icon={IconSpend}
|
||||
label="ROI Dashboard"
|
||||
hint="What the Gulf Coast pipeline has cost, and what that bought"
|
||||
hint="What the Example Client pipeline has cost, and what that bought"
|
||||
active={tab === 'tebra-roi'}
|
||||
onSelect={onTab}
|
||||
/>
|
||||
@@ -146,8 +147,8 @@ export function Sidebar({
|
||||
dropStage="extraction"
|
||||
dropTab="tebra-extraction"
|
||||
icon={IconPage}
|
||||
label="Gulf Coast Charge Extraction"
|
||||
hint="Gulf Coast tickets: extract and clear the flagged fields"
|
||||
label="Example Client Charge Extraction"
|
||||
hint="Example Client tickets: extract and clear the flagged fields"
|
||||
active={tab === 'tebra-extraction'}
|
||||
n={counts.tebra.extraction}
|
||||
onSelect={onTab}
|
||||
@@ -159,8 +160,8 @@ export function Sidebar({
|
||||
dropStage="automation"
|
||||
dropTab="tebra-automation"
|
||||
icon={IconLoop}
|
||||
label="Gulf Coast Charge Automation"
|
||||
hint="Gulf Coast batches that finished extraction and are ready to post"
|
||||
label="Example Client Charge Automation"
|
||||
hint="Example Client batches that finished extraction and are ready to post"
|
||||
active={tab === 'tebra-automation'}
|
||||
n={counts.tebra.automation}
|
||||
onSelect={onTab}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
const KEY = 'superbill.split'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
import { BAND_VAR } from '../bands'
|
||||
import type { Run } from '../types'
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
/**
|
||||
* The icon set.
|
||||
*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
@import 'tailwindcss';
|
||||
|
||||
/* Manual dark mode: Tailwind v4 defaults `dark:` to the OS preference, but this
|
||||
@@ -1662,7 +1663,7 @@ textarea,
|
||||
.sb-brand-name { background-position: 0 0; }
|
||||
}
|
||||
|
||||
/* ── Office Location (a Gulf Coast ticket's yellow note) ──────────────────────
|
||||
/* ── Office Location (a Example Client ticket's yellow note) ──────────────────────
|
||||
The note is amber because it IS amber on the page; everything else stays on
|
||||
the neutral surfaces. No brand ramp here -- it is reserved for its six
|
||||
things (DESIGN.md, "The One Loud Colour Rule"). */
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<!doctype html>
|
||||
<!-- EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* EXAMPLE ONLY: an extract from the Superbill Review prototype, kept as reference for porting. ECW/eClinicalWorks, Tebra, 'Example Client', SPB_* tables and patient/ticket wording are that project's examples. Don't copy them into a new project. */
|
||||
import type { Band } from './bands'
|
||||
|
||||
/**
|
||||
@@ -21,7 +22,7 @@ export type Project = 'ecw' | 'tebra'
|
||||
* stage a card can be dropped into.
|
||||
*
|
||||
* Two projects live in one rail: the ECW boards (templates 1-6) and the Tebra
|
||||
* boards (template 7 / Gulf Coast). Tebra ids are prefixed because a bare
|
||||
* boards (template 7 / Example Client). Tebra ids are prefixed because a bare
|
||||
* 'extraction' can no longer identify a board on its own — see project.ts for
|
||||
* the tab -> (project, stage) mapping.
|
||||
*/
|
||||
@@ -118,7 +119,7 @@ export interface ReviewRow {
|
||||
patient: string | null
|
||||
/**
|
||||
* 0 for every template except one that can carry more than one row per
|
||||
* (page, patient, field) — Gulf Coast's repeated CPT lines, one per coder
|
||||
* (page, patient, field) — Example Client's repeated CPT lines, one per coder
|
||||
* annotation on the same ticket.
|
||||
*/
|
||||
line_index: number
|
||||
|
||||
Reference in new issue
Block a user