PageSourceSearch

https://ronheichman-recipe-app.hf.space/app/assets/PantryDrawer-DVVVEV34.js

js ronheichman-recipe-app.hf.space collected 2026-10-03 17:18:40 UTC 27,118 bytes, 1 lines download raw bytes

1import{r as o,j as e,n as T,g as E,f as ce,a as ne,p as de,b as ue,u as me}from"./index-XVMXvNIo.js";import{p as H}from"./parseIngredientDraft-CEyjgKvK.js";import{F as pe}from"./FoodDropdown-CbzfyKXD.js";import{D as he}from"./DrawerWrapper-YRUgp4ii.js";import{E as Z}from"./EmptyState-Dst9zNIF.js";let ee=0;function ge(){return ee+=1,`bulk_pantry_${Date.now()}_${ee}`}function fe(i){return i.split(/\r?\n/).map(n=>n.trim()).filter(n=>n.length>0)}function ye(i,n){return i===null&&n===null?"No amount":[i,n].filter(c=>c!==null&&c!=="").join(" ")}function xe(i,n){return n===null||i.some(c=>c.id===n)?i:[{enum_name:"MeasurementUnit",id:n,label:n,description:null},...i]}async function be(i){const n=i.text.trim();if(T(n)===T(i.mappingText))return i;const c=await H(n);return{...i,fdcIds:c.fdcIds,specialtyIds:c.specialtyIds,mappingText:c.text.trim()||n}}function ve({items:i,unitOptions:n,onAddItem:c,onUpdateItem:x}){const[f,p]=o.useState(""),[m,v]=o.useState([]),[y,C]=o.useState(!1),[w,d]=o.useState(!1),[N,h]=o.useState(null),P=o.useMemo(()=>m.filter(t=>t.accepted&&t.text.trim().length>0).length,[m]);async function _(){const t=fe(f);if(t.length===0){v([]),h("Paste at least one ingredient line.");return}C(!0),h(null);try{const s=await Promise.all(t.map(async j=>{const I=await H(j);return{rawText:j,parsed:I}})),a=new Set,l=s.map(({rawText:j,parsed:I})=>{const B=T(I.text),r=a.has(B);return a.add(B),{id:ge(),rawText:j,text:I.text,quantity:I.quantity,unit:I.unit,fdcIds:I.fdcIds,specialtyIds:I.specialtyIds,mappingText:I.text,accepted:B.length>0&&!r}});v(l),h(`Review ${l.length} parsed ingredient${l.length===1?"":"s"}.`)}finally{C(!1)}}function R(t,s){v(a=>a.map(l=>l.id===t?s(l):l))}function S(t){v(s=>s.filter(a=>a.id!==t))}async function u(){const t=m.filter(s=>s.accepted&&s.text.trim().length>0);if(t.length===0){h("Select at least one ingredient to add.");return}d(!0);try{const s=await Promise.all(t.map(be)),a=new Map(i.map(l=>[T(l.text),l]));for(const l of s){const j=l.text.trim(),I=T(j),B=a.get(I);if(B!==void 0){const k=B.fdcIds.length===0&&B.specialtyIds.length===0,b={...B,text:j,quantity:l.quantity,unit:l.unit,fdcIds:k?l.fdcIds:B.fdcIds,specialtyIds:k?l.specialtyIds:B.specialtyIds};x(b),a.set(I,b);continue}const r={id:E(),text:j,quantity:l.quantity,unit:l.unit,fdcIds:l.fdcIds,specialtyIds:l.specialtyIds};c(r),a.set(I,r)}p(""),v([]),h(`Added ${t.length} ingredient${t.length===1?"":"s"} to pantry.`)}finally{d(!1)}}return e.jsxs("div",{className:"BulkPantryImporter",children:[e.jsx("label",{className:"BulkPantryLabel",htmlFor:"bulk-pantry-input",children:"Paste many pantry ingredients"}),e.jsx("textarea",{id:"bulk-pantry-input",className:"BulkPantryTextarea",value:f,placeholder:"One ingredient per line",rows:4,disabled:y||w,onChange:t=>{p(t.target.value),v([]),h(null)}}),e.jsxs("div",{className:"BulkPantryActions",children:[e.jsx("button",{className:"Btn BtnCompact",type:"button",disabled:y||w||f.trim().length===0,onClick:()=>{_()},children:y?"Reviewing...":"Review ingredients"}),m.length>0&&e.jsx("button",{className:"Btn BtnPrimary BtnCompact",type:"button",disabled:y||w||P===0,onClick:()=>{u()},children:w?"Adding...":`Add ${P} to pantry`})]}),N!==null&&e.jsx("div",{className:"BulkPantryStatus",role:"status",children:N}),m.length>0&&e.jsx("div",{className:"BulkPantryReviewList","aria-label":"Bulk pantry review",children:m.map((t,s)=>e.jsxs("div",{className:"BulkPantryReviewRow",children:[e.jsx("input",{className:"BulkPantryAccept",type:"checkbox","aria-label":`Accept ${t.text}`,checked:t.accepted,onChange:a=>R(t.id,l=>({...l,accepted:a.target.checked}))}),e.jsxs("div",{className:"BulkPantryParsed",children:[e.jsx("div",{className:"BulkPantryParsedName",children:t.text}),e.jsx("input",{className:"BulkPantryNameInput",type:"text","aria-label":`Ingredient name ${s+1}`,value:t.text,onChange:a=>R(t.id,l=>({...l,text:a.target.value}))}),e.jsxs("div",{className:"BulkPantryRaw muted",title:t.rawText,children:["Line ",s+1]})]}),e.jsx("input",{className:"BulkPantryQuantity",type:"number","aria-label":`Quantity for ${t.text}`,value:t.quantity??"",placeholder:"#",onChange:a=>R(t.id,l=>({...l,quantity:a.target.value===""?null:Number(a.target.value)}))}),e.jsxs("select",{className:"BulkPantryUnit","aria-label":`Unit for ${t.text}`,value:t.unit??"",onChange:a=>R(t.id,l=>({...l,unit:a.target.value===""?null:a.target.value})),children:[e.jsx("option",{value:"",children:ye(t.quantity,t.unit)}),xe(n,t.unit).map(a=>e.jsx("option",{value:a.id,children:a.label},a.id))]}),e.jsx("button",{className:"BulkPantryRemove",type:"button","aria-label":`Remove ${t.text} from bulk review`,onClick:()=>S(t.id),children:"x"})]},t.id))})]})}function se(i){const{items:n,unitOptions:c,onUpdateItem:x,onRemoveItem:f,renderPicker:p,filterMode:m,groupBy:v,groupSort:y,renderGroupTitle:C,draft:w,setDraft:d,isParsing:N,inputRef:h,placeholderIdle:P,ariaLabelAdd:_,onCommitDraft:R}=i,S=o.useRef(null),u=m??"dim",t=w.trim().toLowerCase(),{matchingIds:s,visibleItems:a}=o.useMemo(()=>
1{if(t.length===0)return{matchingIds:new Set,visibleItems:n};const r=new Set,k=[];for(const g of n)g.text.toLowerCase().includes(t)&&(r.add(g.id),k.push(g));if(u==="filter")return{matchingIds:r,visibleItems:k};if(r.size===0||r.size===n.length)return{matchingIds:r,visibleItems:n};const b=n.slice().sort((g,D)=>{const $=r.has(g.id)?0:1,z=r.has(D.id)?0:1;return $-z});return{matchingIds:r,visibleItems:b}},[n,t,u]),l=t.length>0,j=s.size>0,I=o.useMemo(()=>{if(v===void 0)return[];const r=new Map;for(const b of a){const g=v(b),D=g.trim().length>0?g:"Uncategorized",$=r.get(D);$!==void 0?$.push(b):r.set(D,[b])}const k=Array.from(r.entries());return k.sort((b,g)=>y!==void 0?y(b[0],g[0]):b[0].localeCompare(g[0])),k.map(([b,g])=>({groupKey:b,groupItems:g}))},[v,y,a]);function B(r){const k=l&&s.has(r.id),b=u==="dim"&&l&&j&&!k;return e.jsxs("div",{className:"PantryChip PantryChipGrid"+(k?" PantryChipMatch":"")+(b?" PantryChipDimmed":""),children:[(r.quantity!==null||r.unit!==null)&&e.jsx("button",{type:"button",className:"PantryChipClearQty","aria-label":`Clear quantity and unit for ${r.text}`,title:"Clear quantity & unit",onClick:g=>{g.stopPropagation(),x({...r,quantity:null,unit:null})},children:"⌫"}),r.quantity===null&&r.unit===null&&e.jsx("span",{className:"PantryChipClearQtyPlaceholder"}),e.jsx("input",{type:"number",className:"PantryChipQty",value:r.quantity??"",placeholder:"#",title:"Quantity",onClick:g=>g.stopPropagation(),onChange:g=>x({...r,quantity:g.target.value===""?null:Number(g.target.value)})}),e.jsxs("select",{className:"PantryChipUnit",value:r.unit??"",title:"Unit",onClick:g=>g.stopPropagation(),onChange:g=>x({...r,unit:g.target.value===""?null:g.target.value}),children:[e.jsx("option",{value:"",children:"—"}),c.map(g=>e.jsx("option",{value:g.id,children:g.label},g.id))]}),p(r),e.jsx("button",{type:"button",className:"PantryChipRemove","aria-label":`Remove ${r.text}`,onClick:g=>{g.stopPropagation(),f(r.id)},children:"×"})]},r.id)}return e.jsxs("div",{ref:S,className:"Stack GapSm",children:[e.jsx("input",{ref:h,className:"PantryChipInput",type:"text",name:"ingredient-filter",value:w,placeholder:N?"Parsing…":P,"aria-label":_,disabled:N,autoComplete:"off","data-lpignore":"true","data-1p-ignore":"true","data-form-type":"other",onChange:r=>d(r.target.value),onKeyDown:r=>{r.key==="Enter"&&(r.preventDefault(),R()),r.key==="Backspace"&&w===""&&n.length>0&&f(n[n.length-1].id)},onBlur:r=>{const k=r.relatedTarget;k instanceof Node&&S.current?.contains(k)===!0||R()}}),l&&s.size>0&&e.jsxs("div",{className:"PantryFilterHint",children:[s.size," existing item",s.size!==1?"s":""," match"]}),I.length===0&&a.map(r=>B(r)),I.length>0&&e.jsx(e.Fragment,{children:I.map(({groupKey:r,groupItems:k})=>e.jsxs("div",{className:"Stack GapSm",children:[C!==void 0?C(r,k):e.jsx("div",{className:"muted",children:r}),k.map(b=>B(b))]},r))})]})}function Ce(i){const{items:n,unitOptions:c,onAddItem:x,onUpdateItem:f,onRemoveItem:p}=i,[m,v]=o.useState(""),[y,C]=o.useState(!1),w=o.useRef(null),d=o.useRef(!1),N=o.useRef(""),[h,P]=o.useState({}),_=o.useRef(h);o.useEffect(()=>{_.current=h},[h]);async function R(){const s=m.trim();if(s.length===0||d.current||T(s)===T(N.current))return;d.current=!0,C(!0);const a=await H(s),{text:l,quantity:j,unit:I}=a,B=a.fdcIds,r=a.specialtyIds,k=T(l),b=n.find(g=>T(g.text)===k);if(b){const g=b.fdcIds.length===0&&b.specialtyIds.length===0;f({...b,text:l,quantity:j,unit:I,fdcIds:g?B:b.fdcIds,specialtyIds:g?r:b.specialtyIds})}else x({id:E(),text:l,quantity:j,unit:I,fdcIds:B,specialtyIds:r});N.current=l,v(l),C(!1),d.current=!1}o.useEffect(()=>{const s=new Set;for(const j of n)for(const I of j.fdcIds)I>0&&s.add(I);const a=Array.from(s).filter(j=>_.current[j]===void 0);if(a.length===0)return;let l=!1;return ce(a).then(j=>{l||P(I=>{const B={...I};for(const r of j)B[r.fdc_id]=r;return B})}).catch(()=>{}),()=>{l=!0}},[n]);const S=o.useMemo(()=>s=>{if(s.specialtyIds.length>0)return"Specialty";if(s.fdcIds.length===0)return"Uncategorized";for(const a of s.fdcIds){const j=h[a]?.food_category?.trim();if(typeof j=="string"&&j.length>0)return j}return"Uncategorized"},[h]),u=o.useMemo(()=>{function s(a){return a==="Uncategorized"?2:a==="Specialty"?1:0}return(a,l)=>{const j=s(a),I=s(l);return j!==I?j-I:a.localeCompare(l)}},[]);function t(s){v(s),T(s)!==T(N.current)&&(N.current="")}return e.jsx(se,{items:n,unitOptions:c,onUpdateItem:f,onRemoveItem:p,filterMode:"filter",groupBy:S,groupSort:u,renderGroupTitle:s=>e.jsx("div",{className:"PantryCategoryTitle",children:s}),renderPicker:s=>e.jsx(pe,{text:s.text,fdcIds:s.fdcIds,onUpdateFdcIds:a=>f({...s,fdcIds:a}),specialtyIds:s.specialtyIds,onUpdateSpecialtyIds:a=>f({...s,specialtyIds:a})}),draft:m,setDraft:t,isParsing:y,inputRef:w,placeholderIdle:"Type ingredient, press Enter…",ariaLabelAdd:"Add pantry item",onCommitDraft:R})}async function ae(i){const n=await ne(`/api/consumables/fuzzy?query_text=${encodeURIComponent(i.queryText)}&limit=${encodeURIComponent(String(i.limit))}`);if(!n.ok)throw new Error(`Failed to search consumables (${n.status})`);return await n.json()}function Ie(i){const{text:n,specialtyIds:c,onUpdateSpecialtyIds:x}
1=i,[f,p]=o.useState([]),[m,v]=o.useState(!1),y=o.useRef(null);o.useEffect(()=>{!m&&c.length===0||ae({queryText:n,limit:25}).then(d=>p(d.matches)).catch(()=>p([]))},[m,c.length,n]),me(y,()=>v(!1));function C(){if(c.length===0)return n;const d=c.map(N=>f.find(h=>h.specialty_id===N)?.description).filter(N=>N!==void 0);return d.length===0?`${n} (${c.length})`:d.length===1?`${n} → ${d[0]}`:`${n} → ${d[0]} +${d.length-1}`}function w(d){const N=c.includes(d)?c.filter(h=>h!==d):[...c,d];x(N)}return e.jsxs("div",{ref:y,className:"FoodDropdown",children:[e.jsx("button",{type:"button",className:"FoodDropdownTrigger",onClick:()=>v(!m),title:"Choose consumable match",children:C()}),m&&e.jsx("div",{className:"FoodDropdownMenu",children:f.length===0?e.jsx("div",{className:"FoodDropdownEmpty muted",children:"No matches"}):f.map(d=>{const N=c.includes(d.specialty_id),h=`consumable_${d.specialty_id}`;return e.jsxs("div",{className:`FoodDropdownItem${N?" FoodDropdownItemSelected":""}`,children:[e.jsx("input",{id:h,type:"checkbox",checked:N,onChange:()=>w(d.specialty_id),className:"FoodDropdownCheckbox"}),e.jsxs("label",{htmlFor:h,className:"FoodDropdownLabel",children:[e.jsx("span",{className:"FoodDropdownLabelText",children:d.description}),e.jsxs("span",{className:"FoodDropdownMeta",children:[Math.round(d.similarity_score),"% match",d.selection_count!==null?` · used ${d.selection_count}×`:""]})]})]},d.specialty_id)})})]})}function je(i){const{items:n,unitOptions:c,onAddItem:x,onUpdateItem:f,onRemoveItem:p}=i,[m,v]=o.useState(""),[y,C]=o.useState(!1),w=o.useRef(null);async function d(h){const P=await ae({queryText:h,limit:10});return P.matches.length===0?[]:[P.matches[0].specialty_id]}async function N(){const h=m.trim();if(h.length===0||y)return;C(!0);let P=null,_=null,R=h;try{const s=await de(h);P=s.quantity,R=s.food_text,_=s.unit}catch{}const S={id:ue(),text:R,quantity:P,unit:_,specialtyIds:[]},u=T(S.text),t=n.find(s=>T(s.text)===u);t?f({...t,text:S.text,quantity:S.quantity,unit:S.unit}):x(S),(async()=>{try{const s=await d(S.text);if(s.length===0)return;const a=t??S;a.specialtyIds.length===0&&f({...a,specialtyIds:s})}catch{}})(),v(""),C(!1)}return e.jsx(se,{items:n,unitOptions:c,onUpdateItem:f,onRemoveItem:p,renderPicker:h=>e.jsx(Ie,{text:h.text,specialtyIds:h.specialtyIds,onUpdateSpecialtyIds:P=>f({...h,specialtyIds:P})}),draft:m,setDraft:v,isParsing:y,inputRef:w,placeholderIdle:"Type consumable, press Enter…",ariaLabelAdd:"Add consumable item",onCommitDraft:N})}function O(i,n){const c=i.match_fdc_ids??[];return c.length>0?c.some(x=>n.has(x)):n.has(i.fdc_id)}let G=null,Y="";async function Ne(i){const n=i.slice().sort((p,m)=>p-m).join(",");if(G!==null&&n===Y)return G;const c={staples:[],you_might_have:[],clusters:[]};let x;try{x=await ne(`/api/pantry/suggestions?pantry_fdc_ids=${encodeURIComponent(n)}`)}catch{return c}if(!x.ok)return c;const f=await x.json();return G=f,Y=n,f}function Se(){G=null,Y=""}function W({className:i,isOpen:n,title:c,onToggle:x,children:f}){return e.jsxs("details",{className:i??"Accordion",open:n,onToggle:p=>x(p.currentTarget.open),children:[e.jsx("summary",{children:c}),f]})}function ke({staples:i,pantryFdcIds:n,isOpen:c,onToggle:x,onAdd:f}){return i.length===0?null:e.jsx("div",{className:"PantrySuggestions",children:e.jsx(W,{isOpen:c,title:`Pantry Staples (${i.length})`,onToggle:x,children:e.jsxs("div",{className:"AccordionContent",children:[e.jsx("div",{className:"SuggestionSectionHint",children:"Common ingredients most recipes use. Tap to add."}),e.jsx("div",{className:"SuggestionChipGrid",children:i.map(p=>{const m=O(p,n);return e.jsxs("button",{type:"button",className:"SuggestionChip"+(m?" SuggestionChip--added":""),title:p.food_category??p.name,onClick:()=>{m||f(p)},children:[e.jsx("span",{className:"SuggestionChipPlus",children:m?"✓":"+"}),p.name]},p.fdc_id)})})]})})})}const Pe=1;function we(i){return Number.isFinite(i.score)&&i.score>=Pe}function Re({suggestions:i,pantryFdcIds:n,isOpen:c,onToggle:x,onAdd:f}){if(i.length===0)return null;const p=i.filter(we);return e.jsx("div",{className:"PantrySuggestions",children:e.jsx(W,{isOpen:c,title:`You Might Have (${p.length})`,onToggle:x,children:e.jsxs("div",{className:"AccordionContent",children:[e.jsx("div",{className:"SuggestionSectionHint",children:"Ingredients that often appear alongside what's already in your pantry."}),e.jsx("div",{className:"SuggestionChipGrid",children:p.map(m=>{const v=O(m,n);return e.jsxs("button",{type:"button",className:"SuggestionChip"+(v?" SuggestionChip--added":""),title:(m.food_category??m.name)+` (score: ${m.score.toFixed(2)})`,onClick:()=>{v||f(m)},children:[e.jsx("span",{className:"SuggestionChipPlus",children:v?"✓":"+"}),m.name]},m.fdc_id)})})]})})})}function Be({clusters:i,pantryFdcIds:n,isOpen:c,onToggle:x,onAdd:f,onAddMultiple:p}){const[m,v]=o.useState(new Set),[y,C]=o.useState(null),[w,d]=o.use
1State(new Set);if(i.length===0)return null;function N(u){v(t=>{const s=new Set(t);return s.has(u)?s.delete(u):s.add(u),s})}function h(u,t){C(u),d(new Set(t.map(s=>s.fdc_id)))}function P(){C(null),d(new Set)}function _(u){d(t=>{const s=new Set(t);return s.has(u)?s.delete(u):s.add(u),s})}const R=y?.ingredients.filter(u=>!O(u,n))??[],S=R.filter(u=>w.has(u.fdc_id));return e.jsxs("div",{className:"PantrySuggestions",children:[e.jsx(W,{isOpen:c,title:`Ingredient Kits (${i.length})`,onToggle:x,children:e.jsxs("div",{className:"AccordionContent",children:[e.jsx("div",{className:"SuggestionSectionHint",children:"Groups of ingredients that frequently appear together in recipes."}),i.map(u=>{const t=m.has(u.cluster_id),s=u.ingredients.filter(a=>!O(a,n));return e.jsxs("div",{className:"ClusterSection",children:[e.jsxs("div",{className:"ClusterHeader",children:[e.jsxs("button",{type:"button",className:"ClusterLabelBtn",onClick:()=>N(u.cluster_id),children:[t?"▾":"▸"," ",u.label]}),e.jsxs("span",{className:"ClusterRecipeCount",children:[u.recipe_count," recipes"]}),s.length>0&&e.jsxs("button",{type:"button",className:"ClusterAddAllBtn",onClick:()=>h(u,s),children:["Add all (",s.length,")"]})]}),t&&e.jsx("div",{className:"SuggestionChipGrid",children:u.ingredients.map(a=>{const l=O(a,n);return e.jsxs("button",{type:"button",className:"SuggestionChip"+(l?" SuggestionChip--added":""),title:a.food_category??a.name,onClick:()=>{l||f(a)},children:[e.jsx("span",{className:"SuggestionChipPlus",children:l?"✓":"+"}),a.name]},a.fdc_id)})})]},u.cluster_id)})]})}),y!==null&&e.jsxs("div",{className:"KitReviewOverlay",children:[e.jsx("button",{type:"button",className:"KitReviewBackdrop","aria-label":"Cancel ingredient kit review",onClick:P}),e.jsxs("div",{className:"KitReviewDialog",role:"dialog","aria-modal":"true","aria-label":"Review ingredient kit",children:[e.jsxs("div",{className:"KitReviewHeader",children:[e.jsxs("div",{children:[e.jsx("div",{className:"KitReviewTitle",children:y.label}),e.jsxs("div",{className:"KitReviewMeta",children:[R.length," item",R.length!==1?"s":""," available"]})]}),e.jsx("button",{type:"button",className:"Btn BtnCompact",onClick:P,children:"Cancel"})]}),e.jsx("div",{className:"KitReviewList",children:R.map(u=>e.jsxs("label",{className:"KitReviewItem",children:[e.jsx("input",{type:"checkbox","aria-label":u.name,checked:w.has(u.fdc_id),onChange:()=>_(u.fdc_id)}),e.jsx("span",{className:"KitReviewItemName",children:u.name}),u.food_category!==null?e.jsx("span",{className:"KitReviewItemMeta",children:u.food_category}):null]},u.fdc_id))}),e.jsxs("div",{className:"KitReviewActions",children:[e.jsx("button",{type:"button",className:"Btn BtnCompact",onClick:()=>d(new Set(R.map(u=>u.fdc_id))),disabled:R.length===0,children:"Select all"}),e.jsx("button",{type:"button",className:"Btn BtnCompact",onClick:()=>d(new Set),disabled:S.length===0,children:"Clear"}),e.jsxs("button",{type:"button",className:"Btn BtnPrimary",onClick:()=>{p(S),P()},disabled:S.length===0,children:["Add selected (",S.length,")"]})]})]})]})]})}const U=[{id:"dairy",label:"Dairy",items:[{id:"dairy-milk",name:"milk"},{id:"dairy-butter",name:"butter"},{id:"dairy-eggs",name:"eggs"},{id:"dairy-yogurt",name:"yogurt"},{id:"dairy-cheddar-cheese",name:"cheddar cheese"},{id:"dairy-parmesan",name:"parmesan"}]},{id:"produce",label:"Produce",items:[{id:"produce-yellow-onions",name:"yellow onions"},{id:"produce-garlic",name:"garlic"},{id:"produce-carrots",name:"carrots"},{id:"produce-spinach",name:"spinach"},{id:"produce-lemons",name:"lemons"},{id:"produce-tomatoes",name:"tomatoes"}]},{id:"proteins",label:"Proteins",items:[{id:"proteins-chicken-breasts",name:"chicken breasts"},{id:"proteins-ground-beef",name:"ground beef"},{id:"proteins-bacon",name:"bacon"},{id:"proteins-tofu",name:"tofu"},{id:"proteins-black-beans",name:"black beans"},{id:"proteins-tuna",name:"tuna"}]},{id:"grains",label:"Grains",items:[{id:"grains-rice",name:"rice"},{id:"grains-pasta",name:"pasta"},{id:"grains-oats",name:"oats"},{id:"grains-tortillas",name:"tortillas"},{id:"grains-breadcrumbs",name:"breadcrumbs"},{id:"grains-quinoa",name:"quinoa"}]},{id:"baking",label:"Baking",items:[{id:"baking-flour",name:"flour"},{id:"baking-sugar",name:"sugar"},{id:"baking-brown-sugar",name:"brown sugar"},{id:"baking-baking-powder",name:"baking powder"}
1,{id:"baking-baking-soda",name:"baking soda"},{id:"baking-vanilla-extract",name:"vanilla extract"}]},{id:"spices",label:"Spices",items:[{id:"spices-salt",name:"salt"},{id:"spices-black-pepper",name:"black pepper"},{id:"spices-paprika",name:"paprika"},{id:"spices-cumin",name:"cumin"},{id:"spices-cinnamon",name:"cinnamon"},{id:"spices-chili-powder",name:"chili powder"}]}];function K(i){return i.trim().toLowerCase()}function _e({pantryItemTexts:i,onAddItems:n,isAdding:c=!1}){const[x,f]=o.useState(U[0].id),[p,m]=o.useState(new Set),[v,y]=o.useState(!1),C=c||v,w=o.useMemo(()=>new Set(i.map(K)),[i]),d=U.find(t=>t.id===x)??U[0],N=o.useMemo(()=>U.flatMap(t=>t.items),[]),h=d.items.filter(t=>!w.has(K(t.name))),P=N.filter(t=>p.has(t.id)&&!w.has(K(t.name)));function _(t){C||m(s=>{const a=new Set(s);return a.has(t)?a.delete(t):a.add(t),a})}function R(){C||m(new Set)}function S(){C||m(t=>{const s=new Set(t);for(const a of h)s.add(a.id);return s})}async function u(t){if(!(t.length===0||C)){y(!0);try{await n(t),m(s=>{const a=new Set(s);for(const l of t)a.delete(l.id);return a})}finally{y(!1)}}}return e.jsxs("section",{className:"PantryBuilder PantrySuggestions",role:"group","aria-label":"Pantry builder category checklist",children:[e.jsxs("div",{className:"PantryBuilderHeader",children:[e.jsx("div",{className:"SectionTitle",children:"Common staples"}),e.jsxs("button",{type:"button",className:"Btn BtnCompact",onClick:()=>{u(h)},disabled:C||h.length===0,children:["Add all ",d.label," (",h.length,")"]})]}),e.jsx("div",{className:"PantryBuilderCategories","aria-label":"Pantry builder categories",children:U.map(t=>e.jsx("button",{type:"button",className:"PantryBuilderCategoryBtn"+(t.id===d.id?" PantryBuilderCategoryBtn--active":""),"aria-pressed":t.id===d.id,disabled:C,onClick:()=>f(t.id),children:t.label},t.id))}),e.jsx("div",{className:"PantryChecklistItems",role:"group","aria-label":`${d.label} checklist`,children:d.items.map(t=>{const s=w.has(K(t.name)),a=p.has(t.id)||s;return e.jsxs("label",{className:"PantryChecklistItem"+(s?" PantryChecklistItem--added":""),children:[e.jsx("input",{type:"checkbox",checked:a,disabled:C||s,onChange:()=>_(t.id)}),e.jsx("span",{className:"PantryChecklistItemName",children:t.name}),s?e.jsx("span",{className:"PantryChecklistItemMeta",children:"in pantry"}):null]},t.id)})}),e.jsxs("div",{className:"PantryBuilderActions",children:[e.jsxs("button",{type:"button",className:"Btn BtnCompact",onClick:S,disabled:C||h.length===0,children:["Select ",d.label]}),e.jsx("button",{type:"button",className:"Btn BtnCompact",onClick:R,disabled:C||p.size===0,children:"Clear"}),e.jsxs("button",{type:"button",className:"Btn BtnPrimary BtnCompact",onClick:()=>{u(P)},disabled:C||P.length===0,children:["Add selected (",P.length,")"]})]})]})}function Q(i){return i.trim().toLowerCase()}function te(i){const{title:n,hint:c,items:x,onRestore:f,onRemove:p,onClear:m}=i;if(x.length===0)return null;const v=x.slice().sort((y,C)=>C.consumedAt.localeCompare(y.consumedAt));return e.jsxs("div",{className:"MtSm",children:[e.jsxs("div",{className:"Row RowBetween GapSm MbSm",children:[e.jsx("div",{className:"SectionTitle",children:n}),m&&e.jsx("button",{className:"Btn BtnCompact",type:"button",onClick:m,children:"Clear"})]}),e.jsx("div",{className:"muted MbSm",children:c}),e.jsx("div",{className:"ResultsList",children:v.map(y=>e.jsxs("div",{className:"ConsumedCard",children:[e.jsxs("div",{className:"ConsumedCardText",children:[e.jsx("div",{className:"ConsumedCardName",children:y.text}),e.jsxs("div",{className:"ConsumedCardMeta",children:["Used ",new Date(y.consumedAt).toLocaleString()]})]}),e.jsxs("div",{className:"ConsumedCardActions",children:[f&&e.jsx("button",{className:"Btn BtnCompact",type:"button",onClick:()=>f(y),children:"Restore"}),p&&e.jsx("button",{className:"Btn BtnCompact",type:"button",onClick:()=>p(y.id),children:"Remove"})]})]},y.id))})]})}function Fe({isOpen:i,userState:n,pantryOptions:c,headerExtra:x,onClose:f,onAddItem:p,onUpdateItem:m,onRemoveItem:v,onRestoreConsumedItem:y,onRemoveConsumedItem:C,onClearConsumedItems:w,onAddConsumableItem:d,onUp
1dateConsumableItem:N,onRemoveConsumableItem:h,onRestoreConsumedConsumableItem:P,onRemoveConsumedConsumableItem:_,onClearConsumedConsumableItems:R}){const[S,u]=o.useState(null),[t,s]=o.useState(!1),[a,l]=o.useState(!1),[j,I]=o.useState(!1),[B,r]=o.useState(!1),k=o.useRef(null),b=o.useMemo(()=>{const M=new Set;for(const A of n.pantryItems)for(const L of A.fdcIds)M.add(L);return M},[n.pantryItems]),g=o.useMemo(()=>Array.from(b),[b]),D=o.useMemo(()=>n.pantryItems.map(M=>M.text),[n.pantryItems]),$=o.useMemo(()=>new Set(D.map(Q)),[D]);o.useEffect(()=>{if(i)return k.current!==null&&clearTimeout(k.current),k.current=setTimeout(()=>{Se(),Ne(g).then(u)},300),()=>{k.current!==null&&clearTimeout(k.current)}},[i,g]);const z=o.useCallback(M=>{const A={id:E(),text:M.name,quantity:null,unit:null,fdcIds:[M.fdc_id],specialtyIds:[]};p(A)},[p]),ie=o.useCallback(M=>{for(const A of M)if(!b.has(A.fdc_id)){const L={id:E(),text:A.name,quantity:null,unit:null,fdcIds:[A.fdc_id],specialtyIds:[]};p(L)}},[p,b]),re=o.useCallback(async M=>{r(!0);try{const A=new Set($),L=M.filter(q=>{const F=Q(q.name);return A.has(F)?!1:(A.add(F),!0)}),le=await Promise.all(L.map(async q=>({ingredient:q,parsed:await H(q.name)}))),J=new Set($);for(const{ingredient:q,parsed:F}of le){const V=F.text.trim()||q.name,X=Q(V);if(J.has(X))continue;J.add(X);const oe={id:E(),text:V,quantity:F.quantity,unit:F.unit,fdcIds:F.fdcIds,specialtyIds:F.specialtyIds};p(oe)}}finally{r(!1)}},[p,$]);return e.jsxs(he,{isOpen:i,onClose:f,title:"Pantry",ariaLabel:"Pantry",panelClassName:"DrawerPanel--pantry",headerExtra:x,children:[e.jsx(_e,{pantryItemTexts:D,onAddItems:re,isAdding:B}),S!==null&&e.jsxs(e.Fragment,{children:[e.jsx(ke,{staples:S.staples,pantryFdcIds:b,isOpen:t,onToggle:s,onAdd:z}),e.jsx(Re,{suggestions:S.you_might_have,pantryFdcIds:b,isOpen:a,onToggle:l,onAdd:z}),e.jsx(Be,{clusters:S.clusters,pantryFdcIds:b,isOpen:j,onToggle:I,onAdd:z,onAddMultiple:ie})]}),e.jsx("div",{className:"SectionTitle MbSm",children:"Ingredients"}),n.pantryItems.length===0?e.jsx(Z,{icon:"🥫",title:"Your ingredients pantry is empty",text:"Add ingredients you have on hand. Search results will prioritize recipes you can make.",className:"MbSm"}):e.jsxs("div",{className:"muted MbSm",children:[n.pantryItems.length," ingredient",n.pantryItems.length!==1?"s":""," in pantry. Click a food name to refine its nutrition data."]}),e.jsxs("div",{className:"TourTargetPantryMatching",children:[e.jsx(ve,{items:n.pantryItems,unitOptions:c?.measurement_units??[],onAddItem:p,onUpdateItem:m}),e.jsx(Ce,{items:n.pantryItems,unitOptions:c?.measurement_units??[],onAddItem:p,onUpdateItem:m,onRemoveItem:v})]}),e.jsx(te,{title:"Previously in pantry",hint:"These are ingredients that were removed when you cooked a planned recipe.",items:n.pantryConsumedItems,onRestore:y,onRemove:C,onClear:w}),e.jsx("div",{className:"Divider MtMd MbMd"}),e.jsx("div",{className:"SectionTitle MbSm",children:"Consumables"}),n.consumableItems.length===0?e.jsx(Z,{icon:"🧻",title:"No consumables yet",text:"Add consumables you keep on hand (paper towels, foil, etc.). These don't use USDA.",className:"MbSm"}):e.jsxs("div",{className:"muted MbSm",children:[n.consumableItems.length," consumable",n.consumableItems.length!==1?"s":""," in pantry."]}),e.jsx(je,{items:n.consumableItems,unitOptions:c?.measurement_units??[],onAddItem:d,onUpdateItem:N,onRemoveItem:h}),e.jsx(te,{title:"Previously in consumables",hint:"These are consumables that were removed when you cooked a planned recipe.",items:n.consumableConsumedItems,onRestore:P,onRemove:_,onClear:R})]})}export{Fe as PantryDrawer};

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.