Skip to content

feat(ui): polish loading, error, and feedback states across the dashboard#35

Open
study8677 wants to merge 2 commits into
mainfrom
claude/wizardly-kalam-a82fc3
Open

feat(ui): polish loading, error, and feedback states across the dashboard#35
study8677 wants to merge 2 commits into
mainfrom
claude/wizardly-kalam-a82fc3

Conversation

@study8677
Copy link
Copy Markdown
Owner

Summary

This PR is a Jobs-level polish pass over the OpenCMO dashboard. The goal: stop showing the user empty screens, silent actions, and stuck-feeling buttons.

What the user sees differently

  • Loading: Chat, SEO, GEO, SERP, Community, Approvals, Graph, and ProjectMonitors now show layout-shaped skeletons instead of a blank spinner. The UI looks the same shape before and after data arrives — no flash of nothing.
  • Success feedback: Approve / reject, add / remove keyword, create / delete monitor all surface confirmation toasts. Session expiry shows a toast with a "Sign in again" action button.
  • Error recovery: `ErrorAlert` now supports a recovery hint, a Try-again button, an optional error code, and a custom action. Used on the Approvals query failure path.
  • Knowledge graph mobile: Container is now fluid-height (was hardcoded 600px), legend collapses on mobile, and a one-tap "fit view" button helps touch users re-center after dragging.
  • Disabled buttons every `disabled:opacity-50` now also has `disabled:cursor-not-allowed` so disabled buttons stop feeling stuck.
  • Tablet layout: KPI grids on SEO/GEO/SERP/Community now use `md:grid-cols-3` instead of jumping from 2 → 4 columns and squishing at 800px.

New primitives (under `frontend/src/components/common/`)

  • `Toast.tsx` — `ToastProvider` + `useToast()` with success/error/info/loading variants, action buttons, auto-dismiss, stack cap of 5, portal-rendered.
  • `Skeleton.tsx` — Reusable primitives (Block / Text / Chip / Frame) and composite skeletons (KpiRow, ChartCard, ProjectSubpage, Dashboard, Chat, Graph, Approval, ListRow) that mirror real layouts.
  • `ErrorAlert.tsx` — Rewritten with `tone`, `code`, `hint`, `onRetry`, `onDismiss`, `action` props.

i18n

`toast.*` and `common.tryAgain/copied/sessionExpired/…` keys added in en / zh / ja / ko / es.

Pre-existing leaks fixed in passing

  • `KnowledgeGraph`'s `zoomToFit` `setTimeout` is now cancelled in cleanup so rapid `graphData` polling updates don't pile up uncancelled camera snaps.
  • `Toast` stack-overflow drops now clear their auto-dismiss timers.

Known follow-up (spawned as separate task)

There's a stored-XSS hole in `KnowledgeGraph.getNodeLabelHtml` where node fields are interpolated unescaped into innerHTML — flagged for a dedicated PR rather than scope-creeping this one.

Test plan

  • `npm run build` — TypeScript clean, vite build succeeds
  • Dev server smoke test — landing, login, signup all render with zero console errors
  • Mobile viewport (375×812) renders cleanly
  • Manual: trigger approve/reject and verify toast appears
  • Manual: open Graph on mobile and verify the fit-view button works
  • Manual: let session expire and verify the "Sign in again" toast appears

🤖 Generated with Claude Code

study8677 and others added 2 commits May 30, 2026 22:51
…oard

Adds a Skeleton component family and Toast notification system, then
applies them across the high-traffic pages so the app stops feeling
empty during loads and silent during actions.

What changes the user actually sees:
- Loading: Chat, SEO, GEO, SERP, Community, Approvals, Graph, and
  ProjectMonitors now show layout-shaped skeletons instead of a blank
  spinner — no more flash of nothing.
- Success: approving/rejecting content, adding/removing keywords, and
  creating/deleting monitors all surface confirmation toasts. Session
  expiry surfaces a toast with a "Sign in again" action.
- Errors: ErrorAlert now supports a recovery hint, a Try-again button,
  optional error code, and a custom action — used on the Approvals
  query failure path.
- Knowledge graph: container is now fluid-height (was hardcoded 600px),
  legend collapses on mobile, a one-tap "fit view" button helps touch
  users re-center after they get lost dragging.
- Buttons: every disabled:opacity-50 now also has
  disabled:cursor-not-allowed so disabled buttons stop feeling stuck.
- Tablet: KPI grids on SEO/GEO/SERP/Community now use md:grid-cols-3
  instead of jumping straight from 2 → 4 columns and squishing at 800px.

i18n: new toast.* and common.* keys added across en/zh/ja/ko/es.

Also fixes two pre-existing leaks touched by this pass:
- KnowledgeGraph's zoomToFit setTimeout is now cancelled in cleanup so
  rapid graphData updates don't pile up camera snaps.
- Toast stack-overflow drops now clear their auto-dismiss timers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the legacy rsync + venv + systemctl restart deployment with a
single Docker container. Cuts cold-deploy time on a fresh server from
~10 minutes (system Python + venv + pip + playwright install + browsers
+ crawl4ai-setup) to one `docker compose up -d --build`.

Already cut over on newyork (https://www.aidcmo.com/app/ verified 200,
DB migrated, scheduler picked up the 45 existing jobs). The systemd
unit is stopped + disabled but left in place so rollback is one command.

What was broken in the previous Dockerfile

- Exposed 8080 — but nginx-on-host proxies to 8081 and 8080 is taken
  by sub2api on newyork, so the container would never receive traffic.
- Installed `opencmo[all]` which excludes the `browser` extra, so
  playwright wasn't in the image. Crawl4ai's scan fallback paths
  would crash with `BrowserType.launch executable doesn't exist`.
- Ran `playwright install-deps` (system libs only) but never
  `playwright install chromium` (the actual browser binary).
- `crawl4ai-setup || true` masked legitimate post-install failures.
- Ran as root; no signal handling; no healthcheck.

What's new

- Multi-stage build: node:20-alpine for the frontend bundle, then
  python:3.12-slim for the runtime with Chromium + crawl4ai pre-warmed.
- Non-root user (uid 1000), tini as PID 1 for clean shutdowns,
  HEALTHCHECK against /api/v1/health.
- Port configurable via OPENCMO_WEB_PORT (defaults to 8081 to match
  nginx upstream).
- docker-compose.yml binds 127.0.0.1:8081 only, bind-mounts ./data
  for SQLite + report assets, caps log rotation at 20m × 5 files.
- .dockerignore keeps the build context lean (excludes .venv, dist,
  node_modules, screenshots, .env, …).
- deploy/docker-deploy.sh: one-shot rsync + build + up + health probe,
  with safe excludes so `--delete` never wipes the on-server .env or
  data directory.
- docs/DOCKER.md: full migration story + rollback runbook.
- CLAUDE.md: Docker is the primary path; systemd downgraded to
  emergency rollback. Also notes: don't smoke-test locally, the
  laptop isn't the deployment target — verify on newyork.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant