Skip to content

Modernize app#250

Open
deemp wants to merge 307 commits into
schej-it:mainfrom
deemp:migrate/vue3-vite-ts
Open

Modernize app#250
deemp wants to merge 307 commits into
schej-it:mainfrom
deemp:migrate/vue3-vite-ts

Conversation

@deemp

@deemp deemp commented May 2, 2026

Copy link
Copy Markdown

Changes

Frontend

Migrated frontend to:

  • TypeScript (type safety plays nicely with LLMs)
  • Vue3 (Vue 2 has reached End of Life on December 31st, 2023)
  • Temporal (the standard approach to working with dates; seems ideal for this app because it manipulates dates and times a lot)

Fixed:

  • Coloring problems in the grid (when there is more than one response)
  • Grid labelling when the timezone is weird (+05:45)

Added:

  • Guest identifiers stored in the database and in guests' browsers
  • A user can author multiple responses
  • On the event page, clicking "Edit availability" suggests a drop-down list to choose which response to edit
  • Dev server and build using Vite

Breaking changes:

  • A user can edit only the responses available for editing (own responses, responses marked open for editing)
  • Each user name must be non-empty after normalization (replacing space-like characters, etc.). This policy ensures that the Edit availability and Responses always has a list with non-empty human-readable names
  • Time model has changed. See ADR-012

Docker Compose

Added

  • Caddy service
  • Compose override for test (compose.test.yaml)
  • Env variables via interpolation

AI models usage

Almost all of the coding work was done by Claude, Lingma, Codex, and OpenCode.

AI agent usage

  1. After migrating, I asked the agent to compare the new app against the legacy version and write down the findings.
  2. I asked the agent to work on a finding and at the end of the session, I asked it to write down new findings, if any.
  3. I also asked the agent to review the frontend files and identify any smells (vue 2/vuetify 2 approaches, etc.)
  4. I instructed the agent to write a regression unit test whenever it made a change
  5. The agent run linter, type checker, build after producing changes

Current state

What works:

  • npm run lint
  • npm run build
  • npm run typecheck
  • npm run test:unit (need more tests!)

Also:

  • Run frontend via the Vite dev server
  • Run all containers via Docker Compose

TODO

@deemp deemp force-pushed the migrate/vue3-vite-ts branch 3 times, most recently from c0b2a4b to c396303 Compare May 5, 2026 14:20
deemp and others added 2 commits May 5, 2026 18:45
Add the missing SignUp regression for group events loaded through /s/:id.
Assert that route normalization redirects to the canonical group route while preserving the serialized restore-state query payload.

Co-authored-by: OpenAI Codex <codex@openai.com>
Normalize Event.signUpResponses at the main transport boundary so frontend code consumes the existing SignUpResponse app shape instead of a raw nested backend payload.

Update the schedule-overlap types to inherit that normalized event contract, add a regression that revives nested sign-up response users before schedule-overlap consumes them.

Co-authored-by: OpenAI Codex <codex@openai.com>
@deemp deemp force-pushed the migrate/vue3-vite-ts branch from 97d810d to c569d52 Compare May 5, 2026 15:50
deemp and others added 24 commits May 5, 2026 19:09
Mark Vue style blocks that use Tailwind @apply as PostCSS so editor tooling parses the directives with the correct syntax.

This removes persistent false-positive CSS warnings without changing runtime behavior or the existing Tailwind build pipeline.

Co-authored-by: OpenAI Codex <codex@openai.com>
Add a shared frontend ads gate, route event ad surfaces through it,
and document the env switch in .env.example.
This makes advertisement blocks easy to disable from one place
instead of editing multiple components and layout conditions by hand.

Co-authored-by: OpenAI Codex <codex@openai.com>
Add accepted ADRs for frontend boundary models, Temporal and timezone semantics, and operational feature toggles.

These records capture migration guardrails that were previously scattered across findings so future work can treat violations as concrete call-site regressions instead of vague backlog carry-forward.

Co-authored-by: OpenAI Codex <codex@openai.com>
Replace the separate ads toggle path with a shared freemium utility and expose explicit viewer premium-access semantics in the frontend store.

This keeps operational disabling on one env-backed switch, removes redundant ad helpers, and makes owner-versus-viewer gating clearer in ad and paywall checks.

Co-authored-by: OpenAI Codex <codex@openai.com>
Rename ADR 0003 to focus on frontend freemium operational gating instead of a generic toggle pattern.

This keeps the record aligned with the current implementation around VITE_ENABLE_FREEMIUM, shared freemium helpers, and owner-versus-viewer access semantics.

Co-authored-by: OpenAI Codex <codex@openai.com>
Move route draft/timezone parsing, event and response decoding, and user fetch decoding into explicit frontend boundaries.

