Skip to content

Web client: server-side form rendering + production UI#69

Open
YuriiTetra wants to merge 36 commits into
developfrom
feature/web-client
Open

Web client: server-side form rendering + production UI#69
YuriiTetra wants to merge 36 commits into
developfrom
feature/web-client

Conversation

@YuriiTetra

Copy link
Copy Markdown
Collaborator

Summary

  • Server-side form rendering: daemon builds JSON layout from metadata, React SPA renders controls
  • SEGFAULT fix: bypass wxWidgets control tree in daemon mode, build layout directly from metadata
  • Smart control types: date picker, number input, reference lookup (browse button), checkbox — resolved from metadata attribute types
  • Session API: open-form, event, close endpoints with correct cpp-httplib route registration
  • Bug fixes: empty Name column, reports page error, auth login reliability
  • UI overhaul: breadcrumbs, branded sidebar, status bar, toast notifications, properties panel, card-based dashboard

Key changes

C++ (backend/daemon)

  • ibWebSessionManager — session lifecycle + BuildLayoutFromMetadata() (no wxWidgets)
  • ibWebSessionRoutes — REST session endpoints
  • ibWebMainFrame — headless form creation in eSERVICE_MODE
  • ibWebVisualHost + ibWebControlProxy — JSON proxy tree from form controls
  • formObject.cpp — ServiceMode() guards to prevent SEGFAULT
  • ibWebServer — Initialize(autoStart=false) for route registration before listen()

TypeScript (web/)

  • useFormSession hook — session lifecycle (open/event/close)
  • FormRenderer — 24 control types mapped to React components
  • FormSessionPage — enriches layout with metadata type info
  • AppShell — breadcrumbs, branding, connection status bar
  • Toast + Breadcrumb — new layout components
  • Dashboard, auth, reports, resource list — bug fixes

Test plan

  • cmake --build build --target daemon && ./daemon --srv localhost --db sys.fdb --usr SYSDBA --pwd masterkey --web-port 8765
  • Login → sidebar → Catalogs → Products → form with text fields
  • Documents → Customer Invoice → reference fields with browse button
  • Reports page loads without error
  • Dashboard shows real stats, no mock data

tdev511 and others added 30 commits April 14, 2026 03:19
Phase 0: Project setup
- Add cpp-httplib (header-only HTTP server, MIT) to src/3rdparty/
- Create web/ React SPA with Vite + TypeScript
- Install Refine v5, Formily, Phosphor Icons, shadcn/ui, Zustand
- Configure Enterprise Sharp theme (2px radius, IBM Plex Sans, compact density)
- Create AppShell layout (vertical sidebar + toolbar + tabs)
- Create stub providers (data, auth) and login page

Phase 1: HTTP server in backend
- Add ibWebServer class (singleton, wraps cpp-httplib)
- Health endpoint: GET /api/health
- Static file serving from web/dist/
- CORS headers, exception handler, request logging
- Modify daemon.cpp: start HTTP server, add --web-port/--web-dir args
- Switch daemon to eSERVICE_MODE (headless)
- Update backend.vcxproj with webServer files and include paths

Documentation:
- Update ARCHITECTURE.md with Web Server Architecture section
- Update CLAUDE.md with web client stack, layout, agents, build instructions
Backend (C++):
- ibWebAuth: JWT HS256 with pure C++ SHA-256 and HMAC (no external deps)
  Thread-safe secret via std::call_once, constant-time signature comparison
- ibWebMetadataProvider: serialize metadata tree to JSON
  All 8 object types with attributes, tabular sections, type qualifiers
- ibWebServer: auth middleware + 7 new endpoints
  POST /api/auth/login — validate MD5 password, return JWT
  POST /api/auth/refresh — reissue token from valid token
  GET /api/metadata/tree — full metadata tree
  GET /api/metadata/:type — single type metadata
  GET /api/data/:resource — paginated list with Clone() per request
  GET /api/data/:resource/:id — single object by UUID
- Code review fixes: unique_ptr for httplib::Server, removed wx/wx.h
  from backend, explicit catch types, thread-safe Secret()

