Commit Graph

416 Commits

Author SHA1 Message Date
Víctor Falcón 70b603e901
feat: Spanish localization (#74)
## Pending
- [x] Translate landing page
- [x] Dashboard
- [x] Accounts list page
- [x] Account page
- [x] Cashflow
- [x] Budgets
- [x] Transactions
- [x] Settings

## Screenshots
<img width="1210" height="969" alt="image"
src="https://github.com/user-attachments/assets/c7935e5c-488d-4941-8f19-8834e5668257"
/>
<img width="1211" height="972" alt="image"
src="https://github.com/user-attachments/assets/e94e1daf-233a-4a49-aa65-5678c772d178"
/>
2026-02-08 11:58:08 +01:00
Víctor Falcón d9fd01a3cf chore: Don't override the DB in tests env, just the database 2026-02-07 18:55:44 +01:00
Víctor Falcón 1c425da330 chore: Remove MetricsWave tracking integration
MetricsWave visit tracker, event webhooks, and all funnel tracking
(lead, onboarding, import) are no longer needed.
2026-02-07 18:53:21 +01:00
Víctor Falcón e81c4345a0 chore: ignore local .php-version 2026-02-07 18:48:04 +01:00
Víctor Falcón 1930cf229e feat: Remove dev command from whispermoney 2026-02-07 18:31:31 +01:00
Víctor Falcón 1b0f3ba24d fix: Disable email verification on dev/local 2026-02-07 17:52:53 +01:00
Víctor Falcón da328efe79 fix: Install script improvements 2026-02-07 17:46:33 +01:00
Víctor Falcón abc71daa7e
feat: Show PWA install button on mobile landing page (#99)
## Summary

- Detects Android and iOS visitors on the landing page hero section
- Replaces the **Get Started** and **Check Demo** buttons with a single
**Install App** button on mobile
- **Android**: Captures the `beforeinstallprompt` event and triggers the
native PWA installation prompt when tapped
- **iOS**: Opens a dialog with step-by-step instructions (tap Share icon
→ Add to Home Screen)
- Desktop visitors see the original buttons unchanged

## New files

- `resources/js/hooks/use-pwa-install.ts` — Hook that detects platform
(Android/iOS), captures the `beforeinstallprompt` event, and exposes
`promptInstall()`
- `resources/js/components/landing/install-app-button.tsx` — Install
button component with iOS instructions dialog

## Screenshots
| Android & iOS | iOS Instructions |
|--------|--------|
| <img width="1082" height="2402" alt="localhost_8000_(Pixel 7)"
src="https://github.com/user-attachments/assets/bd42c24a-8a82-4f42-9f5f-c13bd5505836"
/> | <img width="1170" height="2532" alt="localhost_8000_(iPhone 12
Pro)"
src="https://github.com/user-attachments/assets/c4ec136f-46f1-44a5-a96a-d7afd41c6015"
/> |
2026-02-07 09:33:12 +01:00
Víctor Falcón 444c81c5aa
chore: Sync users to Resend after email verification (#98)
## Summary
- Changed `SyncUserToResendListener` to listen to the `Verified` event
instead of `Registered`, so contacts are only created in Resend after
the user verifies their email address
- Updated corresponding tests to use the `Verified` event

## Test plan
- [x] `SyncUserToResendListenerTest` passes with `Verified` event
- [x] All auth and listener tests pass
2026-02-03 22:59:28 +01:00
Víctor Falcón 370d388d99
feat: Enable email verification on sign up (#97)
## Summary
- Enables `MustVerifyEmail` on the `User` model so new users must verify
their email before accessing protected routes
- Unverified users are redirected to `/email/verify` when attempting to
access routes behind the `verified` middleware (subscribe, onboarding,
dashboard)
- A verification email is automatically sent on registration via Fortify

## Screenshot

![Email verification
page](https://raw.githubusercontent.com/whisper-money/whisper-money/mail-verification/storage/videos/email-verification-page.png)

## Video


https://github.com/whisper-money/whisper-money/raw/mail-verification/storage/videos/ac0945c527f014b9cd657f18c911f496.webm

## Test plan
- [x] New test: registration sends a `VerifyEmail` notification
- [x] New test: newly registered users have `email_verified_at` as null
- [x] New test: unverified users are redirected to `verification.notice`
from protected routes (subscribe, onboarding, dashboard)
- [x] New test: verified users are not redirected to verification notice
- [x] Existing email verification and notification tests still pass (31
auth tests, 52 settings tests)
- [x] Browser walkthrough: registration → redirected to `/email/verify`
page with "Resend verification email" button
2026-02-03 10:15:07 +01:00
Víctor Falcón 1500e5cd91 fix: Small dashboard UI fix 2026-02-02 13:55:32 +01:00
Víctor Falcón 74ac346ca0 fix: Top spending categories on mobile 2026-02-02 13:34:01 +01:00
Víctor Falcón f31a44bba2 fix: Top spending category must be 100% with always 2026-02-02 13:13:58 +01:00
Víctor Falcón e8e4f47804 fix: Top spending categories bug 2026-02-02 11:54:32 +01:00
Víctor Falcón 1d669b44ca fix: Header on iOS 2026-02-02 09:00:03 +01:00
Víctor Falcón 6101cfdfa0
fix: Prevent automerge when CI checks have failed (#95)
## Summary

- Fixes a race condition where the automerge workflow merged PR #94
despite `browser-tests` failing
- Adds SHA verification to ensure the CI run matches the PR's latest
commit (prevents stale merges from earlier pushes)
- Adds a failed-checks guard that inspects all PR check statuses before
merging

## What happened

PR #94 had two CI runs: an earlier one that passed and a later one where
`browser-tests` failed. The automerge workflow triggered on the first
successful run and merged the PR before the second run completed,
because it only checked `workflow_run.conclusion == 'success'` without
verifying it was for the latest commit.

## Test plan

- [x] Push a PR with the Automerge label, verify it merges when all
checks pass
- [x] Push a follow-up commit that breaks browser-tests — verify the
earlier success doesn't trigger a merge
- [x] Verify PRs without the Automerge label are unaffected
2026-02-02 08:58:47 +01:00
Víctor Falcón 21d36bb53b
feat: Show loading spinner on landing page when in PWA mode (#96)
## Summary
- Prevents the landing page from briefly flashing before redirecting to
`/dashboard` when accessing from an installed PWA
- Detects standalone mode synchronously via a `useState` initializer so
the spinner renders on the very first paint
- Shows a centered loading spinner instead of the full landing page
while the redirect happens

## Test plan
- [x] Open the app as an installed PWA and verify a spinner shows
instead of the landing page before redirecting to dashboard
- [x] Open the landing page in a regular browser and verify the full
landing page renders normally with no spinner
2026-02-02 08:28:52 +01:00
Víctor Falcón 28f9432af4
fix: Welcome page mobile display for iOS (#94)
## Summary

- Replaced the broken `fade-bottom` header with a floating **pill menu**
on mobile (matching the dashboard bottom nav style)
- Fixed landing screenshot images using `w-full` instead of `h-full
w-auto` for proper aspect ratio on mobile
- Reduced skew/rotation transforms on mobile for better readability
- Removed unused `fade-bottom` CSS class

## Changes

### Header (`header.tsx`)
- **Mobile** (`< sm`): Floating pill with `rounded-full`, glassmorphism
(`bg-background/70 backdrop-blur-xl`), positioned at `top-4` with
margin. Compact logo + auth buttons with `size="sm"` and `rounded-full`.
- **Desktop** (`sm+`): Clean fixed header with `bg-background/5
backdrop-blur-lg` (unchanged from original). All nav items (Github,
Discord, auth) preserved.

### Landing images (`welcome.tsx`)
- Changed images from `h-full w-auto` to `w-full` for proper responsive
aspect ratio
- Added `max-sm:` variants to reduce rotation/skew on mobile (`-16deg/8`
instead of `-24deg/12`)

### CSS (`app.css`)
- Removed unused `.fade-bottom` class

## Screenshots

### Mobile (390x844) — Pill Header
<img
src="https://raw.githubusercontent.com/whisper-money/whisper-money/fix/welcome-page-mobile-ios/.github/pr-screenshots/pr-mobile-hero.png"
width="300" />

### Mobile — Images Section
<img
src="https://raw.githubusercontent.com/whisper-money/whisper-money/fix/welcome-page-mobile-ios/.github/pr-screenshots/pr-mobile-images.png"
width="300" />

### Desktop (1440x900)
<img
src="https://raw.githubusercontent.com/whisper-money/whisper-money/fix/welcome-page-mobile-ios/.github/pr-screenshots/pr-desktop.png"
width="800" />

## Test plan

- [ ] Verify pill header displays correctly on iOS Safari (physical
device or simulator)
- [ ] Verify header buttons (Log in, Register) work on mobile
- [ ] Verify desktop header is unchanged with all buttons (Github,
Discord, Log in, Register)
- [ ] Verify landing images display with correct aspect ratio on mobile
- [ ] Verify dark mode works for both mobile pill header and desktop
header
2026-02-01 16:39:38 +01:00
Víctor Falcón 9bbd91ac12
feat: Add previous period comparison to budget chart (#93)
## Summary
- Load the previous budget period (with transactions) in
`BudgetController::show()` and pass it to the frontend
- Overlay previous period's cumulative spending as a dashed line on the
budget spending chart
- Use day-of-period alignment (Day 1, Day 2...) when comparing so
periods of different lengths align
- Tooltip shows both "Spent" (current) and "Last period" values when
hovering
- Chart now shows the full period timeline (not just up to today)
- When no previous period exists, chart behavior is unchanged

## Screenshots

### Light mode
![Budget chart - Light
mode](https://raw.githubusercontent.com/whisper-money/whisper-money/1cad899e771752e46183f8bb882f6eec4e3dd5de/.github/budget-final-light.png)

### Dark mode
![Budget chart - Dark
mode](https://raw.githubusercontent.com/whisper-money/whisper-money/1cad899e771752e46183f8bb882f6eec4e3dd5de/.github/budget-final-dark.png)

## Test plan
- [x] Tests pass: `php artisan test --compact
tests/Feature/BudgetTest.php` (9 tests)
- [x] Pint formatting passes
- [x] ESLint + Prettier pass
- [ ] Manual: view a budget with a prior period — dashed line appears
- [ ] Manual: view a budget with no prior period — chart unchanged
2026-02-01 14:21:13 +01:00
Víctor Falcón ab160ae489 fix: Automerge PR's where not triggering CI on main branch 2026-02-01 12:40:10 +01:00
Víctor Falcón 1d1c0c36fe
feat: Redirect to dashboard when running as installed PWA (#92)
## Summary
- Detect standalone/PWA display mode (`display-mode: standalone` and iOS
`navigator.standalone`) on the welcome page
- Redirect to `/dashboard` immediately so the landing page is never
shown inside the installed app
- Unauthenticated PWA users go: open app → `/dashboard` → `/login` (via
auth middleware)

## Test plan
- [x] Install the PWA and open it — should go straight to
login/dashboard, never the landing page
- [x] Visit the website in a regular browser tab — landing page still
works normally
2026-02-01 11:28:52 +00:00
Víctor Falcón 00b2ca7c55 fix: Budget period not found on last day of period (#91)
## Summary

- Budget periods use `date` columns (`start_date`, `end_date`) but
queries compared them against `now()` which includes a time component
(e.g. `2026-01-31 15:30:00`). MySQL converts the date to midnight for
comparison, so `end_date >= now()` evaluates to `'2026-01-31 00:00:00'
>= '2026-01-31 15:30:00'` → **false** — causing "No active period" on
the last day of every period.
- Replaced `now()` with `today()` (date-only) in all budget period date
comparisons across `Budget::getCurrentPeriod()`,
`BudgetController::index()`, and `GenerateBudgetPeriods` command.

## Test plan

- [x] Added 4 Pest tests covering the edge case (last day at various
times, first day at end of day, index/show endpoints)
- [x] All existing budget tests pass
2026-02-01 11:33:10 +01:00
Víctor Falcón b4897ef425 feat: Improve PWA standalone experience and redirect to dashboard (#90)
## Summary
- Add service worker registration and `viewport-fit=cover` for proper
iOS standalone mode (no Safari chrome)
- Change manifest `start_url` to `/dashboard` so PWA users skip the
landing page
- Add tests for PWA configuration

## Test plan
- [x] Deploy and re-add PWA to iOS home screen — should open as
standalone (no address bar)
- [x] PWA launch should go to `/dashboard` instead of the landing page
- [x] Non-authenticated PWA users should be redirected to login

Closes #71
2026-02-01 11:33:10 +01:00
Víctor Falcón dfd8bf8092 fix: Use workflow_run trigger for automerge (#89)
## Summary
- Fixes the automerge workflow that was merging PRs immediately without
waiting for CI
- Replaces `gh pr merge --auto` (requires branch protection) with a
`workflow_run` trigger that fires after CI completes
- When CI passes on a PR with the "Automerge" label, the PR is
squash-merged automatically

## Test plan
- [x] Merge this PR manually to get the workflow on `main`
- [x] Create a test PR, add the "Automerge" label, and verify it only
merges after CI passes
2026-02-01 11:33:10 +01:00
Víctor Falcón 6aa9da3df3 feat: Replace user avatar with Facehash faces (#86)
## Summary
- Replaced the default initials-based avatar fallback with
[Facehash](https://facehash.dev/) — deterministic, unique avatar faces
generated from the user's name
- Same name always produces the same face, no API calls needed
- Fully rounded with `rounded-full` styling

## Preview

![Facehash avatars](https://facehash.dev/og-image.png)

Each user gets a unique, consistent face based on their name — no more
generic initials.

## Changes
- Installed `facehash` package
- Updated `resources/js/components/user-info.tsx` to use `<Facehash>`
instead of `<Avatar>` with initials fallback

## Test plan
- [x] Verify avatar renders correctly in the sidebar footer (desktop)
- [x] Verify avatar renders correctly in the mobile header
- [x] Verify avatar renders correctly in the user dropdown menu
- [x] Confirm the same user always gets the same face
- [x] Check dark mode appearance
2026-02-01 11:33:09 +01:00
Víctor Falcón cc666a3111 test: Fix Carbon month overflow in date calculations (#87)
## Summary
- Replace `subMonths()` with `subMonthsNoOverflow()` across controllers,
commands, and tests to prevent date overflow on months with 31 days
- On Jan 31, `subMonths(2)` overflows: Nov 31 doesn't exist → Carbon
rolls forward to Dec 1, producing wrong date ranges and off-by-one month
counts

## Root cause
Carbon's `subMonths()` allows day overflow. When the target month has
fewer days than the source date, it rolls into the next month. For
example:
- `2026-01-31 subMonths(2)` → Nov 31 → **2025-12-01** (expected Nov 30)
- `2026-01-31 subMonths(11)` → Feb 31 → **2025-03-03** (expected Feb 28)

`subMonthsNoOverflow()` clamps to the last valid day of the target month
instead.

## Files changed
- `app/Http/Controllers/Api/CashflowAnalyticsController.php` — cashflow
trend start date calculation
- `app/Console/Commands/ResetDemoAccountCommand.php` — balance history
date generation (was causing duplicate `balance_date` entries)
- `tests/Feature/AccountControllerTest.php` — balance evolution `from`
param
- `tests/Feature/CashflowAnalyticsTest.php` — transaction date
generation in loop
- `tests/Feature/DashboardAnalyticsTest.php` — net worth evolution
`from` param

## Test plan
- [x] `account balance evolution returns data` — expects 3 months, was
getting 2
- [x] `cashflow trend returns monthly data` — expects 3 months, was
getting 2
- [x] `cashflow trend defaults to 12 months` — expects 12 months, was
getting 11
- [x] `net worth evolution returns monthly data points` — expects 3
months, was getting 2
- [x] `demo:reset creates demo user` — was throwing
UniqueConstraintViolation on duplicate balance dates
2026-02-01 11:33:09 +01:00
Víctor Falcón 10bd7da5db
feat: Add automerge workflow for labeled PRs (#88)
## Summary
- Adds a new GitHub Actions workflow that enables auto-merge when the
"Automerge" label is added to a PR
- Uses `gh pr merge --auto --squash` to squash-merge once all required
CI checks pass

## Test plan
- [ ] Add the "Automerge" label to this PR to verify the workflow
triggers
- [ ] Confirm the PR is auto-merged after CI passes
2026-01-31 13:34:17 +00:00
Víctor Falcón cfa5bfd728 chore: release v0.1.9 2026-01-28 21:30:14 +01:00
Víctor Falcón 530d21d776 test: Optimize DemoAccountRestrictionsTest to run under 10s
Replace slow demo:reset command with factory-created user since
isDemoAccount() only checks email match. Consolidate 8 tests into 2.
2026-01-28 21:27:27 +01:00
Víctor Falcón 952a5d4be7
feat: Sync new users to Resend contacts (#85)
## Summary
- Add `SyncUserToResendListener` to automatically sync users to Resend
contacts on registration
- Add `ResendService` for Resend API interactions
- Add `resend:sync` command to bulk sync existing users

## Changes
- **New**: `app/Listeners/SyncUserToResendListener.php` -
Auto-discovered queued listener
- **New**: `app/Services/ResendService.php` - Service for Resend
contacts API
- **New**: `app/Console/Commands/ResendSyncCommand.php` - Bulk sync
command
- **New**: Tests for listener and command

## Usage
New users are automatically synced on registration. For existing users:
```bash
php artisan resend:sync
```

## Test plan
- [x] New user registration triggers contact sync
- [x] `resend:sync` command syncs all existing users
- [x] Graceful handling when API key is not configured
- [x] Duplicate contacts are handled by Resend (no errors)
2026-01-28 21:25:58 +01:00
Víctor Falcón f03fcf5ac6 feat: Print sponsor message on whispermoney script 2026-01-28 19:57:28 +01:00
Víctor Falcón a9b889b145
feat: Release budgets feature to all users (#84)
## Summary
- Enable the budgets Pennant feature flag for all users by changing the
default from `false` to `true`

## Test plan
- [x] Existing `BudgetFeatureFlagTest` passes (9 tests, 39 assertions)
2026-01-28 09:56:52 +01:00
Víctor Falcón c32a7ab6ae
Fix chart hover background visibility in dark mode (#82)
## Summary

- Fixed hover background color in dark mode to improve visibility and
contrast with chart bars
- Changed from `fill-muted` (oklch(0.269 0 0)) to `fill-white/5` in dark
mode for a lighter, more transparent background
- Resolves issue where hover background was too similar to bar colors in
dark mode

## Changes

- Updated `ChartContainer` component to use `dark:fill-white/5` for the
recharts tooltip cursor in dark mode
- Maintains existing `fill-muted` behavior for light mode

Closes #66
2026-01-27 19:08:35 +01:00
Víctor Falcón cffa979765
Fix mobile bottom nav alignment for 5 items (#81)
## Summary
- Removed explicit `gap-6` from the mobile bottom navigation bar so
`justify-evenly` distributes items naturally
- Reduced horizontal padding from `px-8` to `px-4` to give items more
room when 5 are present (e.g., when Cashflow and Budgets features are
enabled)

Closes #77 

## Test plan
- [x] Verify the bottom nav looks correct with 4 items (Cashflow or
Budgets feature disabled)
- [x] Verify the bottom nav looks correct with 5 items (all features
enabled)
- [x] Confirm items are evenly spaced and none are clipped on small
screens
2026-01-27 19:02:38 +01:00
Víctor Falcón 9620d3cfe1 chore: Update README to make it clearer that whispermoney install command must be run first 2026-01-27 12:09:35 +01:00
Víctor Falcón d1f69a284a fix: Delete transactions on local browser DB after deleting it on the backend 2026-01-27 11:12:05 +01:00
Víctor Falcón bbc3027545 feat: Reload transactions table on import proccess complete 2026-01-27 11:12:04 +01:00
Víctor Falcón a6a2a0d58c
fix: Apply automation rule labels on transaction creation and import (#79)
## Summary
- Fixes automation rules not applying labels when creating transactions
manually or via CSV import
- Eager-loads the `labels` relationship on automation rules in both
`TransactionController` and `HandleInertiaRequests`
- Syncs `label_ids` on the transaction `store` endpoint (was accepted
but never persisted)
- Passes `automationRules` prop through the full component chain:
`index.tsx` → `TransactionActionsMenu` → `ImportTransactionsDrawer`, and
`index.tsx` → `EditTransactionDialog`
- Passes `automationRules` as the missing 5th argument to
`reEvaluateAll()`

Closes #61

## Test plan
- [x] Existing feature tests pass (`php artisan test
--filter=Transaction`, `--filter=AutomationRule`)
- [x] Pint, ESLint, and Prettier all pass
- [x] Manually verify: create a transaction matching an automation rule
with labels → labels are applied
- [x] Manually verify: import CSV with transactions matching rules with
labels → labels are applied
- [x] Manually verify: "Re-evaluate All" applies labels from matched
rules
2026-01-27 11:11:29 +01:00
Víctor Falcón bc02bf948f chore: Update larevel boot package 2026-01-27 10:55:46 +01:00
Víctor Falcón 0e7ea220b1
chore: Add GitHub issue template for bug reports (#80)
## Summary
- Adds a structured bug report issue template using GitHub's YAML form
format
- Includes fields for environment, steps to reproduce, expected/actual
behavior, and additional context

## Test plan
- [x] Verify the template appears when creating a new issue on GitHub
- [x] Confirm all form fields render correctly
2026-01-26 19:16:32 +01:00
Víctor Falcón ccba0cde12 Add star history to the README 2026-01-26 14:59:29 +01:00
Víctor Falcón f4f25ac48a chore: release v0.1.8 2026-01-25 17:25:05 +01:00
Víctor Falcón 8a8d5962b5
refactor: Simplify transaction endpoints architecture (#76)
## Summary

Simplifies transaction endpoints by separating sync API (read-only) from
web routes (mutations). This creates clearer architectural boundaries
and fixes inconsistent label behavior.

## Architecture Changes

### Before
- Sync API handled both sync (GET) and mutations (POST/PATCH/DELETE)
- Frontend used sync API for all operations
- Bulk updates merged labels, single updates replaced them

### After  
- **Sync API**: Read-only GET endpoint for IndexedDB sync
- **Web Routes**: All mutations (create, update, delete, bulk
operations)
- **Consistent behavior**: All label updates replace instead of merge

## Endpoint Mapping

| Operation | Old Endpoint | New Endpoint |
|-----------|-------------|--------------|
| Fetch/Sync | `GET /api/sync/transactions` | `GET
/api/sync/transactions`  |
| Create | `POST /api/sync/transactions` | `POST /transactions` |
| Update | `PATCH /api/sync/transactions/{id}` | `PATCH
/transactions/{id}` |
| Delete | `DELETE /api/sync/transactions/{id}` | `DELETE
/transactions/{id}` |
| Bulk Update | `PATCH /transactions/bulk` | `PATCH /transactions/bulk`
 |

## Backend Changes

### TransactionSyncController
-  Simplified to read-only `index()` method
-  Removed `store()`, `update()`, `destroy()` methods
-  Added docblock clarifying purpose

### TransactionController
-  Fixed `store()` to return labels with `id, name, color`
-  Fixed `update()` to return labels with `id, name, color`  
-  Changed `bulkUpdate()` to replace labels instead of merging

### Cleanup
-  Removed `UpdateTransactionSyncRequest` (no longer needed)
-  Updated `routes/api.php` to only have GET for sync

## Frontend Changes

### transaction-sync.ts
-  Updated `create()` → `POST /transactions`
-  Updated `update()` → `PATCH /transactions/{id}`
-  Updated `delete()` → `DELETE /transactions/{id}`
-  Replaced all `fetch()` calls with `axios`
-  Removed manual CSRF token handling

## Test Changes

### TransactionSyncTest
-  Removed create/update/delete tests
-  Kept only read-only sync tests
-  Added test for labels format

### BulkUpdateTransactionsTest  
-  Added test verifying label replacement behavior

## Test Results

All tests passing! 

```
Tests:    42 passed (235 assertions)
Duration: 6.17s

✓ TransactionTest: 29 tests
✓ TransactionSyncTest: 4 tests
✓ BulkUpdateTransactionsTest: 9 tests
```

## Benefits

1. **Clear separation of concerns**: Sync API is read-only, web routes
handle mutations
2. **Consistent label behavior**: All updates replace labels (not merge)
3. **Standardized HTTP client**: Axios everywhere, automatic CSRF
handling
4. **Reduced complexity**: Removed duplicate form request class
5. **Better architecture**: Aligns with intended design

## Breaking Changes

None - All changes are internal to how the frontend calls the backend.
The functionality remains the same from the user's perspective.

## Files Changed

- `app/Http/Controllers/Sync/TransactionSyncController.php`
- `app/Http/Controllers/TransactionController.php`
- `app/Http/Requests/UpdateTransactionSyncRequest.php` (deleted)
- `resources/js/services/transaction-sync.ts`
- `routes/api.php`
- `tests/Feature/Sync/TransactionSyncTest.php`
- `tests/Feature/BulkUpdateTransactionsTest.php`
2026-01-25 16:15:17 +01:00
Víctor Falcón 91dd23edc0 fix: Update transactions 2026-01-25 11:31:17 +01:00
Víctor Falcón e5eca1eacb
feat: Add label support to single transaction update endpoint (#75)
## Summary
- Added label update support to the single transaction `update()`
endpoint
- Previously, labels could only be updated via the bulk update endpoint
- Now both endpoints support label updates with consistent behavior

## Changes
- **UpdateTransactionRequest**: Added `label_ids` validation (array of
UUIDs that must belong to user)
- **TransactionController@update**: Now handles label updates alongside
other attributes
  - Syncs labels when `label_ids` is provided
  - Reloads labels relationship for fresh data in events
  - Fires single `updated` event after all changes
- **AssignTransactionToBudget listener**: Ensures labels are loaded
fresh after queue serialization
- **Tests**: Added 6 comprehensive tests covering all label update
scenarios

## Behavior
- **Don't send `label_ids`** → labels unchanged (partial update)
- **Send `label_ids: []`** → removes all labels
- **Send `label_ids: [uuid1, uuid2]`** → replaces labels with these

## Testing
When labels are updated, the `TransactionUpdated` event fires and the
transaction is automatically assigned to matching budgets (via queued
listener).

All tests pass (29 tests, 134 assertions).

Fixes the issue where transactions wouldn't appear in budget after
adding a matching label via the single transaction update endpoint.
2026-01-24 17:54:54 +01:00
Víctor Falcón 134a292ddb
fix: Fire transaction updated event after a label change (#73) 2026-01-22 15:25:52 +01:00
Víctor Falcón f30e600b75 fix: typo in composer dev command 2026-01-22 15:07:37 +01:00
Víctor Falcón fee7ad36ab
feat: Load transactions history on budget created (#72) 2026-01-22 11:10:15 +01:00
Víctor Falcón 839e4993df test: Improve demo reset tests performance 2026-01-22 10:24:07 +01:00
Víctor Falcón d216d0c071 fix: Progress bar color on dark scheme 2026-01-22 09:25:53 +01:00