Resolves the 4 tiptap-independent TypeScript errors that survived
the tiptap 2.27.2 upgrade. All fixes are type-narrowing or type-
annotation refinements; no runtime behavior changes.
Errors fixed:
- vite.config.ts:50 — TS7006: parameter 'componentName' implicitly
has an 'any' type.
Fix: annotate as `(componentName: string)`. The
unplugin-vue-components resolver always passes a component-name
string.
- src/@layouts/types.ts:7 — TS2322 source: Type 'string' is not
assignable to type 'Lowercase<string>'. Vuexy boilerplate
constrained `LayoutConfig.app.title` to all-lowercase, which
rejects "Crewli Portal" in themeConfig.ts. The lowercase
constraint serves no consumer in our code and was a Vuexy
template oversight.
Fix: relax type to `string` at the type definition (root cause).
No call-site changes needed.
- src/plugins/iconify/build-icons.ts:19 — TS2307: Cannot find
module '@iconify/types' or its corresponding type declarations.
The build:icons postinstall script uses `IconifyJSON` as a type
annotation. `@iconify/types@2.0.0` was already in the pnpm
store as a transitive dep of `@iconify/tools` but not hoisted
to portal's node_modules root.
Fix: add `@iconify/types` as an explicit dev-dependency.
- src/@layouts/plugins/casl.ts:51 — TS2345: Argument of type
'{}' is not assignable to parameter of type 'string'.
Vue-router types `RouteMeta` loosely; the if-guard on line 50
narrows truthiness but TS doesn't infer string from `{}`.
The same pattern on line 55 already uses `// @ts-expect-error`;
we prefer an explicit `as string` cast at the call site since
intent is clearer than a suppression comment.
Fix: cast `targetRoute.meta.action` and `targetRoute.meta.subject`
to `string` at the `ability.can(...)` call.
vue-tsc errors:
Pre: 4 own-code (post tiptap upgrade), 0 in node_modules.
Post: 0 own-code, 0 in node_modules.
apps/portal `pnpm exec vue-tsc --noEmit` now exits clean.
Vitest: 113/113 passing. Build: 8.68s, succeeded.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tiptap 2.27.1 ships a packaging bug: dist/index.d.ts re-exports
from '../src/CommandManager.js' (and 22 similar lines), but those
.js files do not exist — only .ts source. With the project's
moduleResolution: "Bundler" config, vue-tsc falls through to
src/CommandManager.ts and pulls tiptap's entire uncompiled source
tree into the program. skipLibCheck is already true but does NOT
suppress the resulting errors: skipLibCheck only affects .d.ts,
not raw .ts reachable through the import graph.
Tiptap 2.27.2 fixes the dist exports to use sibling-relative paths
(./CommandManager.js), which resolve correctly to the existing
dist/CommandManager.d.ts files. No walk into src/.
The existing ^2.27.1 caret already accepted 2.27.2; pnpm-lock just
froze 2.27.1 from when it was the latest. `pnpm update '@tiptap/*'`
brings all 12 packages to 2.27.2:
- @tiptap/core 2.27.1 → 2.27.2 (transitive)
- @tiptap/extension-character-count 2.27.1 → 2.27.2
- @tiptap/extension-highlight 2.27.1 → 2.27.2
- @tiptap/extension-image 2.27.1 → 2.27.2
- @tiptap/extension-link 2.27.1 → 2.27.2
- @tiptap/extension-placeholder 2.27.1 → 2.27.2
- @tiptap/extension-subscript 2.27.1 → 2.27.2
- @tiptap/extension-superscript 2.27.1 → 2.27.2
- @tiptap/extension-text-align 2.27.1 → 2.27.2
- @tiptap/extension-underline 2.27.1 → 2.27.2
- @tiptap/pm 2.27.1 → 2.27.2
- @tiptap/starter-kit 2.27.1 → 2.27.2
- @tiptap/vue-3 2.27.1 → 2.27.2
Patch-level upgrade: no API surface change. Drop-in.
vue-tsc errors:
Pre: 729 total = 22 own-code (incl. 18 downstream tiptap
TS2339 'Property does not exist on type SingleCommands'
leaking from TiptapEditor.vue + ProductDescriptionEditor.vue)
+ 707 in node_modules/@tiptap/
Post: 4 total = 4 tiptap-independent own-code stragglers
(vite.config.ts, themeConfig.ts, casl.ts, build-icons.ts)
+ 0 in node_modules
Vitest: 113/113 passing. Build: 8.69s, succeeded.
The 4 remaining own-code errors are addressed in the next commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Installs @total-typescript/ts-reset 0.6.1 as a dev-dependency in
apps/portal/ and apps/app/. Patches TypeScript's loosest default
types: Array.filter(Boolean) returns non-nullable, JSON.parse
returns unknown, fetch().json() returns unknown, Map.get() strict,
etc.
Configuration: src/reset.d.ts in each SPA imports the reset. Both
tsconfig.json files already include ./src/**/* so the .d.ts is
picked up automatically — no tsconfig edits needed.
Issues surfaced during install:
- apps/app — 0 pre-install tsc errors in own code; install
surfaced 2 errors in src/stores/useImpersonationStore.ts
(both from JSON.parse on sessionStorage content returning
unknown instead of any). Fixed inline at lines 19 + 123 via
`as ImpersonationState` casts that make the existing
trust-in-sessionStorage explicit. Backlog entry
TECH-TS-IMPERSONATION tracks proper runtime shape validation.
- apps/portal — 22 pre-existing tsc errors in own code (mostly
tiptap editor components — tracked as TECH-TS-PORTAL-TSC,
unrelated to ts-reset). Zero new errors in portal's own code.
4 additional errors surfaced in tiptap's uncompiled node_modules
.ts sources (third-party); left as-is.
Neither SPA achieves `tsc --noEmit` clean today — pre-existing
state unrelated to this work package. Build + vitest are the
actual working gates and both remain green:
- apps/portal: vitest 113/113 passing; production build succeeds
- apps/app: (no vitest setup — tracked as TECH-APP-VITEST);
production build succeeds
Documentation: /dev-docs/FRONTEND-TOOLING.md added; CLAUDE.md
quality-gates updated.
Backlog: TECH-TS-IMPERSONATION (runtime validation of stored
impersonation state), TECH-TS-PORTAL-TSC (pre-existing portal tsc
errors), TECH-APP-VITEST (Vitest coverage for apps/app).
No production behavior change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- FieldTagPicker: VAutocomplete multiple with grouped category slots,
empty/null category normalised to "Overig", empty-state info alert
when the server delivers no tags.
- FieldAvailabilityPicker: date-grouped checkbox list, festival-aware
via usePublicFormTimeSlots. Event-name subheaders only surface when
the time-slots span multiple events. Time format strips seconds.
- FieldSectionPriority: tap-to-rank + drag-to-reorder via vuedraggable
for desktop; mobile tap-only. Renumbers priorities on every mutation.
Self-heals malformed modelValue. UI soft cap via
validation_rules.max_priorities clamped to the backend hard cap of 5.
- FieldRenderer: three new types removed from isStubbed.
- publicFormInjection: page-level provide/inject for the public token.
- IdentityMatchBanner: prefers backend-provided Dutch copy with
frontend defaults as defensive fallback.
- FormConfirmation wires the banner inline.
- usePublicFormTimeSlots and usePublicFormSections TanStack composables.
- 40 new Vitest assertions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace monolithic register/[eventSlug].vue with composable field
renderer, conditional-logic engine, stepper, and per-field components
driven by Form Builder schema. Adds flatpickr for date fields.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vulnerable dependencies upgraded:
- Backend: league/commonmark >=2.8.2 (HTML injection bypass),
phpunit/phpunit >=11.5.50, laravel/tinker (psysh LPE)
- Frontend: axios 1.13→1.15 (SSRF + metadata exfiltration),
@casl/ability updated (prototype pollution)
- Removed swiper from all 3 apps (prototype pollution CVE,
only used in Vuexy demo pages)
XSS vectors removed:
- Deleted Vuexy demo pages with v-html rendering API data:
help-center/article, academy/course-details
- Deleted all front-pages (landing, pricing, checkout, payment) —
Vuexy marketing template, not Crewli business logic
- Deleted swiper demo components and views
- Fixed admin main.ts: replaced innerHTML with template literal
with safe DOM construction using textContent
Cookie security:
- Added SameSite=Strict and Secure flags to admin cookie defaults
Cleanup:
- Removed swiper SCSS from all 3 apps
- Removed swiper custom element config from all 3 vite configs
- Portal localStorage cleanup verified: reset() clears all keys,
called on both explicit logout and 401 interceptor
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add GET /api/v1/public/events/{slug}/registration-data endpoint for fetching
event sections and time slots without auth
- Create 5-step registration form: personal info, details, motivation, section
preferences, availability
- VeeValidate + Zod validation per step with Dutch error messages
- Auth-aware: pre-fills name/email for authenticated users
- Mobile responsive with custom chip-based step indicator
- Success page with contextual actions (dashboard vs login)
- Types, composable (TanStack Query), and Zod schemas
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>