This removes remaining ADR 001 leaks from views and schedule-overlap while keeping ADR 002 timezone and Temporal semantics centralized and regression-tested.

Co-authored-by: OpenAI Codex <codex@openai.com>
Keep transport-derived shapes confined to explicit boundary modules and stop exposing them through internal frontend types.

Decode calendar event payloads at fetch boundaries, require canonical calendar event maps in settings components, and make plugin response normalization operate only on canonical Response records. This preserves ADR 001's single internal shape rule and locks the behavior down with regression coverage.

Co-authored-by: OpenAI Codex <codex@openai.com>
Keep route drafts, schedule-overlap state, and event mutation payloads on
canonical Temporal-backed frontend shapes instead of re-widening them to
legacy transport forms.

Add explicit mutation and fetch boundaries plus regression tests so event,
folder, and scheduling payloads are encoded and decoded only at the edges.

Co-authored-by: OpenAI Codex <codex@openai.com>
Split the previous frontend time ADR into narrower timezone, Temporal runtime, and scheduling ADRs.

Make the frontend Temporal runtime rules explicit so runtime/domain logic avoids Date and mixed encoded-or-Temporal unions for a single concept.

This keeps the ADR set easier to maintain and makes the time model constraints clearer for future migration and regression work.

Co-authored-by: OpenAI Codex <codex@openai.com>
Re-center transport decoding and encoding at explicit frontend boundaries.

This moves calendar availability payload narrowing into the transport-facing service layer, keeps plugin legacy response metadata separate from canonical Event responses, and encodes nested group response calendarOptions through the main submission boundary.

It also replaces component-local sign-up block response/user redefinitions with a shared canonical type and adds regression coverage to lock down the repaired boundaries.

Co-authored-by: OpenAI Codex <codex@openai.com>
Restrict serialized timezone and route-draft boundary shapes to encoded inputs only, and revive canonical Temporal runtime values at explicit decode boundaries.

Replace the remaining Stripe redirect Date-based timer logic with Temporal and keep fixed-offset saved timezone rehydration aligned with the shared helper behavior.

This remediates the ADR 004 runtime-model violations without changing backend contracts or widening internal models.

Co-authored-by: OpenAI Codex <codex@openai.com>
Add a frontend lint guardrail that blocks new native Date usage outside the explicit Vuetify date-picker adapter boundary.

Replace remaining Date-based transport and regression test helpers with Temporal-derived epoch helpers, and add picker boundary coverage to lock down the allowed native-Date conversion path.

This keeps ADR 004 enforced in runtime code and tests while preserving the one third-party integration that still requires Date objects.

Co-authored-by: OpenAI Codex <codex@openai.com>
Expand ADR 004 with explicit rules for acceptable native Date boundaries, Temporal-first encoded boundary handling, and Temporal-aligned test fixtures.

This documents the guardrails added during the runtime-model cleanup so future frontend work does not reintroduce Date-based logic or mixed encoded-and-Temporal representations.

Co-authored-by: OpenAI Codex <codex@openai.com>
Document the frontend rule that Temporal runtime collection semantics require specialized shared wrappers or helpers instead of native Set/Map identity behavior.

This adds an ADR that keeps the current ZdtSet and ZdtMap design explicit and sets the expectation for future Temporal collection needs in other types.

Co-authored-by: OpenAI Codex <codex@openai.com>
Sweep the frontend for legacy v-btn props and replace them with explicit Vuetify 3 button variants, sizes, and icon-button semantics.

Add focused regression coverage for landing navigation, floating action buttons, dialog footer actions, and outlined schedule controls so these migrations do not drift visually again.

Co-authored-by: OpenAI Codex <codex@openai.com>
Add a frontend ADR that makes current-version Vuetify primitive semantics explicit at the component boundary.

Document button-specific rules for variants, sizes, contrast, and floating icon actions so future UI-library cleanup work stays consistent and migration-sensitive regressions are easier to review and test.

Co-authored-by: OpenAI Codex <codex@openai.com>
Keep frontend dependency declarations consistently ordered in package.json.
This isolates the staged metadata-only change from the unstaged landing and Playwright work.

Co-authored-by: OpenAI Codex <codex@openai.com>
Add frontend Playwright configuration and supporting frontend config updates.
Keep browser-test artifacts scoped to the frontend test area and separate from the unstaged landing-page implementation work.

Co-authored-by: OpenAI Codex <codex@openai.com>
Reset the landing hero heading and shell spacing to explicit values that match the legacy site more closely.

This keeps the migrated landing page from inheriting oversized heading defaults and restores the header-to-hero vertical offset that drifted during the Vue 3 and Vuetify 3 migration.

