mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
Merge pull request #20 from ArnasDon/fix/pipelines-sizing-and-review
fix(pipelines): column sizing + senior-review pass
This commit is contained in:
commit
f746299df3
@ -69,10 +69,12 @@ export function DealForm({
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [statusAction, setStatusAction] = useState<DealStatus | null>(null);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
||||
|
||||
// Reset when opening
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
setConfirmDelete(false);
|
||||
if (deal) {
|
||||
setTitle(deal.title);
|
||||
setValue(String(deal.value ?? ""));
|
||||
@ -211,10 +213,6 @@ export function DealForm({
|
||||
|
||||
async function handleDelete() {
|
||||
if (!deal) return;
|
||||
const confirmed = window.confirm(
|
||||
"Delete this deal? This cannot be undone.",
|
||||
);
|
||||
if (!confirmed) return;
|
||||
setDeleting(true);
|
||||
const { error } = await supabase.from("deals").delete().eq("id", deal.id);
|
||||
setDeleting(false);
|
||||
@ -223,6 +221,7 @@ export function DealForm({
|
||||
return;
|
||||
}
|
||||
toast.success("Deal deleted");
|
||||
setConfirmDelete(false);
|
||||
onOpenChange(false);
|
||||
onSaved();
|
||||
}
|
||||
@ -426,17 +425,39 @@ export function DealForm({
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{deal && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleDelete}
|
||||
disabled={deleting}
|
||||
className="mt-3 flex w-full items-center justify-center gap-1 text-xs text-red-400 hover:text-red-300"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
{deleting ? "Deleting..." : "Delete Deal"}
|
||||
</button>
|
||||
)}
|
||||
{deal &&
|
||||
(confirmDelete ? (
|
||||
<div className="mt-3 flex items-center justify-between gap-2 rounded-md border border-red-500/30 bg-red-500/10 px-3 py-2 text-xs">
|
||||
<span className="text-red-300">Delete this deal?</span>
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConfirmDelete(false)}
|
||||
disabled={deleting}
|
||||
className="rounded px-2 py-1 text-slate-300 hover:bg-slate-800"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleDelete}
|
||||
disabled={deleting}
|
||||
className="rounded bg-red-600 px-2 py-1 font-medium text-white hover:bg-red-700 disabled:opacity-50"
|
||||
>
|
||||
{deleting ? "Deleting..." : "Confirm"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConfirmDelete(true)}
|
||||
className="mt-3 flex w-full items-center justify-center gap-1 text-xs text-red-400 hover:text-red-300"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
Delete Deal
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</SheetContent>
|
||||
|
||||
@ -99,7 +99,7 @@ export function PipelineAnalytics({ stages, deals }: PipelineAnalyticsProps) {
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<div className="grid grid-cols-2 gap-3 rounded-xl border border-slate-800 bg-slate-900/60 p-4 sm:grid-cols-3 lg:grid-cols-6">
|
||||
<div className="grid grid-cols-2 gap-3 rounded-xl border border-slate-800 bg-slate-900/60 p-4 sm:grid-cols-3 xl:grid-cols-6">
|
||||
<Metric
|
||||
icon={<BarChart3 className="h-4 w-4 text-slate-400" />}
|
||||
label="Total Deals"
|
||||
|
||||
@ -4,6 +4,7 @@ import { useMemo, useState } from "react";
|
||||
import {
|
||||
DndContext,
|
||||
DragOverlay,
|
||||
KeyboardSensor,
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
@ -62,6 +63,9 @@ export function PipelineBoard({
|
||||
const sensors = useSensors(
|
||||
// 5px activation distance avoids clicks being interpreted as drags.
|
||||
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
|
||||
// Keyboard drag support: focus a card, Space to pick up, arrows to move,
|
||||
// Space to drop, Escape to cancel.
|
||||
useSensor(KeyboardSensor),
|
||||
);
|
||||
|
||||
const activeDeal = activeDealId
|
||||
@ -172,7 +176,7 @@ function StageColumn({
|
||||
const { setNodeRef, isOver } = useDroppable({ id: stage.id });
|
||||
|
||||
return (
|
||||
<div className="flex min-w-[280px] max-w-[300px] shrink-0 flex-col rounded-xl border border-slate-800 bg-slate-900/60 p-4">
|
||||
<div className="flex min-w-[260px] flex-1 basis-[260px] flex-col rounded-xl border border-slate-800 bg-slate-900/60 p-4">
|
||||
{/* 3px colored top border — sits above the column's padding */}
|
||||
<div
|
||||
className="-mx-4 -mt-4 h-[3px] rounded-t-xl"
|
||||
|
||||
@ -100,26 +100,33 @@ export function PipelineSettings({
|
||||
|
||||
async function handleSave() {
|
||||
setSaving(true);
|
||||
const { error: renameErr } = await supabase
|
||||
.from("pipelines")
|
||||
.update({ name: name.trim() })
|
||||
.eq("id", pipeline.id);
|
||||
if (renameErr) {
|
||||
toast.error("Failed to rename pipeline");
|
||||
setSaving(false);
|
||||
|
||||
// One upsert for all stages — batches N stage writes into a single
|
||||
// round-trip. Previous implementation did N sequential UPDATEs which
|
||||
// latency-scaled linearly with stage count.
|
||||
const stageRows = localStages.map((s, i) => ({
|
||||
id: s.id,
|
||||
pipeline_id: s.pipeline_id,
|
||||
name: s.name,
|
||||
color: s.color,
|
||||
position: i,
|
||||
}));
|
||||
|
||||
const [renameRes, stagesRes] = await Promise.all([
|
||||
supabase
|
||||
.from("pipelines")
|
||||
.update({ name: name.trim() })
|
||||
.eq("id", pipeline.id),
|
||||
supabase.from("pipeline_stages").upsert(stageRows, { onConflict: "id" }),
|
||||
]);
|
||||
|
||||
setSaving(false);
|
||||
|
||||
if (renameRes.error || stagesRes.error) {
|
||||
toast.error("Failed to save pipeline");
|
||||
return;
|
||||
}
|
||||
|
||||
// Persist each stage's name/color/position
|
||||
for (let i = 0; i < localStages.length; i++) {
|
||||
const s = localStages[i];
|
||||
await supabase
|
||||
.from("pipeline_stages")
|
||||
.update({ name: s.name, color: s.color, position: i })
|
||||
.eq("id", s.id);
|
||||
}
|
||||
|
||||
setSaving(false);
|
||||
onOpenChange(false);
|
||||
onPipelinesChanged();
|
||||
onStagesChanged();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user