diff --git a/PRD.md b/PRD.md index d5941cf..e034d10 100644 --- a/PRD.md +++ b/PRD.md @@ -404,8 +404,8 @@ CREATE TABLE interactions ( total_amount INTEGER, -- Lead capture - lead_type VARCHAR(20) - CHECK (lead_type IN ('line', 'email', 'none')), + lead_type VARCHAR(20) + CHECK (lead_type IN ('line', 'email', 'instagram', 'none')), -- Objection (only if sale_type = 'none') objection VARCHAR(50) diff --git a/api/main.py b/api/main.py index 2e2de28..cd8c535 100644 --- a/api/main.py +++ b/api/main.py @@ -26,7 +26,7 @@ VALID_PERSONAS = {"parent", "gift_buyer", "expat", "future_parent"} VALID_HOOKS = {"physical_kits", "big_garden", "signage"} VALID_SALE_TYPES = {"none", "single", "bundle_3", "full_year"} -VALID_LEAD_TYPES = {"line", "email"} +VALID_LEAD_TYPES = {"line", "email", "instagram"} VALID_OBJECTIONS = { "too_expensive", "not_interested", "no_time", "already_have", "need_to_think", "language_barrier", "other" @@ -387,6 +387,10 @@ async def get_stats(period: str = "today"): "SELECT COUNT(*) FROM interactions WHERE timestamp >= $1 AND lead_type = 'email' AND deleted_at IS NULL", start_date ) + instagram_leads = await conn.fetchval( + "SELECT COUNT(*) FROM interactions WHERE timestamp >= $1 AND lead_type = 'instagram' AND deleted_at IS NULL", + start_date + ) return { "period": period, @@ -409,7 +413,8 @@ async def get_stats(period: str = "today"): "objections": {row["objection"]: row["count"] for row in objections}, "leads": { "line": line_leads or 0, - "email": email_leads or 0 + "email": email_leads or 0, + "instagram": instagram_leads or 0 } } diff --git a/migrations/006_add_instagram_lead_type.sql b/migrations/006_add_instagram_lead_type.sql new file mode 100644 index 0000000..bd319d7 --- /dev/null +++ b/migrations/006_add_instagram_lead_type.sql @@ -0,0 +1,9 @@ +-- Migration: Add Instagram to lead_type options +-- Description: Adds 'instagram' as a valid option for lead_type field + +-- Drop the existing constraint if it exists +ALTER TABLE interactions DROP CONSTRAINT IF EXISTS interactions_lead_type_check; + +-- Add the new constraint with instagram included +ALTER TABLE interactions ADD CONSTRAINT interactions_lead_type_check + CHECK (lead_type IN ('line', 'email', 'instagram', 'none') OR lead_type IS NULL); diff --git a/web/src/App.jsx b/web/src/App.jsx index 0faf27d..1773a47 100644 --- a/web/src/App.jsx +++ b/web/src/App.jsx @@ -1016,6 +1016,10 @@ function StatsScreen({ stats, statsPeriod, onPeriodChange, sellerStats, sankeyDa 📧 Email {data?.leads?.email || 0} +
+ 📸 Instagram + {data?.leads?.instagram || 0} +
{/* Phase 2 Navigation Buttons */} @@ -1152,7 +1156,7 @@ function InteractionCard({ item, onClick }) { const personaLabel = item.persona ? item.persona.replace('_', ' ') : '' const hookIcon = item.hook === 'physical_kits' ? '📦' : item.hook === 'big_garden' ? '📱' : '🪧' - const leadIcon = item.lead_type === 'line' ? '💚' : item.lead_type === 'email' ? '📧' : '' + const leadIcon = item.lead_type === 'line' ? '💚' : item.lead_type === 'email' ? '📧' : item.lead_type === 'instagram' ? '📸' : '' return (
@@ -1233,7 +1237,7 @@ function DetailScreen({ interaction, onBack, onAddNote, onDelete, onEdit }) { )}
Lead - {i.lead_type === 'line' ? '💚 LINE' : i.lead_type === 'email' ? '📧 Email' : 'None'} + {i.lead_type === 'line' ? '💚 LINE' : i.lead_type === 'email' ? '📧 Email' : i.lead_type === 'instagram' ? '📸 Instagram' : 'None'}
{i.objection && (
@@ -2014,6 +2018,10 @@ function FlowLead({ onSelect, onBack }) { 📧 Email +