From c031058d1db14781b84ee077c7182f10ca642015 Mon Sep 17 00:00:00 2001 From: josh Date: Mon, 4 May 2026 19:40:20 -0400 Subject: [PATCH] Remove weed-tracker design handoff bundle Early prototype directory is no longer needed. Clean up .dockerignore reference as well. Co-Authored-By: Claude Opus 4.6 --- .dockerignore | 1 - weed-tracker/README.md | 25 - weed-tracker/chats/chat1.md | 819 ------------------ weed-tracker/project/Apothecary - Deck.html | 411 --------- .../project/Apothecary - Inventory.html | 405 --------- weed-tracker/project/_check.png | Bin 36121 -> 0 bytes weed-tracker/project/_check2.png | Bin 36121 -> 0 bytes weed-tracker/project/data.js | 378 -------- weed-tracker/project/deck-stage.js | 619 ------------- weed-tracker/project/primitives.jsx | 457 ---------- weed-tracker/project/screens-1.jsx | 578 ------------ weed-tracker/project/screens-2.jsx | 664 -------------- weed-tracker/project/tokens.css | 113 --- weed-tracker/project/tweaks-panel.jsx | 419 --------- 14 files changed, 4889 deletions(-) delete mode 100644 weed-tracker/README.md delete mode 100644 weed-tracker/chats/chat1.md delete mode 100644 weed-tracker/project/Apothecary - Deck.html delete mode 100644 weed-tracker/project/Apothecary - Inventory.html delete mode 100644 weed-tracker/project/_check.png delete mode 100644 weed-tracker/project/_check2.png delete mode 100644 weed-tracker/project/data.js delete mode 100644 weed-tracker/project/deck-stage.js delete mode 100644 weed-tracker/project/primitives.jsx delete mode 100644 weed-tracker/project/screens-1.jsx delete mode 100644 weed-tracker/project/screens-2.jsx delete mode 100644 weed-tracker/project/tokens.css delete mode 100644 weed-tracker/project/tweaks-panel.jsx diff --git a/.dockerignore b/.dockerignore index 43bd4ed..eb62a07 100644 --- a/.dockerignore +++ b/.dockerignore @@ -14,7 +14,6 @@ server/data.db-shm server/data.db-wal web/dist web/tsconfig.tsbuildinfo -weed-tracker *.log .env .env.local diff --git a/weed-tracker/README.md b/weed-tracker/README.md deleted file mode 100644 index ce2c5a7..0000000 --- a/weed-tracker/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# CODING AGENTS: READ THIS FIRST - -This is a **handoff bundle** from Claude Design (claude.ai/design). - -A user mocked up designs in HTML/CSS/JS using an AI design tool, then exported this bundle so a coding agent can implement the designs for real. - -## What you should do — IMPORTANT - -**Read the chat transcripts first.** There are 1 chat transcript(s) in `weed-tracker/chats/`. The transcripts show the full back-and-forth between the user and the design assistant — they tell you **what the user actually wants** and **where they landed** after iterating. Don't skip them. The final HTML files are the output, but the chat is where the intent lives. - -**Read `weed-tracker/project/Apothecary - Inventory.html` in full.** The user had this file open when they triggered the handoff, so it's almost certainly the primary design they want built. Read it top to bottom — don't skim. Then **follow its imports**: open every file it pulls in (shared components, CSS, scripts) so you understand how the pieces fit together before you start implementing. - -**If anything is ambiguous, ask the user to confirm before you start implementing.** It's much cheaper to clarify scope up front than to build the wrong thing. - -## About the design files - -The design medium is **HTML/CSS/JS** — these are prototypes, not production code. Your job is to **recreate them pixel-perfectly** in whatever technology makes sense for the target codebase (React, Vue, native, whatever fits). Match the visual output; don't copy the prototype's internal structure unless it happens to fit. - -**Don't render these files in a browser or take screenshots unless the user asks you to.** Everything you need — dimensions, colors, layout rules — is spelled out in the source. Read the HTML and CSS directly; a screenshot won't tell you anything they don't. - -## Bundle contents - -- `weed-tracker/README.md` — this file -- `weed-tracker/chats/` — conversation transcripts (read these!) -- `weed-tracker/project/` — the `Weed Tracker` project files (HTML prototypes, assets, components) diff --git a/weed-tracker/chats/chat1.md b/weed-tracker/chats/chat1.md deleted file mode 100644 index 4dfc886..0000000 --- a/weed-tracker/chats/chat1.md +++ /dev/null @@ -1,819 +0,0 @@ -# Weed Inventory Tracker - -_Started 2026-04-25 14:53 UTC_ - ---- - -## User - -I want to create an inventory system for personal weed tracking - -Dashboard: -- Should contain interesting stats like grams consumed daily, weekly, monthly / average $ per gram / average spend, etc - -Inventory: -Should be somewhat of a standard inventory system. Products have SKUs and sometimes asset tags if we don't have SKUs -Products have name, price, weight (g), THC%, CBD%, Total Cannabinoids %, shop (where we bought it), brand (who made it), type (concentrate, bud, edible, vaporizer, etc), purchase date - -We can mark products as gone / consumed, but that does not necessarily mean that they are not tracked anymore. They just do not live within a bin anymore (they're gone) - -## Assistant - -I'll help you design a personal inventory tracking system. Before I start building, I have some questions to make sure I design this right for you. - -_[tool: questions_v2]_ - -## User - -Questions answered: -- output_format: Both — prototype + deck -- platform: Responsive (works everywhere) -- design_context: No, design something original for me -- aesthetic: Apothecary / botanical (warm, earthy, refined) -- color_palette: Earthy / warm neutrals -- screens: Dashboard with stats, Inventory list (all products), Single product detail view, Add new product flow, Mark as consumed flow, Bins / storage view, Spend over time / charts, Filters & search, Settings -- stats_priorities: Daily/weekly/monthly grams, Avg $ per gram, Avg spend per period, THC consumed (mg), Consumption streaks / patterns, Current inventory value, Favorite shop / brand, Type breakdown (bud vs concentrate, etc), Cost per session / day estimate, Days of supply remaining -- bins: Physical bins (e.g. 'Top drawer', 'Safe') -- consumed_tracking: Date finished + final notes (rating, etc) -- variations: 2-3 directions to compare -- tweaks: None / decide for me -- tone: Decide for me - -## Assistant - -I've got everything I need. Let me lay out the plan and start building. - -**Aesthetic commitment** — Apothecary / botanical, warm earthy neutrals. Type pairing: **Fraktur Mono / Söhne**-feeling pairing... actually, let me commit: **GT Sectra Display** (or similar — I'll use **Cormorant Garamond** for serif headers + **JetBrains Mono** for data/SKUs + **Inter** for UI). Palette: parchment/bone background (oklch warm off-white), deep ink, sage green accent, terracotta accent for warnings/consumed states. - -**Plan:** -1. Build prototype first (responsive, all 9 screens, 2-3 variations as tweaks) -2. Then build the pitch deck showing the concept -3. Both share the visual language - -Let me set up the project structure. - -_[tool: update_todos]_ - -_[tool: copy_starter_component]_ - -_[tool: copy_starter_component]_ - -_[tool: write_file]_ - -_[tool: write_file]_ - -Now the main app file and components: - -_[tool: write_file]_ - -Now the screens: - -_[tool: write_file]_ - -_[tool: snip]_ - -_[tool: write_file]_ - -_[tool: snip]_ - -_[tool: write_file]_ - -_[tool: show_html]_ - -_[tool: snip]_ - -_[tool: show_html]_ - -_[tool: get_webview_logs]_ - -I need to fix the `padded` prop being passed to DOM, then build the deck. - -_[tool: snip]_ - -_[tool: str_replace_edit]_ - -_[tool: snip]_ - -Now let me build the pitch deck: - -_[tool: write_file]_ - -_[tool: register_assets]_ - -_[tool: snip]_ - -_[tool: done]_ - -_[tool: fork_verifier_agent]_ - -I built two pieces under the **Apothecary** concept — warm parchment, deep ink, sage + terracotta accents, Cormorant Garamond serif paired with Inter and JetBrains Mono. - -**Prototype** (`Apothecary - Inventory.html`) -- All 9 screens: dashboard, inventory list, product detail (right-drawer), add product, log session / mark consumed, bins, patterns/charts (90-day bars + spend by month/shop + heatmap), settings, plus filters & search -- Full product schema: SKU, optional asset tag, name, brand, shop, type, weight, price, THC%, CBD%, total cannabinoids, purchase date, bin -- Consumed items stay in the archive with rating + final notes -- **3 dashboard variants** as tweaks (Editorial / Data-dense / Minimal), plus light/dark and a tone switch (Botanical / Neutral / Discreet code-name mode) - -**Deck** (`Apothecary - Deck.html`) -- 13 slides, 1920×1080, parallel section-header style, alternating light parchment / ink / sage / terracotta backgrounds for visual rhythm - -**Caveats / next steps** -- Form submissions are non-persistent (mock data) — wire to localStorage when you're ready -- No barcode/receipt scanner yet (in roadmap on slide 12) -- Charts are hand-rolled SVG — fine for prototyping, swap for a real library if you build this - -## User - -Couple thoughts - -1. Let's do away with the idea of sessions. It's not likely that heavy users will bother will tracking every single session, and it's not likely that moderate to light users will bother using this product at all - -2. I like the theme, but the buttons can be hard to read especially when adding a new product for example the submit button is very light and impossible to read - -## Assistant - -_[tool: snip]_ - -_[tool: read_file]_ - -Good points. Let me address both: - -1. Replace "Sessions" with simpler tracking — just decrement remaining when something gets used (or skip entirely and rely on "mark finished" + purchase patterns) -2. Fix button contrast — the "primary" button uses `--ink` which is correct, but the issue is likely text color in dark mode or the danger button. Let me audit. - -_[tool: snip]_ - -_[tool: read_file]_ - -I see the issue — `var(--bg)` on the primary button is parchment (light) on `var(--ink)` (dark) which should be fine, BUT the disabled state at opacity 0.4 makes the "Save product" button unreadable until name is filled. Let me fix both: remove sessions concept, and improve disabled button contrast. - -_[tool: snip]_ - -_[tool: str_replace_edit]_ - -_[tool: snip]_ - -Now remove sessions — replace ConsumeFlow with a simpler "Mark finished" + "Use some" flow, and rework dashboard stats away from session-based metrics. - -_[tool: snip]_ - -_[tool: str_replace_edit]_ - -_[tool: snip]_ - -Now rework the dashboard stats and data model to remove session-based metrics, and update the deck/copy that references "sessions". - -_[tool: snip]_ - -_[tool: read_file]_ - -I'll rework stats around purchases-only (no session log). The approach: derive daily/weekly/monthly grams from purchase patterns over time (grams purchased ÷ days in period), drop streak entirely, and replace "cost per session" with "average lifespan" (days between purchase and consumed). - -_[tool: snip]_ - -_[tool: str_replace_edit]_ - -_[tool: snip]_ - -_[tool: read_file]_ - -_[tool: snip]_ - -_[tool: str_replace_edit]_ - -_[tool: snip]_ - -_[tool: grep]_ - -_[tool: snip]_ - -_[tool: read_file]_ - -_[tool: snip]_ - -_[tool: str_replace_edit]_ - -_[tool: str_replace_edit]_ - -_[tool: str_replace_edit]_ - -_[tool: str_replace_edit]_ - -_[tool: str_replace_edit]_ - -_[tool: str_replace_edit]_ - -_[tool: snip]_ - -_[tool: grep]_ - -_[tool: grep]_ - -I need to remove the user's request to track sessions/streaks since they only asked for "date finished + final notes" — not session-level tracking. Let me clean these up. - -_[tool: snip]_ - -_[tool: read_file]_ - -I see — the inventory HTML has its own data.log references and dashboard variants. Since the user said "date finished + final notes" (not session journaling), I should remove the consumption log entirely and pivot stats to be derived from purchase + finished dates only. Let me look at the full picture first. - -_[tool: grep]_ - -_[tool: grep]_ - -Now I understand the full scope. The user only wants "date finished + final notes" — they don't track sessions/streaks. I need to: - -1. Replace data.log with derived series from purchase/consumed dates -2. Remove session/streak references -3. Update stats accordingly - -_[tool: read_file]_ - -Now I'll completely refactor the data model to drop the consumption log (since user only wants date finished + final notes), and derive all stats from purchase + finished records. - -_[tool: grep]_ - -Good, computeStats already exists. Let me look at it. - -_[tool: read_file]_ - -Good — computeStats already provides `series7`, `series30`, `series90`, `avgLifespan`, `avgGap`, `consumedCount` (likely). Let me check the return + cleanup the remaining stale references. - -_[tool: read_file]_ - -Now let me fix all the remaining session/streak/log references in screens-2.jsx and Apothecary - Inventory.html (the dashboard variants and patterns screen): - -_[tool: read_file]_ - -Let me see the heatmap part too: - -_[tool: read_file]_ - -Now I'll fix the ChartsView to use stats.series90 instead of building from data.log: - -_[tool: str_replace_edit]_ - -_[tool: str_replace_edit]_ - -_[tool: str_replace_edit]_ - -Now fix the Inventory.html dashboard variants and remove `Log session` references: - -_[tool: read_file]_ - -Now let me see the minimal dashboard and finish reading: - -_[tool: read_file]_ - -Now I'll fix all references in Inventory.html: - -_[tool: str_replace_edit]_ - -Now let me also clean up the data.js to remove the LOG (no longer needed) and find any other stale references. Let me also check ConsumeFlow's copy: - -_[tool: grep]_ - -_[tool: str_replace_edit]_ - -_[tool: snip]_ - -_[tool: str_replace_edit]_ - -Let me read what's around the second "Streak" reference and apply edits more uniquely: - -_[tool: read_file]_ - -There's only one literal "Streak" but probably another. Let me search: - -_[tool: grep]_ - -Now let me apply edits with proper unique context: - -_[tool: str_replace_edit]_ - -Now let me check primitives.jsx for any leftover stats keys (thcLast7 should still work — uses series7), and verify the inventory loads: - -_[tool: grep]_ - -All cleaned up. Let me verify the prototype loads: - -_[tool: done]_ - -Clean. Let me also verify the deck: - -_[tool: show_html]_ - -_[tool: get_webview_logs]_ - -Both files load cleanly. I've removed all the session/streak tracking that crept in — your spec was "date finished + final notes," not session journaling. - -**What changed:** -- Dropped the synthetic 90-day session log; daily/weekly/monthly grams are now **inferred** by prorating each item's used weight across its lifespan (purchase → finished, or purchase → today for active items) -- Replaced "Cost per session" / "Log streak" stats with **Avg lifespan** (days from purchase to finished) and **Avg days between buys** -- "Log session" buttons → "Mark finished" everywhere -- Heatmap relabeled "Inferred consumption" -- Deck slide updated to match - -_[tool: fork_verifier_agent]_ - -Done. The tracker now matches your spec: products are tracked from purchase to finished date with rating + final notes, and all dashboard stats derive from those records — no per-session logging. - -## User - -Verifier agent check completed - -## Assistant - -Let me investigate both issues: - -_[tool: grep]_ - -Confirmed — let me see how Btn spreads `v`: - -_[tool: read_file]_ - -Confirmed: `...v` spreads `bg` (not a CSS prop) into the style. Fix the variants to use `background`: - -_[tool: str_replace_edit]_ - -Now fix the dashboard greeting layout. Let me look at the header: - -_[tool: read_file]_ - -The header layout looks fine to me — but verifier reports overlap. The issue: `marginBottom: 8` on the row might not give enough space, and the Btn flex container could wrap. Let me add `flexWrap: nowrap` and proper alignment plus more bottom margin: - -_[tool: str_replace_edit]_ - -Now verify the fix: - -_[tool: done]_ - -_[tool: fork_verifier_agent]_ - -Fixed both bugs: -- **Button contrast** — variants map used `bg:` instead of `background:`, so the spread never set the button's background. Renamed throughout. -- **Header overlap** — slightly reduced the greeting (44→36px), tightened line-height, switched the row to `align-items: center` with `flex-wrap` and `flex-shrink: 0` on the buttons so they can't squeeze the title into a wrap. - -## User - -Verifier agent check completed - -## Assistant - -All clear — both fixes verified. - -## User - -Okay, I think we have a really good start to this. I have a number of thoughts to flesh it out a bit - -1. I noticed that you included a low stock running out section, but we removed the sessions which told us how much of a particular product that we have. I think that this adds some interesting depth. For example we can have multiple of 1 product; for a example a particular pre roll that we like where we have multiple versus a jar of a particular strain. It's easy to tell how many we have left at all times versus bud where we will show as having 14g for example until the day I mark it as completely consumed. I think we should prompt an audit on a product after say 30 days where we re-weigh the product. This should be based on product type though. Edibles, pre-rolls we should just confirm whether it's still there or not (require entering serial or asset to confirm) whereas bud would get reweighed and wax would get estimated - -2. We do not currently have a way to add a new shop - -3. Brands should be similar to shops where they are a dropdown and we should similarly add a section to add a new brand - -## Assistant - -Great direction. Let me think through the model changes before coding: - -**Multi-unit products** — items like pre-rolls/edibles have a `count` (e.g. 5 pre-rolls). Bud/wax have `weight` only. Tracking different product types differently: -- **Discrete (count-based):** Pre-roll, Edible, Vaporizer → track `quantity` (units left). Audit = "do you still have N?" with SKU/asset confirmation. -- **Bulk (weight-based):** Flower, Concentrate, Tincture → track `remaining` weight. Audit = re-weigh (Flower) or estimate (Concentrate/Tincture). - -**Audits** — after 30 days since purchase or last audit, prompt re-weigh/re-count. Track audit history with date + value. - -**Shops & brands** — both become managed lists with "+ Add new" inline in dropdowns, plus a Settings section to manage them. - -Let me ask a couple of quick clarifications before I build: - -_[tool: questions_v2]_ - -## User - -Questions answered: -- audit_trigger: Configurable per-type cadence (e.g. flower 14d, edibles 60d) -- audit_surface: Inline badge on each product in inventory + dashboard banner -- audit_confirm_method: Type SKU or asset tag -- discrepancy: The point is not to find a discrepancy with weights. We are checking weights (for certain types) to better track inventory and usage. We are checking whether or not the item is still there for other types like edibles and pre rolls that is more where we will find a discrepancy. If one is found then we should have a modal that gives the option to mark it as consumed or gone. Difference is gone counts as money spent but not weed consumed (for metrics) -- shop_brand_metadata: New Shop (Name + Location), New Brand (Name) -- low_stock_logic: For edibles and prerolls we should do count less than or equal to 2, but do it by brand rather than strain (sku). For wax and bud do it by original % (25) -- remaining_display: Show estimated remaining (decays based on avg lifespan) + actual last audit - -## Assistant - -Great answers. Let me plan the build: - -_[tool: update_todos]_ - -_[tool: snip]_ - -_[tool: read_file]_ - -Now I'll rewrite data.js with the new model: - -**Model decisions:** -- `kind: "bulk"` (Flower, Concentrate, Tincture) — track `weight` (g/ml) -- `kind: "discrete"` (Pre-roll, Edible, Vaporizer) — track `count` (units) -- `unitWeight` for discrete bulk-equivalent (e.g. each pre-roll = 1g) -- `audits[]` — `{date, type: "weigh"|"presence", value, byUser}` -- `auditCadenceDays` per type (Flower 14, Concentrate 21, Tincture 30, Pre-roll 30, Edible 60, Vaporizer 30) -- `consumed` (used up via use) vs `gone` (lost / discarded — counts as $ but not consumption) -- Shops: `[{id, name, location}]`, Brands: `[{id, name}]` - -_[tool: write_file]_ - -_[tool: snip]_ - -_[tool: read_file]_ - -_[tool: snip]_ - -_[tool: read_file]_ - -Now rewrite computeStats for the new model. Key changes: -- `p.status` (active/consumed/gone) instead of `p.consumed` -- bulk: use estimatedRemaining decay; discrete: use countLastAudit × unitWeight for grams -- Gone items: count $ but NOT grams in consumption stats -- favShop/favBrand keyed by shopId/brandId — return name+count -- typeBreakdown by total est. grams (discrete uses count × unitWeight) -- New: overdueAudits count - -_[tool: str_replace_edit]_ - -_[tool: snip]_ - -_[tool: grep]_ - -_[tool: snip]_ - -Lots of references. Let me read screens-1.jsx fully and rewrite it cleanly: - -_[tool: read_file]_ - -_[tool: snip]_ - -Now let me check the rest of the codebase to see what needs updating: - -_[tool: list_files]_ - -_[tool: grep]_ - -Let me check data.js and the main HTML file to understand the current state: - -_[tool: read_file]_ - -_[tool: snip]_ - -Now let me check the main HTML, primitives, and screens-2: - -_[tool: read_file]_ - -_[tool: read_file]_ - -_[tool: snip]_ - -Now I have a complete picture. The data model has `status: "active" | "consumed" | "gone"` but `screens-1.jsx` still uses old `p.consumed`, `p.remaining`, `p.weight` patterns and references `data.products[].brand`/`shop` strings. I need to rewrite screens-1.jsx to use the new model. - -Let me look at screens-2.jsx briefly to see how it's structured and check for existing usage patterns: - -_[tool: read_file]_ - -_[tool: snip]_ - -Now I have the full picture. The screens use the OLD model (`p.consumed`, `p.brand`, `p.shop`, `p.remaining`, `p.weight`) but data.js uses the NEW model (`p.status`, `p.brandId`, `p.shopId`, `p.kind`, `p.countLastAudit`, etc.). I need to update all three screens files plus the main HTML to use the new model. Let me also check tokens.css briefly and then rewrite. - -_[tool: read_file]_ - -_[tool: snip]_ - -Now let me look at the user's most recent ask. I should scroll back to see what they want me to do. Let me check what's going on with the file currently — does it have errors? - -_[tool: show_html]_ - -_[tool: get_webview_logs]_ - -No errors so far. Let me screenshot to see the actual rendered state: - -_[tool: save_screenshot]_ - -_[tool: view_image]_ - -_[tool: snip]_ - -The page renders but the data shows 0g/0$ everywhere because the screens are reading the OLD model fields (`p.consumed`, `p.brand`, `p.shop`, `p.remaining`, `p.weight` for grams) which no longer exist directly. I need to update screens-1.jsx, screens-2.jsx, and the main HTML to use the new model with `status`, `brandId`/`shopId` lookups, and helpers for est-remaining and discrete vs bulk. - -Let me delete the old screen files and clean rewrite them. First screens-1.jsx (Dashboard, Inventory, ProductDetail): - -_[tool: write_file]_ - -_[tool: snip]_ - -Now let me write a smaller screens-2.jsx replacement and a separate screens-3.jsx for the audit/mark-gone modals. First update screens-2: - -_[tool: write_file]_ - -_[tool: snip]_ - -Now update the main HTML to wire up the new modals (audit, mark-gone) and update the dense/minimal dashboard variants to use the new model: - -_[tool: str_replace_edit]_ - -_[tool: str_replace_edit]_ - -_[tool: str_replace_edit]_ - -_[tool: str_replace_edit]_ - -_[tool: snip]_ - -Now I need to fix the dense and minimal dashboard variants in the HTML — they still reference `p.consumed`, `p.brand`, `p.shop`, `p.remaining`. Let me update those: - -_[tool: str_replace_edit]_ - -_[tool: str_replace_edit]_ - -_[tool: str_replace_edit]_ - -_[tool: snip]_ - -_[tool: show_html]_ - -_[tool: snip]_ - -_[tool: get_webview_logs]_ - -_[tool: snip]_ - -_[tool: save_screenshot]_ - -_[tool: snip]_ - -_[tool: view_image]_ - -_[tool: snip]_ - -The screen looks like an old cached state. The data showing "0.00 g/day" "0g weekly" suggests the new data with audits + decay isn't computing consumption. Let me look at the issue: the screenshot may have been cached. Let me reload and check fresh: - -_[tool: save_screenshot]_ - -Hmm — `screenshot` after `location.reload()` doesn't actually wait for the reload before screenshotting (the screenshot may capture mid-reload). Let me wait: - -_[tool: show_html]_ - -Now wait + screenshot: - -_[tool: save_screenshot]_ - -_[tool: view_image]_ - -Same render — but the new dashboard should now show "Mark finished" + "Audit" buttons (3 buttons), and counts like "consumed/gone". Looking at the header: "5 bins · 17.9 g on hand · 0 archived" — that's old! Plus only 2 buttons. The new screens-1.jsx isn't loading. Let me check `eval_js` for loaded: - -_[tool: eval_js]_ - -Dashboard exists, but `H` (DATA_HELPERS) is undefined and the products don't have brandId. The data.js isn't the new one I read. Wait — I read data.js as new, but at runtime it's old? Let me check: - -_[tool: eval_js]_ - -The data.js in the cached webview is the OLD version. But I rewrote it. Let me verify the file on disk: - -_[tool: grep]_ - -File on disk is correct. The webview cached the old version. Let me hard reload: - -_[tool: eval_js]_ - -Right, but DATA_HELPERS is undefined and SAMPLE_DATA has old fields. So data.js is being served stale. Let me cache-bust by adding a version comment-edit, or by checking what's actually happening — let me reload the iframe with a hard reload: - -_[tool: eval_js]_ - -So the FILE is fine, but the webview cached an older version. Cache-busting via a query param works. The iframe just held an old script. Let me force reload the page (and verify): - -_[tool: show_html]_ - -_[tool: sleep]_ - -_[tool: eval_js]_ - -Hmm. Despite the network fetching fresh data, the script tag itself loads stale. There must be a syntax error in data.js causing it to fail silently then fall back to old. Let me check console: - -_[tool: get_webview_logs]_ - -No errors but DATA_HELPERS is undefined. So data.js executes but the helpers aren't being attached? Let me check if SAMPLE_DATA itself has new structure: - -_[tool: eval_js]_ - -SAMPLE_DATA has both old AND new keys: it has top-level `shops`, `brands`, `types` (new) but products use OLD `brand`, `shop` strings. That means data.js was edited inconsistently. Let me look at data.js carefully: - -_[tool: read_file]_ - -So data.js should produce products with `brandId`, `kind`, etc. But what the iframe runtime shows is a product with old `brand`/`shop` and no `kind`. There must be TWO data.js loads — let me look at the HTML: - -_[tool: grep]_ - -Single data.js. The runtime shows old keys. Let me check — does data.js have a stale section that sets old keys? Maybe I added new `BRANDS` array but didn't actually change the products inside the function: - -_[tool: grep]_ - -No old fields exist in source. The runtime is using a stale cached version. Let me cache-bust the script tag: - -_[tool: str_replace_edit]_ - -Wait — earlier `eval_js` of fetch directly showed `data.js?bust=...` HAS DATA_HELPERS. So fresh fetch is fine. The script tag without query string was returning OLD content. That's a service-worker caching issue or similar. The query-string bust should fix it. - -_[tool: grep]_ - -Let me bust those too and verify: - -_[tool: str_replace_edit]_ - -Now let me verify everything renders: - -_[tool: show_html]_ - -_[tool: sleep]_ - -_[tool: eval_js]_ - -DATA_HELPERS is now loaded, products have new fields. Let me check console + screenshot: - -_[tool: get_webview_logs]_ - -Clean. Let me screenshot: - -_[tool: save_screenshot]_ - -_[tool: view_image]_ - -_[tool: snip]_ - -Looking at the dashboard now: - -**Issues:** -1. "Good evening" overlaps with "Cuts of... 5 bins · 17.9 g on hand · 0 archived" subtitle -2. All stats show 0: "0.00 g/day", "$0.00 avg cost per gram", "0 mg THC last 7 days" — but "30-day spend $555". The compute is broken for consumption (which derives from audits + estimated decay). -3. The header still says "17.9 g on hand" — but with new estimated-remaining decay, this should reflect estimated total. - -Let me read the Dashboard component: - -_[tool: grep]_ - -Code references `stats.activeCount`, `stats.consumedCount`, `stats.goneCount`, `stats.dailyAvg`. Let me check computeStats: - -_[tool: grep]_ - -Let me look at how series30 (consumption) is computed: - -_[tool: read_file]_ - -OK so computeStats is rich and should produce non-zero numbers. But UI shows 0.00. Let me check what's actually returned at runtime: - -_[tool: eval_js]_ - -So the numbers are correctly computed: 0.49 g/day, $18/g, $639 spend30, 1473mg THC last 7, etc. But the UI shows 0. So screens-1.jsx is rendering with the old data? Let me check what stats the React component actually sees — the React app's `useMemo` may have cached old stats. The `data` reference might still be stale. Actually — React stale closure: `data = window.SAMPLE_DATA` was captured before the new data.js loaded. But it should re-render now. - -Actually I bet what's rendering on screen is from a render BEFORE data.js loaded fresh. Let me force a real reload: - -_[tool: save_screenshot]_ - -Hmm actually looking back — in eval_js the `computeStats` call returned correct values right now. So why does the screenshot show 0? Maybe my reload screenshot races. Let me wait then evaluate the DOM: - -_[tool: sleep]_ - -Wait actually let me check. After `location.reload()`, the page reloads and `computeStats` would run on data with fresh helpers. But reload may have lost iframe state. Let me re-show + grab fresh screenshot: - -_[tool: show_html]_ - -_[tool: sleep]_ - -Going to check the DOM: - -_[tool: eval_js]_ - -Now the dashboard renders correctly with new stats: 15 active, 4 consumed, 2 gone, 0.49 g/day, $17.96/g, $639 spend, 1473mg THC, audit overdue alert for Slate Cherry. - -Now let me screenshot: - -_[tool: save_screenshot]_ - -Header says "Cuts of..." was likely an old screenshot rendering issue. Let me view: - -_[tool: view_image]_ - -_[tool: snip]_ - -The screenshot shows a STALE render — innerText reports the correct content (15 active, 4 consumed, 2 gone, 0.49 g/day, $17.96/g, $639) but the image shows the OLD render (15 items, 17.9g on hand, 0.00 g/day, $0.00, $555). - -This is `save_screenshot` capturing a stale rasterization. The DOM is correct. Let me try again with extra delay: - -_[tool: save_screenshot]_ - -Wait — the discrepancy is clear: innerText shows "21 items" and "15 active items"; screenshot shows "15 items" (sidebar) and old text. So this is two different DOMs being measured. innerText was after fresh reload; screenshot may have triggered a NEW reload that hasn't completed. - -Actually `show_html` reloads. Looking at sidebar in screenshot: "v0.4 · 15 items" — this is OLD count. After my data update there are 21 products. So the screenshot is from BEFORE the refresh. The verifier's iframe may be different from eval_js iframe. - -Let me try `multi_screenshot` which reuses the existing rendered DOM: - -_[tool: multi_screenshot]_ - -Now the dashboard renders correctly! Stats are populated: 0.49 g/day, $17.96/g, $639 spend, 1,473mg THC, audit overdue alert for Slate Cherry, sparkline visible. - -But I see one issue: **"Good evening." overlaps with the subtitle text** — "Good evening." block height collides with the active-items line. The h1 has `lineHeight: 1.1` and `margin: "6px 0 0"`, then below is a div with `marginBottom: 28`. Let me check the structure: - -_[tool: read_file]_ - -The header div uses `alignItems: "center"`. With "Good evening." being two lines wrapped (because the buttons take width and the title flexs to 2 lines), it goes below the buttons. The subtitle below then overlaps because of the negative-ish lineHeight. - -Actually looking again — "Good evening." is wrapping to two lines. That's because the title container is too narrow. Buttons take 3 of them now. Let me make this responsive — let the heading not wrap, and shrink: - -_[tool: str_replace_edit]_ - -Now done: - -_[tool: done]_ - -The dashboard now renders correctly with the new data model — 15 active / 4 consumed / 2 gone items, 0.49 g/day average, $17.96/g cost, $639 30-day spend, an audit-overdue card flagging Slate Cherry, and the new "Audit" button alongside Mark finished. Cache-busted script tags so the iframe picks up the fresh JS. - -_[tool: fork_verifier_agent]_ - -Dashboard's working with the audit + gone/consumed split. Let me know if you want me to keep going on the rest of the screens (Inventory list with audit badges, the AuditModal, etc.) or pause here. - -## User - -Apply comment - -## Assistant - -Let me look at that slide: - -_[tool: grep]_ - -Let me see the full line: - -_[tool: read_file]_ - -The text is corrupted — "the cabinet you'veidn't realize you were on." is gibberish. Let me fix it to something clear: - -_[tool: str_replace_edit]_ - -Fixed — that sentence was garbled (the second half was corrupted text). Now reads cleanly: "The morning glance. Daily averages, spend trends, days of supply — everything you need to see in a single look." - -_[tool: done]_ - -_[tool: snip]_ - -Fixed the garbled sentence. The font-size warnings are decorative micro-labels (mono captions, eyebrow text) — intentional design choices, not real problems. - -## User - -Apply comment - -## Assistant - -The "Dashboard." title at 200px is way too big and getting clipped by the slide bounds. Let me check the slide and fix: - -_[tool: grep]_ - -_[tool: snip]_ - -Let me check slide-pad layout — likely set to flex column with center-justify, but the 200px text overflows. Let me look at the slide-pad CSS and the deck-stage size: - -_[tool: grep]_ - -_[tool: snip]_ - -The 200px section titles are too big for the slide. With slide-pad padding 100px, the available width is 1720px, but more critically the height with `justify-content: center` plus 200px line-height pushes content out. Let me reduce to 160px which still feels strong: - -_[tool: str_replace_edit]_ - -_[tool: done]_ - -_[tool: snip]_ - -Brought the section titles down from 200px to 160px so they fit cleanly within the slide. - -## User - -No I think it's the color that makes it invisible lol - -## Assistant - -Ha — let me check. The slide is `.bg-ink` (dark) and `.h-display` color likely defaults to `var(--ink)` which is also dark on dark. - -_[tool: grep]_ - -Confirmed — `.h-display` color is `var(--ink)` which is dark, on a `bg-ink` (also dark) background. The `.bg-ink .h2, .bg-ink .h1` rule covers h1/h2 but NOT `.h-display`. Same issue exists for `.bg-sage` and `.bg-terra`. Let me fix: - -_[tool: str_replace_edit]_ - -_[tool: str_replace_edit]_ - -_[tool: snip]_ - -You were right — `.h-display` color was `var(--ink)` which made it invisible on the dark, sage, and terra backgrounds. Added it to the override rules alongside `.h1` and `.h2`. - -_[tool: done]_ - -_[tool: snip]_ - -Section titles should now be visible on all three colored section dividers. - diff --git a/weed-tracker/project/Apothecary - Deck.html b/weed-tracker/project/Apothecary - Deck.html deleted file mode 100644 index 21229e4..0000000 --- a/weed-tracker/project/Apothecary - Deck.html +++ /dev/null @@ -1,411 +0,0 @@ - - - - - -Apothecary — A personal inventory system - - - - - - - - - - - -
-
-
-
A
-
-
Apothecary
-
A personal log
-
-
-
-
Concept · Apr 2026
-
A quiet, careful
record of what's
in your cabinet.
-
- A personal inventory system for keeping clean, professional records of cannabis purchases — what you have, where it lives, what you've used, and what's worth buying again. -
-
-
-
For personal use · Local-first · No accounts
-
v0.4 · CONCEPT DECK
-
-
-
- - -
-
-
Why this exists
-
Notes apps and spreadsheets can't keep up with a real apothecary.
-
-
-
01 Memory fades
-
You forget what you paid, where you bought it, or whether the last batch was any good.
-
-
-
02 Spend goes unmeasured
-
Without a real per-gram view, it's easy to over-pay and not realize the trend.
-
-
-
03 Habits are invisible
-
Daily, weekly, and monthly use are hard to see clearly without a structured log.
-
-
-
-
- - -
-
-
The premise
-
$1,847
-
is what a moderate consumer might spend in a year — without a single line item to show for it.
-
Apothecary turns that into a record you actually own.
-
-
- - -
-
-
What it is
-
An inventory system,
designed like a library catalog.
-
-
-
Every item is tracked
-
SKU, asset tag, weight, THC, CBD, total cannabinoids, shop, brand, type, date.
-
-
-
Every item has a place
-
Physical bins — Top Drawer, Apothecary Box, The Safe — with capacity limits.
-
-
-
Mark when finished
-
Date finished, rating, and final notes — the archive becomes a tasting library.
-
-
-
Nothing leaves the archive
-
Consumed items keep their record — final notes, rating, lifespan, all preserved.
-
-
-
-
- - -
-
-
Part 01 / 03
-
Dashboard.
-
The morning glance. Daily averages, spend trends, days of supply — everything you need to see in a single look.
-
-
- - -
-
-
What the dashboard answers
-
Ten quiet questions you've never asked out loud.
-
-
Daily avg
0.52g
-
Avg $/g
$13.40
-
30d spend
$284
-
7d THC
766mg
-
Avg lifespan
37d
-
Inv. value
$398
-
Days supply
28d
-
Avg gap
8.4d
-
Top shop
Greenleaf Co-op
-
Top brand
Foxglove Farms
-
-
-
- - -
-
-
Part 02 / 03
-
Inventory.
-
Each product gets a SKU, an optional asset tag, a bin, a price, a chemistry. The catalog of a small, careful library.
-
-
- - -
-
-
Anatomy of a product
-
Every entry, comprehensive.
-
-
-
◆ Concentrate · Active
-
Indigo Cellar
Live Rosin
-
Heirloom Botanicals · from The Field House
-
-
Price
$65
-
Weight
1.0 g
-
THC
78.4%
-
CBD
0.2%
-
-
- SKU-A39FQX - · - AT-0042 - · - The Safe -
-
-
-
Tracked fields
-
    -
  • — Name & brand
  • -
  • — SKU + optional asset tag
  • -
  • — Type (flower, concentrate, edible, vape, pre-roll, tincture)
  • -
  • — Shop & purchase date
  • -
  • — Price + computed cost-per-gram
  • -
  • — Weight, THC%, CBD%, total cannabinoids%
  • -
  • — Bin location & remaining quantity
  • -
  • — Final notes & rating after consumed
  • -
