PageSourceSearch

https://www.dietforge.ai/assets/Planning-DtzoHvRN.js

js dietforge.ai collected 2026-10-05 11:57:57 UTC 13,010 bytes, 1 lines download raw bytes

1import{q as P,n as e}from"./vendor-react-BcaeZ4hN.js";import{cj as W,ck as H,cl as G,l as w,m as S,p as z,q as F,f as E,n as B,e as J,cc as X}from"./AppEntry-jF4EVTgR.js";import{B as L,R}from"./index-CihutTqe.js";import{T as Y,b as Z,c as D,a as $}from"./tabs-CwIv4n_g.js";import{P as _}from"./progress-C6iMP8Ja.js";import{S as ee,f as se,g as te,a as ne,c as ie}from"./select-C0k1ccPq.js";import{P as K}from"./PageHeader-BE0IHqMI.js";import"./IconButton-R3nNGuBi.js";import{E as ae}from"./ErrorState-BmWE3cdA.js";import{S as re}from"./SEO-lKdC8CLv.js";import{E as le}from"./ErrorBoundary-CIOx5NWw.js";import{u as oe,W as ce,M as de}from"./MillIngredientPlanningPanel-BWAdU2c2.js";import{u as me}from"./vendor-i18n-BhmF7AyI.js";import{F as xe}from"./factory-r9Dh-Yqy.js";import{C as ue}from"./chart-column-B8dEBB6v.js";import{P as pe}from"./package-DRF0I2GQ.js";import{L as he}from"./lightbulb-BHE3NOUx.js";import{T as Q}from"./trending-down-fQ3PCfvt.js";import"./vendor-radix-2tJqhpxG.js";import"./vendor-supabase-3ATeKDWb.js";import"./vendor-query-Bg59Ycle.js";import"./index-_hZ33_6A.js";import"./vendor-dates-CtQBbnUx.js";import"./chevron-down-fpVr79fy.js";import"./design-tokens-CDamzkGU.js";import"./accessibility-BLxkClBc.js";import"./file-question-DEc_F3NQ.js";import"./table-gIUivC6C.js";import"./checkbox-DLIzdsgL.js";import"./number-input-BN1SUEmR.js";import"./save-CXgbgGy6.js";function fe(s){const{mills:l,formulations:x,ingredientPricing:y,ingredientPlanning:b,batchSizes:m}=s,o=new Map;l.forEach(a=>{o.set(a.id,x.filter(p=>p.millId===a.id))});let c=0;const u=[],i=new Map;l.forEach(a=>{const p=o.get(a.id)||[],M=y[a.id]||{},f=p.map(n=>{const r=m[n.id]||a.batchSize/1e3||1,g=n.ingredients.map(j=>{const k=j.ingredient?.costPerKg||0,O=j.ingredient?.name||j.ingredientId,q=(M[j.ingredientId]||k)*1e3,T=j.inclusionPercent/100*r,U=T*q;i.has(j.ingredientId)||i.set(j.ingredientId,{name:O,total:0,byMill:{}});const A=i.get(j.ingredientId);return A.total+=T,A.byMill[a.id]=(A.byMill[a.id]||0)+T,{ingredientId:j.ingredientId,ingredientName:O,inclusionPercent:j.inclusionPercent,quantityTons:T,costPerTon:q,totalCost:U}}),N=g.reduce((j,k)=>j+k.totalCost,0);return c+=N,{formulationId:n.id,formulationName:n.name,batchSize:r,cost:N,ingredients:g}}),V=f.reduce((n,r)=>n+r.cost,0),t=f.reduce((n,r)=>n+r.batchSize,0);u.push({millId:a.id,millName:a.name,formulations:f,totalCost:V,totalVolume:t})});const h=ge(l,u,y,b,i),d=h.reduce((a,p)=>a+p.potentialSavings,0),I=c-d,v={};u.forEach(a=>{v[a.millId]=a.totalCost});const C=Array.from(i.entries()).map(([a,p])=>({ingredientId:a,ingredientName:p.name,totalDemand:p.total,byMill:p.byMill}));return{feasible:!0,allocations:u,totalSystemCost:c,costByMill:v,ingredientDemand:C,recommendations:h,comparisonVsIndependent:{independentCost:c,optimizedCost:I,savings:d,savingsPercent:c>0?d/c*100:0}}}function ge(s,l,x,y,b){const m=[];return b.forEach((o,c)=>{const u=Object.keys(o.byMill);if(u.length>=2){const i=[];if(u.forEach(h=>{const d=x[h]?.[c];d!==void 0&&i.push({millId:h,price:d})}),i.length>=2){i.sort((C,a)=>C.price-a.price);const h=i[0],d=i[i.length-1],I=d.price-h.price,v=I/d.price*100;if(v>5){const a=(o.byMill[d.millId]||0)*I*1e3,p=s.find(f=>f.id===h.millId),M=s.find(f=>f.id===d.millId);a>100&&m.push({type:"sourcing",description:`${o.name} is ${v.toFixed(0)}% cheaper at ${p?.name||"another mill"} vs ${M?.name}. Consider sourcing from ${p?.name}.`,potentialSavings:a})}}}}),y.forEach(o=>{const c=b.get(o.ingredientId);if(c){const u=c.byMill[o.millId]||0;if(o.availableQuantity>0&&u>o.availableQuantity){const i=u-o.availableQuantity,h=s.find(d=>d.id===o.millId);m.push({type:"transfer",description:`${c.name} shortage of ${i.toFixed(1)} tons at ${h?.name}. Check other mill inventories for transfer.`,potentialSavings:0})}}}),m.sort((o,c)=>c.potentialSavings-o.potentialSavings),m.slice(0,10)}function je(s,l){const x=new Map;return s.forEach(y=>{const b=l[y.id]||1;y.ingredients.forEach(m=>{const o=m.ingredient?.name||m.ingredientId,c=m.inclusionPercent/100*b;x.has(m.ingredientId)||x.set(m.ingredientId,{name:o,totalTons:0,formulationBreakdown:[]});const u=x.get(m.ingredientId);u.totalTons+=c,u.formulationBreakdown.push({name:y.name,tons:c})})}),x}function Xe(){const{t:s}=me("planning"),{mills:l}=W(),{formulations:x,error:y,refetch:b}=H(),{ingredients:m,error:o,refetch:c}=G(),{planning:u}=oe(),[i,h]=P.useState(null),[d,I]=P.useState(!1),[v,C]=P.useState(l[0]?.id||""),a=P.useMemo(()=>{const t={};return l.forEach(n=>{t[n.id]={},m.filter(r=>
1r.millId===n.id||!r.millId).forEach(r=>{t[n.id][r.id]=r.millPrices?.[n.id]??r.costPerKg})}),t},[l,m]),p=P.useMemo(()=>{const t={};return x.forEach(n=>{const r=l.find(g=>g.id===n.millId);t[n.id]=(r?.batchSize||1e3)/1e3}),je(x,t)},[x,l]),M=async()=>{I(!0);try{const t={};x.forEach(r=>{const g=l.find(N=>N.id===r.millId);t[r.id]=(g?.batchSize||1e3)/1e3});const n=fe({mills:l,formulations:x,ingredientPricing:a,ingredientPlanning:u,batchSizes:t});h(n)}catch{X.error(s("analysisError","Analysis failed. Please try again."))}finally{I(!1)}},f=t=>new Intl.NumberFormat("en-US",{style:"currency",currency:l[0]?.currency||"USD",maximumFractionDigits:0}).format(t);return y||o?e.jsxs("div",{className:"space-y-6",children:[e.jsx(K,{title:s("title"),description:s("description")}),e.jsx(ae,{variant:"server",title:s("common:loadError","Failed to load planning data"),message:s("common:loadErrorDescription","We had trouble loading your data. Please check your connection and try again."),onRetry:()=>{b(),c()}})]}):e.jsxs(le,{children:[e.jsx(re,{title:"Planning",description:"Multi-mill ingredient planning and system analysis",noIndex:!0}),e.jsxs("div",{className:"space-y-6",children:[e.jsx(K,{title:s("title"),description:s("description"),actions:e.jsxs(L,{onClick:M,disabled:d||l.length<2,children:[e.jsx(R,{className:`h-4 w-4 mr-2 ${d?"animate-spin":""}`}),s(d?"analyzing":"runAnalysis")]})}),l.length<2?e.jsx(w,{children:e.jsxs(S,{className:"flex flex-col items-center justify-center py-12",children:[e.jsx(xe,{className:"h-12 w-12 text-muted-foreground mb-4"}),e.jsx("h3",{className:"text-lg font-medium mb-2",children:s("multipleMillsRequired.title")}),e.jsx("p",{className:"text-muted-foreground text-center max-w-md",children:s("multipleMillsRequired.description")})]})}):e.jsxs(Y,{defaultValue:"overview",className:"space-y-4",children:[e.jsxs(Z,{children:[e.jsx(D,{value:"overview",children:s("tabs.overview")}),e.jsx(D,{value:"inventory",children:s("tabs.inventory")}),e.jsx(D,{value:"demand",children:s("tabs.demand")}),e.jsx(D,{value:"recommendations",children:s("tabs.recommendations")})]}),e.jsxs($,{value:"overview",className:"space-y-4",children:[e.jsx("div",{className:"grid gap-4 md:grid-cols-2 lg:grid-cols-3",children:l.map(t=>{const n=x.filter(g=>g.millId===t.id),r=i?.allocations.find(g=>g.millId===t.id);return e.jsxs(w,{children:[e.jsxs(z,{className:"pb-2",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(F,{className:"text-base",children:t.name}),e.jsx(E,{variant:"outline",children:t.currency})]}),e.jsx(B,{children:t.location||s("overview.noLocation")})]}),e.jsx(S,{children:e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex justify-between text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:s("overview.formulations")}),e.jsx("span",{className:"font-medium",children:n.length})]}),r&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex justify-between text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:s("overview.totalVolume")}),e.jsxs("span",{className:"font-medium",children:[r.totalVolume.toFixed(1)," ",s("demand.tons")]})]}),e.jsxs("div",{className:"flex justify-between text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:s("overview.totalCost")}),e.jsx("span",{className:"font-medium",children:f(r.totalCost)})]})]})]})})]},t.id)})}),i&&e.jsxs(w,{children:[e.jsx(z,{children:e.jsxs(F,{className:"flex items-center gap-2",children:[e.jsx(ue,{className:"h-5 w-5"}),s("systemSummary.title")]})}),e.jsx(S,{children:e.jsxs("div",{className:"grid gap-4 md:grid-cols-3",children:[e.jsxs("div",{className:"p-4 rounded-lg bg-muted/50",children:[e.jsx("p",{className:"text-sm text-muted-foreground",children:s("systemSummary.totalSystemCost")}),e.jsx("p",{className:"text-2xl font-bold",children:f(i.totalSystemCost)})]}),e.jsxs("div",{className:"p-4 rounded-lg bg-muted/50",children:[e.jsx("p",{className:"text-sm text-muted-foreground",children:s("systemSummary.potentialSavings")}),e.jsx("p",{className:"text-2xl font-bold text-success",children:f(i.comparisonVsIndependent.savings)}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:[i.comparisonVsIndependent.savingsPercent.toFixed(1),"% ",s("systemSummary.reduction")]})]}),e.jsxs("div",{className:"p-4 rounded-lg bg-muted/50",children:[e.jsx("p",{className:"text-sm text-muted-foreground",children:s("systemSummary.recommendationsCount")}
1),e.jsx("p",{className:"text-2xl font-bold",children:i.recommendations.length}),e.jsx("p",{className:"text-xs text-muted-foreground",children:s("systemSummary.optimizationOpportunities")})]})]})})]})]}),e.jsx($,{value:"inventory",className:"space-y-4",children:e.jsxs(w,{children:[e.jsx(z,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsxs(F,{className:"flex items-center gap-2",children:[e.jsx(ce,{className:"h-5 w-5"}),s("inventory.title")]}),e.jsx(B,{children:s("inventory.description")})]}),e.jsxs(ee,{value:v,onValueChange:C,children:[e.jsx(se,{className:"w-[200px]",children:e.jsx(te,{placeholder:s("inventory.selectMill")})}),e.jsx(ne,{children:l.map(t=>e.jsx(ie,{value:t.id,children:t.name},t.id))})]})]})}),e.jsx(S,{children:v&&l.find(t=>t.id===v)&&e.jsx(de,{mill:l.find(t=>t.id===v)})})]})}),e.jsx($,{value:"demand",className:"space-y-4",children:e.jsxs(w,{children:[e.jsxs(z,{children:[e.jsxs(F,{className:"flex items-center gap-2",children:[e.jsx(pe,{className:"h-5 w-5"}),s("demand.title")]}),e.jsx(B,{children:s("demand.description")})]}),e.jsx(S,{children:e.jsx("div",{className:"space-y-4",children:Array.from(p.entries()).sort((t,n)=>n[1].totalTons-t[1].totalTons).slice(0,15).map(([t,n])=>{const r=Math.max(...Array.from(p.values()).map(N=>N.totalTons)),g=n.totalTons/r*100;return e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"font-medium text-sm",children:n.name}),e.jsxs("span",{className:"text-sm text-muted-foreground",children:[n.totalTons.toFixed(2)," ",s("demand.tons")]})]}),e.jsx(_,{value:g,className:"h-2"}),e.jsxs("div",{className:"flex gap-2 flex-wrap",children:[n.formulationBreakdown.slice(0,3).map(N=>e.jsxs(E,{variant:"secondary",className:"text-xs",children:[N.name,": ",N.tons.toFixed(2),"t"]},N.name)),n.formulationBreakdown.length>3&&e.jsxs(E,{variant:"outline",className:"text-xs",children:["+",n.formulationBreakdown.length-3," ",s("demand.more")]})]})]},t)})})})]})}),e.jsx($,{value:"recommendations",className:"space-y-4",children:i?i.recommendations.length===0?e.jsx(w,{children:e.jsxs(S,{className:"flex flex-col items-center justify-center py-12",children:[e.jsx(Q,{className:"h-12 w-12 text-success mb-4"}),e.jsx("h3",{className:"text-lg font-medium mb-2",children:s("recommendations.wellOptimized.title")}),e.jsx("p",{className:"text-muted-foreground text-center max-w-md",children:s("recommendations.wellOptimized.description")})]})}):e.jsx("div",{className:"space-y-4",children:i.recommendations.map(t=>e.jsx(w,{children:e.jsx(S,{className:"py-4",children:e.jsxs("div",{className:"flex items-start gap-4",children:[e.jsxs("div",{className:`p-2 rounded-lg ${t.type==="sourcing"?"bg-primary/10 text-primary":t.type==="transfer"?"bg-warning/10 text-warning-foreground":"bg-accent/10 text-accent-foreground"}`,children:[t.type==="sourcing"&&e.jsx(Q,{className:"h-5 w-5"}),t.type==="transfer"&&e.jsx(J,{className:"h-5 w-5"}),t.type==="reallocation"&&e.jsx(R,{className:"h-5 w-5"})]}),e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[e.jsx(E,{variant:"outline",className:"capitalize",children:s(`recommendations.types.${t.type}`)}),t.potentialSavings>0&&e.jsxs(E,{variant:"default",className:"bg-success",children:[s("recommendations.save")," ",f(t.potentialSavings)]})]}),e.jsx("p",{className:"text-sm",children:t.description})]})]})})},`${t.type}-${t.description}`))}):e.jsx(w,{children:e.jsxs(S,{className:"flex flex-col items-center justify-center py-12",children:[e.jsx(he,{className:"h-12 w-12 text-muted-foreground mb-4"}),e.jsx("h3",{className:"text-lg font-medium mb-2",children:s("recommendations.runFirst.title")}),e.jsx("p",{className:"text-muted-foreground text-center max-w-md mb-4",children:s("recommendations.runFirst.description")}),e.jsxs(L,{onClick:M,children:[e.jsx(R,{className:"h-4 w-4 mr-2"}),s("runAnalysis")]})]})})})]})]})]})}export{Xe 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.