module Web.Controller.MarketplaceDashboard where import Web.Types import Web.View.MarketplaceDashboard.Show import Generated.Types import IHP.Prelude import IHP.ControllerPrelude import Database.PostgreSQL.Simple (Query) instance Controller MarketplaceDashboardController where beforeAction = ensureIsUser action MarketplaceDashboardAction = autoRefresh do let mSearch = paramOrNothing @Text "q" let mWType = paramOrNothing @Text "widgetType" let sortBy = paramOrDefault @Text "adopted" "sort" -- Widget patterns: full-text search + filter patterns <- sqlQuery (patternQuery mSearch mWType sortBy) () -- Governance templates: full-text search templates <- sqlQuery (templateQuery mSearch) () -- Trending patterns (most adoptions in last 30 days) trending <- sqlQuery "SELECT wp.id, wp.name, wp.widget_type, COUNT(pa.id) AS recent_adoptions \ \ FROM widget_patterns wp \ \ JOIN pattern_adoptions pa ON pa.widget_pattern_id = wp.id \ \ WHERE wp.is_published = TRUE \ \ AND pa.adopted_at >= NOW() - INTERVAL '30 days' \ \ GROUP BY wp.id, wp.name, wp.widget_type \ \ ORDER BY recent_adoptions DESC \ \ LIMIT 5" () widgetTypeOptions <- sqlQuery "SELECT name, label FROM widget_type_registry WHERE status = 'active' ORDER BY label" () render ShowView { patterns, templates, trending , widgetTypeOptions , searchQuery = mSearch , selectedType = mWType , sortOrder = sortBy } -- | Widget pattern list query with optional search and type filter. patternQuery :: Maybe Text -> Maybe Text -> Text -> Query patternQuery mSearch mWType sortBy = let baseWhere = "wp.is_published = TRUE" searchClause = case mSearch of Nothing -> "" Just _ -> " AND to_tsvector('english', wp.name || ' ' || COALESCE(wp.description,'')) \ \ @@ plainto_tsquery(?)" typeClause = case mWType of Nothing -> "" Just _ -> " AND wp.widget_type = ?" orderClause = case sortBy of "recent" -> "wp.created_at DESC" "alpha" -> "wp.name ASC" _ -> "adopter_count DESC" in "SELECT wp.*, COUNT(pa.id) AS adopter_count \ \ FROM widget_patterns wp \ \ LEFT JOIN pattern_adoptions pa ON pa.widget_pattern_id = wp.id \ \ WHERE " <> baseWhere <> searchClause <> typeClause <> " GROUP BY wp.id \ \ ORDER BY " <> orderClause <> " LIMIT 50" -- | Governance template list query with optional search. templateQuery :: Maybe Text -> Query templateQuery mSearch = let searchClause = case mSearch of Nothing -> "" Just _ -> " AND to_tsvector('english', gt.name || ' ' || COALESCE(gt.description,'')) \ \ @@ plainto_tsquery(?)" in "SELECT gt.*, COUNT(gtc.id) AS clone_count \ \ FROM governance_templates gt \ \ LEFT JOIN governance_template_clones gtc ON gtc.governance_template_id = gt.id \ \ WHERE gt.is_published = TRUE" <> searchClause <> " GROUP BY gt.id \ \ ORDER BY clone_count DESC \ \ LIMIT 50"