diff --git a/resources/js/components/transactions/bulk-actions-bar.tsx b/resources/js/components/transactions/bulk-actions-bar.tsx
index e9d8e9bc..5085c941 100644
--- a/resources/js/components/transactions/bulk-actions-bar.tsx
+++ b/resources/js/components/transactions/bulk-actions-bar.tsx
@@ -1,8 +1,15 @@
import { BulkCategorySelect } from '@/components/transactions/bulk-category-select';
import { Button } from '@/components/ui/button';
import { ButtonGroup } from '@/components/ui/button-group';
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from '@/components/ui/dropdown-menu';
import { type Category } from '@/types/category';
-import { RefreshCw, Trash2, X } from 'lucide-react';
+import { MoreHorizontal, RefreshCw, Trash2, X } from 'lucide-react';
interface BulkActionsBarProps {
selectedCount: number;
@@ -30,7 +37,7 @@ export function BulkActionsBar({
return (
-
+
{selectedCount} transaction{selectedCount !== 1 ? 's' : ''}{' '}
selected
@@ -42,29 +49,39 @@ export function BulkActionsBar({
onCategoryChange={onCategoryChange}
disabled={isUpdating}
/>
-
-
-
-
+
+
+
+
+
+
-
-
+
+
+ Re-evaluate rules
+
+
+
+
+ Delete
+
+
+
+
diff --git a/resources/js/components/transactions/bulk-category-select.tsx b/resources/js/components/transactions/bulk-category-select.tsx
index bf6208f7..45021459 100644
--- a/resources/js/components/transactions/bulk-category-select.tsx
+++ b/resources/js/components/transactions/bulk-category-select.tsx
@@ -1,14 +1,6 @@
+import { CategorySelect } from '@/components/transactions/category-select';
+import { type Category } from '@/types/category';
import { useState } from 'react';
-import { Badge } from '@/components/ui/badge';
-import * as Icons from 'lucide-react';
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from '@/components/ui/select';
-import { type Category, getCategoryColorClasses } from '@/types/category';
interface BulkCategorySelectProps {
categories: Category[];
@@ -30,31 +22,14 @@ export function BulkCategorySelect({
}
return (
-
+
);
}
-
diff --git a/resources/js/components/transactions/category-cell.tsx b/resources/js/components/transactions/category-cell.tsx
index 821adac5..a766fd49 100644
--- a/resources/js/components/transactions/category-cell.tsx
+++ b/resources/js/components/transactions/category-cell.tsx
@@ -1,19 +1,11 @@
-import { useState } from 'react';
-import { Badge } from '@/components/ui/badge';
-import * as Icons from 'lucide-react';
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from '@/components/ui/select';
-import { type Category, getCategoryColorClasses } from '@/types/category';
-import { type DecryptedTransaction } from '@/types/transaction';
-import { type Account, type Bank } from '@/types/account';
-import { transactionSyncService } from '@/services/transaction-sync';
+import { CategorySelect } from '@/components/transactions/category-select';
import { encrypt, importKey } from '@/lib/crypto';
import { getStoredKey } from '@/lib/key-storage';
+import { transactionSyncService } from '@/services/transaction-sync';
+import { type Account, type Bank } from '@/types/account';
+import { type Category } from '@/types/category';
+import { type DecryptedTransaction } from '@/types/transaction';
+import { useState } from 'react';
interface CategoryCellProps {
transaction: DecryptedTransaction;
@@ -58,11 +50,13 @@ export function CategoryCell({
await transactionSyncService.update(transaction.id, updateData);
- const updatedCategory = categoryId
+ const updatedCategory = categoryId
? categories.find((c) => c.id === categoryId) || null
: null;
- const account = accounts.find((a) => a.id === transaction.account_id);
+ const account = accounts.find(
+ (a) => a.id === transaction.account_id,
+ );
const bank = account?.bank?.id
? banks.find((b) => b.id === account.bank.id)
: undefined;
@@ -83,54 +77,19 @@ export function CategoryCell({
}
}
- const currentCategory = transaction.category;
- const colorClasses = currentCategory
- ? getCategoryColorClasses(currentCategory.color)
- : null;
- const CurrentCategoryIconComponent = (currentCategory ? Icons[currentCategory.icon as keyof typeof Icons] : "CircleQuestionMark") as Icons.LucideIcon;
-
return (
-
+ placeholder="Uncategorized"
+ triggerClassName="h-auto w-auto border-0 bg-transparent p-0 shadow-none focus:ring-0"
+ showUncategorized={true}
+ />
);
}
-
diff --git a/resources/js/components/transactions/category-select.tsx b/resources/js/components/transactions/category-select.tsx
new file mode 100644
index 00000000..24def989
--- /dev/null
+++ b/resources/js/components/transactions/category-select.tsx
@@ -0,0 +1,106 @@
+import { Badge } from '@/components/ui/badge';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '@/components/ui/select';
+import { type Category, getCategoryColorClasses } from '@/types/category';
+import * as Icons from 'lucide-react';
+
+interface CategorySelectProps {
+ value: string | null;
+ onValueChange: (value: string) => void;
+ categories: Category[];
+ disabled?: boolean;
+ placeholder?: string;
+ className?: string;
+ triggerClassName?: string;
+ showUncategorized?: boolean;
+}
+
+export function CategorySelect({
+ value,
+ onValueChange,
+ categories,
+ disabled = false,
+ placeholder = 'Select category',
+ className,
+ triggerClassName,
+ showUncategorized = true,
+}: CategorySelectProps) {
+ const selectedCategory =
+ value && value !== 'null'
+ ? categories.find((c) => c.id === parseInt(value))
+ : null;
+
+ const colorClasses = selectedCategory
+ ? getCategoryColorClasses(selectedCategory.color)
+ : null;
+
+ const SelectedIconComponent = selectedCategory
+ ? (Icons[
+ selectedCategory.icon as keyof typeof Icons
+ ] as Icons.LucideIcon)
+ : Icons.CircleQuestionMark;
+
+ return (
+
+ );
+}
diff --git a/resources/js/components/transactions/edit-transaction-dialog.tsx b/resources/js/components/transactions/edit-transaction-dialog.tsx
index a33282c4..3b3c7b50 100644
--- a/resources/js/components/transactions/edit-transaction-dialog.tsx
+++ b/resources/js/components/transactions/edit-transaction-dialog.tsx
@@ -1,4 +1,4 @@
-import { useState, useEffect } from 'react';
+import { CategorySelect } from '@/components/transactions/category-select';
import { Button } from '@/components/ui/button';
import {
Dialog,
@@ -8,22 +8,15 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from '@/components/ui/select';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
-import { Badge } from '@/components/ui/badge';
-import { type Category, getCategoryColorClasses } from '@/types/category';
-import { type DecryptedTransaction } from '@/types/transaction';
-import { transactionSyncService } from '@/services/transaction-sync';
import { encrypt, importKey } from '@/lib/crypto';
import { getStoredKey } from '@/lib/key-storage';
+import { transactionSyncService } from '@/services/transaction-sync';
+import { type Category } from '@/types/category';
+import { type DecryptedTransaction } from '@/types/transaction';
import { format, parseISO } from 'date-fns';
+import { useEffect, useState } from 'react';
interface EditTransactionDialogProps {
transaction: DecryptedTransaction | null;
@@ -47,7 +40,9 @@ export function EditTransactionDialog({
useEffect(() => {
if (transaction) {
setCategoryId(
- transaction.category_id ? String(transaction.category_id) : 'null',
+ transaction.category_id
+ ? String(transaction.category_id)
+ : 'null',
);
setNotes(transaction.decryptedNotes || '');
}
@@ -116,10 +111,6 @@ export function EditTransactionDialog({
return null;
}
- const selectedCategory = categories.find(
- (c) => c.id === parseInt(categoryId),
- );
-
return (