Uranus Dashboard is a Vue 3 single-page application that helps cultural organisations run their events, venues, and spaces. The app offers a visitor-facing calendar as well as an authenticated workspace where editors can manage organisers, venues, event metadata, translations, and personal settings.
Good to know
The UI expects a Uranus API server that exposes the/apiendpoints used throughout the codebase (for example/api/admin/login,/api/admin/organization/:id,/api/admin/event/create, …). You can still explore the UI without a backend, but data loads and mutations will fail.
- Authentication flow with login, signup, forgotten-password, and reset-password screens, token refresh, and local storage persistence.
- Dashboard workspace for logged-in users featuring organiser, venue, space, and event management, including split create/update flows.
- Rich form experiences such as Markdown-enabled descriptions, Leaflet-powered maps for picking coordinates, and dependent selects for legal forms, countries, and states.
- Event builder with a two-stage tag selector, availability calendar, language selector backed by live API data, and validation before submission.
- Visitor layout that exposes the public event calendar, language selector, theme switcher, and legal links without requiring an account.
- Internationalisation with German, English, and Danish translations wired through Vue I18n and ready for extension.
- Personalisation with light/dark themes, profile editing, avatar upload, and user permissions quick links.
- Vue 3 with the
<script setup>syntax - TypeScript
- Vite for development and bundling
- Pinia for state management
- Vue Router navigation guards and layouts
- Vue I18n internationalisation
- Leaflet for interactive maps in address forms
- Quill (via
MarkdownEditorComponent.vue) for enhanced text editing
- Node.js 20 LTS or newer (use nvm or a similar tool to manage versions).
- npm 10 (bundled with Node 20).
- Access to a Uranus API instance (development or staging) with credentials you can use.
- Clone the repository
git clone https://github.com/sndcds/uranus-dashboard.git cd uranus-dashboard - Install dependencies
npm install
Create a .env.local (or .env) file in the project root and set the API base URL:
# .env.local
VITE_API_URL=https://uranus-api.example.orgThe value can point at http://localhost:8000 when running a local backend. During development the Vite dev server proxies /api/* requests to the URL defined here (see vite.config.ts).
-
Development server
npm run dev
Vite prints a local and network URL (default:
http://localhost:5173). Hot Module Replacement is enabled out of the box. -
Type checking & linting
The project relies on TypeScript’s compiler diagnostics and ESLint. Run
npm run buildorvue-tsc --noEmitmanually if you need a dedicated type check step. -
Production build
npm run build
Bundled assets are emitted into
dist/. -
Preview a production build
npm run preview
Uranus Dashboard ships with a Vitest suite that exercises components, views, Pinia stores, and high-level authentication flows. Run the commands below from the project root:
# Run the full suite (default happy-dom environment)
npm run test
# Launch the Vitest UI runner
npm run test:ui
# Collect coverage information
npm run test:coverageTest helpers, mocks, and specs live inside the tests/ directory—see tests/README.md for a full breakdown of available specs and coverage focus areas.
├─ public/ # Static assets copied as-is
├─ src/
│ ├─ api.ts # apiFetch helper with token refresh logic
│ ├─ components/ # Reusable UI units (forms, layout, editors, maps)
│ ├─ i18n/ # Vue I18n setup and translation catalogue
│ ├─ models/ # Shared TypeScript interfaces (event models, …)
│ ├─ router/ # Route definitions and navigation guards
│ ├─ store/ # Pinia stores (token, theme, app, user)
│ ├─ styles/ # Global SCSS theme tokens and mixins
│ ├─ utils/ # Helpers (e.g. theme application)
│ └─ views/ # Page-level components registered in the router
├─ vite.config.ts # Vite build and dev-server configuration
└─ tsconfig.json # TypeScript project settings
- Login & Signup: Accessed via
/loginand/signupunder the visitor layout. Successful login stores access and refresh tokens inlocalStorage(useTokenStore) and redirects to the dashboard. - Forgot / Reset password:
/forgot-passwordsends a reset link,/reset-password?token=…lets users choose a new password with double entry and eye icons for toggling visibility. - Session refresh:
apiFetchtransparently refreshes JWTs when a 401 response comes back, using the stored refresh token and reattempting the failed request.
- Dashboard home (
/): A welcome hero reminding users of key tasks. - Organisers (
/organizations): List fetched from/api/admin/organization/dashboard, with cards linking to create/update flows. - Create & edit organiser (
/organization/create,/organization/:id/edit):- Forms capture legal form, country/state via live dropdowns (
/api/choosable-legal-forms,/api/choosable-countries,/api/choosable-states). - Use the integrated Markdown editor for rich descriptions and the Leaflet map to refine coordinates.
- Non-profit status is toggled via an accessible checkbox.
- Forms capture legal form, country/state via live dropdowns (
- Venues (
/organization/:id/venuesand/organization/:id/venue/:venueId/edit):- Reuses the shared
VenueForm.vue, ensuring consistent layout between create and update flows. - Supports optional Markdown descriptions, opened/closed dates, website URLs without protocol prefixes, and geocoding.
- Reuses the shared
- Spaces (
/organization/:id/venue/:venueId/space/...):FormSpaceView.vueloads existing spaces when editing and submits updates through the admin API.
- Events:
EventDashboardView.vueaggregates an organiser’s events.FormEventView.vueguides editors through basic info, scheduling (including multiple time slots), and content details, blocking submission until all sections validate.EventDetailsComponent.vuefetches choosable languages from/api/choosable-languages?lang=deand records selections as language codes.
- User profile & permissions: Accessed via
/user/profileand/user/permissions, linked from the sidebar user menu. Avatar uploads hit/api/user/:id/avatar/256and bump a cache-busting version.
- Default layout:
DefaultVisitorLayout.vueprovides a responsive mobile menu that slides in from the left, language and theme dropdowns, and hides login/signup buttons when a session exists. - Events calendar (
/events):- Fetches events, categories, and filters via the public API.
- Offers search, date range selection, type filtering, and tag shortcuts.
- Shows “View details” links for authenticated users.
- Theme switcher: Stored via
useThemeStore; selections persist inlocalStorageand update the DOMdata-themeattribute (light/dark). - Organization selection memory:
useAppStorekeeps the last chosen organiser ID to streamline event/venue creation. - Locale persistence: Vue I18n syncs with user preferences returned by the API after login.
- Supported locales live in
src/i18n/uranus-i18n-index.tswith German (de), English (en), and Danish (da) catalogues. - Shared translation groups (e.g., authentication copy) are centralised for consistency.
- Use
te('key')in components when you must guard against missing translations. - To add a new language:
- Extend the
LocaleKeyunion. - Update the
messagesobject with the new locale. - Add the locale to the
localeOptionsarray inDefaultVisitorLayout.vue.
- Extend the
- Global tokens and mixins reside in
src/styles/global.scssandsrc/styles/_mixins.scss. - Components adopt mixins like
form-page,form-card, andform-gridto keep layout consistent. - Theme switching updates CSS custom properties on the root element, allowing you to customise colours or spacing globally.
apiFetch(insrc/api.ts) wrapsfetchto:- Prefix all paths with
VITE_API_URL. - Automatically set
Content-Type: application/jsonunless the body isFormData. - Inject
Authorizationheaders when tokens exist. - Parse JSON/text responses and throw an
ApiErrorwith the HTTP status. - Handle 401s by posting to
/api/admin/refreshand retrying the original request.
- Prefix all paths with
fetchCoordinatesForAddressleverages the OSM Nominatim service athttps://nominatim.oklabflensburg.deto locate coordinates for map components.- When building new features, prefer
apiFetchto keep token handling and error surfacing consistent across the app.
- Forms use labelled inputs,
aria-*attributes, and focus outlines by default. Password fields include eye toggles with appropriate labels. - Important feedback messages (
auth-feedback, calendar errors, etc.) userole="alert"/aria-livefor screen readers. - The visitor layout provides an accessible mobile menu with
aria-expandedandaria-controlshooks.
- Blank data / 401 errors: Ensure
VITE_API_URLpoints to a reachable API and that your credentials are valid. - CORS issues in development: Verify the backend allows the Vite dev origin or rely on the proxy specified in
vite.config.ts. - “Module 'node:test' has been externalized” error: Importing Node-only modules inside Vue components causes Vite to externalise them. Remove those imports or wrap test helpers in environment checks.
- Translations missing: Add the key to all supported locales;
te('key')allows you to fall back gracefully while you build out translations. - Leaflet map tiles not loading: Confirm outbound network access to
nominatim.oklabflensburg.deand the tile provider, or configure your own tile URL withinLocationMapComponent.vue.
- Connect the dashboard to your Uranus backend and seed sample data for organisers, venues, and events.
- Extend the translation catalogue if you serve additional languages.
- Add automated tests (
vitest,cypress, …) to cover critical flows—no test suite ships with the repo yet.
Happy organising! 🎉