Skip to content
Rafi Akmal Widikta

frontend

Veilig Melden

Reporting platform where employees raise workplace concerns and companies follow them up.

Role
Software Engineer
Date
January 2026

Technologies

  • vue
  • nuxt
  • typescript
  • vite
  • firebase
  • aws ses
  • unocss
  • radix-vue
  • vue-i18n
  • zod
  • playwright
  • sentry

Problem

A reporting channel only works if the person using it trusts it. That sets the constraints: no password for a reporter to forget or reuse, a clear line between what an employee sees and what a company’s HR sees, and notifications that actually arrive; a report that silently fails to send is worse than having no reporting feature at all.

The second problem was that there was no safety net. The first commit on the project is the one that sets Playwright up, so everything before it shipped on manual checks. Building that net was awkward because the target kept moving: the app was being rewritten from a Vue SPA into Nuxt, and while that happened layouts and landing components changed folders more than once and the locale code was renamed. Specs written against the old ground broke in layers: first on selectors that nothing had agreed on, then on timing, and one dashboard spec ended up being touched eighteen times. Setup lived in every file until it was centralised, two files were duplicated or misspelled, and Firebase Admin was initialised again inside every parallel test worker, which is a failure that arrives loudly.

Solution

The app is a Vue 3 SPA on Firebase Hosting. Routes are split per audience (landing, employee, company, login), and an auth guard requires a verified email address before anything behind the login is reachable. State is bound straight to Firestore through VueFire rather than routed through a store library: there is no client state here worth centralising. Folders are organised by domain (company, employee, landing) instead of by file type, and validation runs through the same Zod schemas on both sides. Everything that leaves the browser does so through four HTTPS callable functions rather than database triggers: the login link, submitting a report, and the contact and termination flows. Email goes out through Amazon SES with hand-written HTML templates and a BCC into the platform’s own inbox, so there is always a copy that lives outside the customer’s tenant.

The frontend is being rewritten on Nuxt 4, one page at a time while the old app keeps running. Because the brand palette, Raleway and the type scale live in a shared UnoCSS preset instead of inside either app, the two frontends stay visually identical during the move. Both ship Dutch and English.

Making the migration safe needed the tests to have something stable to hold on to. Selectors moved out of CSS classes and visible text and into data-testid attributes on the components themselves, including one generated per option, so a dropdown entry can be targeted by its slug rather than by its translated label. Every spec was then rewritten against getByTestId and the naming made consistent. Flakiness came next: fixed waits were replaced with assertions on a real condition (toBeVisible with a timeout, .or() where either of two states is acceptable, and a domcontentloaded wait on navigation), and the debug leftovers were removed. Setup moved into beforeEach and test.use, scenarios were grouped with test.step, and the file names were cleaned up. Firebase Admin was the one that bit hardest: a guard around getApps() stopped the duplicate initialisation, and caching the Auth instance at module level keeps it to one initialisation per process across parallel workers. The contact form then landed end to end: composable, plugin, the emulator with Firestore and Storage rules, and a server endpoint that renders the email template and sends it through SES.

One loose end stayed loose, and it is worth naming: the ticket IDs and one commit verb carry typos in the history. The durable fix for that is a commit-message hook rather than more care.

Gallery

Screenshots

  • Veilig Melden homepage with the headline “Veilig-melden: Samen Beter” and a section about improving workplace culture by listening first.
    Homepage

Contact

Interested in working together?

Tell me what you are building and what you need. A short note is enough.

Back to top