SUPPLIES
Healthcare Supply Inventory
var SPEC={"slug":"healthcare-supply-inventory","title":"Healthcare Supply Inventory","kicker":"SUPPLIES","accent":"#5f7f4f","tagline":"Supply inventory with categories, quantities, and restock alerts.","description":"A browser-local supply inventory that stores supply aliases, categories, and quantities only. It holds no patient data, medication details, or financial records and is not an inventory management system, pharmacy tool, or medical record system.","category":"trackers","color":"green","height":490,"fields":[{"key":"title","label":"Supply alias","type":"text","required":true,"max":60,"placeholder":"Gloves","span":"half"},{"key":"category","label":"Category","type":"select","options":["PPE","Consumables","Equipment","Medications","Other"],"default":"Consumables","span":"half"},{"key":"quantity","label":"Quantity on hand","type":"number","min":0,"max":1000000,"step":1,"placeholder":"0","span":"third"},{"key":"minStock","label":"Minimum stock","type":"number","min":0,"max":1000000,"step":1,"placeholder":"10","span":"third"},{"key":"unit","label":"Unit","type":"text","max":20,"placeholder":"boxes","span":"third"},{"key":"note","label":"Notes","type":"text","max":100,"placeholder":"Supply notes, no patient data or medication details","span":"wide"}],"titleField":"title","cardFields":[{"key":"category","label":"Category"},{"key":"quantity","label":"Qty","kind":"number"},{"key":"minStock","label":"Min","kind":"number"},{"key":"unit","label":"Unit"}],"status":{"field":"status","values":["In stock","Low stock","Out of stock","On order"],"default":"In stock","colors":{"In stock":"success","Low stock":"warning","Out of stock":"danger","On order":"info"}},"sortPriority":{"field":"status","order":["Out of stock","Low stock","On order","In stock"]},"dateField":{"key":"lastChecked","label":"Last checked","future":"in","past":"ago"},"metrics":[{"kind":"total"},{"kind":"sumField","field":"quantity","format":"number","label":"Total on hand"},{"kind":"statusCount","status":"Low stock","label":"Low stock"},{"kind":"statusCount","status":"Out of stock","label":"Out of stock"}],"checks":[],"search":true,"empty":"No supplies logged yet. Add a supply above to start your inventory.","footnote":"Manual log only. Not an inventory management system, pharmacy tool, or medical record system.","submit":"Add supply","searchPlaceholder":"Filter supplies or categories"}; (function(){ var cfgEl=document.getElementById('bk-config'),base={};try{base=cfgEl?JSON.parse(cfgEl.textContent||'{}'):{};}catch(e){} var q=new URLSearchParams(location.search),c={};for(var k in base)c[k]=base[k];q.forEach(function(v,k){c[k]=v;}); function safeColor(v,d){return /^#[0-9a-f]{6}$/i.test(String(v||''))?String(v):d;} var theme=['auto','light','dark'].indexOf(String(c.theme||'auto'))>=0?String(c.theme||'auto'):'auto',ACCENT=safeColor(c.accent,SPEC.accent),LABEL=String(c.label||SPEC.title).slice(0,48),KEY="notion-arena.healthcare-supply-inventory.v1",LIMIT=120; document.documentElement.setAttribute('data-theme',theme);document.documentElement.style.setProperty('--accent',ACCENT);document.getElementById('title').textContent=LABEL;document.title=LABEL; function rgb(h){var s=h.slice(1);return[0,2,4].map(function(i){return parseInt(s.slice(i,i+2),16);});}function lum(a){return a.map(function(v){v/=255;return v<=.04045?v/12.92:Math.pow((v+.055)/1.055,2.4);}).reduce(function(sum,v,i){return sum+v*[.2126,.7152,.0722][i];},0);}function ratio(a,b){var x=lum(a),y=lum(b);return(Math.max(x,y)+.05)/(Math.min(x,y)+.05);}function hex(a){return'#'+a.map(function(v){return Math.max(0,Math.min(255,v)).toString(16).padStart(2,'0');}).join('');}function mix(a,b,t){return a.map(function(v,i){return Math.round(v+(b[i]-v)*t);});}function setAccent(){var dark=theme==='dark'||(theme==='auto'&&window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches),bg=rgb(dark?'#222220':'#ffffff'),a=rgb(ACCENT),soft=mix(bg,a,.12),target=dark?[255,255,255]:[0,0,0],text=a;for(var t=.02;t<=1;t+=.02){if(ratio(text,soft)>=4.5)break;text=mix(a,target,t);}var black=[0,0,0],white=[255,255,255],ink=ratio(a,black)>=ratio(a,white)?black:white;document.documentElement.style.setProperty('--accent-text',hex(text));document.documentElement.style.setProperty('--accent-ink',hex(ink));document.documentElement.style.setProperty('--soft',hex(soft));document.documentElement.style.colorScheme=dark?'dark':'light';}setAccent();if(theme==='auto'&&window.matchMedia){var mq=window.matchMedia('(prefers-color-scheme: dark)');if(mq.addEventListener)mq.addEventListener('change',setAccent);else if(mq.addListener)mq.addListener(setAccent);} var items=[];try{var raw=JSON.parse(localStorage.getItem(KEY)||'[]');if(Array.isArray(raw))items=raw.filter(function(x){return x&&typeof x.id==='string';}).slice(0,LIMIT);}catch(e){} function el(tag,cls,text){var n=document.createElement(tag);if(cls)n.className=cls;if(text!==undefined&&text!==null)n.textContent=String(text);return n;}function clean(s,max){return String(s||'').trim().slice(0,max);}function uid(){return Date.now().toString(36)+Math.random().toString(36).slice(2,8);}function localToday(){var d=new Date();return d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0')+'-'+String(d.getDate()).padStart(2,'0');}function dateDiff(s){if(!/^\d{4}-\d{2}-\d{2}$/.test(String(s||'')))return null;var a=new Date(s+'T00:00:00'),b=new Date(localToday()+'T00:00:00');if(Number.isNaN(a.getTime()))return null;return Math.round((a-b)/86400000);}function prettyDate(s){if(!/^\d{4}-\d{2}-\d{2}$/.test(String(s||'')))return String(s||'');var d=new Date(s+'T12:00:00');return Number.isNaN(d.getTime())?s:d.toLocaleDateString(undefined,{month:'short',day:'numeric'});}function announce(s){document.getElementById('status').textContent=s;}function save(s){try{localStorage.setItem(KEY,JSON.stringify(items));announce(s||'Saved only in this browser.');}catch(e){announce('Browser storage is unavailable. Changes may not persist.');}} function buildForm(){var form=document.getElementById('form');SPEC.fields.forEach(function(f){var box=el('div','field '+(f.span||'half'));var id='f-'+f.key,lab=el('label','',f.label);lab.htmlFor=id;box.appendChild(lab);var input;if(f.type==='select'){input=el('select','input');(f.options||[]).forEach(function(v){var o=el('option','',v);o.value=v;input.appendChild(o);});}else if(f.type==='textarea'){input=el('textarea','input');input.rows=2;}else{input=el('input','input');input.type=f.type||'text';if(f.min!==undefined)input.min=String(f.min);if(f.max!==undefined){if(f.type==='number')input.max=String(f.max);else input.maxLength=f.max;}if(f.step!==undefined)input.step=String(f.step);}input.id=id;if(f.required)input.required=true;if(f.placeholder)input.placeholder=f.placeholder;if(f.type==='number')input.inputMode='decimal';box.appendChild(input);form.appendChild(box);});var btn=el('button','btn primary submit',SPEC.submit||'Add item');btn.type='submit';form.appendChild(btn);} buildForm(); function statusOptions(){return SPEC.status?SPEC.status.options:[];}function isDone(item){return !!(SPEC.status&&SPEC.status.done.indexOf(item.status)>=0);}function wanted(m){return m.values?m.values:(m.value!==undefined?[m.value]:[]);}function inWanted(m,v){var w=wanted(m);return !w.length||w.indexOf(v)>=0;}function num(v){return Number(v)||0;}function sumOf(rows,key){return rows.reduce(function(a,x){return a+num(x[key]);},0);} function countKind(m){if(m.kind==='total')return items.length; if(m.kind==='open')return items.filter(function(x){return!isDone(x);}).length; if(m.kind==='countStatus')return items.filter(function(x){return inWanted(m,x.status);}).length; if(m.kind==='countField')return items.filter(function(x){return(!m.excludeDone||!isDone(x))&&inWanted(m,x[m.key]);}).length; if(m.kind==='dueSoon'){var w=m.days===undefined?7:m.days;return items.filter(function(x){var n=dateDiff(x[m.key]);return!isDone(x)&&n!==null&&n>=0&&n<=w;}).length;} if(m.kind==='overdue')return items.filter(function(x){var n=dateDiff(x[m.key]);return!isDone(x)&&n!==null&&n<0;}).length; if(m.kind==='recent'){var r=m.days===undefined?7:m.days;return items.filter(function(x){var n=dateDiff(x[m.key]);return n!==null&&n<=0&&n>=-r;}).length;} if(m.kind==='sum')return sumOf(items,m.key); if(m.kind==='sumStatus')return sumOf(items.filter(function(x){return inWanted(m,x.status);}),m.key); if(m.kind==='sumField')return sumOf(items.filter(function(x){return(!m.excludeDone||!isDone(x))&&inWanted(m,x[m.by]);}),m.key); if(m.kind==='checkRate'){var all=0,done=0;items.forEach(function(x){(x.checks||[]).forEach(function(ch){all++;if(ch.done)done++;});});return all?Math.round(done/all*100):0;} if(m.kind==='rate'){var rows=items.filter(function(x){return!m.excludeDone||!isDone(x);});if(!rows.length)return 0;var hit=rows.filter(function(x){return inWanted(m,m.by?x[m.by]:x.status);}).length;return Math.round(hit/rows.length*100);} if(m.kind==='ratio'){var parts=m.parts||[m.key],top=0,total=0;items.forEach(function(x){parts.forEach(function(k,i){var v=num(x[k]);total+=v;if(!i)top+=v;});});return total?Math.round(top/total*100):0;} return 0;} function metricText(m){var n=countKind(m);if(m.format==='money')return'$'+Math.round(n).toLocaleString('en-US');if(m.format==='hours'||m.format==='days')return Number(n).toFixed(1);if(m.format==='percent')return String(n)+'%';if(m.format==='duration'){var s=Math.max(0,Math.round(n));return Math.floor(s/60)+':'+String(s%60).padStart(2,'0');}if(m.kind==='sum'&&m.key==='estimate')return'$'+Math.round(n).toLocaleString('en-US');return String(n);} function drawMetrics(){var root=document.getElementById('metrics');root.textContent='';SPEC.metrics.forEach(function(m){var box=el('div','metric');box.appendChild(el('span','metric-value',metricText(m)));box.appendChild(el('span','metric-label',m.label));root.appendChild(box);});} function makeChip(text,kind){return el('span','chip'+(kind?' '+kind:''),text);}function formatCardField(f,item){var raw=item[f.key];if(raw===undefined||raw===null||raw==='')return null;if(f.kind==='detail')return el('p','detail',String(raw));var value=String(raw);if(f.kind==='date')value=prettyDate(value);if(f.kind==='money')value='$'+num(raw).toLocaleString('en-US',{maximumFractionDigits:0})+' USD est.';if(f.kind==='number')value=f.label+': '+raw;else if(f.label&&f.kind!=='priority')value=f.label+': '+value;return makeChip(value,f.kind==='priority'?'priority':'');} function sortRows(rows){var a=rows.slice();if(SPEC.sortPriority){var p=SPEC.sortPriority.values; a.sort(function(x,y){var ax=p.indexOf(x[SPEC.sortPriority.key]),ay=p.indexOf(y[SPEC.sortPriority.key]);if(ax<0)ax=99;if(ay<0)ay=99;return ax-ay;});}if(SPEC.dateField){a.sort(function(x,y){var ax=x[SPEC.dateField]||'9999-99-99',ay=y[SPEC.dateField]||'9999-99-99';if(SPEC.sortPriority){var p=SPEC.sortPriority.values,px=p.indexOf(x[SPEC.sortPriority.key]),py=p.indexOf(y[SPEC.sortPriority.key]);if(px<0)px=99;if(py<0)py=99;if(px!==py)return px-py;}return ax.localeCompare(ay);});}return a;} function render(){drawMetrics();var root=document.getElementById('list');root.textContent='';var term=document.getElementById('search').value.trim().toLowerCase(),rows=items.filter(function(x){var hay=Object.keys(x).map(function(k){return typeof x[k]==='string'?x[k]:'';}).join(' ').toLowerCase();return!term||hay.indexOf(term)>=0;});rows=sortRows(rows);document.getElementById('count').textContent=rows.length+' shown';if(!rows.length){root.appendChild(el('div','empty',items.length?'No entries match the filter.':SPEC.empty));return;}rows.forEach(function(item){var row=el('article','entry'),main=el('div','entry-main');main.appendChild(el('div','entry-title',item[SPEC.titleField]||'Untitled'));var meta=el('div','chips');(SPEC.cardFields||[]).forEach(function(f){var n=formatCardField(f,item);if(n){if(f.kind==='detail')main.appendChild(n);else meta.appendChild(n);}});if(meta.childNodes.length)main.appendChild(meta);row.appendChild(main);var actions=el('div','actions');if(SPEC.status){var select=el('select','status');statusOptions().forEach(function(v){var o=el('option','',v);o.value=v;select.appendChild(o);});select.value=item.status||SPEC.status.default;select.setAttribute('data-status',item.id);select.setAttribute('aria-label',SPEC.status.label+' for '+(item[SPEC.titleField]||'entry'));actions.appendChild(select);}var del=el('button','remove','Remove');del.type='button';del.setAttribute('data-remove',item.id);del.setAttribute('aria-label','Remove '+(item[SPEC.titleField]||'entry'));actions.appendChild(del);row.appendChild(actions);if(SPEC.checks){var checks=el('div','checklist');(item.checks||[]).forEach(function(ch,i){var lab=el('label','check-row');var input=el('input','check');input.type='checkbox';input.checked=!!ch.done;input.setAttribute('data-check',item.id);input.setAttribute('data-index',String(i));input.id='check-'+item.id+'-'+i;lab.htmlFor=input.id;lab.appendChild(input);lab.appendChild(el('span','',ch.label));checks.appendChild(lab);});var progress=el('div','progress-row');var done=(item.checks||[]).filter(function(ch){return ch.done;}).length;progress.appendChild(el('span','small',done+' of '+(item.checks||[]).length+' checks'));var bar=el('div','bar'),fill=el('span');fill.style.width=(item.checks&&item.checks.length?Math.round(done/item.checks.length*100):0)+'%';bar.appendChild(fill);progress.appendChild(bar);checks.appendChild(progress);row.appendChild(checks);}root.appendChild(row);});} document.getElementById('form').addEventListener('submit',function(e){e.preventDefault();if(items.length>=LIMIT){announce('This local list is full. Remove an entry to add another.');return;}var item={id:uid(),status:SPEC.defaultStatus||statusOptions()[0]||''};SPEC.fields.forEach(function(f){var input=document.getElementById('f-'+f.key),v=input.value;if(f.type==='number'&&v!==''){var n=Number(v);v=Number.isFinite(n)?String(Math.min(f.max===undefined?1e9:f.max,Math.max(f.min||0,n))):'';}item[f.key]=f.type==='date'?String(v||''):clean(v,f.max||160);});if(SPEC.checks)item.checks=SPEC.checks.map(function(label){return{label:label,done:false};});items.unshift(item);try{localStorage.setItem(KEY,JSON.stringify(items));announce('Saved only in this browser.');}catch(err){announce('Browser storage is unavailable. Changes may not persist.');}this.reset();render();document.getElementById('f-'+SPEC.titleField).focus();}); document.getElementById('search').addEventListener('input',render);document.getElementById('list').addEventListener('click',function(e){var n=e.target;while(n&&n!==this){var id=n.getAttribute&&n.getAttribute('data-remove');if(id){items=items.filter(function(x){return x.id!==id;});save('Entry removed.');render();return;}n=n.parentNode;}});document.getElementById('list').addEventListener('change',function(e){var id=e.target.getAttribute('data-status');if(id){var item=items.find(function(x){return x.id===id;});if(item){item.status=e.target.value;save('State updated.');render();}return;}var checkId=e.target.getAttribute('data-check');if(checkId){var row=items.find(function(x){return x.id===checkId;});var index=Number(e.target.getAttribute('data-index'));if(row&&row.checks&&row.checks[index]){row.checks[index].done=e.target.checked;save('Checklist updated.');render();}}});document.getElementById('clear').addEventListener('click',function(){document.getElementById('search').value='';render();});render(); })();