(()=>{'use strict';const root=document.querySelector('#saved-view-table');if(!root)return;const $=s=>root.querySelector(s),$$=s=>[...root.querySelectorAll(s)];const KEY='uic-table-saved-views-23';const esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));const initial=[{"id":"ORD-1082","customer":"Hana Kim","email":"[email protected]","owner":"Mira","status":"Review","total":280},{"id":"ORD-1083","customer":"Leo Bennett","email":"[email protected]","owner":"James","status":"Ready","total":145},{"id":"ORD-1084","customer":"Amara Okafor","email":"[email protected]","owner":"Mira","status":"Review","total":620},{"id":"ORD-1085","customer":"Nora Ellis","email":"[email protected]","owner":"Avery","status":"Ready","total":96},{"id":"ORD-1086","customer":"Priya Shah","email":"[email protected]","owner":"James","status":"Review","total":340},{"id":"ORD-1087","customer":"Ravi Kapoor","email":"[email protected]","owner":"Mira","status":"Ready","total":192},{"id":"ORD-1088","customer":"Sofia Reyes","email":"[email protected]","owner":"Avery","status":"Review","total":455},{"id":"ORD-1089","customer":"Omar Haddad","email":"[email protected]","owner":"James","status":"Ready","total":85},{"id":"ORD-1090","customer":"Clara Dubois","email":"[email protected]","owner":"Mira","status":"Review","total":760},{"id":"ORD-1091","customer":"Theo Walsh","email":"[email protected]","owner":"Avery","status":"Ready","total":120},{"id":"ORD-1092","customer":"Mei Tan","email":"[email protected]","owner":"James","status":"Review","total":215},{"id":"ORD-1093","customer":"Kai Osei","email":"[email protected]","owner":"Mira","status":"Ready","total":410}];const defaults=[{name:'All orders',query:'',status:'All statuses',owner:'All owners',sort:'id',ascending:true,size:4},{name:'Needs review',query:'',status:'Review',owner:'All owners',sort:'id',ascending:true,size:4}];let records=structuredClone(initial),views=structuredClone(defaults);try{const saved=JSON.parse(localStorage.getItem(KEY));if(saved&&Array.isArray(saved.records)&&Array.isArray(saved.views)){records=saved.records;views=[...defaults,...saved.views.filter(v=>!defaults.some(d=>d.name===v.name))];}}catch{}let selected=new Set(),page=0,sort='id',ascending=true;const say=s=>$('#sv-status').textContent=s;const persist=()=>{try{localStorage.setItem(KEY,JSON.stringify({records,views:views.slice(2)}));return true;}catch{say('Browser storage is unavailable. Changes last for this tab only.');return false;}};const state=()=>({query:$('#sv-search').value,status:$('#sv-state').value,owner:$('#sv-owner').value,sort,ascending,size:+$('#sv-page-size').value});const filtered=()=>records.filter(r=>(r.id+' '+r.customer+' '+r.email).toLowerCase().includes($('#sv-search').value.toLowerCase())&&($('#sv-state').value==='All statuses'||r.status===$('#sv-state').value)&&($('#sv-owner').value==='All owners'||r.owner===$('#sv-owner').value)).sort((a,b)=>(sort==='total'?a.total-b.total:a.id.localeCompare(b.id))*(ascending?1:-1));function viewOptions(value='All orders'){$('#sv-view').innerHTML=views.map(v=>'<option>'+esc(v.name)+'</option>').join('');$('#sv-view').value=value;$('#sv-delete').disabled=!views.slice(2).some(v=>v.name===value);}function render(){const rows=filtered(),size=+$('#sv-page-size').value;page=Math.min(page,Math.max(0,Math.ceil(rows.length/size)-1));const visible=rows.slice(page*size,page*size+size);$('#sv-rows').innerHTML=visible.map(r=>'<tr class="data-[selected=true]:bg-[#f2f0fc]" data-selected="'+selected.has(r.id)+'"><td class="w-[42px] border-b border-[#d9dfea] px-[14px] py-[15px] pr-0 sm:pl-5"><input class="h-4 w-4 accent-[#4338a2]" type="checkbox" data-row="'+r.id+'" aria-label="Select '+r.id+'" '+(selected.has(r.id)?'checked':'')+'></td><td class="border-b border-[#d9dfea] px-[14px] py-[15px] align-middle whitespace-nowrap sm:px-5"><strong class="block font-semibold font-mono">'+r.id+'</strong></td><td class="border-b border-[#d9dfea] px-[14px] py-[15px] align-middle whitespace-nowrap sm:px-5"><strong class="block font-semibold">'+esc(r.customer)+'</strong><small class="block text-[10px] text-[#526176]">'+esc(r.email)+'</small></td><td class="border-b border-[#d9dfea] px-[14px] py-[15px] align-middle whitespace-nowrap sm:px-5">'+r.owner+'</td><td class="border-b border-[#d9dfea] px-[14px] py-[15px] align-middle whitespace-nowrap sm:px-5"><span class="inline-flex rounded-[5px] px-2 py-1 text-[10px] font-semibold '+(r.status==='Ready'?'bg-[#e3f3e9] text-[#186344]':r.status==='Review'?'bg-[#fff0d5] text-[#7b4911]':'bg-[#f0f2f6] text-[#445166]')+'">'+r.status+'</span></td><td class="border-b border-[#d9dfea] px-[14px] py-[15px] align-middle whitespace-nowrap sm:px-5 font-mono tabular-nums">$'+r.total.toFixed(2)+'</td></tr>').join('')||'<tr><td colspan="6" class="p-8 text-center text-[#526176]">No matching orders. Clear the search or change your filters.</td></tr>';$('#sv-page-count').textContent=rows.length?'Showing '+(page*size+1)+'–'+Math.min(page*size+size,rows.length)+' of '+rows.length+' orders':'0 orders';$('#sv-prev').disabled=page===0;$('#sv-next').disabled=(page+1)*size>=rows.length;$('#sv-all').disabled=!visible.length;$('#sv-all').checked=visible.length>0&&visible.every(r=>selected.has(r.id));$('#sv-all').indeterminate=visible.some(r=>selected.has(r.id))&&!$('#sv-all').checked;$('#sv-bulk').hidden=selected.size===0;$('#sv-selected').textContent=selected.size+' order'+(selected.size===1?'':'s')+' selected';$$('[data-sort]').forEach(b=>{const th=b.closest('th');if(b.dataset.sort===sort)th.setAttribute('aria-sort',ascending?'ascending':'descending');else th.removeAttribute('aria-sort');});$$('[data-row]').forEach(c=>c.onchange=()=>{c.checked?selected.add(c.dataset.row):selected.delete(c.dataset.row);render()});}
function filtersChanged(){selected.clear();page=0;$('#sv-view').selectedIndex=-1;$('#sv-delete').disabled=true;render();}['sv-search','sv-state','sv-owner'].forEach(id=>$('#'+id).addEventListener('input',filtersChanged));$('#sv-page-size').onchange=()=>{page=0;render()};$$('[data-sort]').forEach(b=>b.onclick=()=>{ascending=b.dataset.sort===sort?!ascending:true;sort=b.dataset.sort;render()});$('#sv-prev').onclick=()=>{page--;render()};$('#sv-next').onclick=()=>{page++;render()};$('#sv-all').onchange=()=>{const size=+$('#sv-page-size').value;filtered().slice(page*size,page*size+size).forEach(r=>$('#sv-all').checked?selected.add(r.id):selected.delete(r.id));render()};$('#sv-clear').onclick=()=>{selected.clear();render();say('Selection cleared.')};function bulk(status){const n=selected.size;records.forEach(r=>{if(selected.has(r.id))r.status=status});selected.clear();const saved=persist();render();say(n+' orders moved to '+status+'.'+(saved?' Saved in this browser.':' Changes last for this tab only.'));}$('#sv-ready').onclick=()=>bulk('Ready');$('#sv-archive').onclick=()=>bulk('Archived');$('#sv-view').onchange=()=>{const v=views.find(v=>v.name===$('#sv-view').value);if(!v)return;$('#sv-search').value=v.query;$('#sv-state').value=v.status;$('#sv-owner').value=v.owner;$('#sv-page-size').value=v.size;sort=v.sort;ascending=v.ascending;page=0;selected.clear();$('#sv-delete').disabled=defaults.some(d=>d.name===v.name);render();say(v.name+' view applied.')};$('#sv-save').onclick=()=>{$('#sv-error').textContent='';$('#sv-view-name').value='';$('#sv-dialog').showModal()};$('#sv-cancel').onclick=()=>$('#sv-dialog').close();$('#sv-view-form').onsubmit=e=>{e.preventDefault();if(!e.currentTarget.reportValidity())return;const name=$('#sv-view-name').value.trim();if(!name){$('#sv-error').textContent='Enter a view name.';return;}if(views.some(v=>v.name.toLowerCase()===name.toLowerCase())){$('#sv-error').textContent='Choose a different name; this view already exists.';return;}views.push({name,...state()});const saved=persist();viewOptions(name);$('#sv-dialog').close();say(saved?'View saved in this browser.':'View created for this tab only.');};$('#sv-delete').onclick=()=>{const name=$('#sv-view').value;if(defaults.some(v=>v.name===name))return;views=views.filter(v=>v.name!==name);persist();viewOptions();$('#sv-view').dispatchEvent(new Event('change'));say('Custom view deleted. Default views remain available.')};$('#sv-export').onclick=()=>{const cell=x=>'"'+String(x).replace(/^[=+@-]/,"'$&").replaceAll('"','""')+'"';const text='\uFEFF'+[['Order','Customer','Email','Owner','Status','Total'],...filtered().map(r=>[r.id,r.customer,r.email,r.owner,r.status,r.total.toFixed(2)])].map(row=>row.map(cell).join(',')).join('\r\n');const url=URL.createObjectURL(new Blob([text],{type:'text/csv;charset=utf-8'}));const a=document.createElement('a');a.href=url;a.download='filtered-orders.csv';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);say('Filtered orders exported as CSV.')};viewOptions();render();})();
About this set
A complete order workspace that joins saved filters, sorting, page selection and bulk status changes in one table. Start with All orders or Needs review, search by customer, email or order number, and narrow the list by status and owner. Save this view remembers those filters together with the sort direction and page size under a custom name. Custom views and demo order changes survive a reload in this browser. Checkbox selection can span several pages of the same filtered list; changing a filter clears it so hidden selections cannot be updated by accident. The page checkbox exposes an indeterminate state when only some visible rows are selected. Mark ready and Archive selected update the chosen orders, and archived orders remain available through the status filter. Export CSV downloads every matching row, rather than just the current page. Use this pattern for internal order desks and admin lists that repeat the same searches each day. Replace localStorage and the demo array with your API for shared records.
What’s included
Named views remember filters, sorting and page size
Selection across pages of the current filter
Indeterminate select-page checkbox and selection count
Bulk ready/archive actions with reversible status filtering
CSV export of the complete filtered result
Browser-local persistence with a session-only fallback
Accessibility
The table has a caption, column headers, named checkboxes and aria-sort on the sorted column. All controls use native inputs, selects and buttons; the save-view form uses a labelled native dialog with Escape support. Keyboard focus is outlined, the horizontal scroll region is focusable, and status changes are announced through a polite live region. Nothing animates.
Customise it
Change the order array and owner/status options in the inline script. The plain CSS and Bootstrap versions use the --sv-accent, --sv-ink and --sv-line properties; the Tailwind version uses the matching color and spacing utilities directly in its markup.