Commit Graph

8 Commits

Author SHA1 Message Date
Víctor Falcón 87c63f2100
fix(auth): make build deterministic when REGISTRATION_ENABLED=false (#720)
## Problem

`bun run build` failed whenever `REGISTRATION_ENABLED=false`.

`config/fortify.php` only added `Features::registration()` when the env
flag
was truthy, so with the flag off Fortify never registered the
`/register`
routes. Wayfinder only generates helpers for registered routes, so the
`register` / `register.store` helpers imported by
`resources/js/pages/auth/login.tsx`
and `resources/js/pages/auth/register.tsx` no longer resolved and the
build
broke. The build outcome depended on an env flag — not deterministic.

## Fix

Decouple *route registration* from *whether sign-ups are accepted*:

- **Always register** `Features::registration()` in
`config/fortify.php`, so the
`/register` routes (and their Wayfinder helpers) always exist and the
build is
  deterministic regardless of the flag.
- Gate acceptance at **runtime** via a new
`config('auth.registration_enabled')`
value (mapped from `env('REGISTRATION_ENABLED', true)`), the single
source of truth.
- When registration is disabled:
- `GET /register` (Fortify register view) and `POST /register`
(`CreateNewUser`)
    return **403**.
- Registration CTAs stay hidden via `canRegister` (login view + landing
page).
- Guests are redirected to `/login`
(`AuthEntryPointService::guestRedirectRoute`).
- With the flag enabled (the default), registration behaves exactly as
before.

## Tests

- Rewrote `tests/Feature/Auth/RegistrationDisabledTest.php` to the
runtime
mechanism: registration enabled by default, route helpers always
registered
regardless of the flag, `GET`/`POST /register` return 403 when disabled
(and no
  user is created), and the landing/login CTAs hide.
- Updated the `DashboardTest` disabled-registration case to toggle
`config('auth.registration_enabled')` instead of filtering the Fortify
feature.
- Verified `bun run build` succeeds with `REGISTRATION_ENABLED=false`
and that the
  `register` / `register.store` Wayfinder helpers are generated.

## Docs

- Updated `README.md` and `.env.example` to describe the 403 behavior
(routes stay
  registered rather than being removed).
2026-07-22 07:04:20 +00:00
Víctor Falcón a5117aaae6
refactor: remove HIDE_AUTH_BUTTONS launch gate and waitlist apparatus (#717)
## What & why

The `HIDE_AUTH_BUTTONS` flag and its signed-link auth-override existed
only to gate registration/login behind a waitlist during the launch
period. That period is long over, so this removes the flag and the whole
waitlist apparatus. Registration and login are now always open, still
governed by `REGISTRATION_ENABLED` (Fortify's registration feature),
which is untouched.

## Removed

- **`HIDE_AUTH_BUTTONS`** config/env and
**`LandingAuthOverrideService`** — the signed-link override, override
cookie, and `?force=` bypass — plus its
`GenerateLandingAuthLinkCommand`.
- **Waitlist lead-capture + invitation apparatus**: `UserLeadController`
+ `waitlist.*`/`user-leads.*` routes, `StoreUserLeadRequest`, the
landing `WaitlistForm`, `waitlist/*` pages, waitlist/invitation mails,
invitation & re-invitation commands, the lead verification notification,
`LeadCohort`/`LeadCohortResolver`/`LeadPromoCodeAllocator`, and the
lead→Resend segment sync (scheduled tasks removed too).
- Frontend `hideAuthButtons`/`forcedRegistration` threading and the
`?force=` query on auth links (header, welcome, register, login).
- All tests for the removed features; `landing.hide_auth_buttons` config
stubs stripped from surviving tests.

## Kept on purpose (data preservation)

The `user_leads` **table, its migrations, and a lean `UserLead` model
are retained** — no destructive migration. Pre-launch contacts are not
lost, and new signups whose email matches a preserved lead still receive
that lead's promo code at Stripe checkout
(`SubscriptionController::resolveLeadPromotionCodeId`, unchanged).

## Testing

- Full non-Browser Pest suite green locally (**1948 passed**), plus
Pint, Prettier, ESLint.
- Browser QA (Playwright) on the running app: landing no longer shows
the waitlist form and renders the register/login CTAs; `/register`
renders the form (previously returned null when hidden); `/login` shows
the "Sign up" link pointing to `/register` with no `?force`. 16/16
functional checks passed; only pre-existing external-resource 404s in
console (gravatar `d=404`, `via.placeholder.com` seed images).

## Demo


https://github.com/user-attachments/assets/18a70f6d-b82f-4d92-b9de-da2610dc017a
2026-07-22 08:51:48 +02:00
Víctor Falcón 1cc10566a3
feat: parent/child category tree (#474)
## Summary

Adds nested categories (parent → child, up to **3 levels**) across the
app. Children inherit their parent's type and cashflow direction, and
every category selector now renders the hierarchy as an indented tree.
Gated behind the `CategoryTree` Pennant flag (off by default).

## Backend

- **Migration**: nullable self-referencing `parent_id`; uniqueness
scoped per-parent via a `parent_unique_marker` virtual column (root
names stay unique). New composite unique created before dropping the old
one so the `user_id` FK keeps a supporting index.
- **`CategoryTree` service**: descendant/ancestor resolution, depth &
cycle checks, type cascade, subtree deletion.
- **Validation**: depth limit, cycle prevention, inherited + locked
child type.
- **Delete strategies**: reparent (default), promote to root, or cascade
(uncategorizes affected transactions).
- **Transaction filter** expands a selected parent to its descendants.
- **Cashflow Sankey & breakdown** roll up to top-level parents with
click-to-drill (children + a parent "direct" node).
- **Budgets** tracking a parent also count their children's
transactions.
- **Unified** the frontend category query behind
`Category::forDisplay()` / `FRONTEND_COLUMNS` (8 call sites) so every
selector receives the full Category shape, including `parent_id`.

## Frontend

- New `category-tree.ts` helpers (build/flatten/descendants/path,
tree-aware selection toggle + tri-state).
- **Settings page**: indented tree, sortable by name/color/type
(siblings sorted, hierarchy preserved), parent picker, delete-strategy
dialog.
- **Combobox** (transaction table cell + edit/create modal + parent
picker): indented tree, search keeps matches with their ancestors.
- **Transaction filter**: indented tree, tri-state cascading selection
(parent ↔ children), themed checkboxes, selected branches float to top
on open, ancestor-aware search.
- **Categorizer palette** and **budget multi-select**: same indented
tree + ancestor-aware search.
- **Sankey**: click a parent node to drill into its children, with
breadcrumb.
- Pennant `CategoryTree` flag gates the parent UI.

## Tests

- Pest: model/validation, delete strategies, filter expansion, cashflow
rollup/drill, budget child inclusion.
- Vitest: tree-aware selection logic.
- All green; Pint / ESLint / Prettier clean.

## Rollout

Flag is off by default — enable per user with:
\`\`\`
php artisan feature:enable "App\\Features\\CategoryTree" you@example.com
\`\`\`
2026-06-03 19:30:12 +02:00
Víctor Falcón 905edeb4a2
fix: route new PWA guests to signup (#313)
## Summary
- redirect guest access to protected routes based on returning-user
cookie
- send new PWA guests to registration and returning guests to login
- persist returning-user cookie after register, login, and 2FA login
- keep explicit login links working via `force=1`

## Testing
- vendor/bin/pint --dirty --format agent
- php artisan test --compact tests/Feature/Auth/AuthenticationTest.php
tests/Feature/DashboardTest.php tests/Feature/Auth/RegistrationTest.php
2026-04-21 10:53:05 +01:00
Víctor Falcón c433088806
User Onboarding Flow (#23)
## Summary

Implements a complete user onboarding flow that guides new users through
setting up their account after registration.

## Features

- **Step-by-step wizard UI** with progress indicator and smooth
animations
- **E2E Encryption setup** with password strength indicator and
explanation of how encryption works
- **Account creation** supporting all account types (checking, savings,
credit card, investment, pension)
- **Category customization** with explanation of category types
(expenses, income, transfer)
- **Smart rules explanation** covering why there is no AI
auto-categorization (privacy & E2EE)
- **Transaction/balance import** based on account type
- **Sync integration** to ensure data consistency with backend

## Flow Diagram

```mermaid
flowchart TD
    A[User Registration] --> B[Welcome]

    B --> E[Encryption Explained]
    
    E --> C{Has encryption key?}

    C -->|No|F[Encryption Setup]
    C -->|Yes|G{Has Existing Accounts?}
    G -->|Yes| H[Show Existing Accounts]
    G -->|No| I[Create First Account]
    
    H --> J[Continue]
    I --> K{Account Type?}
    
    K -->|Checking/Savings/Credit Card| L[Import Transactions]
    K -->|Investment/Pension| M[Import Balances]
    
    J --> N[Category Types Explanation]
    L --> N
    M --> N
    
    N --> O[Customize Categories]
    O --> P[Smart Rules Explanation]
    P --> Q[More Accounts?]
    
    Q -->|Add More| I
    Q -->|Finish| R[Complete]
    
    R --> S[Redirect to Dashboard]
    S --> T{Subscribed?}
    T -->|No| U[Subscribe Page]
    T -->|Yes| V[Dashboard]
```

## Technical Changes

### Backend
- Added `onboarded_at` field to users table with migration
- Created `EnsureOnboardingComplete` middleware for redirect logic
- Created `OnboardingController` with index and complete actions
- Custom `RegisterResponse` to redirect new users to onboarding
- Updated `AccountController::store` to return JSON for fetch requests

### Frontend
- `OnboardingLayout` - fullscreen layout with step progress
- `useOnboardingState` hook - manages step navigation and state
- 12 step components for each onboarding screen
- Backend sync after account creation and imports

### Tests
- Feature tests for onboarding middleware
- Updated existing tests to use `onboarded()` factory state
2025-12-12 13:06:08 +01:00
Víctor Falcón 292297f483 E2E Encryption 2025-11-07 14:21:25 +00:00
Víctor Falcón 2eaad3204c Install Pest 2025-11-07 12:01:58 +00:00
Víctor Falcón fb140bbece Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00