1 - Expose UUIDs in API responses and update frontend types

This commit is contained in:
myrmidex 2026-03-19 21:01:56 +01:00
parent 367f255200
commit 72cc6ff0b7
12 changed files with 43 additions and 31 deletions

View file

@ -19,7 +19,7 @@ public function index(Scenario $scenario): JsonResponse
->get() ->get()
->map(function ($bucket) { ->map(function ($bucket) {
return [ return [
'id' => $bucket->id, 'id' => $bucket->uuid,
'name' => $bucket->name, 'name' => $bucket->name,
'priority' => $bucket->priority, 'priority' => $bucket->priority,
'sort_order' => $bucket->sort_order, 'sort_order' => $bucket->sort_order,
@ -136,12 +136,12 @@ public function updatePriorities(Request $request, Scenario $scenario): JsonResp
{ {
$validated = $request->validate([ $validated = $request->validate([
'bucket_priorities' => 'required|array', 'bucket_priorities' => 'required|array',
'bucket_priorities.*.id' => 'required|exists:buckets,id', 'bucket_priorities.*.id' => 'required|exists:buckets,uuid',
'bucket_priorities.*.priority' => 'required|integer|min:1', 'bucket_priorities.*.priority' => 'required|integer|min:1',
]); ]);
foreach ($validated['bucket_priorities'] as $bucketData) { foreach ($validated['bucket_priorities'] as $bucketData) {
$bucket = Bucket::find($bucketData['id']); $bucket = Bucket::where('uuid', $bucketData['id'])->first();
if ($bucket && $bucket->scenario_id === $scenario->id) { if ($bucket && $bucket->scenario_id === $scenario->id) {
$bucket->update([ $bucket->update([
'priority' => $bucketData['priority'], 'priority' => $bucketData['priority'],
@ -161,7 +161,7 @@ public function updatePriorities(Request $request, Scenario $scenario): JsonResp
private function formatBucketResponse(Bucket $bucket): array private function formatBucketResponse(Bucket $bucket): array
{ {
return [ return [
'id' => $bucket->id, 'id' => $bucket->uuid,
'name' => $bucket->name, 'name' => $bucket->name,
'priority' => $bucket->priority, 'priority' => $bucket->priority,
'sort_order' => $bucket->sort_order, 'sort_order' => $bucket->sort_order,

View file

@ -41,7 +41,7 @@ public function rules(): array
], ],
'start_date' => ['required', 'date', 'date_format:Y-m-d'], 'start_date' => ['required', 'date', 'date_format:Y-m-d'],
'end_date' => ['nullable', 'date', 'date_format:Y-m-d', 'after_or_equal:start_date'], 'end_date' => ['nullable', 'date', 'date_format:Y-m-d', 'after_or_equal:start_date'],
'bucket_id' => ['nullable', 'exists:buckets,id'], 'bucket_id' => ['nullable', 'exists:buckets,uuid'],
'description' => ['nullable', 'string', 'max:1000'], 'description' => ['nullable', 'string', 'max:1000'],
]; ];
} }
@ -67,7 +67,7 @@ public function withValidator($validator): void
$scenario = $this->route('scenario'); $scenario = $this->route('scenario');
$bucketBelongsToScenario = $scenario->buckets() $bucketBelongsToScenario = $scenario->buckets()
->where('id', $this->bucket_id) ->where('uuid', $this->bucket_id)
->exists(); ->exists();
if (! $bucketBelongsToScenario) { if (! $bucketBelongsToScenario) {

View file

@ -34,7 +34,7 @@ public function rules(): array
], ],
'start_date' => ['required', 'date', 'date_format:Y-m-d'], 'start_date' => ['required', 'date', 'date_format:Y-m-d'],
'end_date' => ['nullable', 'date', 'date_format:Y-m-d', 'after_or_equal:start_date'], 'end_date' => ['nullable', 'date', 'date_format:Y-m-d', 'after_or_equal:start_date'],
'bucket_id' => ['nullable', 'exists:buckets,id'], 'bucket_id' => ['nullable', 'exists:buckets,uuid'],
'description' => ['nullable', 'string', 'max:1000'], 'description' => ['nullable', 'string', 'max:1000'],
'is_active' => ['boolean'], 'is_active' => ['boolean'],
]; ];
@ -61,7 +61,7 @@ public function withValidator($validator): void
// Validate that the bucket belongs to the stream's scenario // Validate that the bucket belongs to the stream's scenario
if ($this->bucket_id) { if ($this->bucket_id) {
$bucketBelongsToScenario = $stream->scenario->buckets() $bucketBelongsToScenario = $stream->scenario->buckets()
->where('id', $this->bucket_id) ->where('uuid', $this->bucket_id)
->exists(); ->exists();
if (! $bucketBelongsToScenario) { if (! $bucketBelongsToScenario) {

View file

@ -10,7 +10,7 @@ class BucketResource extends JsonResource
public function toArray(Request $request): array public function toArray(Request $request): array
{ {
return [ return [
'id' => $this->id, 'id' => $this->uuid,
'name' => $this->name, 'name' => $this->name,
'priority' => $this->priority, 'priority' => $this->priority,
'sort_order' => $this->sort_order, 'sort_order' => $this->sort_order,

View file

@ -10,8 +10,8 @@ class DrawResource extends JsonResource
public function toArray(Request $request): array public function toArray(Request $request): array
{ {
return [ return [
'id' => $this->id, 'id' => $this->uuid,
'bucket_id' => $this->bucket_id, 'bucket_id' => $this->bucket?->uuid,
'amount' => $this->amount_currency, 'amount' => $this->amount_currency,
'formatted_amount' => $this->formatted_amount, 'formatted_amount' => $this->formatted_amount,
'date' => $this->date->format('Y-m-d'), 'date' => $this->date->format('Y-m-d'),

View file

@ -10,8 +10,8 @@ class InflowResource extends JsonResource
public function toArray(Request $request): array public function toArray(Request $request): array
{ {
return [ return [
'id' => $this->id, 'id' => $this->uuid,
'stream_id' => $this->stream_id, 'stream_id' => $this->stream?->uuid,
'amount' => $this->amount_currency, 'amount' => $this->amount_currency,
'formatted_amount' => $this->formatted_amount, 'formatted_amount' => $this->formatted_amount,
'date' => $this->date->format('Y-m-d'), 'date' => $this->date->format('Y-m-d'),

View file

@ -10,9 +10,9 @@ class OutflowResource extends JsonResource
public function toArray(Request $request): array public function toArray(Request $request): array
{ {
return [ return [
'id' => $this->id, 'id' => $this->uuid,
'stream_id' => $this->stream_id, 'stream_id' => $this->stream?->uuid,
'bucket_id' => $this->bucket_id, 'bucket_id' => $this->bucket?->uuid,
'amount' => $this->amount_currency, 'amount' => $this->amount_currency,
'formatted_amount' => $this->formatted_amount, 'formatted_amount' => $this->formatted_amount,
'date' => $this->date->format('Y-m-d'), 'date' => $this->date->format('Y-m-d'),

View file

@ -10,7 +10,7 @@ class ScenarioResource extends JsonResource
public function toArray(Request $request): array public function toArray(Request $request): array
{ {
return [ return [
'id' => $this->id, 'id' => $this->uuid,
'name' => $this->name, 'name' => $this->name,
'description' => $this->description, 'description' => $this->description,
'created_at' => $this->created_at, 'created_at' => $this->created_at,

View file

@ -10,7 +10,7 @@ class StreamResource extends JsonResource
public function toArray(Request $request): array public function toArray(Request $request): array
{ {
return [ return [
'id' => $this->id, 'id' => $this->uuid,
'name' => $this->name, 'name' => $this->name,
'type' => $this->type, 'type' => $this->type,
'type_label' => $this->getTypeLabel(), 'type_label' => $this->getTypeLabel(),
@ -19,7 +19,7 @@ public function toArray(Request $request): array
'frequency_label' => $this->getFrequencyLabel(), 'frequency_label' => $this->getFrequencyLabel(),
'start_date' => $this->start_date->format('Y-m-d'), 'start_date' => $this->start_date->format('Y-m-d'),
'end_date' => $this->end_date?->format('Y-m-d'), 'end_date' => $this->end_date?->format('Y-m-d'),
'bucket_id' => $this->bucket_id, 'bucket_id' => $this->bucket?->uuid,
'bucket_name' => $this->bucket?->name, 'bucket_name' => $this->bucket?->name,
'description' => $this->description, 'description' => $this->description,
'is_active' => $this->is_active, 'is_active' => $this->is_active,

View file

@ -2,6 +2,7 @@
namespace App\Repositories; namespace App\Repositories;
use App\Models\Bucket;
use App\Models\Scenario; use App\Models\Scenario;
use App\Models\Stream; use App\Models\Stream;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -11,7 +12,7 @@ class StreamRepository
public function getForScenario(Scenario $scenario): Collection public function getForScenario(Scenario $scenario): Collection
{ {
return $scenario->streams() return $scenario->streams()
->with('bucket:id,name') ->with('bucket:id,uuid,name')
->orderBy('type') ->orderBy('type')
->orderBy('name') ->orderBy('name')
->get(); ->get();
@ -19,11 +20,15 @@ public function getForScenario(Scenario $scenario): Collection
public function create(Scenario $scenario, array $data): Stream public function create(Scenario $scenario, array $data): Stream
{ {
$this->resolveBucketId($data);
return $scenario->streams()->create($data); return $scenario->streams()->create($data);
} }
public function update(Stream $stream, array $data): Stream public function update(Stream $stream, array $data): Stream
{ {
$this->resolveBucketId($data);
$stream->update($data); $stream->update($data);
return $stream->fresh('bucket'); return $stream->fresh('bucket');
@ -46,17 +51,24 @@ public function toggleActive(Stream $stream): Stream
/** /**
* Check if a bucket belongs to the scenario * Check if a bucket belongs to the scenario
*/ */
public function bucketBelongsToScenario(Scenario $scenario, ?int $bucketId): bool public function bucketBelongsToScenario(Scenario $scenario, ?string $bucketUuid): bool
{ {
if (! $bucketId) { if (! $bucketUuid) {
return true; return true;
} }
return $scenario->buckets() return $scenario->buckets()
->where('id', $bucketId) ->where('uuid', $bucketUuid)
->exists(); ->exists();
} }
private function resolveBucketId(array &$data): void
{
if (! empty($data['bucket_id'])) {
$data['bucket_id'] = Bucket::where('uuid', $data['bucket_id'])->value('id');
}
}
/** /**
* Get streams grouped by type * Get streams grouped by type
*/ */
@ -64,12 +76,12 @@ public function getGroupedByType(Scenario $scenario): array
{ {
return [ return [
'income' => $scenario->streams() 'income' => $scenario->streams()
->with('bucket:id,name') ->with('bucket:id,uuid,name')
->byType(Stream::TYPE_INCOME) ->byType(Stream::TYPE_INCOME)
->orderBy('name') ->orderBy('name')
->get(), ->get(),
'expense' => $scenario->streams() 'expense' => $scenario->streams()
->with('bucket:id,name') ->with('bucket:id,uuid,name')
->byType(Stream::TYPE_EXPENSE) ->byType(Stream::TYPE_EXPENSE)
->orderBy('name') ->orderBy('name')
->get(), ->get(),

View file

@ -2,7 +2,7 @@ import { Head, router } from '@inertiajs/react';
import React, { useState } from 'react'; import React, { useState } from 'react';
interface Scenario { interface Scenario {
id: number; id: string;
name: string; name: string;
created_at: string; created_at: string;
updated_at: string; updated_at: string;

View file

@ -2,14 +2,14 @@ import { Head, Link, router } from '@inertiajs/react';
import { useState } from 'react'; import { useState } from 'react';
interface Scenario { interface Scenario {
id: number; id: string;
name: string; name: string;
created_at: string; created_at: string;
updated_at: string; updated_at: string;
} }
interface Bucket { interface Bucket {
id: number; id: string;
name: string; name: string;
priority: number; priority: number;
sort_order: number; sort_order: number;
@ -23,7 +23,7 @@ interface Bucket {
} }
interface Stream { interface Stream {
id: number; id: string;
name: string; name: string;
type: 'income' | 'expense'; type: 'income' | 'expense';
type_label: string; type_label: string;
@ -32,7 +32,7 @@ interface Stream {
frequency_label: string; frequency_label: string;
start_date: string; start_date: string;
end_date: string | null; end_date: string | null;
bucket_id: number | null; bucket_id: string | null;
bucket_name: string | null; bucket_name: string | null;
description: string | null; description: string | null;
is_active: boolean; is_active: boolean;
@ -140,7 +140,7 @@ export default function Show({ scenario, buckets, streams = { data: [] }, stream
} }
}; };
const handlePriorityChange = async (bucketId: number, direction: 'up' | 'down') => { const handlePriorityChange = async (bucketId: string, direction: 'up' | 'down') => {
const bucket = buckets.data.find(b => b.id === bucketId); const bucket = buckets.data.find(b => b.id === bucketId);
if (!bucket) return; if (!bucket) return;