Files
anchorhost-frontend/SKILL.md
T

33 KiB
Raw Blame History

AnchorHost.eu frontend project guide

This skill is the working guide for understanding, changing, and extending the AnchorHost.eu React frontend in this repository. Read it before making substantial UI, routing, authentication, API, WebSocket, or dashboard changes. Treat the code as the source of truth when details here and implementation diverge; update this guide when a contract or convention changes.

What this project is

AnchorHost.eu is a React 18 single-page client for a hosting platform. It combines a public marketing site and documentation with a customer control panel for game servers, VPS instances, databases, websites, account management, support tickets, plugin discovery, and a privileged administration panel. The client talks to an HTTP API and a persistent WebSocket service. Most operations are user initiated, but dashboard data and operational status are kept live through WebSocket events.

The repository README states that the source was recovered from production source maps. recovered-production/ keeps a compiled production bundle and maps as a reference artifact; normal development should modify src/, not compiled output. This is a frontend-only repository. Backend route implementation, validation, permission enforcement, schemas beyond what the client sends/reads, and server-side side effects cannot be established from this project alone.

Runtime and local development

  • React 18.3, Create React App / react-scripts 5, React Router 6, Tailwind CSS 3, PostCSS, and Autoprefixer.
  • npm start starts CRA; npm run build produces the static bundle; npm test is not declared as a project script even though CRA's test runner exists. Do not eject CRA without a clear need.
  • src/index.js mounts the app and imports global CSS. public/index.html is the HTML shell.
  • Dockerfile builds/serves the static app with nginx; nginx.conf must preserve SPA route fallback so deep links such as /dashboard and /plugins/:pluginId resolve to index.html.
  • Runtime environment values are compiled into the frontend. .env.example shows REACT_APP_API_URL=http://localhost:8080 and REACT_APP_WS_URL=ws://localhost:8081.
  • src/config/api.js exports API_BASE_URL, WS_BASE_URL, and SITE_BASE_URL. Defaults are https://anchorhost.eu and wss://anchorhost.eu/ws; URL normalization strips one trailing slash. SITE_BASE_URL currently defaults to API_BASE_URL. Other code has a few direct origins/relative paths; review those before assuming changing the base URL relocates every request.
  • Never put secrets in REACT_APP_*; these values are public in the generated bundle.

Repository map

src/
  App.js                         App shell, routes, auth and global notices/login/register
  config/api.js                  HTTP, WebSocket, and site base URLs
  index.css                      Global Tailwind layers, base styles, control/accessibility rules
  theme/colors.js                Canonical named palette
  theme/dashboardTokens.js       Dashboard semantic tokens and shared shell class names
  lib/utils.js                   Shared class-name helpers
  hooks/useWebSocket.js          WebSocket lifecycle, reconnect, queue, ping and JSON dispatch
  hooks/usePanelCurrency.js      Exchange-rate fetch and panel currency state
  pages/                         Home, dashboard, admin, legal, docs, and email verification pages
  components/                    Dashboard primitives, shared UI and background effects
  features/                      Focused admin, server, store, plugin, support, announcement, legal UI
public/assets/                   Brand and marketing imagery; asset documentation is alongside it
recovered-production/            Compiled production snapshot and source maps; reference only

The organization is feature-oriented but not fully uniform: large page components (especially App.js, HomePage.js, and DashboardPage.js) contain substantial orchestration and UI, while server/admin/support subfeatures have their own files. Follow existing imports and state ownership before extracting code into a new abstraction.

Routes and application state

src/App.js owns the BrowserRouter route table and provides AuthContext (authentication flag, user, login/register/logout actions, and loading state). Routes:

