diff --git a/.phase21-state.json b/.phase21-state.json index 3fda0b3..c56fab3 100644 --- a/.phase21-state.json +++ b/.phase21-state.json @@ -1,9 +1,10 @@ { "completed_features": [], - "current_feature": "smart-rounding-on-input", + "current_feature": "color-coded-customer-rows", "started_at": "2026-05-23T08:09:40.135892", "attempted_features": [ "keyboard-undo-stack", - "snippet-shortcuts" + "snippet-shortcuts", + "smart-rounding-on-input" ] } \ No newline at end of file diff --git a/GENERATION_LOG.md b/GENERATION_LOG.md index 1ed047a..dc3d36c 100644 --- a/GENERATION_LOG.md +++ b/GENERATION_LOG.md @@ -2533,3 +2533,20 @@ src/index.ts(27,25): error TS2769: No overload matches this call. Overload 2 of 3, '(plugin: FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error. Argument of type 'Promise' is not assignable to parameter of type 'FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'. Type 'Promise' provides no match for the signature '(instance: FastifyInstance, FastifyBaseLogger, FastifyTy +- `08:12:10` **INFO** Committed feature smart-rounding-on-input +- `08:12:10` **INFO** Pushed: rc=0 + +## Phase-3 Feature: color-coded-customer-rows (2026-05-23 08:12:10) + +- `08:12:10` **INFO** Description: Customer-Rows mit Hash-basierter Pastell-Background-Color +- `08:12:10` **INFO** Generating apps/web/src/pages/Customers.tsx (ERWEITERT — behalte alles. Pro Customer-Row left-border-Color basieren…) +- `08:13:56` **INFO** wrote 12552 chars in 105.9s (attempt 1) +- `08:13:56` **INFO** Running tsc --noEmit on api… +- `08:13:58` **WARN** tsc errors: +src/index.ts(27,25): error TS2769: No overload matches this call. + Overload 1 of 3, '(plugin: FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error. + Argument of type 'Promise' is not assignable to parameter of type 'FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'. + Type 'Promise' provides no match for the signature '(instance: FastifyInstance, FastifyBaseLogger, FastifyTypeProvider>, opts: { ...; }, done: (err?: Error | undefined) => void): void'. + Overload 2 of 3, '(plugin: FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error. + Argument of type 'Promise' is not assignable to parameter of type 'FastifyPluginAsync<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'. + Type 'Promise' provides no match for the signature '(instance: FastifyInstance, FastifyBaseLogger, FastifyTy diff --git a/apps/web/src/pages/Customers.tsx b/apps/web/src/pages/Customers.tsx index b163ef4..af4aa5e 100644 --- a/apps/web/src/pages/Customers.tsx +++ b/apps/web/src/pages/Customers.tsx @@ -1,8 +1,24 @@ import { useState, useRef, useMemo } from "react" import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query" -import { Star, Merge, X } from "lucide-react" +import { Star, Merge, X, Upload, Plus, Archive, Trash2 } from "lucide-react" import { api } from "../lib/api" +const COLOR_PALETTE = [ + 'border-l-4 border-rose-300', + 'border-l-4 border-amber-300', + 'border-l-4 border-emerald-300', + 'border-l-4 border-sky-300', + 'border-l-4 border-violet-300' +] + +function getColorForName(name: string) { + let hash = 0 + for (let i = 0; i < name.length; i++) { + hash = name.charCodeAt(i) + ((hash << 5) - hash) + } + return COLOR_PALETTE[Math.abs(hash) % COLOR_PALETTE.length] +} + export default function Customers() { const queryClient = useQueryClient() const [name, setName] = useState("") @@ -115,158 +131,166 @@ export default function Customers() { ref={fileInputRef} onChange={handleFileChange} /> - -
-
- - setName(e.target.value)} - placeholder="Acme Corp" + +
+ + setName(e.target.value)} + placeholder="Customer Name" className="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none" />
-
+
- setTags(e.target.value)} - placeholder="VIP, Enterprise, Tech" + setTags(e.target.value)} + placeholder="VIP, Lead, Tech..." className="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none" />
- +
+ +
-
-
- setFilterTag(e.target.value)} - placeholder="Filter by tag..." - className="w-full pl-3 pr-3 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 outline-none bg-white" - /> +
+
+
+ setFilterTag(e.target.value)} + placeholder="Filter by tag..." + className="pl-3 pr-10 py-2 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none w-64" + /> + {filterTag && ( + + )} +
+
-
-
- - - - - - - - - - - {filteredAndSortedCustomers.map((customer) => ( - - - - - - - ))} - -
CustomerTagsActions
- - {customer.name} -
- {customer.tags?.map(tag => ( - - {tag} - - ))} -
-
-
- - -
-
+
+ {filteredAndSortedCustomers.map(customer => ( +
+
+ +
+
+ {customer.name} + {!customer.active && Archived} +
+
+ {customer.tags?.map(tag => ( + + {tag} + + ))} +
+
+
+ +
+ + + +
+
+ ))} + {filteredAndSortedCustomers.length === 0 && ( +
+ No customers found matching your criteria. +
+ )}
{mergeTarget && (
-
-
-

Merge Customer

- -
+
+

Merge Customer

- Select the target customer. All data from the source will be moved to the target, and the source will be deleted. + Merge {customers?.find(c => c.id === mergeTarget.sourceId)?.name} into another customer. All tags and associations will be moved.

-
-
- -