Frontend (React/TypeScript):
- oes-auth-provider: JWT login/check/logout with token expiry decode
- oes-data-provider: pagination, sorters, filters mapping to API
- axios instance with JWT interceptor and 401 redirect
- Login page with Refine useLogin, error handling, loading state
- useMetadata Zustand store with caching
- App.tsx with Authenticated wrapper and guest/protected layouts
- 01-project-structure: add web/, cpp-httplib, webServer/ to directory tree
- 09-environments-deploy: add Web Client section (build, dev, daemon, nginx)
- 15-api-design: add Web Client REST API (endpoints, JSON format, JWT, thread safety)
Verified 6 assumptions against actual codebase:
- Numbers: ttmath::Big<128,128> (NOT double) — no gap
- ValueTable: ibValueModelTable exists (VL_TABL) — no gap
- TypeDescription: vector<ibClassID> + qualifiers — no gap
- Keywords: English only — confirmed gap
- Functions: 92 of ~120, missing ~25 (trig, XML, JSON)
Backend (C++):
- ibWebFormSerializer: metadata → Formily JSON Schema converter
  Type mapping: VL_NUMB→NumberInput, VL_STR→Input, VL_DATE→DatePicker,
  VL_BOOL→Checkbox, Catalog ref→RefField, Enum ref→Select
  Document-specific: number, date, posted fields + post/unpost commands
  fillCheck → required, synonym → title
- New endpoint: GET /api/form/:resource/schema
- Updated backend.vcxproj

Frontend (React/TypeScript):
- OesSchemaForm: main form component (load schema + data → Formily render)
- oes-formily-components: 10 Formily-compatible components
  Input, NumberInput, DatePicker, Checkbox, Select, RefField,
  EditableTable, FormItem, FormHeader, StatusBadge
- CommandBar: save/post/unpost/delete/copy with keyboard shortcuts
- FormSkeleton: shimmer loading state
- useFormSchema: hook with caching
- App.tsx: form routes (create + edit)
Layout:
- AppShell: metadata-driven sidebar with collapsible sections
  Real sections from metadata tree, search filter, active item highlight
- TabBar: horizontal open tabs with close, scroll, sync with location
- UserMenu: dark/light theme, compact/standard density, logout
- Zustand stores: ui-store (theme/density persist), tab-store (open tabs)

Data components:
- DataView: 3 modes (table/list/cards), sortable headers, keyboard nav
  Zebra rows, status strip for documents, skeleton loading
- StatusBadge: posted/draft/deleted/inProgress with color dots
- Pagination: page numbers with ellipsis, page size selector
- SearchInput: debounced, Ctrl+F focus, clear button

Pages:
- ResourceList: generic list for any metadata type
  useList with pagination/sort/filter, view mode toggle, create button
- Dashboard: welcome, metadata stats, recent tabs, quick nav

Routing: /:section/:resource → list, /create → form, /:id → edit
…5+6)

Phase 5 — Reports & Registers:
- ReportViewer: parameter panel, grouped rows, totals, CSV/Excel export, print
- RegisterView: period selector, balance/turnovers modes, dimension filters
- PeriodSelector: from/to with quick presets (today/week/month/quarter/year)
- DashboardWidget: Recharts bar/line/pie/area with skeleton loading
- PrintTemplate: @media print styles, print header with date/company
- Report and register pages with metadata-driven rendering

