WIP: 调整 UI 设计 #9

Draft
gycherish wants to merge 14 commits from gycherish/closer:main into main
First-time contributor
No description provided.
- Add [tool.pixi.*] config: pixi provisions Python 3.12, node, and a
  local PostgreSQL + pgvector instance, mapping the PEP 621 extras to
  features via an editable self-install (pyproject deps stay the source
  of truth).
- Add pixi tasks for backend (test/serve/serve-pg/demo-flow), frontend
  (fe-install/fe-dev/fe-build/e2e), and local postgres (db-setup/pg-*).
- Tidy backend deps: add postgres extra (psycopg), cap major versions,
  move httpx (TestClient only) to dev.
- Commit pixi.lock for reproducible installs; ignore .pixi/ and .pgdata/.

Verified: pixi run test -> 197 passed; local postgres migration creates
18 tables with the pgvector extension loaded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`pixi run dev` brings up the FastAPI backend (8000, SQLite default so it
works with no extra setup) and the Vite frontend (5173) together, after
auto-running fe-install. Open http://127.0.0.1:5173/ ; Ctrl+C stops both.

Verified: both services come up, the Vite /api proxy reaches the backend
(health 200), and both exit cleanly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- dev-pg: same full stack as `dev` but the backend points at local
  PostgreSQL via CLOSER_DATABASE_URL; depends on an idempotent pg-up.
- pg-up: init the cluster if missing and start it only if not already
  running (single redirect; deno_task_shell rejects `2>&1`).
- db-setup now reuses pg-up instead of pg-init+pg-start.

Verified: pg-up is idempotent across repeated runs; dev-pg brings up the
backend on postgres (health 200), the Vite frontend (200), the /api proxy
works, and Ctrl+C stops both cleanly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
README is now engineering-focused: one-line positioning, online demo,
a new Architecture Overview, the 8-skills table, a pixi-first quick start
(with manual fallback), verification, project structure, and a docs index.

- Move the product narrative (痛点 / 三层价值 / 商业价值+ROI / 为什么是
  Closer) to docs/PRODUCT_OVERVIEW.md instead of inlining it.
- Replace sections already covered in docs/ with links: end-to-end sample
  -> END_TO_END_EVIDENCE, Wave3 positioning -> WAVE3_SUBMISSION, production
  boundary -> PRODUCTION_RUNBOOK/ENVIRONMENT, cross-review -> action plan.
- Update quick start to the pixi workflow; refresh test count to 197.

README 318 -> 161 lines; all internal links verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Turn docs/ into a standalone, embeddable VitePress site and add the
documentation that was missing.

- VitePress scaffold (docs/package.json, .vitepress/config.mjs, index.md):
  audience-based nav/sidebar (Guide / Manual / API / Operations / Product)
  classifies existing docs in place — no file moves, no broken links.
  Competition/audit docs kept in repo but srcExcluded from the public site.
  base is env-configurable (DOCS_BASE) for standalone deploy or product-site
  subpath embedding.
- API docs: scripts/export_openapi.py dumps app.main:app schema to
  docs/public/openapi.json (pixi run openapi); api/reference renders it with
  Redoc; api/index is a hand-written auth/pagination/errors guide. Stays in
  sync with code.
- New dev docs (guide/getting-started, architecture, development) and user
  manual (manual/overview, workbench).
- pixi tasks: openapi, docs, docs-build, docs-preview.
- CI: deploy-pages.yml also exports OpenAPI, builds the docs site, and
  publishes it at /chengjiaoguan/docs/ alongside the demo.
- README slimmed (~160 -> ~95 lines): compact architecture, docs site
  pointer instead of the 14-row table, dropped the long production-boundary
  section. skills/README links to the docs site.
- .gitignore: docs build artifacts (dist, cache, generated openapi.json).

Verified: pixi run openapi (46 paths); pixi run docs-build succeeds and
srcExclude hides internal docs; DOCS_BASE switching works; pixi run test
197 passed; all README links resolve.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Online demo now points to https://61.29.254.154:94432 (full-stack);
  removed the old Ubuntu IP. GitHub Pages kept as a read-only backup.
- Now that docs/ carries the detail, trim README to a hub (95 -> 57 lines):
  drop the 8-skills table, verification block and project tree (all live in
  docs/), keep positioning, demo, compact architecture, quick start, and a
  docs index.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Foundation for "smart intake": classify every inbound message before an
inquiry is created, so noisy channels (email, future IM/platforms) don't
turn newsletters/notifications/internal mail into inquiries.

- app/services/inquiry_triage.py: TriageContext (channel-normalized, DB-free)
  -> TriageDecision (category + route + confidence + signals). Categories:
  inquiry / existing_customer / platform_bridge / notification / noise /
  uncertain; routes: create_inquiry / attach_customer / bridge_parse /
  triage_queue / archive. Clean channels (site_form) skip triage.
- RuleBasedTriageProvider: deterministic stage-0 prefilter (auto-reply, bulk
  /newsletter, bounce, internal/automated/platform sender) + stage-1 (known
  customer, spam/vendor-pitch, buying intent). Uncertain -> triage queue,
  never silently created or dropped.
- LLM-optional providers (http / openai-compatible) + get_triage_provider/
  _config, mirroring the graph decision provider pattern. triage_message
  falls back to rules if the LLM errors.