-
-
-
-
- - -
-
-
Storage
-
Every product knows
where it lives.
-
-
-
Bin 01
-
Top Drawer
-
Bedroom
-
-
-
4 / 14
-
-
-
Bin 02
-
Apothecary Box
-
Office shelf
-
-
-
2 / 10
-
-
-
Bin 03
-
The Safe
-
Closet
-
-
-
3 / 8
-
-
-
Bin 04
-
Travel Tin
-
Backpack
-
-
-
2 / 4
-
-
-
Bin 05
-
Cold Storage
-
Fridge — back
-
-
-
1 / 6
-
-
-
When a product is consumed, it leaves its bin but stays in the archive — keeping the record of what worked, what didn't, and what's worth a rebuy.
-
-
- - -
-
-
Part 03 / 03
-
Patterns.
-
Where the data quietly becomes useful — habits made visible, spend made measurable.
-
-
- - -
-
-
-
"You can't keep
what you don't
write down."
-
— Operating principle
-
-
-
- - -
-
-
What's next
-
Three releases
to feel done.
-
-
-
v0.5 · May
-
Add & consume
-
Polish on the entry forms, barcode & receipt photo capture, faster bulk add.
-
-
-
v0.7 · July
-
Charts & export
-
Heatmap, monthly spend, shop comparisons. CSV & JSON export with no cloud.
-
-
-
v1.0 · September
-
Mobile app
-
Native iOS / Android with biometric lock. Same local-first principle.
-
-
-
-
- - -
-
-
-
A
-
-
Apothecary
-
A personal log
-
-
-
-
Keep
a record.
-
A small, careful inventory of a small, careful library.
-
-
-
Live prototype available
-
END · 13 / 13
-
-
-
- -
- - - - diff --git a/weed-tracker/project/Apothecary - Inventory.html b/weed-tracker/project/Apothecary - Inventory.html deleted file mode 100644 index c13236b..0000000 --- a/weed-tracker/project/Apothecary - Inventory.html +++ /dev/null @@ -1,405 +0,0 @@ - - - - - -Apothecary — Personal Inventory - - - - - - - -
- - - - - - - - - - - - - diff --git a/weed-tracker/project/_check.png b/weed-tracker/project/_check.png deleted file mode 100644 index 9e024a98626cfc1d102f3527d55e5da95a371c4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36121 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ธ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 diff --git a/weed-tracker/project/_check2.png b/weed-tracker/project/_check2.png deleted file mode 100644 index 9e024a98626cfc1d102f3527d55e5da95a371c4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36121 zcmdSB2UHYU*DhQQP0mOL0iltMM9I*AWF==%$x(6=5NJS95SomDzIdwg8JrCe3$jHk8AP@k65P!h+ z6d(l<;o=eE;Sdqx6Os}Wkx_HsqNbvv7G>jraLYsQE6PG;q*e5sja4*lwWMWCUz*vv zd-?hMDH(>n4)u<6_VIi6y%7*GDJeAtweYQ5!q3!Y)SvywpX)||03C@6ISCm=2OtrE zkO@H7EdUMTI#EDBKEQ7u5E3#N1r-e)0}~5zKm|U41VTne0wbfKfWe5Py%FC7U;-4v zTU-*TH&l(#=p2c-{UX!R=_N~=h}8zaLU@dw{4p>|NXf`47;ZB%F|+XU-Q^b$6q33p zEh8%@ub{4>sim!>t7l?rW^Q3=W$ogN8`!!zJ{V9<+~!68x6F|l#4;}a4yGPAOC za`W;FO3TVCDyyn%YCko%w6?W(bao95kBp9uPkf$S__DaPyt2BszOlD|aCmfl^6m8O zyImjv`KMWk|33};U+f}4*o6cJBZJYt+XX^$M=Zz$V3b>2sDu)#Xhx1V=(zpRi6kS_ zOPVm~dDOlV8#@hRl0bMD81}xK_QSG&W|;r~FUx*6><_yp0c>Ot;^83^0OG(#UPefi ze+W|=(tol%zXo*XBrYw}#mD#ueD%Qc(nKnt|A!W^i>fp-CQ}9slcoH12_%SjjxC^| zt*+!q6m9Xrxz!`n@*3%*3CcsY2Y`M?W{~A7bWf=EEI{qMk)y8OQ#yBt0|Jt06aZHu zRVWIP2I@X>oxWPnZ9@hB7ho?cV;Eyb2ovyE%Wrp#N%<)3kY>z}P*s`;_z((Qrj&FLQtJ>6?sWwq5mhhYb-vnNeF;k{L7nK|V8k8BlM;QP( zt_03%54iL)^laK9cQsa`VP-Of8;bUt=+)+Yz`8i!p*4%Bzn@?zvFLCUjYa`%^-yxK zq_*<$Y+iTXH4v9MaDGNnM)iu#24cZ9&z-7bb)^2_%dq8Isa)Y9Zlg~Oh!4@QJ*5A{ zM6ErsZO8(Sqowa!19x=rx9wPXk#Hdu{-`3~%U-l8R7&VxUQ>r8Jw@8X*U9<+p|kvw{OY5i?z49#DW8O$wB6MOLXYv(xF z)H%gD5$GGabG!DvsXaR+Xx%u%7Q!{nDJz0!UE#@rnwtaLVarKh!1lE?S`g2O+#gy4D|{fXk|GSZfh>XyX=Yb6=R`fSFqZ- z>g&gTlXgHNHxQ`RhrS`@x`Cs3J6W{#jsEn^E~;pV>fM8l>7=WGca?;qVhbF`)#n;! zi=nOIj969hsoK5GQx!1DxM%T9og#YKDfx(haUAO~)ydg45U6bH;pmDI6DcAXbAyR1 z1=tLK6H0}9>8xl=Hn_EHQy6GjwKGjeZ)GwUJsskfU>)%$IJ2*Fi7nv$WBdHhGh^7D zL1`jvri>8a$MSQmh1|F_@kTpJyBDJ>4G_hYtAP1=PSJCbSDu)?u@2AA>|?bX>UiVk z??6d=W_UiFtxJZ-0ew!r8DULahD5{aL-g?&^KN+BDtrXUS-9#l*BNKQJu+|O8n2+S zbfmh!m&Yxjf@}ettShifwsW&4m%>jWgq$9XiMs43%@kB~@72TP+WpeAH z?ZAC^CK}!_(_M@EwwLx5TMEi7 ztni|TMMAw)UfW3u-^<|lhP~+QlUonB*YmE(a_dOf#PaiBfZonj1c#g4Qsw3nK0fMP z^F@Z@c@+l>Kk1KY@;vGjZz~$LVt3ElprN)w?Gthk|CAFd2Rb0vxduwBuX14YQSNX} z)LYAC&vEKf--?P;JaK0ks|YOP z?()O>RkF=>=#3E^;i9fEeDv)Kr5#TTfy2eI><6t%UqPMg>nY+2!A21V^?@*mZ!W$k->5~RbZCIR1lO8qg}d!4@GGpF~dG+S5on*KZS zWH!zYcrWDTd2RA@nto9x5WC&SA$iPn2lQrt*%3EB^FsZ}Qx39f!~7D!z|f*0zmWHb z^N%V29z|547>Yll0ax}t0mf7)x32==%^#FPyGkG)kYruey(gv!%XzmWz{zoaJj}`?v5#MjV0Sg z!&#HT+f+uN2%FlpZhW5M*@QnX5XAm}-U{5h7|&9CaaDwIa)RVUpb?sML%yrK*TAiYZz`x2#L?FP1_$DwxbG>>HSj6?o4iZv z>G?I#VShygDpb@J{~EO}esFRPM9f_dvZIF|3M@C3=&?W!sO+-Iqc7B&nx94p;x_Gejq4@#$gAA&W1wGBBPX!ez zAx&A)ixuaj#a)geU#ak+Vm8%G61h>GBU;16$LyS$#upz1)}h4dHFq4koJ-2eSfGn@ zA3aCLdLB(On#7rcQ=h3Ha}e2*J+9>%dPBu^Pae)I>A9`Kgrx8tjsAK08Ll#ZL2Uwt zA5inVqtNs!+|;MV=~<`tMDh?11avaWq78<#9Q>D9`IWhJK^Jnwc9ScVdEJc*BV1~- zd7IE1e?Uy_x~LVVW_|sl?V0;;#5r&fXP1^gG~H;M=WB>CVnLs)6YPI~SgiLmbp8sV z|7L9(R?6?Y<8L)6Lk|^B(K$lvck~(3ZH^+%UHPR+lXJv16F20_S686&3(W=O) z@A@_7D>azYSvhiyzUVv8jI6V|=^4-pSgUGm^O4yz5PYJ#S~p|&gHfi59OWrhAa&A} ztApY849m2(S{Dc5pJ2RsMTx&)xb|=a5l8pa}~0XR~hNOHr+S5j1+L!4=lX z*7%jnU!rrd7_d~;3D9|Jw>!Jcpu@fx_FP)qDf&?4cvt)OXmOfXY=pqM5Gz-kb6pzs zA;>4B>*MHBWz}q@I$Mf$dnFWDt)`tnbMQQBwPt2Zi)E4P!&8Og#BtH#dmf<;oDCb> z?$KWZ9uoQ1SoYtJUtKm`f3+$(U@`5UgCyJ--X(LT|wv`H)02Wnma{|Ri%W=LU?teP8 zyjW4?r$ne_JbUiwcXky!=-WMf4OBPu7QWtVzo@?vuR6q$9#iU z(?L4O8!hf0xkW(%H00?oFwV7NMcZ31KAYD>F~F_r8`$EeC!P>AUX>@#CbDrD_`ig2 zFSl05Vd8RoH&6gQ8MTx++@o7;2JP4f23?IW+#jJCLKkxdX%zBmi4ID+<-x{QH%(;B z`!tf^DI=h0X`A*eMOS;{_pKE2Ofh8%7=2^y^i(Ff)k`r9XU|SR;;n4RoJe zmR$p%3yZRTW!!&cv%mQd)@eDqD8fDhMXD$VA&MuZGD887I%5`0{BsFG!6hGJ>kuHr z;MCEhuj@H~k50~F>TAX-V$n>}7mNbfP~j?5FDfS%&%e%Jf914)lgEP8)$1vIQu#W` znZ#5#l#!2$QgQUWGwzPw5%+TBxmhธ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 diff --git a/weed-tracker/project/data.js b/weed-tracker/project/data.js deleted file mode 100644 index b2823ea..0000000 --- a/weed-tracker/project/data.js +++ /dev/null @@ -1,378 +0,0 @@ -// Sample inventory data -window.SAMPLE_DATA = (function() { - const TODAY = "2026-04-25"; - const daysAgo = (n) => { - const d = new Date(TODAY); d.setDate(d.getDate() - n); - return d.toISOString().slice(0, 10); - }; - - // Shops are objects now - const SHOPS = [ - { id: "shp-01", name: "Greenleaf Co-op", location: "Capitol Hill" }, - { id: "shp-02", name: "Verdant Apothecary", location: "Ballard" }, - { id: "shp-03", name: "The Field House", location: "Fremont" }, - { id: "shp-04", name: "Northstar Dispensary",location: "Roosevelt" }, - { id: "shp-05", name: "Wildwood Provisions", location: "West Seattle" } - ]; - - // Brands are objects too - const BRANDS = [ - { id: "brd-01", name: "Foxglove Farms" }, - { id: "brd-02", name: "Slow Burn" }, - { id: "brd-03", name: "Heirloom Botanicals" }, - { id: "brd-04", name: "Terra Vera" }, - { id: "brd-05", name: "North Field" }, - { id: "brd-06", name: "Cinder & Sage" }, - { id: "brd-07", name: "Old Forest" }, - { id: "brd-08", name: "Marigold Ext." } - ]; - - // Type config — kind + audit cadence - // bulk: track weight (g) — audit re-weighs (or estimates for concentrate) - // discrete: track count (units) — audit confirms presence by SKU/asset - const TYPES = [ - { id: "Flower", kind: "bulk", auditMode: "weigh", cadenceDays: 14, unit: "g", weighable: true }, - { id: "Concentrate", kind: "bulk", auditMode: "estimate", cadenceDays: 21, unit: "g", weighable: true }, - { id: "Tincture", kind: "bulk", auditMode: "estimate", cadenceDays: 30, unit: "ml", weighable: false }, - { id: "Pre-roll", kind: "discrete", auditMode: "presence", cadenceDays: 30, unit: "ct", weighable: false }, - { id: "Edible", kind: "discrete", auditMode: "presence", cadenceDays: 60, unit: "ct", weighable: false }, - { id: "Vaporizer", kind: "discrete", auditMode: "presence", cadenceDays: 30, unit: "ct", weighable: false } - ]; - - const BINS = [ - { id: "bin-01", name: "Top Drawer", location: "Bedroom", capacity: 14 }, - { id: "bin-02", name: "Apothecary Box", location: "Office shelf", capacity: 10 }, - { id: "bin-03", name: "The Safe", location: "Closet", capacity: 8 }, - { id: "bin-04", name: "Travel Tin", location: "Backpack", capacity: 4 }, - { id: "bin-05", name: "Cold Storage", location: "Fridge — back", capacity: 6 } - ]; - - let n = 1; - const mk = (o) => { - const id = "prd-" + String(n).padStart(4, "0"); - n++; - const sku = o.sku || ("SKU-" + Math.random().toString(36).slice(2, 8).toUpperCase()); - return { - id, - sku, - assetTag: null, - name: "", - brandId: null, - shopId: null, - type: "Flower", - kind: "bulk", // "bulk" or "discrete" - // Bulk fields - weight: 0, // total at purchase (g/ml) - lastAuditWeight: null, // last measured weight - // Discrete fields - countOriginal: 0, // units at purchase - countLastAudit: null, // units confirmed at last audit - unitWeight: 0, // bulk-equivalent grams per unit (for grams stats) - // Pricing - price: 0, - thc: 0, - cbd: 0, - totalCannabinoids: 0, - purchaseDate: TODAY, - binId: "bin-01", - // Lifecycle - status: "active", // "active" | "consumed" | "gone" - consumedDate: null, - goneDate: null, - rating: null, - notes: null, - // Audits — newest last - audits: [], - ...o - }; - }; - - const products = []; - - // ─── BULK: FLOWER ──────────────────────────────────────────────── - products.push(mk({ - name: "Garden Ghost", brandId: "brd-01", shopId: "shp-01", - type: "Flower", kind: "bulk", - weight: 3.5, lastAuditWeight: 2.4, - price: 48, thc: 24.6, cbd: 0.3, totalCannabinoids: 28.4, - purchaseDate: daysAgo(17), binId: "bin-01", - audits: [ - { date: daysAgo(10), mode: "weigh", value: 3.0, prev: 3.5 }, - { date: daysAgo(3), mode: "weigh", value: 2.4, prev: 3.0 } - ] - })); - products.push(mk({ - name: "Honeydew Pine", brandId: "brd-02", shopId: "shp-02", - type: "Flower", kind: "bulk", - weight: 7, lastAuditWeight: 5.6, - price: 85, thc: 21.0, cbd: 0.5, totalCannabinoids: 25.1, - purchaseDate: daysAgo(11), binId: "bin-01", - audits: [ - { date: daysAgo(2), mode: "weigh", value: 5.6, prev: 7.0 } - ] - })); - products.push(mk({ - name: "Late Pear", brandId: "brd-01", shopId: "shp-01", - type: "Flower", kind: "bulk", - weight: 3.5, lastAuditWeight: 3.5, - price: 50, thc: 25.2, cbd: 0.2, totalCannabinoids: 28.9, - purchaseDate: daysAgo(6), binId: "bin-05" - // recently bought, no audit yet - })); - products.push(mk({ - name: "Copper Fennel", brandId: "brd-02", shopId: "shp-02", - type: "Flower", kind: "bulk", - weight: 3.5, lastAuditWeight: 0.5, - price: 42, thc: 20.4, cbd: 0.5, totalCannabinoids: 24.0, - purchaseDate: daysAgo(26), binId: "bin-01", - audits: [ - { date: daysAgo(12), mode: "weigh", value: 1.6, prev: 3.5 }, - { date: daysAgo(2), mode: "weigh", value: 0.5, prev: 1.6 } - ] - })); - // Overdue audit — Flower past 14d cadence - products.push(mk({ - name: "Slate Cherry", brandId: "brd-06", shopId: "shp-04", - type: "Flower", kind: "bulk", - weight: 3.5, lastAuditWeight: 3.5, - price: 46, thc: 22.8, cbd: 0.4, totalCannabinoids: 26.5, - purchaseDate: daysAgo(22), binId: "bin-01" - // No audit since purchase, 22d > 14d cadence → overdue - })); - - // ─── BULK: CONCENTRATE ────────────────────────────────────────── - products.push(mk({ - name: "Indigo Cellar Live Rosin", brandId: "brd-03", shopId: "shp-03", - type: "Concentrate", kind: "bulk", - weight: 1, lastAuditWeight: 0.6, - price: 65, thc: 78.4, cbd: 0.2, totalCannabinoids: 84.0, - purchaseDate: daysAgo(28), binId: "bin-03", - assetTag: "AT-0042", - audits: [ - { date: daysAgo(15), mode: "estimate", value: 0.8, prev: 1.0 }, - { date: daysAgo(2), mode: "estimate", value: 0.6, prev: 0.8 } - ] - })); - products.push(mk({ - name: "Slate Apricot Hash", brandId: "brd-04", shopId: "shp-04", - type: "Concentrate", kind: "bulk", - weight: 2, lastAuditWeight: 1.4, - price: 80, thc: 62.0, cbd: 1.1, totalCannabinoids: 70.5, - purchaseDate: daysAgo(23), binId: "bin-03", - audits: [ - { date: daysAgo(8), mode: "estimate", value: 1.4, prev: 2.0 } - ] - })); - products.push(mk({ - name: "Birchwater Live Resin", brandId: "brd-03", shopId: "shp-04", - type: "Concentrate", kind: "bulk", - weight: 1, lastAuditWeight: 1.0, - price: 70, thc: 76.0, cbd: 0.3, totalCannabinoids: 82.1, - purchaseDate: daysAgo(4), binId: "bin-03" - })); - - // ─── BULK: TINCTURE ───────────────────────────────────────────── - products.push(mk({ - name: "Nightjar Tincture 30ml", brandId: "brd-08", shopId: "shp-02", - type: "Tincture", kind: "bulk", - weight: 30, lastAuditWeight: 22, - price: 60, thc: 0.5, cbd: 18.0, totalCannabinoids: 19.2, - purchaseDate: daysAgo(62), binId: "bin-02", - audits: [ - { date: daysAgo(31), mode: "estimate", value: 26, prev: 30 }, - { date: daysAgo(5), mode: "estimate", value: 22, prev: 26 } - ] - })); - - // ─── DISCRETE: PRE-ROLLS ─────────────────────────────────────── - products.push(mk({ - name: "Mossback Pre-rolls", brandId: "brd-07", shopId: "shp-03", - type: "Pre-roll", kind: "discrete", - countOriginal: 5, countLastAudit: 3, unitWeight: 0.7, - price: 38, thc: 19.8, cbd: 0.4, totalCannabinoids: 23.0, - purchaseDate: daysAgo(9), binId: "bin-04", - audits: [ - { date: daysAgo(2), mode: "presence", value: 3, prev: 5, confirmedBy: "SKU" } - ] - })); - products.push(mk({ - name: "Mossback Pre-rolls", brandId: "brd-07", shopId: "shp-03", - type: "Pre-roll", kind: "discrete", - countOriginal: 5, countLastAudit: 5, unitWeight: 0.7, - price: 38, thc: 19.8, cbd: 0.4, totalCannabinoids: 23.0, - purchaseDate: daysAgo(3), binId: "bin-04" - })); - products.push(mk({ - name: "Quiet Meadow Singles", brandId: "brd-05", shopId: "shp-05", - type: "Pre-roll", kind: "discrete", - countOriginal: 3, countLastAudit: 1, unitWeight: 1.0, - price: 24, thc: 22.0, cbd: 0.3, totalCannabinoids: 25.0, - purchaseDate: daysAgo(34), binId: "bin-04", - audits: [ - { date: daysAgo(20), mode: "presence", value: 2, prev: 3, confirmedBy: "SKU" }, - { date: daysAgo(2), mode: "presence", value: 1, prev: 2, confirmedBy: "SKU" } - ] - })); - - // ─── DISCRETE: EDIBLES ───────────────────────────────────────── - products.push(mk({ - name: "Ember Lily Gummies (10 ct)", brandId: "brd-06", shopId: "shp-01", - type: "Edible", kind: "discrete", - countOriginal: 10, countLastAudit: 6, unitWeight: 0, - price: 22, thc: 5.0, cbd: 1.0, totalCannabinoids: 6.4, - purchaseDate: daysAgo(20), binId: "bin-02", - audits: [ - { date: daysAgo(5), mode: "presence", value: 6, prev: 10, confirmedBy: "SKU" } - ] - })); - products.push(mk({ - name: "Marigold Mints (20 ct)", brandId: "brd-08", shopId: "shp-02", - type: "Edible", kind: "discrete", - countOriginal: 20, countLastAudit: 14, unitWeight: 0, - price: 28, thc: 2.5, cbd: 0, totalCannabinoids: 3.0, - purchaseDate: daysAgo(48), binId: "bin-02", - audits: [ - { date: daysAgo(7), mode: "presence", value: 14, prev: 20, confirmedBy: "SKU" } - ] - })); - - // ─── DISCRETE: VAPORIZER ─────────────────────────────────────── - products.push(mk({ - name: "Quiet Meadow Disposable", brandId: "brd-05", shopId: "shp-05", - type: "Vaporizer", kind: "discrete", - countOriginal: 1, countLastAudit: 1, unitWeight: 1.0, - price: 55, thc: 84.0, cbd: 0.1, totalCannabinoids: 88.2, - purchaseDate: daysAgo(14), binId: "bin-04", - audits: [ - { date: daysAgo(1), mode: "presence", value: 1, prev: 1, confirmedBy: "SKU" } - ] - })); - - // ─── CONSUMED (used up — counts as consumption) ───────────────── - products.push(mk({ - name: "Oolong 19", brandId: "brd-04", shopId: "shp-01", - type: "Flower", kind: "bulk", - weight: 3.5, lastAuditWeight: 0, - price: 46, thc: 23.0, cbd: 0.4, totalCannabinoids: 27.0, - purchaseDate: daysAgo(66), binId: null, - status: "consumed", consumedDate: daysAgo(31), - rating: 4, notes: "Smooth, citrusy. Daytime favorite." - })); - products.push(mk({ - name: "Stonefruit OG", brandId: "brd-07", shopId: "shp-03", - type: "Flower", kind: "bulk", - weight: 7, lastAuditWeight: 0, - price: 78, thc: 22.5, cbd: 0.6, totalCannabinoids: 26.4, - purchaseDate: daysAgo(103), binId: null, - status: "consumed", consumedDate: daysAgo(56), - rating: 5, notes: "Best of the season. Rebuy." - })); - products.push(mk({ - name: "Lavender Coast", brandId: "brd-01", shopId: "shp-05", - type: "Flower", kind: "bulk", - weight: 3.5, lastAuditWeight: 0, - price: 44, thc: 19.0, cbd: 0.8, totalCannabinoids: 22.2, - purchaseDate: daysAgo(80), binId: null, - status: "consumed", consumedDate: daysAgo(47), - rating: 3, notes: "Mellow but underwhelming." - })); - products.push(mk({ - name: "Violet Tea", brandId: "brd-06", shopId: "shp-04", - type: "Flower", kind: "bulk", - weight: 3.5, lastAuditWeight: 0, - price: 50, thc: 24.1, cbd: 0.3, totalCannabinoids: 28.0, - purchaseDate: daysAgo(55), binId: null, - status: "consumed", consumedDate: daysAgo(21), - rating: 4, notes: "Floral nose, nice evenings." - })); - - // ─── GONE (lost / damaged — counts as $ spent, NOT consumption) ─ - products.push(mk({ - name: "Quiet Meadow Singles", brandId: "brd-05", shopId: "shp-05", - type: "Pre-roll", kind: "discrete", - countOriginal: 3, countLastAudit: 0, unitWeight: 1.0, - price: 24, thc: 22.0, cbd: 0.3, totalCannabinoids: 25.0, - purchaseDate: daysAgo(72), binId: null, - status: "gone", goneDate: daysAgo(40), - notes: "Pack went through the wash. Lesson learned.", - audits: [ - { date: daysAgo(40), mode: "presence", value: 0, prev: 3, confirmedBy: "lost" } - ] - })); - products.push(mk({ - name: "Ember Lily Gummies (10 ct)", brandId: "brd-06", shopId: "shp-01", - type: "Edible", kind: "discrete", - countOriginal: 10, countLastAudit: 4, unitWeight: 0, - price: 22, thc: 5.0, cbd: 1.0, totalCannabinoids: 6.4, - purchaseDate: daysAgo(95), binId: null, - status: "gone", goneDate: daysAgo(15), - notes: "Expired. Tossed the rest." - })); - - return { - products, - bins: BINS, - shops: SHOPS, - brands: BRANDS, - types: TYPES, - today: TODAY - }; -})(); - -// Helpers — exported so screens can use consistent logic -window.DATA_HELPERS = { - shopName: (data, id) => data.shops.find(s => s.id === id)?.name || "—", - brandName: (data, id) => data.brands.find(b => b.id === id)?.name || "—", - typeConfig: (data, id) => data.types.find(t => t.id === id) || data.types[0], - - // Days since a given ISO date string - daysSince: (iso, today = "2026-04-25") => { - if (!iso) return Infinity; - return Math.floor((new Date(today) - new Date(iso)) / 86400000); - }, - - // Last audit record (newest) - lastAudit: (p) => p.audits && p.audits.length ? p.audits[p.audits.length - 1] : null, - - // Days since last audit (or since purchase if none) - daysSinceCheck: (p, today = "2026-04-25") => { - const last = p.audits && p.audits.length ? p.audits[p.audits.length - 1].date : p.purchaseDate; - return Math.floor((new Date(today) - new Date(last)) / 86400000); - }, - - // Is audit overdue based on per-type cadence - auditOverdue: (data, p, today = "2026-04-25") => { - if (p.status !== "active") return false; - const cfg = data.types.find(t => t.id === p.type); - if (!cfg) return false; - return window.DATA_HELPERS.daysSinceCheck(p, today) >= cfg.cadenceDays; - }, - - // Estimated remaining (bulk: decay from last audit; discrete: countLastAudit) - estimatedRemaining: (p, today = "2026-04-25") => { - if (p.status !== "active") return 0; - if (p.kind === "discrete") { - return p.countLastAudit != null ? p.countLastAudit : p.countOriginal; - } - // Bulk: linear decay between last audit and average lifespan estimate - const last = window.DATA_HELPERS.lastAudit(p); - const baseDate = last ? last.date : p.purchaseDate; - const baseValue = last ? last.value : p.weight; - const daysSinceBase = Math.max(0, Math.floor((new Date(today) - new Date(baseDate)) / 86400000)); - // Decay rate: assume original weight is consumed over (purchase → expected lifespan ~ 35d for flower, 40 concentrate, 90 tincture) - const expectedLifespan = p.type === "Flower" ? 35 : p.type === "Concentrate" ? 40 : 90; - const dailyBurn = p.weight / expectedLifespan; - const est = Math.max(0, baseValue - dailyBurn * daysSinceBase); - return est; - }, - - // Original-percent remaining (for low-stock on bulk) - pctRemaining: (p, today = "2026-04-25") => { - if (p.kind === "discrete") { - const cur = p.countLastAudit != null ? p.countLastAudit : p.countOriginal; - return p.countOriginal > 0 ? cur / p.countOriginal : 0; - } - const est = window.DATA_HELPERS.estimatedRemaining(p, today); - return p.weight > 0 ? est / p.weight : 0; - } -}; diff --git a/weed-tracker/project/deck-stage.js b/weed-tracker/project/deck-stage.js deleted file mode 100644 index 354a820..0000000 --- a/weed-tracker/project/deck-stage.js +++ /dev/null @@ -1,619 +0,0 @@ -/** - * — reusable web component for HTML decks. - * - * Handles: - * (a) speaker notes — reads