Co-authored-by: OpenAI Codex <codex@openai.com>
Align the migrated landing hero more closely with the legacy site by restoring explicit CTA and legacy-note styling and by matching the header brand and note spacing more closely.

Add regression coverage for the landing hero style hooks so future parity work catches these visual contract regressions before they drift again.

Co-authored-by: OpenAI Codex <codex@openai.com>
Replace the remaining Vuetify 2 button props in the frontend with explicit Vuetify 3 variants so these views no longer rely on legacy prop aliases.

This keeps the migration aligned with the new button API and prevents lint failures from the legacy v-btn prop guard when editing weekday selectors, the pricing plan card, and the overlap pager button.

Co-authored-by: OpenAI Codex <codex@openai.com>
deemp and others added 26 commits June 12, 2026 18:05
Setting 'Shown in' on the event page was writing to localStorage['timezone'],
which is the same key the NewEvent and NewGroup forms read for their initial
timezone. The viewer's display preference bled into event creation.

Changed the ScheduleOverlap viewer to use a per-event key
'shownInTimezone_<eventId>' instead, isolating it from the shared
'timezone' key used by event/group creation forms.

A configurable storageKey option was added to useOwnedTimezone and the
underlying timezone_utils helpers (readSavedTimezone,
resolveSavedTimezoneSelection, resolveInitialTimezoneSelection) to
support this.

Co-authored-by: DeepSeek V4 Flash Free <noreply@deepseek.com>
Render advanced options inline in the event editor instead of behind a collapsible toggle so the settings stay visible during create and edit flows.

Restore a static Advanced options heading with the same section-title treatment as the other editor prompts and cover the always-open behavior with a unit regression test.

Co-authored-by: OpenAI Codex <codex@openai.com>
Switch the advanced-options time increment select to the underlined variant so it matches the timezone field styling in the new event form.

Update the component test expectation to cover the underlined variant and keep the styling change pinned to the current editor surface.

Co-authored-by: OpenAI Codex <codex@openai.com>
Update the specific-times helper in the new event flow to use the
requested "Click the Next button below" copy.

Keep the helper aligned under the checkbox label while overriding the
Vuetify message opacity so it matches the intended darker helper text.

Co-authored-by: OpenAI Codex <codex@openai.com>
Replace the new-event specific-times checkbox with the shared compact switch styling and remove switch shadow from the shared compact toggle.

Restructure the specific-times helper copy into a two-row layout so the toggle row stays aligned while the secondary instruction sits beneath the primary label.

Co-authored-by: OpenAI Codex <codex@openai.com>
Remove the trailing colon from the advanced options time increment label and match the select's active underline color to the timezone selector.

Update the NewEvent unit test to assert the new label and color prop, and extend the shared v-select test stub so the prop is observable in shallow mounts.

Co-authored-by: OpenAI Codex <codex@openai.com>
Refresh the NewEvent day-of-week selector to use the compact switch treatment and token-backed segmented control styling.

Default Start on Monday to enabled for new events and preserve Sunday selections when toggling the week start so weekday choices remain stable.

Co-authored-by: OpenAI Codex <codex@openai.com>
Adjust the NewEvent weekday segmented control to use moderate corner radii instead of pill-shaped ends.
Keep the current segmented layout, colors, and selection behavior while locking the rectangular geometry in the unit test.

Co-authored-by: OpenAI Codex <codex@openai.com>
Keep the new event time increment select aligned with its label by removing the offset and inheriting the surrounding typography.

Add regression coverage so the inline control keeps the same class contract and font treatment in future refactors.

Co-authored-by: OpenAI Codex <codex@openai.com>
Hide the muted underlined baseline on the advanced-options time increment select while the field is focused.
Keep the idle baseline intact and add a component regression test so the green active underline remains the only visible focus indicator.

Co-authored-by: OpenAI Codex <codex@openai.com>
Increase the bottom margin under the specific-times toggle in the new event form.
This brings the gap before the date selection heading closer to the legacy layout for timed events.

Co-authored-by: OpenAI Codex <codex@openai.com>
- mark completed items
- start prioritizing using MoSCoW
In view mode, each line was a separate <div> with tw-space-y-1
(4px gap), while edit mode used a single contenteditable div
with white-space: pre-wrap and no extra gap. This made multi-line
descriptions shift when toggling between modes.

Replaced the per-line split with a single white-space: pre-wrap
div to match edit mode rendering.

Co-authored-by: DeepSeek V4 Flash Free <noreply@deepseek.com>
…ability

Co-authored-by: DeepSeek V4 Flash Free <noreply@deepseek.com>
….vue