- InboundMessage gains an optional headers field (additive) so the email
  adapter can pass List-Id/Auto-Submitted/Precedence to triage later.

Verified: pixi run test -> 216 passed (was 197; +19 triage tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
M2: email inbound now passes the triage brain before an inquiry is created;
clean channels (site_form) and unmanaged channels (whatsapp) keep their
direct-ingest behavior.

- models.TriageItem + migrations/002_triage_item.sql: storage for the four
  buckets (inquiry-promoted / existing / triage queue / archive/bridge),
  with decision snapshot, confidence, signals, status (pending/confirmed/
  dismissed) and dedup by channel_message_id.
- app/services/inbound_intake.py: intake_message orchestrates triage ->
  create_inquiry (existing path) or land in a TriageItem bucket; dedup spans
  Message + TriageItem. confirm_triage_item promotes to an inquiry,
  dismiss_triage_item archives. Gate scoped to TRIAGE_CHANNELS = {email}.
- email_polling now calls intake_message and reports ingested vs triaged.
- app/routers/triage.py (+ main wiring): GET /api/v1/triage (bucket/status),
  POST /triage/{id}/confirm, POST /triage/{id}/dismiss.
- triage rule: a specific quantity now counts as buying intent (not only
  keywords), so real inquiries like "Need 1200 ... lamps" classify correctly.
- Updated email_polling/workers fixtures to a realistic external buyer domain
  (same-domain buyer was being flagged as internal mail).

Verified: pixi run test -> 226 passed; migration 002 applies on PostgreSQL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Faithfully port the .claude/Closer.html inline-React prototype into the
Vite app so the UI matches the design spec pixel-for-pixel.

- styles.css: full design system + 5 theme skins (aurora/midnight/
  graphite/sand/indigo)
- icons.jsx: line-stroke ICONS set + Icon component
- ui.jsx: design primitives (Grade/StatCard/Drawer/Modal/Ring/Toast…)
  + Logo (moved here to break the wizard→app cycle)
- theme.jsx: 5-theme switcher with live preview + localStorage persist
- pages/: Dashboard, Inbox (4-bucket + conversation + guardrail
  takeover), QuoteRules (3-tab workbench), Crm, Products, Settings,
  Sysconfig, Analytics, Conversation, Mobile prototype board, Wizard
- App.jsx: shell (sidebar + glass topbar + routing), with added nav
  entries for the mobile board and setup wizard so both are reachable
- main.jsx: mount App inside ToastHost
- remove superseded catalog.jsx / design_pages.jsx / forms.js

Pages render on the prototype's sample data as a baseline (full UI
without a backend); real API data overlay is a follow-up. Build is
green; all routes smoke-tested with zero console errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- topbar: remove fullscreen (大屏) button
- inbox: emphasize new customers (新客户 badge), mute old-customer marker;
  unify page header to eyebrow+h1+muted + tab strip
- quoting: unify page header to eyebrow+h1+muted + tab strip
- crm: add AI next-best-action card + structured preference profile to the
  drawer; relabel list to 客户价值 (distinct from inquiry A/B/C grade)
- products: add quote-ready badge + readiness meter; real new-product drawer
  and Excel import wizard (AI column mapping / dup check / cost confirm)
- mobile: reframe as interactive-first workbench (live phone + capability
  cards + QR), demote static screens to a collapsible design reference
- app: close customer drawer on route change
- sampleData: add customer prefs/nextAction/vtier and product priced fields

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- quoting/inbox: workbench & bucket containers used .row (align-items:center),
  which clipped content top/bottom under the taller header; switch to
  align-items:stretch so the split panels fill height
- mobile: drop "不是静态原型——右侧真机可点按" from the subtitle
- wizard: channel step now reuses the real channel catalog from Settings
  (11 channels in 4 groups, reply-capability badges, interactive toggles)
  instead of 4 hardcoded entries — single source of truth with 渠道接入

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- import customer RFQ wizard (upload → AI column mapping → product-library
  match → generate work item), researched standard RFQ columns
- new multi-line RfqQuotePanel: per-product quote source toggle
  (customer sheet price vs AI rule-based price), default = customer price
  when ≥ soft floor, global default switch
- soft-floor guardrail per line: below-floor customer price shown red,
  customer option disabled and forced to AI; hard-floor backend cutoff note
- totals, blended margin, source composition, send guardrail
- workbench list marks RFQ items (报价单 · N 项); sampleData adds rfq item

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- auth: branded Login screen with cross-border-trade background + feature
  showcase; guest mode (mock data, no persistence); session gate in App;
  UserMenu shows guest state + logout
- settings: 大模型配置 section (provider/model/key/base/temp/max-tokens/
  streaming), defaults to Claude Opus 4.8
- products: real product photos (public/products) mapped per SKU, card <img>
- docs: endpoints.md (63 endpoints, by domain) + gen_api_markdown.py;
  openapi.json kept in sync via scripts/export_openapi.py
- gitignore: ignore .claude (local design refs / plans)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This pull request is marked as a work in progress.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u main:gycherish-main
git switch gycherish-main

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff gycherish-main
git switch gycherish-main
git rebase main
git switch main
git merge --ff-only gycherish-main
git switch gycherish-main
git rebase main
git switch main
git merge --no-ff gycherish-main
git switch main
git merge --squash gycherish-main
git switch main
git merge --ff-only gycherish-main
git switch main
git merge gycherish-main
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cj66666/closer!9
No description provided.