your-local-cost-explorer 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,9 +1,13 @@
1
- # 0.4.0 - Your Local Cost Explorer
2
-
3
- - Rename the public project and npm command to `your-local-cost-explorer`. Keep the existing private data/runtime directory so upgrading does not lose saved work.
4
- - Keep the local light design and sidebar. Remove Data Sources navigation; upload and retained-file controls appear only on Dashboard. Replace broken dash characters with `-`.
5
- - Inspect observed CUR dates locally for CSV, gzip and Parquet. Offer supported 7/30/90/180-day windows ending at the supplied data's latest date, with earlier rows as baseline. Missing history stays unavailable.
6
- - Put previous-check and device-file comparisons below the baseline fields. Retained comparisons verify original file hashes and cannot contaminate primary data.
7
- - Preserve all eight detectors, exact decimal financial calculations, commitments, allocation, settings and downloadable local reports. No hosted or automatic connections are included.
8
-
9
- Focused verification: real loopback uploads, CUR date aliases/formats/conflicts, calendar windows, previous-check provenance and separation, DOM upload/period/button behavior, small independent signed-charge/rounding ledger, duplicate removal, original eight-detector and commitment controls, hosted pricing withdrawal tests, frontend typecheck and production build. No large-file laptop stress test.
1
+ # 0.4.1
2
+
3
+ - Restore the selected device baseline when switching comparison modes. Respect the saved supported date-range preference and keep empty metric placeholders as `-`. Focused DOM regression passed.
4
+
5
+ # 0.4.0 - Your Local Cost Explorer
6
+
7
+ - Rename the public project and npm command to `your-local-cost-explorer`. Keep the existing private data/runtime directory so upgrading does not lose saved work.
8
+ - Keep the local light design and sidebar. Remove Data Sources navigation; upload and retained-file controls appear only on Dashboard. Replace broken dash characters with `-`.
9
+ - Inspect observed CUR dates locally for CSV, gzip and Parquet. Offer supported 7/30/90/180-day windows ending at the supplied data's latest date, with earlier rows as baseline. Missing history stays unavailable.
10
+ - Put previous-check and device-file comparisons below the baseline fields. Retained comparisons verify original file hashes and cannot contaminate primary data.
11
+ - Preserve all eight detectors, exact decimal financial calculations, commitments, allocation, settings and downloadable local reports. No hosted or automatic connections are included.
12
+
13
+ Focused verification: real loopback uploads, CUR date aliases/formats/conflicts, calendar windows, previous-check provenance and separation, DOM upload/period/button behavior, small independent signed-charge/rounding ledger, duplicate removal, original eight-detector and commitment controls, hosted pricing withdrawal tests, frontend typecheck and production build. No large-file laptop stress test.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "your-local-cost-explorer",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/leejan2647-a11y/your-local-cost-explorer.git"
package/web/app.js CHANGED
@@ -4,7 +4,7 @@ if (token) sessionStorage.setItem('local-token', token);
4
4
  history.replaceState(null, '', '/');
5
5
  const $ = id => document.getElementById(id);
6
6
  let periodTouched=false;
7
- let fileMetadata=[], comparisonMode='history', comparisonIds=new Set(), detectedSpan=null;
7
+ let fileMetadata=[], comparisonMode='history', comparisonIds=new Set(), detectedSpan=null, deviceSpan=null;
8
8
  let selected = new Set(), current = null, result = null, activeView = 'overview', pending = false, running = false;
9
9
  const titles = {overview:'Dashboard',check:'Check Charges',findings:'Findings',commitments:'Commitments',allocation:'Allocation',reports:'Reports',settings:'Settings',teams:'Team mappings',evidence:'Check history & evidence'};