Moves the 'Delete availability' button and its confirmation dialog
from ScheduleOverlapSidebar.vue into Event.vue. Positions the button
below Cancel and Save on desktop (full-width, outlined red), and on
the left side on mobile (filled red). Adds retain-focus=false to the
dialog to fix desktop rendering.

Co-authored-by: DeepSeek V4 Flash Free <noreply@deepseek.com>
…vailability editing

Enables hour collapsing when respondents edit availability by
removing the EDIT_AVAILABILITY exclusion from canCollapseTimes.
Fixes drag interaction with collapsed rows by reading data-row/data-col
attributes from cell elements for all draggable states (not just
SET_SPECIFIC_TIMES) instead of using broken pixel-based row calculation.
Adds @pointerdown.stop and a defensive guard to prevent collapsed-row
clicks from triggering spurious drags.

Adds a Show all hours toggle to the desktop editing header (row 2,
between Save/Cancel and Delete) and a mobile Options menu (left of
Cancel) containing the same toggle.

Co-authored-by: DeepSeek V4 Flash Free <noreply@deepseek.com>
…k merging across gaps

buildOverlaidAvailability returned early without advancing idx when
getDateFromRowCol returned null for gap rows. This caused non-adjacent
valid slots to merge into a single block, dropping fragments like the
23:00 UTC overlay in America/Phoenix viewing timezone.

Also adds:
- startBaseRowIndex to OverlaidAvailabilityBlock for direct row tracking
- buildRenderedOverlayAvailability to project overlay fragments onto
  visible rendered rows, handling gaps and day-specific visibility
- regression test for UTC-7 specific-times overlay boundary

Co-authored-by: DeepSeek V4 Flash Free <noreply@deepseek.com>
Group saved UTC times by viewer-local date in edit mode instead of
anchoring columns to membership dates. When UTC slots cross midnight
in the viewer timezone, they now appear in the correct viewer-local
column. Supplementary columns for membership dates without saved
times keep newly-added dates editable.

Co-authored-by: DeepSeek V4 Flash Free <noreply@deepseek.com>
Keep the event page primary availability action visible as a disabled edit button when responses exist but the current viewer has nothing editable, and keep add availability available as the secondary action.

Avoid creating extra specific-times edit columns for membership dates that already own saved timed slots in the event timezone, which keeps the reopened edit grid aligned with saved times.

Co-authored-by: OpenAI Codex <codex@openai.com>
Normalize overlapping availability and if-needed slots at the frontend and backend response boundaries.

This keeps persisted response data canonical, prevents contradictory payloads from being written, and makes schedule-overlap views render dirty existing rows consistently.

Co-authored-by: OpenAI Codex <codex@openai.com>
Document the picked-date model for timed specific-date events.\n\nClarify that picked dates drive membership, event timezone drives slot generation, display timezone only affects rendering, and active slots are the editable subset within the enabled domain.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
Keep timed specific-times drafts aligned with the ADR-012 canonical event type mapping.
Tighten timed event transport decoding to require canonical timed fields and stop rebuilding unsupported legacy payloads on read.
Add regression coverage for strict timed decode paths and specific-times save/edit behavior.

Co-authored-by: OpenAI Codex <codex@openai.com>
Co-authored-by: OpenAI Codex <codex@openai.com>
Add VITE_ENABLE_SIGN_IN to the frontend build-time variable list and describe its behavior, including the default-enable semantics, what happens when set to false, and that it is a frontend-only gate.

Co-authored-by: DeepSeek V4 Flash Free <noreply@deepseek.com>
…flag

Add a build-time toggle to hide sign-in UI, guard auth routes, and
show a disabled-build message in place of sign-in prompts.

- Add signInAvailability utility (defaults to enabled when unset)
- Gate SignInDialog, header sign-in button, and landing sign-in
  button behind the flag
- Add router guard to redirect away from sign-in/auth routes
- Add disabled-build fallback text in NewEvent, NewSignUp, FAQ,
  NotSignedIn, EmailInput, ConfirmDetailsDialog
- Hide +Add calendar and reauthentication in CalendarAccounts
  and CalendarAccount when disabled
- Wire env var through Dockerfile, compose.yaml, env.d.ts, and
  .env.*.example files
- Add unit tests for utility, router guard, and landing page
- Guard signInGoogle/signInOutlook with silent early return

Co-authored-by: DeepSeek V4 Flash Free <noreply@deepseek.com>
@deemp deemp changed the title Modernize frontend Modernize app Jun 17, 2026
@deemp

deemp commented Jun 17, 2026

Copy link
Copy Markdown
Author

The current version seems usable. I'll continue improving it in my fork https://github.com/deemp/timeful, not on this branch.

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.

2 participants