PageSourceSearch

https://lexuvoapp.lovable.app/assets/PortfolioDetail-CscqZ6R0.js

js lexuvoapp.lovable.app collected 2026-10-03 03:36:40 UTC 6,350 bytes, 1 lines download raw bytes

1import{b2 as z,ae as A,G as E,H as I,r as l,j as s,ap as y,B as p,Z as D,af as U,b3 as W,aP as $,ag as G,ah as K,ai as Z,W as J,al as Q,C as d,x as c,i as V,am as X,g as m}from"./index-Ch9yA5uW.js";import{P as Y}from"./PageHeader-BabG749J.js";import{T as ss,F as es}from"./TasksModuleNav-CUo4SYvh.js";import{P as R}from"./progress-BOn9g6NS.js";import"./layers-ISd83ovk.js";import"./layout-grid-CBYYPlQr.js";import"./calendar-days-CH5u3w5H.js";import"./award-D1AZC0ET.js";import"./timer-CeQ4ZTdy.js";import"./repeat-qm1_-ZJv.js";function xs(){const{portfolioId:i}=z(),{workspaceId:x}=A(),{toast:q}=E(),g=I(),[F,N]=l.useState(!0),[j,M]=l.useState(null),[u,T]=l.useState([]),[v,H]=l.useState({}),[k,B]=l.useState([]),[L,f]=l.useState(!1),[h,_]=l.useState(new Set),w=async()=>{if(!i||!x)return;N(!0);const[e,t,a,o]=await Promise.all([m.from("task_portfolios").select("*").eq("id",i).eq("workspace_id",x).maybeSingle(),m.from("task_portfolio_projects").select("project_id").eq("portfolio_id",i),m.from("task_projects").select("id, name, code, color, status, target_date").eq("workspace_id",x).is("archived_at",null).order("name"),m.from("v_project_health").select("*").eq("workspace_id",x)]);M(e.data);const P=new Set((t.data??[]).map(n=>n.project_id)),C=a.data??[];B(C),T(C.filter(n=>P.has(n.id)));const S={};for(const n of o.data??[])S[n.project_id]=n;H(S),_(P),N(!1)};l.useEffect(()=>{w()},[i,x]);const O=async()=>{if(!i)return;const e=new Set(u.map(o=>o.id)),t=[...h].filter(o=>!e.has(o)),a=[...e].filter(o=>!h.has(o));t.length&&await m.from("task_portfolio_projects").insert(t.map(o=>({portfolio_id:i,project_id:o}))),a.length&&await m.from("task_portfolio_projects").delete().eq("portfolio_id",i).in("project_id",a),f(!1),q({title:"Portfolio updated"}),w()};if(F)return s.jsxs("div",{className:"p-6 space-y-3",children:[s.jsx(y,{className:"h-12 w-72"}),s.jsx(y,{className:"h-48 w-full"})]});if(!j)return s.jsxs("div",{className:"p-6 space-y-3",children:[s.jsxs(p,{variant:"ghost",size:"sm",onClick:()=>g("/firm/tasks/portfolios"),children:[s.jsx(D,{className:"h-4 w-4 mr-1"})," Back"]}),s.jsx("p",{className:"text-sm text-muted-foreground",children:"Portfolio not found."})]});const r=u.reduce((e,t)=>{const a=v[t.id];return a&&(e.tasks+=a.total_tasks??0,e.completed+=a.completed_tasks??0,e.overdue+=a.overdue_tasks??0,e.hours+=Number(a.actual_hours??0),e.estimated+=Number(a.estimated_hours??0)),e},{tasks:0,completed:0,overdue:0,hours:0,estimated:0}),b=r.tasks>0?Math.round(r.completed/r.tasks*100):0;return s.jsxs("div",{className:"p-4 md:p-6 space-y-4",children:[s.jsxs(p,{variant:"ghost",size:"sm",onClick:()=>g("/firm/tasks/portfolios"),className:"-ml-2",children:[s.jsx(D,{className:"h-4 w-4 mr-1"})," Portfolios"]}),s.jsx(Y,{title:j.name,description:j.description??void 0,actions:s.jsxs(U,{open:L,onOpenChange:f,children:[s.jsx(W,{asChild:!0,children:s.jsxs(p,{size:"sm",children:[s.jsx($,{className:"h-4 w-4 mr-1.5"}),"Manage projects"]})}),s.jsxs(G,{className:"max-w-md",children:[s.jsx(K,{children:s.jsx(Z,{children:"Pick projects"})}),s.jsxs("div",{className:"space-y-1 max-h-80 overflow-auto",children:[k.map(e=>s.jsxs("label",{className:"flex items-center gap-2 p-2 rounded hover:bg-muted cursor-pointer",children:[s.jsx(J,{checked:h.has(e.id),onCheckedChange:t=>{const a=new Set(h);t?a.add(e.id):a.delete(e.id),_(a)}}),s.jsx("span",{className:"h-2 w-2 rounded-full",style:{backgroundColor:e.color??"hsl(var(--muted-foreground))"}}),s.jsx("span",{className:"text-sm",children:e.name})]},e.id)),k.length===0&&s.jsx("p",{className:"text-sm text-muted-foreground p-3",children:"No projects available."})]}),s.jsxs(Q,{children:[s.jsx(p,{variant:"outline",onClick:()=>f(!1),children:"Cancel"}),s.jsx(p,{onClick:O,children:"Save"})]})]})]})}),s.jsx(ss,{}),s.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-5 gap-3",children:[s.jsx(d,{children:s.jsxs(c,{className:"p-4",children:[s.jsx("div",{className:"text-xs text-muted-foreground",children:"Roll-up progress"}),s.jsxs("div",{className:"text-2xl font-bold mt-1",children:[b,"%"]}),s.jsx(R,{value:b,className:"h-1.5 mt-2"})]})}),s.jsx(d,{children:s.jsxs(c,{className:"p-4",children:[s.jsx("div",{className:"text-xs text-muted-foreground",children:"Projects"}),s.jsx("div",{className:"text-2xl font-bold mt-1",children:u.length})]})}),s.jsx(d,{children:s.jsxs(c,{className:"p-4",children:[s.jsx("div",{className:"text-xs text-muted-foreground",children:"Tasks"}),s.jsx("div",{className:"text-2xl font-bold mt-1",children:r.tasks})]})}),s.jsx(d,{children:s.jsxs(c,{className:"p-4",children:[s.jsx("div",{className:"text-xs text-muted-foreground",children:"Overdue"}),s.jsx("div",{className:`text-2xl font-bold mt-1 ${r.overdue>0?"text-destructive":""}`,children:r.overdue})]})}),s.jsx(d,{children:s.jsxs(c,{className:"p-4",children:[s.jsx("div",{className:"text-xs text-muted-foreground",children:"Hours"}),s.jsx("div",{className:"text-2xl font-bold mt-1",children:r.hours.toFixed(1)}),s.jsxs("div",{className:"text-xs text-muted-foreground",children:["of ",r.estimated.toFixed(1)," est"]})]})})]}),u.length===0?s.jsx(d,{children:s.jsxs(c,{className:"p-8 text-center text-sm text-muted-foreground",children:[s.jsx(es,{className:"h-8 w-8 mx-auto mb-2 opacity-50"}),'No projects in this portfolio yet. Use "Manage projects" to add some.']})}):s.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3",children:u.map(e=>
1{const t=v[e.id];return s.jsx(V,{to:`/firm/tasks/projects/${e.id}`,children:s.jsx(d,{className:"hover:shadow-md transition-shadow h-full",children:s.jsxs(c,{className:"p-4 space-y-2",children:[s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("span",{className:"h-2.5 w-2.5 rounded-full",style:{backgroundColor:e.color??"hsl(var(--muted-foreground))"}}),s.jsx("span",{className:"font-semibold text-sm truncate",children:e.name}),s.jsx(X,{variant:"outline",className:"ml-auto text-[10px]",children:e.status.replace("_"," ")})]}),s.jsx(R,{value:(t==null?void 0:t.percent_complete)??0,className:"h-1.5"}),s.jsxs("div",{className:"text-xs text-muted-foreground flex justify-between",children:[s.jsxs("span",{children:[(t==null?void 0:t.completed_tasks)??0,"/",(t==null?void 0:t.total_tasks)??0," tasks"]}),s.jsxs("span",{children:[Number((t==null?void 0:t.actual_hours)??0).toFixed(1)," h"]})]})]})})},e.id)})})]})}export{xs as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.