Skip to content

React SPA cutover: replace EJS views with /api/v1 + React 19 client#319

Merged
cmyers-mieweb merged 28 commits into
mainfrom
feature/react-ui
May 29, 2026
Merged

React SPA cutover: replace EJS views with /api/v1 + React 19 client#319
cmyers-mieweb merged 28 commits into
mainfrom
feature/react-ui

Conversation

@wreiske
Copy link
Copy Markdown
Member

@wreiske wreiske commented May 19, 2026

Summary

This branch cuts the create-a-container app over from server-rendered EJS views to a React 19 SPA backed by a new versioned JSON API at /api/v1. Every legacy resource page has a React equivalent and the EJS view layer has been removed.

Highlights

Backend — /api/v1 JSON API

  • New routers under create-a-container/routers/api/v1/ for auth, users, groups, sites, nodes, containers, external-domains, jobs, settings, and api-keys
  • CSRF, session, and API-key auth wired through new middlewares/api.js
  • OpenAPI spec at openapi.v1.yaml
  • Legacy EJS routers and views removed
  • A few migrations adjusted to match the new constraints (site-scoped containers, nullable external-domain site, removal of node-name unique index)

Frontend — React 19 + Vite + Tailwind 4 SPA

  • Scaffolded with @mieweb/ui, React Router 7, TanStack Query, react-hook-form + zod
  • Pages for every resource: containers, nodes, sites, groups, users, api keys, external domains, jobs, settings, plus full auth flow (login/register/reset)
  • Shared FormPageHeader / FormPageLayout components and useDocumentTitle hook for consistent create/edit pages
  • Two-panel marketing auth layout with mobile fallback
  • Responsive list/table action rows
  • Vite dev proxy scoped to /api/* so client routes like /apikeys aren't intercepted

Other

  • Legacy EJS screenshots stashed under .attic/ for reference

Verification

  • npm run type-check (client) — clean
  • npm run build (client) — succeeds
  • node --check across server.js, routers/api/v1/*.js, and middlewares/api.js — clean

Commits

  • feat(client): scaffold React 19 + Tailwind 4 + @mieweb/ui SPA
  • feat(api): add /api/v1 JSON API + CSRF + OpenAPI spec
  • feat(client): SPA shell + auth flows on /api/v1
  • feat(client): Phase 4 — feature pages for all resources
  • feat: cutover from EJS to React SPA
  • fix(dev): SPA boots end-to-end on sqlite
  • fix(client): align top bar with sidebar header, polish footer, fix mobile
  • feat(client): polish auth layout, form scaffolding, and responsive UI
Screenshot 2026-05-19 at 12 07 09 AM Screenshot 2026-05-19 at 12 17 02 AM Screenshot 2026-05-19 at 12 28 24 AM Screenshot 2026-05-19 at 12 28 31 AM Screenshot 2026-05-19 at 12 28 35 AM Screenshot 2026-05-19 at 12 28 00 AM Screenshot 2026-05-19 at 12 28 10 AM

wreiske added 10 commits May 14, 2026 16:30
Phase 1 of manager UI rewrite. Creates create-a-container/client/ with:

- Vite 6 + React 19 + TypeScript
- Tailwind CSS 4 via @tailwindcss/vite
- @mieweb/ui 0.6.1 with BlueHive brand CSS
- React Router 7 (data routers) with full route tree placeholder
- TanStack Query 5, react-hook-form + zod, lucide-react

Adds client:* scripts to create-a-container/package.json.
- New middlewares/api.js: apiAuth (session OR Bearer API key),
  apiAdmin, asyncHandler, ApiError, jsonErrorHandler, csrfGuard.
- CSRF: csrf-csrf double-submit, exempts Bearer requests.
- Routers under /api/v1: auth (login w/ 2FA push, logout, register,
  password reset), sites, sites/:id/containers (CRUD + metadata),
  sites/:id/nodes (CRUD + Proxmox import + storages),
  external-domains, groups, users (+invite), apikeys, settings, jobs
  (incl. SSE stream).
- openapi.v1.yaml exposed via /api/v1/openapi.{json,yaml}.
- Mounted in server.js before legacy EJS routes.
- Legacy /apikeys, /sites, /jobs etc. remain functional.
- lib/api.ts: typed JSON client with credential cookies, CSRF
  double-submit (lazy fetch + retry on 403), { data }/{ error }
  envelope, 401 hook for redirect-to-login.
- lib/auth.ts: useSession query + login mutation w/ 2FA challenge
  support + logout (clears CSRF, resets query cache).
- providers.tsx: ThemeProvider, ToastProvider, SidebarProvider,
  CommandPaletteProvider.
- AppLayout: @mieweb/ui Sidebar + AppHeader + CommandPalette shell.
- AuthLayout: branded auth shell.
- RequireAuth: route guard, redirects to /login w/ redirect param.
- Sidebar/Header components composed from @mieweb/ui primitives.
- Auth pages: LoginPage (w/ 2FA push polling),
  RegisterPage (supports invite tokens), RegisterSuccessPage
  (w/ 2FA QR code), ResetPasswordRequestPage, ResetPasswordPage.
- Remove all legacy EJS routers and views (login, register, verify, users, groups, sites, external-domains, jobs, settings, apikeys, reset-password, nodes, containers)
- Extract nginx-conf and dnsmasq template endpoints to routers/templates.js
- Mount client/dist as static + SPA fallback for non-API, non-template routes
- Drop connect-flash, method-override deps (no longer needed)
- Keep views/nginx-conf.ejs and views/dnsmasq/* (server-side configuration templates)
- middlewares/api.js: drop __Host- prefix off-prod (requires Secure) and only
  bind CSRF token to session id once a user is signed in; saveUninitialized:
  false handed out a fresh session id per anon request, breaking double-submit.
- client/vite.config.ts: drop /login, /logout, /nginx-conf, /dnsmasq proxies
  now that those are SPA routes; /api is the only backend proxy.
- migrations: make 3 postgres-first migrations sqlite-compatible
  (guard undefined fk.constraintName, tolerate missing named constraints,
  rewrite UPDATE...FROM as scalar subquery, tolerate re-added columns).
- package.json: add sqlite3 as a real dependency.
…bile

- AppLayout: switch to h-screen overflow-hidden shell with internal main
  scroll so the sidebar can't scroll out of view on long pages.
- Sidebar: pin SidebarHeader to h-16 (was 65px from py-4) so its bottom
  border matches the AppHeader bottom border to the pixel; rebuild the
  footer as a single user card (avatar + name + role + icon sign-out)
  with a top border so it visually anchors the bottom of the sidebar.
- Header: drop the duplicate Container Manager brand on desktop (the
  sidebar already shows it); render a plain span only on mobile where
  the sidebar is collapsed off-canvas (AppHeaderBrand is hidden below
  md by @mieweb/ui so it can't be used there).
- Redesign AuthLayout with two-panel marketing/form layout and mobile header
- Add FormPageHeader and FormPageLayout shared components for create/edit pages
- Add useDocumentTitle hook and apply consistent titles across pages
- Make list/table action rows wrap on narrow viewports
- Tighten Sidebar: remove redundant Containers entry, use Button for logout
- Scope Vite dev proxy to /api/* so client routes (e.g. /apikeys) aren't proxied
- Stash legacy EJS screenshots under .attic/ for reference
# Conflicts:
#	create-a-container/routers/containers.js
#	create-a-container/routers/external-domains.js
#	create-a-container/routers/groups.js
#	create-a-container/routers/login.js
#	create-a-container/routers/nodes.js
#	create-a-container/routers/register.js
#	create-a-container/routers/sites.js
#	create-a-container/routers/users.js
#	create-a-container/views/layouts/header.ejs
#	create-a-container/views/login.ejs
#	create-a-container/views/users/index.ejs
- POST /api/v1/auth/dev: one-click dev login (admin/user) when NODE_ENV != production
- GET /api/v1/health: now returns isDev flag
- GET /api/v1/session: admins also receive pushNotificationUrl
- POST /api/v1/users/email-all: broadcast email via existing sendBulkEmail util
- LoginPage: dev-mode login buttons gated on /health.isDev
- UsersListPage: 'Email all' action + modal (subject + message)
- Sidebar: external 'MFA Admin' link rendered for admins when push URL configured
@wreiske
Copy link
Copy Markdown
Member Author

wreiske commented May 19, 2026

Merged latest main and ported every feature that landed inside the now-deleted EJS code to the new /api/v1 + React SPA. Branch is now at parity with main.

Ported in 97190e4:

  • One-click dev login (feat: add one-click dev login buttons #304) — POST /api/v1/auth/dev (404 in production) + Login page buttons gated on GET /api/v1/healthisDev.
  • Email All (Add 'Email All' feature for users #306) — POST /api/v1/users/email-all using the existing sendBulkEmail (BCC + smtp_noreply_address) + new EmailAllModal wired into the Users list with a live recipient count.
  • MFA Admin link ([Bug]: MIEAuth issue #309) — GET /api/v1/session now returns pushNotificationUrl for admins; Sidebar renders an external link to `${pushNotificationUrl}/admin` when configured.

Already covered, no port needed:

  • Surface better errors on database constraint failures #310 plain-english SQL validation errors — structurally superseded by `jsonErrorHandler` returning HTTP 422 with per-field `fields` on `SequelizeValidationError`/`SequelizeUniqueConstraintError`.
  • CSS issue #311 sidebar collapse fix — the new `@mieweb/ui` `Sidebar` handles responsive collapse natively.
  • Clean up manager logs #313 dedicated access log, Debian env filepath, SMTP no-reply enforcement / BCC bulk batching — auto-merged cleanly into `server.js`, `systemd/`, and `utils/email.js`.

Typecheck + Vite build are clean. `git diff origin/main..HEAD` now shows only the expected EJS deletes plus the SPA + JSON API surface.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR completes the cutover of create-a-container from server-rendered EJS pages to a React SPA, backed by a new versioned JSON API under /api/v1, while preserving a small EJS-rendered “templates” surface for nginx/dnsmasq config generation.

Changes:

  • Replaces legacy page routers/views with /api/v1/* JSON routers (session + API-key auth, CSRF, consistent JSON envelopes, and error handling).
  • Adds a React 19 + Vite + Tailwind SPA client and serves the compiled app from Express for non-API routes.
  • Updates migrations and supporting server wiring to match the new SPA/API architecture.

Reviewed changes

Copilot reviewed 109 out of 116 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
create-a-container/views/users/invite.ejs Removed legacy EJS “invite user” view.
create-a-container/views/users/index.ejs Removed legacy EJS users list page.
create-a-container/views/users/form.ejs Removed legacy EJS users create/edit form.
create-a-container/views/sites/index.ejs Removed legacy EJS sites list page.
create-a-container/views/sites/form.ejs Removed legacy EJS sites create/edit form.
create-a-container/views/reset-password/reset.ejs Removed legacy EJS reset-password “set new password” view.
create-a-container/views/reset-password/request.ejs Removed legacy EJS reset-password request view.
create-a-container/views/register.ejs Removed legacy EJS registration view.
create-a-container/views/register-success.ejs Removed legacy EJS registration success/QR view.
create-a-container/views/nodes/index.ejs Removed legacy EJS nodes list view.
create-a-container/views/nodes/import.ejs Removed legacy EJS nodes import view.
create-a-container/views/login.ejs Removed legacy EJS login view.
create-a-container/views/layouts/footer.ejs Removed legacy EJS footer partial (version/issue link + bootstrap).
create-a-container/views/groups/index.ejs Removed legacy EJS groups list view.
create-a-container/views/groups/form.ejs Removed legacy EJS groups create/edit form.
create-a-container/views/external-domains/index.ejs Removed legacy EJS external domains list view.
create-a-container/views/apikeys/show.ejs Removed legacy EJS API key detail view.
create-a-container/views/apikeys/index.ejs Removed legacy EJS API keys list view.
create-a-container/views/apikeys/form.ejs Removed legacy EJS API key create form.
create-a-container/views/apikeys/created.ejs Removed legacy EJS “API key created” one-time display view.
create-a-container/server.js Switches to mounting /api/v1, templates router, and SPA static serving.
create-a-container/routers/verify.js Removed legacy nginx auth_request verification route.
create-a-container/routers/templates.js Adds EJS template endpoints for nginx/dnsmasq file generation.
create-a-container/routers/settings.js Removed legacy settings page router (replaced by API).
create-a-container/routers/reset-password.js Removed legacy reset-password router (replaced by API).
create-a-container/routers/register.js Removed legacy register router (replaced by API).
create-a-container/routers/login.js Removed legacy login router (replaced by API).
create-a-container/routers/groups.js Removed legacy groups CRUD router (replaced by API).
create-a-container/routers/external-domains.js Removed legacy external domains CRUD router (replaced by API).
create-a-container/routers/apikeys.js Removed legacy API keys router (replaced by API).
create-a-container/routers/api/v1/index.js Adds /api/v1 mount with CSRF token endpoint, OpenAPI endpoints, session endpoint, and sub-routers.
create-a-container/routers/api/v1/sites.js Adds /api/v1/sites CRUD + nested mounts for site-scoped resources.
create-a-container/routers/api/v1/settings.js Adds admin-only /api/v1/settings read/update endpoints.
create-a-container/routers/api/v1/jobs.js Adds /api/v1/jobs read + status pagination + SSE stream endpoints.
create-a-container/routers/api/v1/groups.js Adds admin-only /api/v1/groups CRUD endpoints.
create-a-container/routers/api/v1/external-domains.js Adds admin-only /api/v1/external-domains CRUD; keeps Cloudflare key write-only.
create-a-container/routers/api/v1/apikeys.js Adds per-user /api/v1/apikeys CRUD with one-time plaintext key return on create.
create-a-container/middlewares/api.js Introduces API auth (session + bearer key), CSRF guard, JSON envelopes, and error handler.
create-a-container/package.json Adds client helper scripts and new server dependencies for SPA/API support.
create-a-container/migrations/20260218000001-container-site-scoped-constraints.js Makes migration more idempotent and adjusts backfill SQL.
create-a-container/migrations/20260218000000-remove-node-name-unique.js Makes constraint removal tolerant of DB differences/idempotent.
create-a-container/migrations/20260217000000-make-external-domain-site-id-nullable.js Safeguards FK constraint removal when constraint name is missing.
create-a-container/client/vite.config.ts Adds Vite config with /api-scoped dev proxy and build output to dist/.
create-a-container/client/tsconfig.node.json Adds TS config for Vite config/type-checking.
create-a-container/client/tsconfig.json Adds TS project references.
create-a-container/client/tsconfig.app.json Adds strict TS config for the SPA app sources.
create-a-container/client/src/vite-env.d.ts Adds Vite client types reference.
create-a-container/client/src/styles/index.css Adds Tailwind + @mieweb/ui brand styles entry.
create-a-container/client/src/pages/users/UsersListPage.tsx Adds SPA users list UI (actions, delete, email-all modal integration).
create-a-container/client/src/pages/users/InviteUserPage.tsx Adds SPA invite-user form page.
create-a-container/client/src/pages/users/EmailAllModal.tsx Adds “email all users” modal + API integration.
create-a-container/client/src/pages/sites/SitesListPage.tsx Adds SPA sites list UI with admin-only creation/deletion actions.
create-a-container/client/src/pages/sites/SiteFormPage.tsx Adds SPA site create/edit form.
create-a-container/client/src/pages/settings/SettingsPage.tsx Adds SPA settings page with env var field arrays and admin-only settings edits.
create-a-container/client/src/pages/NotFoundPage.tsx Adds SPA 404 page.
create-a-container/client/src/pages/nodes/NodesListPage.tsx Adds SPA nodes list UI for a site.
create-a-container/client/src/pages/nodes/NodeImportPage.tsx Adds SPA Proxmox node import form.
create-a-container/client/src/pages/jobs/JobDetailPage.tsx Adds SPA job detail + log streaming UI via SSE.
create-a-container/client/src/pages/groups/GroupsListPage.tsx Adds SPA groups list UI.
create-a-container/client/src/pages/groups/GroupFormPage.tsx Adds SPA group create/edit form.
create-a-container/client/src/pages/external-domains/ExternalDomainsListPage.tsx Adds SPA external domains list UI.
create-a-container/client/src/pages/containers/ContainersListPage.tsx Adds SPA containers list UI (site-scoped) with status badges and links.
create-a-container/client/src/pages/auth/ResetPasswordRequestPage.tsx Adds SPA reset-password request page.
create-a-container/client/src/pages/auth/ResetPasswordPage.tsx Adds SPA reset-password form with token validation.
create-a-container/client/src/pages/auth/RegisterSuccessPage.tsx Adds SPA registration success page with optional 2FA enrollment QR.
create-a-container/client/src/pages/apikeys/ApiKeysListPage.tsx Adds SPA API keys list + one-time key display + revoke UI.
create-a-container/client/src/main.tsx Adds SPA entrypoint with TanStack Query client setup and router mounting.
create-a-container/client/src/lib/useDocumentTitle.ts Adds a small hook to manage page titles.
create-a-container/client/src/lib/types.ts Adds typed models matching /api/v1 serializers.
create-a-container/client/src/lib/toast.ts Adds centralized API error → toast helper.
create-a-container/client/src/lib/queries.ts Adds shared TanStack Query keys and fetchers for /api/v1 resources.
create-a-container/client/src/lib/auth.ts Adds session + login/logout + dev-login + 2FA challenge support helpers.
create-a-container/client/src/lib/api.ts Adds typed fetch wrapper (JSON envelopes, CSRF token handling, 401 handling).
create-a-container/client/src/components/FormPageLayout.tsx Adds shared create/edit form scaffold layout component.
create-a-container/client/src/components/FormPageHeader.tsx Adds shared form header component.
create-a-container/client/src/app/Sidebar.tsx Adds SPA sidebar navigation + logout action.
create-a-container/client/src/app/router.tsx Adds SPA route map (auth + protected app routes).
create-a-container/client/src/app/RequireAuth.tsx Adds authenticated-route guard around app routes.
create-a-container/client/src/app/providers.tsx Adds provider composition for theme/toast/sidebar/command palette.
create-a-container/client/src/app/PlaceholderPage.tsx Adds a placeholder page component (currently unused).
create-a-container/client/src/app/Header.tsx Adds top header with search/theme toggle/user menu.
create-a-container/client/src/app/AuthLayout.tsx Adds marketing-style two-panel auth layout with mobile fallback.
create-a-container/client/src/app/AppLayout.tsx Adds main app layout shell (sidebar + header + outlet).
create-a-container/client/README.md Adds SPA client dev/build instructions.
create-a-container/client/package.json Adds client dependencies/scripts for React/Vite/Tailwind app.
create-a-container/client/index.html Adds Vite HTML entrypoint.
create-a-container/client/.gitignore Adds client-local ignore rules (dist, node_modules, etc.).
.gitignore Ignores .tmp-verify/.
Files not reviewed (2)
  • create-a-container/client/package-lock.json: Language not supported
  • create-a-container/package-lock.json: Language not supported

Comment thread create-a-container/client/src/pages/users/UsersListPage.tsx
Comment thread create-a-container/client/src/app/Sidebar.tsx
Comment thread create-a-container/routers/templates.js
Comment thread create-a-container/server.js
Comment thread create-a-container/client/README.md
Comment thread create-a-container/client/src/app/PlaceholderPage.tsx Outdated
Comment thread create-a-container/client/src/lib/toast.ts Outdated
Comment thread create-a-container/middlewares/api.js Outdated
wreiske added 2 commits May 19, 2026 00:37
- UsersListPage: render EmailAllModal once at page root (not nested per row)
- Sidebar: import ReactNode type instead of using React.ReactNode namespace
- templates.js: return 404 when site not found in nginx route
- server.js: load openapi.v1.yaml for /api Swagger UI
- client/README.md: correct dev proxy description (only /api/*)
- Remove unreferenced PlaceholderPage.tsx
- Remove unused useApiErrorToast helper (lib/toast.ts)
- middlewares/api.js: throw on missing CSRF secret in production
Copy link
Copy Markdown
Collaborator

@runleveldev runleveldev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theres more but Im hitting limits of GH API. This PR is so big I had to review from the CLI

Comment thread create-a-container/routers/templates.js Outdated
Comment thread create-a-container/package.json Outdated
Comment thread create-a-container/client/package.json
Comment thread create-a-container/client/src/app/Sidebar.tsx
Comment thread create-a-container/client/src/pages/nodes/NodeImportPage.tsx Outdated
Comment thread create-a-container/client/src/app/Sidebar.tsx
Comment thread create-a-container/client/src/pages/apikeys/ApiKeysListPage.tsx Outdated
Comment thread create-a-container/client/src/pages/auth/LoginPage.tsx
Comment thread create-a-container/routers/api/v1/auth.js Outdated
Comment thread create-a-container/server.js
Comment thread create-a-container/client/src/pages/containers/ContainersListPage.tsx Outdated
Comment thread create-a-container/client/src/pages/sites/SiteFormPage.tsx Outdated
Comment thread create-a-container/client/src/pages/sites/SiteFormPage.tsx Outdated
Comment thread create-a-container/routers/api/v1/containers.js
Comment thread create-a-container/routers/templates.js Outdated
Comment thread create-a-container/package.json Outdated
Comment thread create-a-container/client/package.json
Comment thread create-a-container/client/src/app/Sidebar.tsx
Comment thread create-a-container/client/src/pages/nodes/NodeImportPage.tsx Outdated
Comment thread create-a-container/openapi.v1.yaml
Comment thread create-a-container/middlewares/api.js Outdated
Comment thread create-a-container/middlewares/api.js Outdated
- middlewares/api.js: load CSRF secret from SessionSecret table (not env);
  only bypass CSRF when a valid Bearer header is present AND there is no
  session cookie, so an attacker cannot bypass CSRF with a bogus Bearer.
- server.js: initialize CSRF secret from DB at boot; mount /verify router;
  exclude /verify from SPA fallback.
- routers/verify.js: nginx auth_request endpoint, supports session and
  Bearer auth and sets X-User-* identity headers.
- routers/api/v1/auth.js: only define /auth/dev when NODE_ENV != production.
- routers/templates.js: render an empty nginx config when no site exists
  yet so the manager API remains reachable for bootstrapping.
- routers/api/v1/containers.js: include node.apiUrl in serialized container
  responses so the UI can link to Proxmox.
- client/lib/types.ts: align ContainerMetadata with API (ports/httpServices/
  env object/entrypoint string); add nodeApiUrl on Container.
- client/lib/auth.ts: refetch session after login (and dev login) so guarded
  routes see the populated session before navigation.
- client/pages/auth/LoginPage.tsx: also refetch session on 2FA approval.
- client/pages/containers/ContainerFormPage.tsx: use mieweb-provided
  ghcr.io templates; parse HTTP service labels and ExposedPorts properly;
  merge the Custom template + lookup-metadata fields into one item;
  disable existing service fields except Require auth; pass dnsWarnings
  via location state when redirecting to the list.
- client/pages/containers/ContainersListPage.tsx: restore VSCode and SSH
  links; link node name to Proxmox UI; display dnsWarnings from state.
- client/pages/apikeys/ApiKeysListPage.tsx: dark-mode-readable code block.
- client/pages/nodes/NodeImportPage.tsx: call /import (not /import-proxmox).
- client/pages/sites/SiteFormPage.tsx: render DHCP range and DNS forwarders
  with commas (dnsmasq format).
- client/app/Sidebar.tsx: site-scoped Containers (all users) and Nodes
  (admin) links when inside a site.
- package.json / client/package.json / Makefile: 'dev' runs server +
  vite build --watch; Makefile installs and builds the client.
- openapi.yaml: removed legacy spec; openapi.v1.yaml is the source of truth.
Copy link
Copy Markdown
Member Author

@wreiske wreiske left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed all unresolved review threads in 1bab2f1. Replies and resolutions to follow per-thread.

Comment thread create-a-container/client/src/pages/containers/ContainerFormPage.tsx Outdated
Comment thread create-a-container/client/src/app/Sidebar.tsx
Comment thread create-a-container/client/src/app/Sidebar.tsx
Comment thread create-a-container/client/src/pages/containers/ContainerFormPage.tsx Outdated
Comment thread create-a-container/client/src/pages/containers/ContainerFormPage.tsx Outdated
Comment thread create-a-container/client/src/pages/auth/RegisterPage.tsx
Comment thread create-a-container/client/src/lib/api.ts Outdated
Comment thread create-a-container/client/src/pages/auth/LoginPage.tsx
@runleveldev
Copy link
Copy Markdown
Collaborator

@wreiske Doug didn't think you'd have the cycles to finish this PR so I was fixing it up in a local branch (that was the random letters I was commenting, references to local commits I had). If you want to take the ball on seeing this through I'll let you but let me know soon which way you'd rather lean so I know I need to start implementing the fixes instead.

Comment thread create-a-container/package.json
@cmyers-mieweb
Copy link
Copy Markdown
Collaborator

I found some obscure bugs that occur when rapidly moving between pages that will crash the webpage, but we can merge now and address those in a separate PR since it is difficult to replicate. I'm also going to work on some additional UI/UX improvements.
image

@cmyers-mieweb cmyers-mieweb merged commit 292a2ee into main May 29, 2026
8 checks passed
@cmyers-mieweb cmyers-mieweb deleted the feature/react-ui branch May 29, 2026 20:21
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.

4 participants