From 2c696aa88a24567c7c089e5ef4570b60c4737daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vi=CC=81ctor=20Falco=CC=81n?= Date: Tue, 11 Nov 2025 18:10:32 +0000 Subject: [PATCH] Update default categories --- app/Actions/CreateDefaultCategories.php | 78 ++++++-- app/Console/Commands/ResetUserCategories.php | 64 +++++++ .../Settings/StoreCategoryRequest.php | 23 ++- .../Settings/UpdateCategoryRequest.php | 23 ++- database/factories/CategoryFactory.php | 4 +- database/seeders/CategorySeeder.php | 12 +- .../categories/create-category-dialog.tsx | 4 +- .../categories/delete-category-dialog.tsx | 4 +- .../categories/edit-category-dialog.tsx | 4 +- resources/js/pages/settings/categories.tsx | 30 +--- resources/js/types/category.ts | 166 +++++++++--------- .../ResetUserCategoriesCommandTest.php | 66 +++++++ tests/Feature/Settings/CategoryTest.php | 14 +- tests/Feature/Sync/TransactionSyncTest.php | 2 +- 14 files changed, 333 insertions(+), 161 deletions(-) create mode 100644 app/Console/Commands/ResetUserCategories.php create mode 100644 tests/Feature/Console/ResetUserCategoriesCommandTest.php diff --git a/app/Actions/CreateDefaultCategories.php b/app/Actions/CreateDefaultCategories.php index 3f53272b..22240b9a 100644 --- a/app/Actions/CreateDefaultCategories.php +++ b/app/Actions/CreateDefaultCategories.php @@ -11,19 +11,77 @@ class CreateDefaultCategories */ public function handle(User $user): void { - $defaultCategories = [ - ['name' => 'Groceries', 'icon' => 'ShoppingCart', 'color' => 'green'], - ['name' => 'Transportation', 'icon' => 'Car', 'color' => 'blue'], - ['name' => 'Entertainment', 'icon' => 'Film', 'color' => 'purple'], - ['name' => 'Dining Out', 'icon' => 'Utensils', 'color' => 'orange'], - ['name' => 'Healthcare', 'icon' => 'Heart', 'color' => 'red'], - ['name' => 'Utilities', 'icon' => 'Zap', 'color' => 'yellow'], - ['name' => 'Shopping', 'icon' => 'ShoppingBag', 'color' => 'pink'], - ['name' => 'Travel', 'icon' => 'Plane', 'color' => 'sky'], - ]; + $defaultCategories = self::getDefaultCategories(); foreach ($defaultCategories as $category) { $user->categories()->create($category); } } + + /** + * Get the default categories configuration. + * + * @return array + */ + public static function getDefaultCategories(): array + { + return + ['name' => 'Administrative violations', 'icon' => 'AlertTriangle', 'color' => 'red'], + ['name' => 'Alimony and child support', 'icon' => 'Users', 'color' => 'teal'], + ['name' => 'Association membership fees', 'icon' => 'Users2', 'color' => 'cyan'], + ['name' => 'Bailiffs', 'icon' => 'Scale', 'color' => 'amber'], + ['name' => 'Bills', 'icon' => 'FileText', 'color' => 'red'], + ['name' => 'Cash deposit', 'icon' => 'Banknote', 'color' => 'green'], + ['name' => 'Cash withdrawal', 'icon' => 'Banknote', 'color' => 'emerald'], + ['name' => 'Cashback rewards', 'icon' => 'Gift', 'color' => 'pink'], + ['name' => 'Cheque', 'icon' => 'Receipt', 'color' => 'gray'], + ['name' => 'Cheque deposit', 'icon' => 'Receipt', 'color' => 'green'], + ['name' => 'Children', 'icon' => 'Baby', 'color' => 'yellow'], + ['name' => 'Clothing and shoes', 'icon' => 'ShoppingBag', 'color' => 'pink'], + ['name' => 'Currency exchange', 'icon' => 'Repeat', 'color' => 'green'], + ['name' => 'Debt collection', 'icon' => 'FileText', 'color' => 'orange'], + ['name' => 'Dishonors', 'icon' => 'AlertCircle', 'color' => 'red'], + ['name' => 'Dividends Income', 'icon' => 'TrendingUp', 'color' => 'emerald'], + ['name' => 'Education, health and beauty', 'icon' => 'Heart', 'color' => 'red'], + ['name' => 'Enquiries', 'icon' => 'HelpCircle', 'color' => 'blue'], + ['name' => 'Entertainment', 'icon' => 'Heart', 'color' => 'purple'], + ['name' => 'Financial services and commission', 'icon' => 'Building2', 'color' => 'slate'], + ['name' => 'Food', 'icon' => 'Utensils', 'color' => 'orange'], + ['name' => 'Gambling', 'icon' => 'Dices', 'color' => 'red'], + ['name' => 'Groceries', 'icon' => 'ShoppingBag', 'color' => 'green'], + ['name' => 'Healthcare', 'icon' => 'Heart', 'color' => 'red'], + ['name' => 'Household goods', 'icon' => 'Home', 'color' => 'green'], + ['name' => 'Income from rent', 'icon' => 'Building', 'color' => 'indigo'], + ['name' => 'Insurance', 'icon' => 'Shield', 'color' => 'cyan'], + ['name' => 'Insurance indemnity', 'icon' => 'ShieldCheck', 'color' => 'cyan'], + ['name' => 'Interest received', 'icon' => 'TrendingUp', 'color' => 'emerald'], + ['name' => 'Invoices', 'icon' => 'FileText', 'color' => 'indigo'], + ['name' => 'Leisure activities, traveling', 'icon' => 'Plane', 'color' => 'purple'], + ['name' => 'Loans', 'icon' => 'Landmark', 'color' => 'gray'], + ['name' => 'Online transactions', 'icon' => 'CreditCard', 'color' => 'indigo'], + ['name' => 'Other incoming payments', 'icon' => 'ArrowUpCircle', 'color' => 'green'], + ['name' => 'Other incoming payments from employer', 'icon' => 'Briefcase', 'color' => 'blue'], + ['name' => 'Other outgoing payments', 'icon' => 'ArrowDownCircle', 'color' => 'gray'], + ['name' => 'Pension', 'icon' => 'Wallet', 'color' => 'teal'], + ['name' => 'Personal transfers', 'icon' => 'ArrowLeftRight', 'color' => 'purple'], + ['name' => 'Postal services', 'icon' => 'Mail', 'color' => 'blue'], + ['name' => 'Regular income', 'icon' => 'TrendingUp', 'color' => 'emerald'], + ['name' => 'Restaurants', 'icon' => 'Utensils', 'color' => 'orange'], + ['name' => 'Return debit', 'icon' => 'Undo2', 'color' => 'amber'], + ['name' => 'Returned payments', 'icon' => 'RotateCcw', 'color' => 'orange'], + ['name' => 'Salary', 'icon' => 'Wallet', 'color' => 'green'], + ['name' => 'Savings and investments', 'icon' => 'PiggyBank', 'color' => 'green'], + ['name' => 'Scholarships', 'icon' => 'GraduationCap', 'color' => 'purple'], + ['name' => 'Shopping', 'icon' => 'ShoppingBag', 'color' => 'pink'], + ['name' => 'Social security benefits', 'icon' => 'HandHeart', 'color' => 'cyan'], + ['name' => 'State aid payments', 'icon' => 'Landmark', 'color' => 'blue'], + ['name' => 'Tax return', 'icon' => 'ReceiptText', 'color' => 'green'], + ['name' => 'Taxes and government fees', 'icon' => 'Receipt', 'color' => 'slate'], + ['name' => 'Transportation', 'icon' => 'Car', 'color' => 'blue'], + ['name' => 'Travel', 'icon' => 'Plane', 'color' => 'blue'], + ['name' => 'Utilities', 'icon' => 'Home', 'color' => 'yellow'], + ['name' => 'Utility services', 'icon' => 'Home', 'color' => 'blue'], + ['name' => 'Work on demand', 'icon' => 'Clock', 'color' => 'orange'], + ]; + } } diff --git a/app/Console/Commands/ResetUserCategories.php b/app/Console/Commands/ResetUserCategories.php new file mode 100644 index 00000000..ac71fcf8 --- /dev/null +++ b/app/Console/Commands/ResetUserCategories.php @@ -0,0 +1,64 @@ +argument('user'); + + $user = is_numeric($userIdentifier) + ? User::find($userIdentifier) + : User::where('email', $userIdentifier)->first(); + + if (! $user) { + $this->error("User not found: {$userIdentifier}"); + + return self::FAILURE; + } + + $this->info("Resetting categories for user: {$user->name} ({$user->email})"); + + $categoriesCount = $user->categories()->count(); + + if ($categoriesCount > 0) { + $user->categories()->delete(); + $this->info("Deleted {$categoriesCount} existing categories."); + } else { + $this->info('No existing categories found.'); + } + + $createDefaultCategories = new CreateDefaultCategories; + $createDefaultCategories->handle($user); + + $newCategoriesCount = $user->categories()->count(); + $this->info("Created {$newCategoriesCount} default categories."); + + $this->newLine(); + $this->info('✓ Categories reset successfully!'); + + return self::SUCCESS; + } +} diff --git a/app/Http/Requests/Settings/StoreCategoryRequest.php b/app/Http/Requests/Settings/StoreCategoryRequest.php index 612485f6..dd4088fd 100644 --- a/app/Http/Requests/Settings/StoreCategoryRequest.php +++ b/app/Http/Requests/Settings/StoreCategoryRequest.php @@ -30,23 +30,20 @@ class StoreCategoryRequest extends FormRequest 'required', 'string', Rule::in([ - 'red', - 'orange', 'amber', - 'yellow', - 'lime', - 'green', - 'emerald', - 'teal', - 'cyan', - 'sky', 'blue', + 'cyan', + 'emerald', + 'gray', + 'green', 'indigo', - 'violet', - 'purple', - 'fuchsia', + 'orange', 'pink', - 'rose', + 'purple', + 'red', + 'slate', + 'teal', + 'yellow', ]), ], ]; diff --git a/app/Http/Requests/Settings/UpdateCategoryRequest.php b/app/Http/Requests/Settings/UpdateCategoryRequest.php index 3ce8db8e..2b24c720 100644 --- a/app/Http/Requests/Settings/UpdateCategoryRequest.php +++ b/app/Http/Requests/Settings/UpdateCategoryRequest.php @@ -30,23 +30,20 @@ class UpdateCategoryRequest extends FormRequest 'required', 'string', Rule::in([ - 'red', - 'orange', 'amber', - 'yellow', - 'lime', - 'green', - 'emerald', - 'teal', - 'cyan', - 'sky', 'blue', + 'cyan', + 'emerald', + 'gray', + 'green', 'indigo', - 'violet', - 'purple', - 'fuchsia', + 'orange', 'pink', - 'rose', + 'purple', + 'red', + 'slate', + 'teal', + 'yellow', ]), ], ]; diff --git a/database/factories/CategoryFactory.php b/database/factories/CategoryFactory.php index 288adff9..7f0d9eb6 100644 --- a/database/factories/CategoryFactory.php +++ b/database/factories/CategoryFactory.php @@ -19,8 +19,8 @@ class CategoryFactory extends Factory { return [ 'name' => fake()->randomElement(['Food', 'Transport', 'Entertainment', 'Shopping', 'Healthcare', 'Utilities', 'Travel', 'Education']), - 'icon' => fake()->randomElement(['🍔', '🚗', '🎮', '🛒', '💊', '💡', '✈️', '📚']), - 'color' => fake()->hexColor(), + 'icon' => fake()->randomElement(['AlertCircle', 'AlertTriangle', 'ArrowDownCircle', 'ArrowLeftRight', 'ArrowUpCircle', 'Baby', 'Banknote', 'Briefcase', 'Building', 'Building2', 'Car', 'Clock', 'CreditCard', 'Dices', 'FileText', 'Gift', 'GraduationCap', 'HandHeart', 'Heart', 'HelpCircle', 'Home', 'Landmark', 'Mail', 'PiggyBank', 'Plane', 'Receipt', 'ReceiptText', 'Repeat', 'RotateCcw', 'Scale', 'Shield', 'ShieldCheck', 'ShoppingBag', 'TrendingUp', 'Undo2', 'Users', 'Users2', 'Utensils', 'Wallet']), + 'color' => fake()->randomElement(['amber', 'blue', 'cyan', 'emerald', 'gray', 'green', 'indigo', 'orange', 'pink', 'purple', 'red', 'slate', 'teal', 'yellow']), 'user_id' => User::factory(), ]; } diff --git a/database/seeders/CategorySeeder.php b/database/seeders/CategorySeeder.php index 78342beb..5566ae80 100644 --- a/database/seeders/CategorySeeder.php +++ b/database/seeders/CategorySeeder.php @@ -2,6 +2,7 @@ namespace Database\Seeders; +use App\Actions\CreateDefaultCategories; use App\Models\Category; use App\Models\User; use Illuminate\Database\Seeder; @@ -19,16 +20,7 @@ class CategorySeeder extends Seeder return; } - $categories = [ - ['name' => 'Food & Dining', 'icon' => '🍔', 'color' => '#FF6B6B'], - ['name' => 'Transportation', 'icon' => '🚗', 'color' => '#4ECDC4'], - ['name' => 'Entertainment', 'icon' => '🎮', 'color' => '#95E1D3'], - ['name' => 'Shopping', 'icon' => '🛒', 'color' => '#F38181'], - ['name' => 'Healthcare', 'icon' => '💊', 'color' => '#AA96DA'], - ['name' => 'Utilities', 'icon' => '💡', 'color' => '#FCBAD3'], - ['name' => 'Travel', 'icon' => '✈️', 'color' => '#FFFFD2'], - ['name' => 'Education', 'icon' => '📚', 'color' => '#A8D8EA'], - ]; + $categories = CreateDefaultCategories::getDefaultCategories(); foreach ($users as $user) { foreach ($categories as $category) { diff --git a/resources/js/components/categories/create-category-dialog.tsx b/resources/js/components/categories/create-category-dialog.tsx index 94f1ff8f..ddfa56f9 100644 --- a/resources/js/components/categories/create-category-dialog.tsx +++ b/resources/js/components/categories/create-category-dialog.tsx @@ -18,6 +18,7 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; +import { categorySyncService } from '@/services/category-sync'; import { CATEGORY_COLORS, CATEGORY_ICONS, @@ -48,7 +49,8 @@ export function CreateCategoryDialog({
{ + onSuccess={async () => { + await categorySyncService.sync(); setOpen(false); onSuccess?.(); }} diff --git a/resources/js/components/categories/delete-category-dialog.tsx b/resources/js/components/categories/delete-category-dialog.tsx index fffe97f5..3b52a298 100644 --- a/resources/js/components/categories/delete-category-dialog.tsx +++ b/resources/js/components/categories/delete-category-dialog.tsx @@ -8,6 +8,7 @@ import { DialogHeader, DialogTitle, } from '@/components/ui/dialog'; +import { categorySyncService } from '@/services/category-sync'; import { type Category } from '@/types/category'; import { Form } from '@inertiajs/react'; @@ -36,7 +37,8 @@ export function DeleteCategoryDialog({ { + onSuccess={async () => { + await categorySyncService.delete(category.id); onOpenChange(false); onSuccess?.(); }} diff --git a/resources/js/components/categories/edit-category-dialog.tsx b/resources/js/components/categories/edit-category-dialog.tsx index bf38dd5e..31558095 100644 --- a/resources/js/components/categories/edit-category-dialog.tsx +++ b/resources/js/components/categories/edit-category-dialog.tsx @@ -17,6 +17,7 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; +import { categorySyncService } from '@/services/category-sync'; import { CATEGORY_COLORS, CATEGORY_ICONS, @@ -50,7 +51,8 @@ export function EditCategoryDialog({ { + onSuccess={async () => { + await categorySyncService.sync(); onOpenChange(false); onSuccess?.(); }} diff --git a/resources/js/pages/settings/categories.tsx b/resources/js/pages/settings/categories.tsx index a010afbd..fa993252 100644 --- a/resources/js/pages/settings/categories.tsx +++ b/resources/js/pages/settings/categories.tsx @@ -5,7 +5,6 @@ import { flexRender, getCoreRowModel, getFilteredRowModel, - getPaginationRowModel, getSortedRowModel, SortingState, useReactTable, @@ -140,7 +139,13 @@ export default function Categories() { header: 'Color', cell: ({ row }) => { const color = row.getValue('color') as Category['color']; + if (!color) { + return null; + } const colorClasses = getCategoryColorClasses(color); + if (!colorClasses) { + return null; + } return ( -
-
+
+
{table.getFilteredRowModel().rows.length}{' '} category(ies) total.
-
- - -
diff --git a/resources/js/types/category.ts b/resources/js/types/category.ts index 8a2a3a6f..f183d327 100644 --- a/resources/js/types/category.ts +++ b/resources/js/types/category.ts @@ -1,44 +1,62 @@ export const CATEGORY_ICONS = [ - 'ShoppingCart', - 'Home', - 'Car', - 'Utensils', - 'Film', - 'Heart', - 'Zap', - 'ShoppingBag', - 'Plane', - 'Coffee', - 'Smartphone', - 'Book', - 'DollarSign', - 'Gift', + 'AlertCircle', + 'AlertTriangle', + 'ArrowDownCircle', + 'ArrowLeftRight', + 'ArrowUpCircle', + 'Baby', + 'Banknote', 'Briefcase', - 'GamepadIcon', - 'Music', - 'Shirt', + 'Building', + 'Building2', + 'Car', + 'Clock', + 'CreditCard', + 'Dices', + 'FileText', + 'Gift', + 'GraduationCap', + 'HandHeart', + 'Heart', + 'HelpCircle', + 'Home', + 'Landmark', + 'Mail', + 'PiggyBank', + 'Plane', + 'Receipt', + 'ReceiptText', + 'Repeat', + 'RotateCcw', + 'Scale', + 'Shield', + 'ShieldCheck', + 'ShoppingBag', + 'TrendingUp', + 'Undo2', + 'Users', + 'Users2', + 'Utensils', + 'Wallet', ] as const; export type CategoryIcon = (typeof CATEGORY_ICONS)[number]; export const CATEGORY_COLORS = [ - 'red', - 'orange', 'amber', - 'yellow', - 'lime', - 'green', - 'emerald', - 'teal', - 'cyan', - 'sky', 'blue', + 'cyan', + 'emerald', + 'gray', + 'green', 'indigo', - 'violet', - 'purple', - 'fuchsia', + 'orange', 'pink', - 'rose', + 'purple', + 'red', + 'slate', + 'teal', + 'yellow', ] as const; export type CategoryColor = (typeof CATEGORY_COLORS)[number]; @@ -55,73 +73,61 @@ export function getCategoryColorClasses(color: CategoryColor): { text: string; } { const colorMap: Record = { - red: { - bg: 'bg-red-100 dark:bg-red-700', - text: 'text-red-700 dark:text-red-100', - }, - orange: { - bg: 'bg-orange-100 dark:bg-orange-700', - text: 'text-orange-700 dark:text-orange-100', - }, amber: { bg: 'bg-amber-100 dark:bg-amber-700', text: 'text-amber-700 dark:text-amber-100', }, - yellow: { - bg: 'bg-yellow-100 dark:bg-yellow-700', - text: 'text-yellow-700 dark:text-yellow-100', - }, - lime: { - bg: 'bg-lime-100 dark:bg-lime-700', - text: 'text-lime-700 dark:text-lime-100', - }, - green: { - bg: 'bg-green-100 dark:bg-green-700', - text: 'text-green-700 dark:text-green-100', - }, - emerald: { - bg: 'bg-emerald-100 dark:bg-emerald-700', - text: 'text-emerald-700 dark:text-emerald-100', - }, - teal: { - bg: 'bg-teal-100 dark:bg-teal-700', - text: 'text-teal-700 dark:text-teal-100', - }, - cyan: { - bg: 'bg-cyan-100 dark:bg-cyan-700', - text: 'text-cyan-700 dark:text-cyan-100', - }, - sky: { - bg: 'bg-sky-100 dark:bg-sky-700', - text: 'text-sky-700 dark:text-sky-100', - }, blue: { bg: 'bg-blue-100 dark:bg-blue-700', text: 'text-blue-700 dark:text-blue-100', }, + cyan: { + bg: 'bg-cyan-100 dark:bg-cyan-700', + text: 'text-cyan-700 dark:text-cyan-100', + }, + emerald: { + bg: 'bg-emerald-100 dark:bg-emerald-700', + text: 'text-emerald-700 dark:text-emerald-100', + }, + gray: { + bg: 'bg-gray-100 dark:bg-gray-700', + text: 'text-gray-700 dark:text-gray-100', + }, + green: { + bg: 'bg-green-100 dark:bg-green-700', + text: 'text-green-700 dark:text-green-100', + }, indigo: { bg: 'bg-indigo-100 dark:bg-indigo-700', text: 'text-indigo-700 dark:text-indigo-100', }, - violet: { - bg: 'bg-violet-100 dark:bg-violet-700', - text: 'text-violet-700 dark:text-violet-100', - }, - purple: { - bg: 'bg-purple-100 dark:bg-purple-700', - text: 'text-purple-700 dark:text-purple-100', - }, - fuchsia: { - bg: 'bg-fuchsia-100 dark:bg-fuchsia-700', - text: 'text-fuchsia-700 dark:text-fuchsia-100', + orange: { + bg: 'bg-orange-100 dark:bg-orange-700', + text: 'text-orange-700 dark:text-orange-100', }, pink: { bg: 'bg-pink-100 dark:bg-pink-700', text: 'text-pink-700 dark:text-pink-100', }, - rose: { - bg: 'bg-rose-100 dark:bg-rose-700', - text: 'text-rose-700 dark:text-rose-100', + purple: { + bg: 'bg-purple-100 dark:bg-purple-700', + text: 'text-purple-700 dark:text-purple-100', + }, + red: { + bg: 'bg-red-100 dark:bg-red-700', + text: 'text-red-700 dark:text-red-100', + }, + slate: { + bg: 'bg-slate-100 dark:bg-slate-700', + text: 'text-slate-700 dark:text-slate-100', + }, + teal: { + bg: 'bg-teal-100 dark:bg-teal-700', + text: 'text-teal-700 dark:text-teal-100', + }, + yellow: { + bg: 'bg-yellow-100 dark:bg-yellow-700', + text: 'text-yellow-700 dark:text-yellow-100', }, }; diff --git a/tests/Feature/Console/ResetUserCategoriesCommandTest.php b/tests/Feature/Console/ResetUserCategoriesCommandTest.php new file mode 100644 index 00000000..831f02cd --- /dev/null +++ b/tests/Feature/Console/ResetUserCategoriesCommandTest.php @@ -0,0 +1,66 @@ +create(); + Category::factory()->count(5)->create(['user_id' => $user->id]); + + expect($user->categories()->count())->toBe(5); + + $this->artisan('categories:reset', ['user' => $user->id]) + ->expectsOutput("Resetting categories for user: {$user->name} ({$user->email})") + ->expectsOutput('Deleted 5 existing categories.') + ->expectsOutput('Created 49 default categories.') + ->expectsOutput('✓ Categories reset successfully!') + ->assertSuccessful(); + + expect($user->fresh()->categories()->count())->toBe(49); + + $categoryNames = $user->categories->pluck('name')->toArray(); + expect($categoryNames)->toContain('Food', 'Transportation', 'Salary', 'Insurance'); +}); + +test('command resets categories for a user by email', function () { + $user = User::factory()->create(['email' => 'test@example.com']); + Category::factory()->count(3)->create(['user_id' => $user->id]); + + expect($user->categories()->count())->toBe(3); + + $this->artisan('categories:reset', ['user' => 'test@example.com']) + ->expectsOutput("Resetting categories for user: {$user->name} (test@example.com)") + ->expectsOutput('Deleted 3 existing categories.') + ->expectsOutput('Created 49 default categories.') + ->expectsOutput('✓ Categories reset successfully!') + ->assertSuccessful(); + + expect($user->fresh()->categories()->count())->toBe(49); +}); + +test('command works when user has no existing categories', function () { + $user = User::factory()->create(); + + expect($user->categories()->count())->toBe(0); + + $this->artisan('categories:reset', ['user' => $user->id]) + ->expectsOutput("Resetting categories for user: {$user->name} ({$user->email})") + ->expectsOutput('No existing categories found.') + ->expectsOutput('Created 49 default categories.') + ->expectsOutput('✓ Categories reset successfully!') + ->assertSuccessful(); + + expect($user->fresh()->categories()->count())->toBe(49); +}); + +test('command fails when user is not found by ID', function () { + $this->artisan('categories:reset', ['user' => 99999]) + ->expectsOutput('User not found: 99999') + ->assertFailed(); +}); + +test('command fails when user is not found by email', function () { + $this->artisan('categories:reset', ['user' => 'nonexistent@example.com']) + ->expectsOutput('User not found: nonexistent@example.com') + ->assertFailed(); +}); diff --git a/tests/Feature/Settings/CategoryTest.php b/tests/Feature/Settings/CategoryTest.php index 79c16e3d..e8d24e74 100644 --- a/tests/Feature/Settings/CategoryTest.php +++ b/tests/Feature/Settings/CategoryTest.php @@ -38,7 +38,7 @@ test('authenticated users can create a category', function () { $categoryData = [ 'name' => 'Shopping', - 'icon' => 'ShoppingCart', + 'icon' => 'ShoppingBag', 'color' => 'blue', ]; @@ -49,7 +49,7 @@ test('authenticated users can create a category', function () { $this->assertDatabaseHas('categories', [ 'user_id' => $user->id, 'name' => 'Shopping', - 'icon' => 'ShoppingCart', + 'icon' => 'ShoppingBag', 'color' => 'blue', ]); }); @@ -58,7 +58,7 @@ test('category name is required', function () { $user = User::factory()->create(); $response = $this->actingAs($user)->post(route('categories.store'), [ - 'icon' => 'ShoppingCart', + 'icon' => 'ShoppingBag', 'color' => 'blue', ]); @@ -81,7 +81,7 @@ test('category color is required', function () { $response = $this->actingAs($user)->post(route('categories.store'), [ 'name' => 'Shopping', - 'icon' => 'ShoppingCart', + 'icon' => 'ShoppingBag', ]); $response->assertSessionHasErrors(['color']); @@ -92,7 +92,7 @@ test('category color must be valid', function () { $response = $this->actingAs($user)->post(route('categories.store'), [ 'name' => 'Shopping', - 'icon' => 'ShoppingCart', + 'icon' => 'ShoppingBag', 'color' => 'invalid-color', ]); @@ -183,8 +183,8 @@ test('default categories are created when user registers', function () { $service = new \App\Actions\CreateDefaultCategories; $service->handle($user); - expect($user->categories()->count())->toBe(8); + expect($user->categories()->count())->toBe(49); $categoryNames = $user->categories->pluck('name')->toArray(); - expect($categoryNames)->toContain('Groceries', 'Transportation', 'Entertainment'); + expect($categoryNames)->toContain('Food', 'Transportation', 'Salary', 'Insurance'); }); diff --git a/tests/Feature/Sync/TransactionSyncTest.php b/tests/Feature/Sync/TransactionSyncTest.php index 139d9307..c6b24c6c 100644 --- a/tests/Feature/Sync/TransactionSyncTest.php +++ b/tests/Feature/Sync/TransactionSyncTest.php @@ -216,7 +216,7 @@ it('can delete a transaction', function () { $response->assertSuccessful(); - $this->assertDatabaseMissing('transactions', [ + $this->assertSoftDeleted('transactions', [ 'id' => $transaction->id, ]); });