// ============================================================ // LynkedUp Pro — Projects mock data (Construction jobs + Pipeline // leads). Used only when the Shell isn't configured, so the demo // stays fully interactive without a backend — mirrors team-data.ts. // ============================================================ export type ConstructionStatus = "active" | "complete" | "stuck" | "followup"; export type ConstructionLead = { id: string; name: string; address: string; status: ConstructionStatus; stage: string; jobType: string; agent: string; progress: number; // 0-100 health: number; // 0-100 value: number; // dollars }; // The first 8 rows mirror the reference design exactly (name, address, job // type, stage, status, progress and health). The rest extend the set to 15 // construction jobs with a realistic status/stage spread so the quick // filters (Active/Complete/Stuck/Follow-up) all have something to show. export const constructionLeads: ConstructionLead[] = [ { id: "c1", name: "Derek Holloway", address: "2814 Ravenswood Dr, Plano TX 75023", status: "active", stage: "New Lead", jobType: "Roof Replacement", agent: "Cody Tatum", progress: 14, health: 65, value: 28500 }, { id: "c2", name: "Brenda Castillo", address: "5501 Shady Brook Ln, Plano TX 75093", status: "active", stage: "New Lead", jobType: "Roof Inspection", agent: "Cody Tatum", progress: 14, health: 65, value: 15400 }, { id: "c3", name: "Antonio Reyes", address: "1122 Custer Rd, Plano TX 75075", status: "active", stage: "New Lead", jobType: "Gutter Replacement", agent: "Cody Tatum", progress: 14, health: 65, value: 19200 }, { id: "c4", name: "Sylvia Nguyen", address: "3308 Roundrock Trl, Plano TX 75023", status: "active", stage: "New Lead", jobType: "Siding Repair", agent: "Cody Tatum", progress: 14, health: 65, value: 21900 }, { id: "c5", name: "Raymond Osei", address: "2814 Ravenswood Dr, Plano TX 75023", status: "active", stage: "New Lead", jobType: "Roof Replacement", agent: "Cody Tatum", progress: 14, health: 65, value: 31200 }, { id: "c6", name: "Carolyn Estrada", address: "2814 Ravenswood Dr, Plano TX 75023", status: "active", stage: "New Lead", jobType: "Window Replacement", agent: "Cody Tatum", progress: 14, health: 65, value: 22400 }, { id: "c7", name: "Marcus Tillman", address: "2814 Ravenswood Dr, Plano TX 75023", status: "active", stage: "New Lead", jobType: "Roof Replacement", agent: "Cody Tatum", progress: 14, health: 65, value: 26800 }, { id: "c8", name: "Diane Kowalski", address: "815 Independence Pkwy, Plano TX 75023", status: "active", stage: "New Lead", jobType: "Roof Replacement", agent: "Cody Tatum", progress: 14, health: 65, value: 29500 }, { id: "c9", name: "Felicia Grant", address: "9021 Legacy Dr, Plano TX 75024", status: "complete", stage: "Completed", jobType: "Gutter Replacement", agent: "Emma Wilson", progress: 100, health: 92, value: 18600 }, { id: "c10", name: "Harold Jennings", address: "4477 Coit Rd, Plano TX 75075", status: "stuck", stage: "Permit Hold", jobType: "Siding Repair", agent: "Liam Foster", progress: 42, health: 38, value: 24800 }, { id: "c11", name: "Yolanda Brooks", address: "6650 Parker Rd, Plano TX 75093", status: "followup", stage: "Awaiting Customer", jobType: "Window Replacement", agent: "Sophie Turner", progress: 55, health: 51, value: 20700 }, { id: "c12", name: "Preston Wallace", address: "3312 Alma Dr, Plano TX 75075", status: "active", stage: "Scheduled", jobType: "Roof Repair", agent: "Cody Tatum", progress: 30, health: 70, value: 27500 }, { id: "c13", name: "Nadia Ferreira", address: "8890 Independence Pkwy, Plano TX 75025", status: "complete", stage: "Completed", jobType: "Roof Inspection", agent: "Emma Wilson", progress: 100, health: 95, value: 16200 }, { id: "c14", name: "Louis Abernathy", address: "2200 K Ave, Plano TX 75074", status: "stuck", stage: "Material Delay", jobType: "Roof Replacement", agent: "Liam Foster", progress: 60, health: 44, value: 24900 }, { id: "c15", name: "Grace Delgado", address: "7301 Ohio Dr, Plano TX 75093", status: "active", stage: "In Progress", jobType: "Roof Replacement", agent: "Cody Tatum", progress: 68, health: 78, value: 51600 }, ]; export type CollectionStatus = "paid" | "pending" | "overdue"; export type CollectionRecord = { id: string; name: string; address: string; referenceId: string; date: string; // YYYY-MM-DD type: string; // "Deposit — 30%", "Progress Payment — 40%", "Final Payment — 30%" status: CollectionStatus; amount: number; // dollars }; // Deterministic 3-installment payment schedule per construction job (deposit / // progress / final), so "Total Collected" has something real to show without a // billing backend. Which installments are PAID vs PENDING/OVERDUE follows the // job's own status — a completed job is fully paid, a stuck job has an overdue // progress payment, etc. const INSTALLMENTS = [ { label: "Deposit — 30%", pct: 0.3 }, { label: "Progress Payment — 40%", pct: 0.4 }, { label: "Final Payment — 30%", pct: 0.3 }, ] as const; function statusFor(jobStatus: ConstructionStatus, i: number): CollectionStatus { if (jobStatus === "complete") return "paid"; if (i === 0) return "paid"; // deposit is always collected up front if (jobStatus === "stuck" && i === 1) return "overdue"; return "pending"; } export function collectionsFor(lead: ConstructionLead, leadIndex: number): CollectionRecord[] { const deposit = Math.round(lead.value * INSTALLMENTS[0].pct); const progress = Math.round(lead.value * INSTALLMENTS[1].pct); const final = lead.value - deposit - progress; // remainder avoids rounding drift const amounts = [deposit, progress, final]; const ref = `PRJ-2026-${String(leadIndex + 1).padStart(3, "0")}`; return INSTALLMENTS.map((inst, i) => { const month = ((leadIndex * 2 + i * 2) % 12) + 1; const day = ((leadIndex * 5 + i * 7) % 27) + 1; return { id: `${lead.id}-r${i + 1}`, name: lead.name, address: lead.address, referenceId: `${ref}-R${i + 1}`, date: `2026-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`, type: inst.label, status: statusFor(lead.status, i), amount: amounts[i], }; }); } export type PipelineLead = { id: string; name: string; address: string; jobType: string; stage: string; agent: string; createdAgo: string; }; const FIRST_NAMES = [ "Wesley", "Ivy", "Corey", "Renata", "Miles", "Paula", "Jasper", "Dana", "Terrence", "Alina", "Grant", "Bethany", "Owen", "Marisol", "Kurt", "Tanya", "Reggie", "Selena", "Blake", "Vivian", "Colton", "Priya", "Gerald", "Fiona", ]; const LAST_NAMES = [ "Whitfield", "Caldwell", "Rourke", "Sanborn", "Delacroix", "Winters", "Blackwood", "Herrera", "Sweeney", "Okafor", "Lindgren", "Pruitt", "Castellano", "Marsh", "Yaeger", "Doyle", "Kowalczyk", "Beaumont", "Ashworth", "Nakamura", "Villanueva", "Prescott", "Hutchins", "Loomis", "Stanhope", "Everly", "Boone", ]; const STREETS = [ "Independence Pkwy", "Legacy Dr", "Coit Rd", "Parker Rd", "Alma Dr", "K Ave", "Ohio Dr", "Preston Rd", "Spring Creek Pkwy", "Custer Rd", "Shady Brook Ln", "Roundrock Trl", "Ridgeview Dr", "Chisholm Trl", "Los Rios Blvd", ]; const ZIPS = ["75023", "75024", "75025", "75074", "75075", "75093"]; const LEAD_JOB_TYPES = ["Roof Replacement", "Roof Inspection", "Gutter Replacement", "Siding Repair", "Window Replacement", "Roof Repair"]; const LEAD_STAGES = ["New Inquiry", "Contacted", "Qualifying", "Quote Requested", "Nurture"]; const LEAD_AGENTS = ["Cody Tatum", "Emma Wilson", "Liam Foster", "Sophie Turner", "Chloe Adams", "Unassigned"]; // Deterministic (no Math.random/Date.now) so server- and client-render match. export const pipelineLeads: PipelineLead[] = Array.from({ length: 45 }, (_, i) => { const first = FIRST_NAMES[i % FIRST_NAMES.length]; const last = LAST_NAMES[(i * 7) % LAST_NAMES.length]; const streetNum = 1000 + ((i * 137) % 8900); const street = STREETS[(i * 3) % STREETS.length]; const zip = ZIPS[i % ZIPS.length]; const daysAgo = ((i * 3) % 21) + 1; return { id: `p${i + 1}`, name: `${first} ${last}`, address: `${streetNum} ${street}, Plano TX ${zip}`, jobType: LEAD_JOB_TYPES[i % LEAD_JOB_TYPES.length], stage: LEAD_STAGES[(i * 2) % LEAD_STAGES.length], agent: LEAD_AGENTS[(i * 5) % LEAD_AGENTS.length], createdAgo: daysAgo === 1 ? "1 day ago" : `${daysAgo} days ago`, }; });