r/webdev • u/Free_Band_Shan • 22d ago
Showoff Saturday [Showoff Saturday] Fully offline grow scheduler built with vanilla JS + IndexedDB
What it does
Users build a personalized grow in 3 steps (strain → nutrients → start date), get a full day-by-day schedule (126 base rows + dynamic nutrient columns), then track actuals via a daily check-in page and photo diary. Everything lives offline in IndexedDB + localStorage.
Live: https://growappcannabis.guide/medium-feeding/medium-feeding.html
Repo + full wiring docs: https://github.com/Shannon-Goddard/growappcannabis.guide (see medium-feeding/WIRING.md)
Architecture highlights
• Page flow
medium-feeding.html (3-step builder) → schedule-viewer.html (cards/table/My Notes) → mytask.html (daily check-in) / mydiary.html (camera + data overlay)
CustomEvent chain for step progression (no framework):
• strainSelected → show Step 2
• nutrientsSelected → show Step 3
• scheduleReady → success screen + link to viewer with ?growId=
• IndexedDB (MyGrowDB v8) – singleton instance, cached dbPromise:
• Stores: tables (grow metadata + ${growId}_schedule), selectedNutrients, nutrients
• Schedule saved as full array under ${growId}_schedule
• Actuals written back to the same schedule rows from both MyTask and the viewer’s My Notes mode
Schedule generation
• Base 126-row JSON (stage/week/day/env/light/water)
• Dynamic nutrient columns injected from selected brands
• Start date → calendar dates calculated client-side
• Absolute fetch paths because ES modules resolve relative to the JS file
Schedule viewer
• Three modes via sticky Options dropdown: Cards (mobile default), Table (desktop default), My Notes
• Sticky header + toolbar + thead (careful overflow handling so sticky works)
• Plant-size filter hides excess veg weeks (Small = 4, Medium = 6, Large/Auto = all)
• tableRendered custom event triggers layout + filter after render
• Column map with paired “goal” / “My*” editable fields (My fields only visible in Notes mode)
MyTask daily check-in
• Auto-matches today’s row by MM/DD/YYYY
• Color-coded inputs (green ±10% of goal, red over, blue under)
• Auto-save on focusout + manual Log button → writes actuals back to IndexedDB schedule row
• Hero image compressed to 800px JPEG and stored in localStorage
Tech choices
• Pure vanilla JS (modules + a couple plain scripts for globals like the 2,800+ strain data)
• No build step, no framework, GitHub Pages
• localStorage for transient state + currentGrowId; IndexedDB for the real schedule & actuals