groceries: mobile-first tracker redesign — compact rows per product group (cheapest store badge, unit price, spread, promo), tap to compare stores; category tree (23 tops, subcategories, icons/colours) with chips, store tiles, sort and non-food toggle; owner shelf prices (manual source with date, photo, note)
Some checks failed
Deploy production / deploy (push) Failing after 1m1s

This commit is contained in:
2026-10-25 19:02:44 +00:00
parent 67d74f39b8
commit 707f07efd4
13 changed files with 33141 additions and 447 deletions

View File

@@ -0,0 +1,71 @@
import { describe, expect, it } from 'vitest';
import { classify, subSlug, TAXONOMY } from './taxonomy';
const c = (category: string, path: string | null, name: string, isFood = true) => {
const r = classify({ category, path, name, isFood });
return r.sub ? `${r.top}/${r.sub}` : r.top;
};
describe('taxonomy', () => {
it('has unique, valid slugs', () => {
const slugs = TAXONOMY.flatMap((t) => [t.slug, ...t.subs.map((s) => subSlug(t.slug, s.slug))]);
expect(new Set(slugs).size).toBe(slugs.length);
for (const s of slugs) expect(s).toMatch(/^[a-z0-9][a-z0-9-]{0,62}$/);
});
it('refines importer categories from the store path', () => {
expect(
c('Zuivel & eieren', 'Zuivel, boter en eieren > Yoghurt en kwark', 'AH Griekse yoghurt')
).toBe('zuivel-eieren/yoghurt-kwark');
expect(c('Bier & wijn', 'Bier en wijn > Witte wijn > Fris droog', 'Pinot Grigio')).toBe(
'bier-wijn/wit'
);
expect(c('Koffie & thee', 'Koffie en thee > Koffiecups', 'L’OR Espresso')).toBe(
'koffie-thee/cups'
);
});
it('falls back to the product name, then the top', () => {
expect(c('Vlees, vis & vega', 'Vlees, kip, vis, vega', 'PLUS Kipfilet blokjes')).toBe(
'vlees-vis-vega/kip'
);
expect(c('Drogisterij', 'Gezondheid en sport > Anale verzorging', 'AH Zalf')).toBe(
'drogisterij'
);
});
it('uses the only segment of flat store trees', () => {
expect(c('Soepen, sauzen & conserven', 'Kruiden', 'Asli Kerrie Masala 40g')).toBe(
'soepen-sauzen/kruiden'
);
});
it('splits pets from household', () => {
expect(
c('Huishouden & dieren', 'Huisdier > Hond > Nat hondenvoer', 'Pedigree blik rund', false)
).toBe('dieren/hond');
expect(c('Huishouden & dieren', 'Huishouden en dieren > Afwasmiddel', 'Dreft', false)).toBe(
'huishouden/afwas'
);
});
it('spreads Overig over non-food tops', () => {
expect(c('Overig', 'Koken, tafelen, vrije tijd > Hoeslakens', 'Hoeslaken beige', false)).toBe(
'wonen-tuin/textiel-bed'
);
expect(
c('Overig', 'Koken en Tafelen > Servies > Serviessets', 'Serviesset 32-delig', false)
).toBe('keuken-tafel/servies');
expect(c('Overig', 'Kerst > Kerstkleding', 'Huispak kerst', false)).toBe('feest-seizoen/kerst');
expect(c('Overig', 'Huis, Beauty & Welzijn', 'Leung Tim Wok 35cm', false)).toBe(
'keuken-tafel/koken'
);
});
it('keeps food in seasonal shelves on food tops', () => {
expect(c('Overig', 'Glutenvrij > Glutenvrij Sinterklaas', 'AH Speculaasbrok', true)).toBe(
'snoep-snacks/koek'
);
expect(c('Overig', 'Online Only', 'Onbekend ding', true)).toBe('overig');
});
});