feat(07-unified-service-catalog): rewire /admin/catalog CRUD + query layer to services table
- Task 1: Update getAllServices() and activeServices queries to read from services table instead of service_catalog - Both ClientFullDetail and ProjectFullDetail now return Service[] (not ServiceCatalog[]) - Quote items label join remains unchanged — still references service_catalog for historical quote_items.service_id FK integrity - Task 2: Rewire /admin/catalog actions and components to operate on services table - src/app/admin/catalog/actions.ts: createService/updateService/toggleServiceActive now write to services with optional category field - ServiceTable.tsx: Updated to Service[] type, added category column rendering - ServiceForm.tsx: Added optional category input field - QuoteTab.tsx: Updated activeServices type annotation to Service[] - New services created via /admin/catalog have migrated_from=null, migrated_id=null (not migrated) - TypeScript compiles successfully - npm run build: PASS Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,14 @@ export function ServiceForm() {
|
||||
placeholder="es. Incluso: analisi competitor, posizionamento"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="category">Categoria (opzionale)</Label>
|
||||
<Input
|
||||
id="category"
|
||||
name="category"
|
||||
placeholder="es. Branding, Social media, Consulenza"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="unit_price">Prezzo unitario (€)</Label>
|
||||
<Input
|
||||
|
||||
@@ -6,9 +6,9 @@ import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { updateService, toggleServiceActive } from "@/app/admin/catalog/actions";
|
||||
import type { ServiceCatalog } from "@/db/schema";
|
||||
import type { Service } from "@/db/schema";
|
||||
|
||||
function ServiceRow({ service }: { service: ServiceCatalog }) {
|
||||
function ServiceRow({ service }: { service: Service }) {
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [, startTransition] = useTransition();
|
||||
@@ -72,6 +72,14 @@ function ServiceRow({ service }: { service: ServiceCatalog }) {
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 min-w-[120px] space-y-1">
|
||||
<Label htmlFor={`category-${service.id}`}>Categoria</Label>
|
||||
<Input
|
||||
id={`category-${service.id}`}
|
||||
name="category"
|
||||
defaultValue={service.category ?? ""}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{error && <p className="text-xs text-red-600">{error}</p>}
|
||||
<div className="flex gap-2">
|
||||
@@ -110,6 +118,9 @@ function ServiceRow({ service }: { service: ServiceCatalog }) {
|
||||
<td className="py-3 px-4 text-[#71717a] text-sm max-w-xs truncate">
|
||||
{service.description ?? "—"}
|
||||
</td>
|
||||
<td className="py-3 px-4 text-[#71717a] text-sm max-w-xs truncate">
|
||||
{service.category ?? "—"}
|
||||
</td>
|
||||
<td className="py-3 px-4 tabular-nums font-mono">
|
||||
€{parseFloat(service.unit_price).toLocaleString("it-IT", { minimumFractionDigits: 2 })}
|
||||
</td>
|
||||
@@ -138,7 +149,7 @@ function ServiceRow({ service }: { service: ServiceCatalog }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function ServiceTable({ services }: { services: ServiceCatalog[] }) {
|
||||
export function ServiceTable({ services }: { services: Service[] }) {
|
||||
return (
|
||||
<div className="bg-white rounded-xl border border-[#e5e7eb] overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
@@ -146,6 +157,7 @@ export function ServiceTable({ services }: { services: ServiceCatalog[] }) {
|
||||
<tr>
|
||||
<th className="text-left py-3 px-4 font-medium text-[#71717a]">Nome</th>
|
||||
<th className="text-left py-3 px-4 font-medium text-[#71717a]">Descrizione</th>
|
||||
<th className="text-left py-3 px-4 font-medium text-[#71717a]">Categoria</th>
|
||||
<th className="text-left py-3 px-4 font-medium text-[#71717a]">Prezzo</th>
|
||||
<th className="text-left py-3 px-4 font-medium text-[#71717a]">Stato</th>
|
||||
<th className="py-3 px-4"></th>
|
||||
|
||||
@@ -11,12 +11,12 @@ import {
|
||||
updateAcceptedTotal,
|
||||
} from "@/app/admin/clients/[id]/quote-actions";
|
||||
import type { QuoteItemWithLabel } from "@/lib/admin-queries";
|
||||
import type { ServiceCatalog } from "@/db/schema";
|
||||
import type { Service } from "@/db/schema";
|
||||
|
||||
type Props = {
|
||||
clientId: string;
|
||||
items: QuoteItemWithLabel[];
|
||||
activeServices: ServiceCatalog[];
|
||||
activeServices: Service[];
|
||||
acceptedTotal: string;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user