Path View Behavior
/ HomePage Public marketing and product navigation.
/dashboard/* DashboardPage Requires client auth; unauthenticated users are redirected to /.
/admin/* AdminPage Requires client auth and admin role; unauthorized users go to /dashboard.
/terms TermsOfService Public legal document.
/privacy PrivacyPolicy Public legal document.
/verify-email VerifyEmail Reads verification token from query string and posts it.
/plugins PluginStore Public plugin search.
/plugins/:pluginId PluginDetails Plugin detail and optional install action.
/documentation DocumentationPage Public long-form platform docs.
/docs redirect Replaces URL with /documentation.
* redirect Replaces unknown path with /.

Authentication is frontend-managed: anchorhost_user stores serialized user information and anchorhost_token stores a bearer token in localStorage. Restored state is parsed by App.js; malformed/missing pairs are cleared. This is not a secure cookie session. When changing auth persistence, update all consumers (many feature files read anchorhost_token directly), logout behavior, route guards, and WebSocket URL authentication together. OAuth state values are held temporarily in sessionStorage under google_oauth_state and apple_oauth_state. Cookie consent is stored as cookie_consent=true.

Login, registration, and two-factor challenge/verification primarily travel over the shared WebSocket (see protocol section). Google and Apple login bootstrap through HTTP and then redirect the browser to the returned auth_url. Password reset starts through HTTP. Email verification is a separate HTTP action.

Design language and product philosophy

Visual character

The product uses a dark neobrutalist hosting-console identity. The public site, dashboard, admin, docs, and stores share graphite planes, high-contrast neutral text, squared geometry, visible borders, and hard offset shadows. Neobrutalism is expressed through structure and type, not a novelty palette: the homepage uses a large server image framed like a physical panel; operational pages use crisp section boundaries, dense but legible controls, and direct status labels. The homepage puts hosting choices and the live-config price estimator first and avoids invented testimonials or unsupported reliability claims. The dashboard emphasizes service state and controls.

Use flat fills, 2px rules, 2px or near-square corners, and short hard shadows (typically 3–6px with zero blur) on prominent interactive blocks. Prefer a clear left-aligned hierarchy and deliberate asymmetry over a grid of identical floating cards. Use heavy system-sans headlines, readable system-sans body copy, and monospace only for true machine data. Reserve blue for primary actions/focus and amber for warnings or emphasis. Keep ordinary copy readable, status text explicit, and decorative elements sparse. Do not add gradients, glass effects, soft shadows, pill-shaped everything, arbitrary accent colors, or hover-only affordances. Motion is short, communicates state, and respects reduced-motion preferences.

Maintain the product principles of purpose, agency, familiarity, flexibility, simplicity, craft, and delight in the interaction layer: concise labels, reachable controls, visible keyboard focus, responsive layouts, and clear confirmation for destructive actions. Brutalist presentation does not excuse unclear hierarchy, poor contrast, or inaccessible controls.

Canonical palette

src/theme/colors.js is the named JS palette. tailwind.config.js mirrors it and adds dash semantic colors. Keep both aligned if changing a token.

Token Value Usage
background #151515 Main page background.
panel / backgroundSecondary #181818 Header, dialogs, and panel fill.
surface #202020 Raised content surface.
hover #282828 Hover surface.
border / brightBlack #2F2F2F Strong outline and hard-shadow ink.
focus #454545 Focus-adjacent neutral boundary.
foreground #D0D0D0 Body copy.
muted #757575 Secondary labels; do not use for essential small text.
dim #505050 Decorative or disabled content only.
brightWhite #F5F5F5 Headings and key values.
primary / brightBlue #6C99BB Primary actions and keyboard focus.
blue #547C99 Secondary accent.
yellow / brightYellow #D3A04D Warning state and selective emphasis.
semantic green / red / cyan #7B9246 / #A53C23 / #5EA8A2 Success, danger, and supporting status roles where required by existing UI. Keep labels/icons with color so meaning never depends on color alone.

Dashboard semantic Tailwind tokens are dash.surface, dash.surfaceElevated, dash.border, dash.text, dash.textMuted, dash.accent, dash.danger, dash.success, and dash.warning. dashboardTokens.js exports matching JS values and dashLayout class strings: viewShell is a squared, bordered panel, headerBar is a responsive header strip, and bodyPadding establishes section spacing. Prefer semantic tokens such as text-dash-text over repeated hex values or arbitrary colors. colors.ini is the palette source of truth; src/theme/colors.js, the dash tokens, and tailwind.config.js must stay aligned with it.

Typography, spacing, and responsive behavior

  • The global interface face is the platform system sans-serif stack. It picks up familiar system rendering across Apple devices and remains available offline. Code uses the standard monospace stack.
  • Body text uses a 16px base and 1.6 line-height. Marketing headings use a restrained responsive scale and tight tracking; interface controls use sentence case and medium weights.
  • Tailwind utility classes dominate component styling. Reuse existing button/card/dashboard primitives and the dashLayout shells rather than inventing one-off component chrome.
  • Surfaces use flat fills, strong neutral borders, squared corners, and crisp zero-blur offset shadows on key blocks. Use spacing and hierarchy to avoid turning every nested element into a floating card. Hover changes should be small and should not depend on scale animation.
  • Inputs are forced to at least 16px on narrow viewports to avoid mobile browser zoom. Touch target helpers provide 44px minimums. Horizontal overflow is clipped at the page level; code blocks and tables gain their own horizontal scrolling on mobile.
  • Global visible focus uses a 2px primary outline. Some dashboard controls opt out with focus-skip-global because they draw their own focus ring; preserve a visible keyboard focus indicator when editing those controls.
  • Number steppers are globally hidden, range sliders use blue thumbs, and scrollbars are thin blue-gray.
  • HomePage.js uses semantic site-* classes in index.css; this is the public landing-page system. Keep its header, service rows, estimator, FAQ, and footer aligned when adjusting the homepage.
  • dashboard-app and admin-app root classes scope cross-panel normalization in index.css. Dashboard primitives live in components/dashboardPrimitives.js; update them before hand-styling repeated panels.
  • The homepage hero uses a manually controlled photo gallery backed by the local assets in public/assets/; keep arrow and thumbnail controls keyboard accessible and do not add autoplay by default. The homepage service selector and account country selector use the shared Dropdown primitive.
  • components/dashboardPrimitives.js exports the shared Dropdown and InlineDropdown. The dropdown is a portal listbox, supports arrow/Home/End/Enter/Escape and prefix type-ahead, clamps itself to the viewport, and installs outside-click/scroll/resize listeners only while open. Keep popover styling in the global .ah-dropdown-* rules because portals render under document.body, outside .app-container.
  • Visible brand copy uses AnchorHost.eu. Preserve lowercase technical identifiers and hostnames such as anchorhost_token, anchorhost.eu, and downloaded filenames; these are integration contracts, not display copy.
  • Keep the layout useful at phone/tablet widths; the dashboard is a real management UI and should not depend on hover-only affordances.

Interaction and content principles

  • Display loading, empty, error, connected/disconnected, and completion states explicitly. Async work should not leave a button appearing inert.
  • Use the shared alert/modal patterns in App.js, DashboardPage.js, and feature components where practical. Confirm operations that delete accounts, servers, backups, or files; some existing flows use window.confirm and this pattern is intentional safety feedback.
  • Keep server operations scoped by serverId, and ticket operations scoped by ticket ID. WebSocket messages are asynchronous; update the UI on the corresponding server acknowledgment/event rather than pretending a request succeeded immediately.
  • Maintain existing capitalization and plain operational language. Preserve product terms (credits, VPS, SFTP, Modrinth) rather than replacing them with generic labels.
  • Respect reduced screen space and safe areas; global styles already account for dynamic viewport and mobile safe-area insets in some fixed UI.

REST API inventory

All /api/... routes below are observed in src/. HTTP methods and bodies are listed as the frontend uses them. Bearer <token> means Authorization: Bearer ${localStorage.getItem('anchorhost_token')} unless stated. JSON POST/PUT requests generally set Content-Type: application/json. Error handling varies by call; do not infer consistent response schemas from the UI. Verify backend behavior before changing a field name, method, or URL.

App/authentication and public information

Method Path Auth Client request / use
GET /api/pricing No App-level pricing initialization; response JSON stored as pricing.
GET /api/auth/config No App-level auth-provider/config initialization.
GET /api/auth/google/login No Starts OAuth; expects { auth_url, state }; saves state in sessionStorage, redirects to auth_url.
GET /api/auth/apple/login No Same OAuth bootstrap pattern for Apple.
POST /api/auth/forgot-password No JSON { email } (trimmed); consumes response JSON for user feedback.
POST /api/verify-email No JSON { token }; token is read by VerifyEmail from URL query parameters.
GET /api/public/homepage-showcase No Home page expects success and links array.
GET /api/public/stats No Home page expects success and user_count.
GET /api/pricing No Same route also loaded by App.js; keep in sync with public price display.
GET /api/announcements?limit=20 No Customer announcements panel; expects { success, announcements }.
GET /api/announcements?limit=50 No Admin announcement list loader; public read call.
GET /api/exchange-rates No Currency hook, store, and server creation form; expects { success, rates }.
GET https://geoapi.info/api/geo No, third party Geolocation/country preselection in app and dashboard flows; external origin, response inspected at data.location.country.

Account/profile endpoints

Method Path Auth Client request / use
GET /api/user/profile Bearer Dashboard and admin page load current user/profile; AdminPage reads data.user.credits.
PUT /api/user/profile Bearer JSON profileData; dashboard profile save.
PUT /api/user/email Bearer JSON { email: newEmail }.
PUT /api/user/password Bearer JSON includes currentPassword and newPassword.
DELETE /api/user/account Bearer JSON { confirmation: confirmationText }; on success the client clears auth storage/logs out.
GET /api/user/export-data Bearer Parses JSON; on success, serializes data.data as a downloadable JSON file.
POST /api/user/resend-verification Bearer JSON request (inspect DashboardPage.js for current body; request is initiated from verification reminder).

Servers, provisioning and checkout

Method Path Auth Client request / use
POST /api/capacity/check Bearer JSON serverConfig; form checks capacity before server provisioning. One legacy fallback token key jwtToken is also checked.
POST /api/servers/:serverId/sync Bearer No JSON body; sync action in server details modal. Uses a relative /api URL (same-origin), not API_BASE_URL.
POST /api/servers/:serverId/reinstall Bearer No JSON body; reinstall action in server details modal. Uses a relative same-origin URL.
POST /api/servers/:serverId/adjust-parameters Bearer JSON { cpu, ram, storage }; each value is formatted as a display string such as "2 vCPU" or "4GB".
POST /api/stripe/create-checkout-session Bearer JSON { credits, currency }; response drives checkout/payment redirection.
POST /api/plugins/download-to-server Bearer JSON { server_id, plugin_url, plugin_name, file_name }, using the primary selected version file URL and filename.

Plugin/Modrinth proxy endpoints

Method Path Auth Client request / use
GET /api/modrinth/search?... No Query generated with URLSearchParams; includes plugin project type and current search/filter/page terms. Response uses hits and total-result metadata. Used in both standalone and embedded stores.
GET /api/modrinth/project/:pluginId No Loads project metadata; one caller sends User-Agent: AnchorHost.eu/1.0.
GET /api/modrinth/project/:pluginId/versions No Loads compatible versions; UI uses list and first version for initial selection.

Admin endpoints

All /api/admin/... requests use bearer auth in the client. Server-side authorization is required even if the admin route guard is bypassed.

Method Path Client request / use
GET /api/admin/settings Loads panel settings, including demo_servers_enabled.
POST /api/admin/settings JSON settings object (or current panelSettings).
GET /api/admin/pricing Loads pricing object.
POST /api/admin/pricing JSON newPricing object.
POST /api/admin/announcements Creates announcement from form payload; used by announcement creation and add flow.
PUT /api/admin/announcements/:id Updates announcement form payload.
DELETE /api/admin/announcements/:id Deletes selected announcement.
GET /api/admin/homepage-showcase Loads showcase links; UI expects exactly three links to accept the result.
PUT /api/admin/homepage-showcase JSON { links: homepageShowcase }.
GET /api/admin/users Loads user list.
GET /api/admin/servers Loads all servers.
GET /api/admin/cluster/nodes Loads cluster node list.
GET /api/admin/stats Loads admin statistics.
GET /api/admin/users/:userId/servers Loads selected user's servers.
GET /api/admin/users/:userId/credits/history Loads credit history.
GET /api/admin/users/:userId/billing/history Loads billing history.
POST /api/admin/users/:userId/ban or /unban No JSON body; suffix selected from current ban state. The request includes bearer auth.
DELETE /api/admin/users/:userId Deletes selected user.
POST /api/admin/users/:userId/password JSON { password } from new password field.
POST /api/admin/users/:userId/2fa/disable Disables user's two-factor authentication.
POST /api/admin/users/:userId/credits JSON { amount: parseInt(creditAmount), operation: creditOperation }.

REST implementation notes

  • The frontend uses native fetch; Axios is installed but is not the common request layer. There is no single API client/interceptor. Auth headers and error/JSON handling are repeated per feature.
  • Most requests use API_BASE_URL, but the two server detail actions above use relative URLs. Avoid introducing more inconsistent origin behavior; if consolidating requests, preserve same-origin/proxy assumptions and query/body semantics.
  • Most endpoints expect JSON responses, including some failure responses. Check response.ok and the expected data keys; inconsistent checks are existing behavior, not proof of backend guarantees.
  • Do not place bearer tokens in query strings for HTTP calls. Existing WebSocket URL authentication is a separate legacy pattern and is described below.
  • When adding an endpoint, document method, path, auth, query/body fields, response fields actually consumed, and owning component here.

WebSocket transport and message protocol

src/hooks/useWebSocket.js is the shared transport. It takes (url, onMessage) and exposes the socket, sendMessage, connectionStatus, and latest JSON message. It parses incoming JSON, pings while connected, reconnects after drops with delayed retries, queues outbound messages until connection is ready, and prevents duplicate sockets for a URL. It returns status so UI can disable or explain actions while disconnected. Check hook cleanup and queue behavior before adding another socket implementation.

Dashboard/admin sockets use WS_BASE_URL plus /dashboard?token=<token> (query-string token). The app's login/register socket uses the base WS URL. Server management and support generally share the dashboard socket. Plugin details contains a direct new WebSocket implementation and sends get_servers after open; prefer the shared hook for new code unless a clear lifecycle reason requires otherwise. WebSocket payloads are JSON objects with a type discriminator; exact server-side schemas and authorization rules are not present in this repo.

Client-to-server message families

The following message types are sent by the client. Fields shown are the fields explicitly visible in the frontend; additional server-required fields must be verified against backend code.

Authentication and account security

  • { type: 'login', username, password }
  • { type: 'register', username, email, password, confirmPassword, displayName, firstName, lastName, address, city, country, postalCode, phone }; username/email are trimmed, country is resolved to a display name, and empty country/phone become null.
  • { type: '2fa_login', temp_token, otp_code }
  • { type: 'authenticate', token } after dashboard connection (token field is handled by dashboard auth setup)
  • { type: '2fa_setup' }, { type: '2fa_enable', otp_code }, { type: '2fa_disable', ... }
  • Transport emits { type: 'ping' } while open.

Dashboard/server lifecycle and information

  • get_servers, create_server (form/config fields), delete_server { serverId }, restart_server { serverId }, get_server_creation_progress, get_server_metrics { serverId }, get_server_logs { serverId, tailLines }
  • change_server_version { serverId, version }; get_minecraft_settings { serverId }; update_minecraft_settings { serverId, settings }
  • adjust_server_parameters { serverId, cpu, ram, storage }; in the dashboard these resource values are formatted strings. convert_demo_to_paid { serverId }.
  • get_credits, add_credits { ... }, check_demo_eligibility, generate_sftp { serverId }
  • expose_port { serverId, internalPort, protocol, ... }, remove_port { serverId, internalPort, protocol }
  • Domain management: list_domains { includeServerless }, list_custom_domains, create_domain { ... }, link_domain { ... }, unlink_domain { domainId }, create_custom_domain { domainName }.

Server files and uploads

  • Directory browsing uses exec_command { serverId, command } (for example ls -la ... and pwd) and consumes command output into the file list.
  • read_file { serverId, filePath }; write_file { serverId, filePath, content }; create_directory { serverId, dirPath }; delete_path { serverId, path }.
  • move_path { serverId, srcPath, destPath }; copy_path { serverId, paths, ... }; clipboard paste may send a list of paths.
  • download_file { serverId, filePath }.
  • Chunk upload: start_chunked_upload { serverId, filePath, ... }, then upload_file_chunk { serverId, filePath, uploadId, chunkIndex, totalChunks, chunkData, ... }. The UI chunks at 512 KiB and resumes using in-memory upload tracking after reconnect. Keep encoding and acknowledgment flow aligned with FileManager.js.
  • Archive operations: pack_zip { serverId, dirPath, zipPath }, unpack_zip { serverId, zipPath, ... }.

Console and terminal

  • Game console: game_console_stream { serverId, tailLines }, game_console_command { serverId, command }.
  • VPS terminal: vps_terminal_connect { serverId }, vps_terminal_disconnect { serverId }, vps_terminal_input { serverId, input }.
  • Admin node terminal: node_terminal_connect { nodeName, ... }, node_terminal_disconnect { ... }, node_terminal_input { ... }.
  • General terminal command: exec_command { serverId, command }.

Backups

  • get_backups { serverId }, create_backup { serverId, name? }, restore_backup { serverId, backupId }, delete_backup { serverId, backupId }.

Support tickets

  • get_tickets { ...filters/pagination... }, get_ticket { ticketId }, create_ticket { ...form fields... }, send_ticket_message { ...ticket and message fields... }, update_ticket_status { ...ticket and status fields... }.
  • Admin actions include claim and close (ticket context is sent by the caller); inspect TicketSystem.js before changing the exact keys.

Server-to-client event families

The UI explicitly handles these message types; the backend's full payloads are not defined here.

  • Auth: authenticated, auth_error, email_verified, 2fa_setup_details, 2fa_enabled, 2fa_disabled, generic error.
  • Server list/lifecycle: servers_list, server_created, server_updated, server_deleted, server_creation_progress, server_version_changed, server_metrics, server_logs, sftp_generated.
  • Credits/demo: credits_balance, credits_added, demo_eligibility, demo_converted.
  • Port/Minecraft settings: port_exposed, port_removed, minecraft_settings, minecraft_settings_updated.
  • File manager: files_list, file_content, file_written, file_uploaded, upload_started, upload_chunk_received, upload_error, file_downloaded, path_moved, path_copied, path_deleted, directory_created, zip_packed, zip_unpacked, zip_error, and command_result.
  • Console/terminal: game_console_output, game_console_result, vps_terminal_output, node_terminal_output.
  • Backups: backups_list, backup_status_updated, backup_deleted, plus informational restoration status.
  • Tickets: tickets_list, ticket_created, ticket_details, ticket_message_sent, ticket_status_updated, ticket_stats, admin_notification_new_ticket, ticket_new_message.
  • Domain management events are routed through the domain manager callback in DashboardPage.js and DashboardComponents.js; search both files for domainMessageTypes and corresponding handlers when changing that protocol.

Always inspect the receiving switch/handler as well as the sending call before modifying a WebSocket contract. Preserve correlation fields such as serverId, file path, ticket ID, upload ID, or node name so events do not update the wrong active view. Keep high-volume terminal/file events scoped to their relevant UI, and avoid logging secrets or full file contents.

Feature ownership and extension map

  • App.js: global routes, auth state, login/register UI and WebSocket, OAuth entry points, cookie consent, shared alert/location/pricing configuration. New routes belong in its route table; avoid duplicating auth state in page components.
  • HomePage.js: public brand/product landing page, homepage showcase and public stats. Marketing content and CTA route behavior live here.
  • DashboardPage.js: authenticated customer workspace and its dashboard WebSocket dispatcher. It coordinates server selection, account panels, server actions, 2FA, credits, domain feature hooks, and server detail view composition. Keep dispatcher and child consumers coordinated.
  • AdminPage.js / AdminSection.js: admin route shell and admin settings, pricing, announcements, users, servers, cluster nodes, stats, and support actions.
  • ServerCreationForm.js: provisioning configuration, capacity check, exchange rates, and demo eligibility. Creation itself is sent over WebSocket.
  • ServerDetailsModal.js: selected server's metrics/logs/settings/version/ports, SFTP, sync/reinstall HTTP actions, and child tabs.
  • FileManager.js: remote file browsing/editing, safe destructive controls, upload/download, chunk resume, and zip actions. Its callbacks are fed by the dashboard socket; preserve this integration.
  • BackupSection.js, GameConsole.js, VPSTerminal.js: narrowly scoped server tools driven by WebSocket.
  • StoreSection.js: credits packages/currency and Stripe session start.
  • usePanelCurrency.js: shared conversion rates/currency state.
  • PluginStore.js and PluginStoreEmbedded.js: Modrinth search and two contexts; PluginDetails.js loads project versions and installs to a selected server.
  • TicketSystem.js: support-ticket UI and live socket protocol.
  • AnnouncementsSection.js: customer announcement list.
  • DocumentationPage.js: long-form in-app documentation. Treat described defaults/credentials as user-facing claims that must match backend behavior.
  • LegalDocumentLayout.js, TermsOfService.js, PrivacyPolicy.js: shared legal shell and static legal content.
  • VerifyEmail.js: HTTP verification callback view.
  • DashboardComponents.js, dashboardPrimitives.js, components/ui/*: shared dashboard panels, forms, navigation primitives, buttons/cards; extend before duplicating patterns.

Change workflow and quality rules

  1. Trace a feature end-to-end: route or parent state, owning UI, outgoing fetch/socket message, matching response handler, loading/error/success state, and styling tokens.
  2. Check whether a message is HTTP or WebSocket before changing it. Do not replace one transport with another based only on endpoint naming.
  3. Reuse palette tokens and primitives. If a new token is necessary, update both src/theme/colors.js and tailwind.config.js; update dashboard semantic references when relevant.
  4. Keep API contracts explicit at the call site. Include auth, JSON headers, body serialization, response checks, and user feedback. Do not silently swallow failed destructive actions.
  5. Preserve responsive behavior, keyboard access, visible focus, and touch targets. Check mobile overflow when adding wide data tables or terminal output.
  6. Keep state local to the feature unless several pages genuinely share it. Avoid introducing a broad global store for one component's state.
  7. Do not edit recovered-production/ to implement application changes. It is a forensic/reference snapshot.
  8. The frontend uses plain JavaScript and CommonJS for build config. Match existing file extensions and import conventions unless a migration is intentional.
  9. Before claiming behavior, distinguish what the UI sends or reads from backend facts. Backend contract verification requires the companion backend repository or API documentation.
  10. Update this skill when routes, REST calls, message types, storage keys, theme tokens, or feature ownership changes.

Things that are easy to miss

  • The WebSocket base already defaults to a path ending in /ws; dashboard/admin append /dashboard, resulting in /ws/dashboard. Do not duplicate /ws in environment configuration unless intended.
  • /api/servers/:id/sync and /reinstall are relative paths while most REST calls use API_BASE_URL; deployment/proxy behavior matters.
  • Exchange-rate requests are repeated in the store, creation form, and shared currency hook; avoid letting their assumptions diverge.
  • Modrinth logic appears in both standalone and embedded store flows. Keep search params, selected version, and install payload coherent.
  • The dashboard dispatcher forwards certain message types to child handlers before handling its own switch. Removing forwarding can silently break uploads, console, or domains.
  • Tokens are stored in browser local storage and are included in dashboard WebSocket query strings. This has security implications; do not expand token exposure, and coordinate any auth redesign across all consumers.
  • No backend source is present here; endpoint inventory is exhaustive for calls found in src/ at the time this file was written, not a guarantee of all server endpoints.