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()
->map(function ($bucket) {
return [
'id' => $bucket->id,
'id' => $bucket->uuid,
'name' => $bucket->name,
'priority' => $bucket->priority,
'sort_order' => $bucket->sort_order,
@ -136,12 +136,12 @@ public function updatePriorities(Request $request, Scenario $scenario): JsonResp
{
$validated = $request->validate([
'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',
]);
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) {
$bucket->update([
'priority' => $bucketData['priority'],
@ -161,7 +161,7 @@ public function updatePriorities(Request $request, Scenario $scenario): JsonResp
private function formatBucketResponse(Bucket $bucket): array
{
return [
'id' => $bucket->id,
'id' => $bucket->uuid,
'name' => $bucket->name,
'priority' => $bucket->priority,
'sort_order' => $bucket->sort_order,

View file

@ -41,7 +41,7 @@ public function rules(): array
],
'start_date' => ['required', 'date', 'date_format:Y-m-d'],
'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'],
];
}
@ -67,7 +67,7 @@ public function withValidator($validator): void
$scenario = $this->route('scenario');
$bucketBelongsToScenario = $scenario->buckets()
->where('id', $this->bucket_id)
->where('uuid', $this->bucket_id)
->exists();
if (! $bucketBelongsToScenario) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,7 +10,7 @@ class StreamResource extends JsonResource
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'id' => $this->uuid,
'name' => $this->name,
'type' => $this->type,
'type_label' => $this->getTypeLabel(),
@ -19,7 +19,7 @@ public function toArray(Request $request): array
'frequency_label' => $this->getFrequencyLabel(),
'start_date' => $this->start_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,
'description' => $this->description,
'is_active' => $this->is_active,

View file

@ -2,6 +2,7 @@
namespace App\Repositories;
use App\Models\Bucket;
use App\Models\Scenario;
use App\Models\Stream;
use Illuminate\Support\Collection;
@ -11,7 +12,7 @@ class StreamRepository
public function getForScenario(Scenario $scenario): Collection
{
return $scenario->streams()
->with('bucket:id,name')
->with('bucket:id,uuid,name')
->orderBy('type')
->orderBy('name')
->get();
@ -19,11 +20,15 @@ public function getForScenario(Scenario $scenario): Collection
public function create(Scenario $scenario, array $data): Stream
{
$this->resolveBucketId($data);
return $scenario->streams()->create($data);
}
public function update(Stream $stream, array $data): Stream
{
$this->resolveBucketId($data);
$stream->update($data);
return $stream->fresh('bucket');
@ -46,17 +51,24 @@ public function toggleActive(Stream $stream): Stream
/**
* 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 $scenario->buckets()
->where('id', $bucketId)
->where('uuid', $bucketUuid)
->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
*/
@ -64,12 +76,12 @@ public function getGroupedByType(Scenario $scenario): array
{
return [
'income' => $scenario->streams()
->with('bucket:id,name')
->with('bucket:id,uuid,name')
->byType(Stream::TYPE_INCOME)
->orderBy('name')
->get(),
'expense' => $scenario->streams()
->with('bucket:id,name')
->with('bucket:id,uuid,name')
->byType(Stream::TYPE_EXPENSE)
->orderBy('name')
->get(),

View file

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

View file

@ -2,14 +2,14 @@ import { Head, Link, router } from '@inertiajs/react';
import { useState } from 'react';
interface Scenario {
id: number;
id: string;
name: string;
created_at: string;
updated_at: string;
}
interface Bucket {
id: number;
id: string;
name: string;
priority: number;
sort_order: number;
@ -23,7 +23,7 @@ interface Bucket {
}
interface Stream {
id: number;
id: string;
name: string;
type: 'income' | 'expense';
type_label: string;
@ -32,7 +32,7 @@ interface Stream {
frequency_label: string;
start_date: string;
end_date: string | null;
bucket_id: number | null;
bucket_id: string | null;
bucket_name: string | null;
description: string | null;
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);
if (!bucket) return;