10
10
  function navigate(view) {
@@ -24,7 +24,7 @@ $('start-check').onclick = () => { navigate('overview'); $('upload').click(); };
24
24
  $('new-check').onclick = () => { navigate(selected.size?'check':'overview'); if(!selected.size)$('upload').click(); };
25
25
  const money = cents => Number.isSafeInteger(cents) ? new Intl.NumberFormat(undefined,{style:'currency',currency:'USD'}).format(cents/100) : 'Unavailable';
26
26
  function renderSummary(data) {
27
- $('metric-charges').textContent = money(data?.validation?.window_total_cents);
27
+ $('metric-charges').textContent = data ? money(data.validation?.window_total_cents) : '-';
28
28
  $('metric-findings').textContent = Array.isArray(data?.findings) ? data.findings.length : '-';
29
29
  $('metric-rows').textContent = Number.isSafeInteger(data?.validation?.unique_rows) ? data.validation.unique_rows.toLocaleString() : '-';
30
30
  $('metric-commitments').textContent = Array.isArray(data?.commitments) ? data.commitments.length : '-';
@@ -216,7 +216,7 @@ function updatePeriods(){
216
216
  const spans=fileMetadata.filter(file=>selected.has(file.id)&&file.role==='primary').map(file=>file.inspection).filter(span=>span?.status==='available');
217
217
  if(!spans.length){detectedSpan=null;$('date-notice').textContent='Upload supported files to detect dates, or enter dates manually.';return;}
218
218
  const first=spans.map(s=>s.min_date).sort()[0],last=spans.map(s=>s.max_date).sort().at(-1);detectedSpan={first,last};const count=Math.round((new Date(last)-new Date(first))/86400000)+1;
219
- const picker=$('period-choice'),old=picker.value;picker.replaceChildren();for(const days of [7,30,90,180])if(days<=count){const option=document.createElement('option');option.value=String(days);option.textContent='Last '+days+' days';picker.append(option);}const manual=document.createElement('option');manual.value='manual';manual.textContent='Manual dates';picker.append(manual);picker.value=periodTouched&&Array.from(picker.options).some(o=>o.value===old)?old:String([180,90,30,7].find(days=>days<=count)||'manual');if(!periodTouched&&count<7){$('start').value=first;$('end').value=last;if(comparisonMode==='history'){$('baseline_start').value=$('baseline_end').value=shiftDay(first,-1);}$('date-notice').textContent=`Observed file dates: ${first} to ${last}. No earlier rows are available for a baseline. Bounds do not prove completeness.`;}applyPeriod();
219
+ const picker=$('period-choice'),old=picker.value;picker.replaceChildren();for(const days of [7,30,90,180])if(days<=count){const option=document.createElement('option');option.value=String(days);option.textContent='Last '+days+' days';picker.append(option);}const manual=document.createElement('option');manual.value='manual';manual.textContent='Manual dates';picker.append(manual);picker.value=periodTouched&&Array.from(picker.options).some(o=>o.value===old)?old:String([7,30,90,180].includes(Number(storedSettings?.defaultDateRange))&&Number(storedSettings.defaultDateRange)<=count?storedSettings.defaultDateRange:count>=30?30:count>=7?7:'manual');if(!periodTouched&&count<7){$('start').value=first;$('end').value=last;if(comparisonMode==='history'){$('baseline_start').value=$('baseline_end').value=shiftDay(first,-1);}$('date-notice').textContent=`Observed file dates: ${first} to ${last}. No earlier rows are available for a baseline. Bounds do not prove completeness.`;}applyPeriod();
220
220
  }
221
221
  function applyPeriod(){
222
222
  if(!detectedSpan||$('period-choice').value==='manual')return;
@@ -224,9 +224,9 @@ function applyPeriod(){
224
224
  $('date-notice').textContent=`Observed file dates: ${first} to ${last}. ${first<start?'All supplied history before the analysis window is the baseline.':'No earlier rows exist in this file; historical comparisons are unavailable unless you supply a baseline.'} Bounds do not prove complete coverage.`;
225
225
  }
226
226
  $('period-choice').onchange=()=>{periodTouched=true;applyPeriod();};
227
- function compareMode(mode){comparisonMode=mode;$('previous-wrap').hidden=mode!=='previous';$('device-wrap').hidden=mode!=='device';$('comparison-notice').textContent=mode==='history'?'Use all earlier rows in the selected primary files.':mode==='device'?'Selected comparison files remain separate from primary data.':'Previous check must retain unchanged source files and end before this analysis.';if(mode==='history')applyPeriod();}
227
+ function compareMode(mode){comparisonMode=mode;$('previous-wrap').hidden=mode!=='previous';$('device-wrap').hidden=mode!=='device';$('comparison-notice').textContent=mode==='history'?'Use all earlier rows in the selected primary files.':mode==='device'?'Selected comparison files remain separate from primary data.':'Previous check must retain unchanged source files and end before this analysis.';if(mode==='history')applyPeriod();if(mode==='device'&&deviceSpan){$('baseline_start').value=deviceSpan.first;$('baseline_end').value=deviceSpan.last;}}
228
228
  $('compare-previous').onclick=()=>{compareMode('previous');if($('previous-check').value)$('previous-check').dispatchEvent(new Event('change'));};$('compare-device').onclick=()=>compareMode('device');$('compare-history').onclick=()=>compareMode('history');
229
229
  $('previous-check').onchange=async()=>{const requested=$('previous-check').value;try{const old=await request('/api/runs/'+requested);if(comparisonMode==='previous'&&$('previous-check').value===requested&&old.result?.window){$('baseline_start').value=old.result.window.start;$('baseline_end').value=old.result.window.end;}}catch(error){status(error.message);}};
230
- $('comparison-upload').onchange=async event=>{try{comparisonIds.clear();const spans=[];for(const file of event.target.files){status('Uploading comparison '+file.name);const uploaded=await request('/api/upload?name='+encodeURIComponent(file.name)+'&role=comparison',{method:'PUT',body:file});comparisonIds.add(uploaded.id);if(uploaded.inspection?.status==='available')spans.push(uploaded.inspection);}if(comparisonMode==='device'&&spans.length){$('baseline_start').value=spans.map(s=>s.min_date).sort()[0];$('baseline_end').value=spans.map(s=>s.max_date).sort().at(-1);}status(comparisonIds.size+' comparison files selected. Review baseline dates before running.');}catch(error){status(error.message);}finally{event.target.value='';}};
230
+ $('comparison-upload').onchange=async event=>{try{comparisonIds.clear();deviceSpan=null;const spans=[];for(const file of event.target.files){status('Uploading comparison '+file.name);const uploaded=await request('/api/upload?name='+encodeURIComponent(file.name)+'&role=comparison',{method:'PUT',body:file});comparisonIds.add(uploaded.id);if(uploaded.inspection?.status==='available')spans.push(uploaded.inspection);}if(spans.length){deviceSpan={first:spans.map(s=>s.min_date).sort()[0],last:spans.map(s=>s.max_date).sort().at(-1)};if(comparisonMode==='device')compareMode('device');}status(comparisonIds.size+' comparison files selected. Review baseline dates before running.');}catch(error){status(error.message);}finally{event.target.value='';}};
231
231
  for(const id of ['start','end','baseline_start','baseline_end'])$(id).addEventListener('input',()=>{periodTouched=true;$('period-choice').value='manual';});
232
232
  navigate('overview');