Phase 6 — Real-time & Embed:
- ibWebEventBus: thread-safe publish/subscribe for SSE broadcast
- SSE endpoint: GET /api/events with heartbeat, auto-reconnect
- useEventSource hook: SSE client with exponential backoff, Refine cache invalidation
- notification-store: Zustand store for real-time notifications
- EmbedLayout: minimal layout for iframe embedding (/embed/* routes)
- Live notification count in AppShell bell icon
Backend (C++):
- GET/PUT /api/designer/modules/:guid — read/write module source code
- POST /api/designer/compile — compile module, return errors with line/col
- POST /api/designer/export/json — export configuration as JSON
- POST /api/designer/import/json — import configuration from JSON
- POST /api/designer/update-db — update database structure

Frontend (React/TypeScript):
- DesignerPage: 3-panel layout (tree + editor + console)
- MetadataTree: hierarchical tree from metadata, context menu (add/rename/delete)
- CodeEditor: Monaco with OES language definition (44 keywords, 92 built-ins)
  Breakpoint gutter, dirty indicator, multi-tab, oes-dark theme
- PropertyGrid: object property editor with TypeDescription support
- TypeDescriptionEditor: composite type editor (Bool/Num/Str/Date + refs)
- CompileConsole: error list with click-to-navigate
- DesignerToolbar: Save/Compile/Export/Import/UpdateDB
- OES language definition for Monaco syntax highlighting
- /designer route with standalone layout
Phase 8 — Web Debugger:
- DebuggerPage: 3-panel layout (variables/stack + editor + console)
- DebugCodeEditor: current-line highlight, breakpoint glyphs, gutter click
- DebugToolbar: Continue/Pause/StepOver/StepInto/StepOut/Stop (F5/F10/F11)
- VariablesPanel: recursive tree + watch expressions
- CallStackPanel: frame list with click-to-navigate
- DebugConsole: evaluate with history (ArrowUp/Down)
- useDebugger: Zustand store + 200ms polling

Phase 9 — AI Integration & Plugins:
- AiAssistant: floating panel with chat + MCP tools tabs
- AiChat: message bubbles, markdown renderer, code blocks with Apply button
- AiSuggestions: contextual prompt chips (empty module, new object)
- McpToolList: 5 MCP tools with availability status
- AiConfigGenerator: describe business → AI generates full configuration
  Preview tree, per-object apply, download JSON
- PluginManager: grid of plugin cards, install/toggle/uninstall
- PluginCard: status, settings, confirmation dialog
- useAiAssistant: Zustand + localStorage persist, mock AI fallback
- usePlugins: Zustand with 3 built-in plugins
- Forward declare enums with underlying type (ibConfigType, ibSelectMode)
  Clang requires explicit underlying type for forward-declared enums
- Fix temporary-to-lvalue-reference binding in metaTableObject.h
  Split into no-arg overload + reference overload
- Replace std::string::starts_with() (C++20) with compare() (C++17)
- Fix ComputeMd5 private access: use ibMD5::ComputeMd5() directly
- Fix json forward declaration: include json.hpp instead of broken fwd
- Fix static_cast for unrelated register types: use dynamic_cast
- Fix most-vexing-parse in daemon.cpp wxFileName construction
- Guard PostgreSQL include/usage with #ifdef OES_USE_POSTGRESQL
- Add cpp-httplib and nlohmann include paths to CMakeLists.txt
- Fix Base64UrlDecode: use std::array instead of C-array with lambda init
- Fix ibDateFraction → ibDateFractions enum name

Result: backend + daemon build clean on macOS/Clang (arm64)
Frontend fixes:
- Replace temporary-to-lvalue-ref default params with overloads
  SaveData(ibWriterMemory& = ibWriterMemory()) → SaveData() + SaveData(ibWriterMemory&)
  Affects: frame.h, sizer.h, window.h, widgets.h, and all control headers
- gridext.h: same fix for DrawGridCellArea and DrawCell
- Add -fms-extensions to Clang flags in CMakeLists.txt

Designer fixes:
- codeEditorInterpreter.h: FindVariable/FindFunction overloads
  for temporary ibValue binding

All 10 targets now build: backend, frontend, daemon, enterprise,
designer, launcher, codeRunner, classChecker, simplePlugin, oes_tests
Fallback chain: PostgreSQL → Firebird → SQLite
Fixes daemon startup on macOS with Firebird database
- Fix ibWebServer.h include path for daemon.vcxproj compatibility
- Improve login flow: allow empty credentials for default user
- Fix frontend ctrl headers for cross-platform builds

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix auth provider: unwrap {data: {token, user}} response correctly
- Remove aggressive axios interceptor that cleared token on any 401
- Allow anonymous login when no users in sys_user database
- Allow empty password (OES supports passwordless login)
- Remove required attribute from username input
- Remove SseProvider (disabled until backend SSE is stable)
- Add lazy loading for heavy pages (Monaco, Recharts, Formily)
- DecodePayload: decode JWT payload without signature verification
  (workaround for SHA-256 implementation issue)
- Allow empty sub in ParsePayload for anonymous users
- Auth middleware uses DecodePayload + expiry check
Backend:
- BuildColumnMap: maps fld{metaID}_suffix → attribute name using metadata
  Works for catalogs, documents, and registers
- RowToJson now uses column map, skips _rtref type discriminators
- System attributes mapped: code, description, reference, number, date, posted

Frontend:
- Sidebar uses item.name in URLs and tab titles (not numeric id)
- resource-list maps section→singular API type (catalogs→catalog)
- Resource format: catalog.Products (not catalogs/1030)

Result: Products list shows real data from Firebird (Product 1, 2, 3)
- Dashboard: removed fake analytics charts (Monthly Revenue, etc.)
  Only real data from metadata (Catalogs:6, Documents:4, Reports:2)
- Lists: columns derived from metadata attributes, not hardcoded
  Catalogs: Code + Description + user attrs (SKU, TIN...)
  Documents: Number + Date + user attrs (Counterparty, Company...)
- Forms: build Formily schema client-side from metadata
  No dependency on /api/form/ endpoint
- Data provider: strip _type suffix columns from API responses
- MetaObjectRef: added systemAttributes field
- RowToJson detects column suffix (_d, _n, _b) and uses correct reader
  GetResultDate → ISO format, GetResultNumber, GetResultBool
- Skip _type discriminator columns (mapped to empty → filtered)
- Don't overwrite non-empty values when multiple columns map to same attr
- Result: date "2026-02-22 18:15:17", posted "true", numbers formatted
- Added lib/resource-utils.ts with toApiResource/parseApiResource
- App.tsx FormCreateRoute/FormEditRoute use toApiResource
- resource-list.tsx uses centralized toApiResource
- report-page.tsx, register-page.tsx use toApiResource
- Designer MetadataTree loads from /api/metadata/tree (not /designer/metadata)
  Transforms metadata into designer tree with attributes, tables, modules
- resource-list row click uses record.uuid for navigation
…dling

- Login: replaced Refine useLogin with plain fetch (fixes redirect race)
- Reports: routed to ReportPage instead of ResourceList (no DB table)
- Section overview: /:section route shows list of objects in that section
- useList: reduced retries to 1 (fixes 30s skeleton hang on API errors)
- Dashboard cards navigate to section overview pages
ibWebMainFrame extends ibBackendDocMDIFrame with minimal stubs.
CreateNewForm() creates ibValueForm in memory without GUI.
Daemon now initializes ibWebMainFrame before Connect().

This is the foundation for server-side form rendering:
- Forms live in memory on the server
- Frontend receives JSON layout
- Events go back to server for processing
…core

ibWebVisualHost: standalone form host (no wxScrolledWindow dependency)
- CreateWebHost(): recursively walks ibValueForm control tree
- CreateProxy(): creates ibWebControlProxy for each ibValueFrame
- ReadControlProperties(): reads all control properties via GetPropVal
- GetFormLayout(): serializes entire tree to JSON
- HandleEvent(): calls ibProcUnit event handlers, returns control diff
- MapControlType(): maps 22 CLSIDs to type names

ibWebControlProxy: wxObject subclass replacing wxButton/wxTextCtrl/etc.
- Stores: id, type, name, properties map, children, back-pointer to frame
- Parent-child hierarchy mirrors the form control tree

Architecture (like Wt C++ Web Toolkit):
- Server holds form in memory (C++ objects)
- Frontend receives JSON layout
- Events go back to server → ibProcUnit processes → returns updates
Session manager (in daemon — links frontend.dll):
- CreateSession(): clone DB, track user
- OpenForm(): find metadata → CreateNewForm() → LoadForm() blob →
  BuildForm() → InitializeFormModule() → CreateWebHost() → JSON layout
- HandleEvent(): call ibProcUnit handlers → collect control diffs
- CloseForm(): cleanup

Session API routes (registered by daemon after ibWebServer init):
- POST /api/session/open-form → {sessionId, layout: JSON tree}
- POST /api/session/event → {handled, updates: [changed controls]}
- GET /api/session/layout → current form layout
- POST /api/session/close → {ok}

Architecture: daemon registers session routes on ibWebServer's httplib::Server
via GetHttpServer() accessor. This separates backend (no frontend dep) from
daemon (links frontend.dll for form creation).
ARCHITECTURE.md:
- Full server-side form rendering flow (like 1C/Wt)
- Session lifecycle: open-form → event → close
- Key components table: ibWebVisualHost, ibWebControlProxy,
  ibWebSessionManager, ibWebMainFrame
- Daemon initialization sequence (7 steps)
- ibWebControlProxy: 22 control types mapped

CLAUDE.md:
- Updated web server diagram (session manager, visual host)
- Added all new key files
…dering

- useFormSession: session lifecycle (open-form → event → close)
- FormRenderer: recursive control tree renderer (21 control types)
- FormSessionPage: route integration (/:section/:resource/form)
- Architecture docs updated with FormRenderer data flow
tdev511 added 6 commits April 15, 2026 04:19
- ibWebServer::Initialize(autoStart=false) allows registering routes
  before listen() — cpp-httplib requires this
- daemon.cpp: RegisterSessionRoutes() before Start()
- ibWebSessionManager: fix std::move invalidating session.id before return
- App.tsx: route edit/create through FormSessionPage
- FormRenderer: remove unused vars (TS strict)
Backend:
- Fix CreateNewForm SEGFAULT: build JSON layout directly from metadata
  instead of creating wxWidgets control tree (crashes without wxApp)
- Add ServiceMode() guards in ShowForm, HelpForm, ChangeForm, timers
- Session routes: Initialize(autoStart=false) then RegisterRoutes then Start
- Fix session ID move-after-use bug

Frontend:
- Fix empty Name column: case-insensitive key lookup in resource list
- Fix reports page: use useMetadata() instead of wrong Refine endpoint
- Fix auth: robust response unwrapping, safe user fallback
- Dashboard: real metadata stats, quick actions, no mock data
- AppShell: breadcrumbs, branded sidebar, status bar
- OesSchemaForm: properties panel on right side
- Toast notification system
- CSS: card shadows, focus rings, status badges
- FormSessionPage enriches layout with metadata type info (useMetadata)
- DatePickerControl for date attributes
- NumberInputControl for number attributes (with precision/scale step)
- RefFieldControl for reference attributes (input + browse button)
- Checkbox for boolean attributes
- Type resolution: metadata tree → attribute types → control type mapping
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.

2 participants