Kanban board, Project Management, Proposals/Quotes, Time Tracking, Custom Fields
Build & Auto-Repair / build (push) Has been cancelled
Build & Auto-Repair / build (push) Has been cancelled
- Kanban board with drag-and-drop per stage, colour-coded cards, score bars - Project Management with milestones, Gantt timeline, inline add milestone/task - Proposal/Quote builder with line items, VAT, terms, e-signature capture - Time Tracking with stopwatch, manual entry, hours-by-project chart - Custom Fields system: admin-defined fields per entity (leads/customers/ projects/opportunities) with drag-and-drop reordering in settings - Reusable CustomFieldsSection component for entity detail pages - Customers API endpoint - All builds pass with zero errors
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { NextRequest, NextResponse } from "next/server"
|
||||
import { getSessionUser } from "@/lib/auth"
|
||||
import { query } from "@/lib/db"
|
||||
|
||||
function stageStatus(name: string): string {
|
||||
switch (name) {
|
||||
case "New": return "open"
|
||||
case "Contacted": return "contacted"
|
||||
case "Qualified":
|
||||
case "Interested":
|
||||
case "Demo Scheduled":
|
||||
case "Negotiation": return "pending"
|
||||
case "Closed Won": return "closed"
|
||||
case "Closed Lost": return "ignored"
|
||||
default: return "open"
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const user = await getSessionUser()
|
||||
if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
|
||||
|
||||
const isAdmin = user.role === "admin" || user.role === "super_admin"
|
||||
|
||||
const result = await query(
|
||||
`SELECT ls.id, ls.name, ls.sort_order, ls.probability,
|
||||
COALESCE(
|
||||
json_agg(
|
||||
json_build_object(
|
||||
'id', l.id,
|
||||
'company_name', l.company_name,
|
||||
'contact_name', l.contact_name,
|
||||
'email', l.email,
|
||||
'phone', l.phone,
|
||||
'score', l.score,
|
||||
'notes', l.notes,
|
||||
'assigned_to', l.assigned_to,
|
||||
'created_at', l.created_at
|
||||
)
|
||||
ORDER BY l.created_at DESC
|
||||
) FILTER (WHERE l.id IS NOT NULL),
|
||||
'[]'::json
|
||||
) AS leads
|
||||
FROM lead_stages ls
|
||||
LEFT JOIN leads l ON l.stage_id = ls.id AND l.deleted_at IS NULL
|
||||
WHERE ls.is_active = true
|
||||
GROUP BY ls.id, ls.name, ls.sort_order, ls.probability
|
||||
ORDER BY ls.sort_order`
|
||||
)
|
||||
|
||||
const columns = result.rows.map((r: any) => {
|
||||
const status = stageStatus(r.name)
|
||||
let leads = r.leads || []
|
||||
if (!isAdmin) {
|
||||
leads = leads.filter((l: any) => l.assigned_to === user.id)
|
||||
}
|
||||
return {
|
||||
id: r.id,
|
||||
name: r.name,
|
||||
status,
|
||||
sortOrder: r.sort_order,
|
||||
probability: r.probability,
|
||||
leads,
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json(columns)
|
||||
} catch (error) {
|
||||
console.error("Kanban API error:", error)
|
||||
return NextResponse.json({ error: "Failed to load kanban data" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user