mockforge_ui/handlers/
community.rs

1//! Community portal handlers
2//!
3//! Provides endpoints for showcase gallery, learning resources, and community features
4
5use axum::{
6    extract::{Path, Query, State},
7    http::StatusCode,
8    response::Json,
9};
10use chrono::Utc;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14use crate::handlers::AdminState;
15use crate::models::ApiResponse;
16
17/// Showcase project entry
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ShowcaseProject {
20    pub id: String,
21    pub title: String,
22    pub author: String,
23    pub author_avatar: Option<String>,
24    pub description: String,
25    pub category: String,
26    pub tags: Vec<String>,
27    pub featured: bool,
28    pub screenshot: Option<String>,
29    pub demo_url: Option<String>,
30    pub source_url: Option<String>,
31    pub template_id: Option<String>,
32    pub scenario_id: Option<String>,
33    pub stats: ShowcaseStats,
34    pub testimonials: Vec<Testimonial>,
35    pub created_at: chrono::DateTime<Utc>,
36    pub updated_at: chrono::DateTime<Utc>,
37}
38
39/// Showcase statistics
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct ShowcaseStats {
42    pub downloads: u64,
43    pub stars: u64,
44    pub forks: u64,
45    pub rating: f64,
46}
47
48/// Testimonial
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct Testimonial {
51    pub author: String,
52    pub company: Option<String>,
53    pub text: String,
54}
55
56/// Success story
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct SuccessStory {
59    pub id: String,
60    pub title: String,
61    pub company: String,
62    pub industry: String,
63    pub author: String,
64    pub role: String,
65    pub date: chrono::DateTime<Utc>,
66    pub challenge: String,
67    pub solution: String,
68    pub results: Vec<String>,
69    pub featured: bool,
70}
71
72/// Learning resource
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct LearningResource {
75    pub id: String,
76    pub title: String,
77    pub description: String,
78    pub category: String,
79    pub resource_type: String, // tutorial, example, video, guide
80    pub difficulty: String,    // beginner, intermediate, advanced
81    pub tags: Vec<String>,
82    pub content_url: Option<String>,
83    pub video_url: Option<String>,
84    pub code_examples: Vec<CodeExample>,
85    pub author: String,
86    pub views: u64,
87    pub rating: f64,
88    pub created_at: chrono::DateTime<Utc>,
89    pub updated_at: chrono::DateTime<Utc>,
90}
91
92/// Code example
93#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct CodeExample {
95    pub title: String,
96    pub language: String,
97    pub code: String,
98    pub description: Option<String>,
99}
100
101/// Get showcase projects
102pub async fn get_showcase_projects(
103    Query(params): Query<HashMap<String, String>>,
104) -> Json<ApiResponse<Vec<ShowcaseProject>>> {
105    let category = params.get("category");
106    let featured = params.get("featured").map(|s| s == "true");
107    let limit = params.get("limit").and_then(|s| s.parse::<usize>().ok()).unwrap_or(20);
108    let offset = params.get("offset").and_then(|s| s.parse::<usize>().ok()).unwrap_or(0);
109
110    // Note: Community showcase requires database or content storage integration
111    // To enable database persistence:
112    // 1. Create showcase_projects table in database
113    // 2. Add database accessor to handler state
114    // 3. Query projects with filters (category, featured, pagination)
115    // For now, returns mock data for UI development
116    let projects = vec![ShowcaseProject {
117        id: "ecommerce-platform".to_string(),
118        title: "E-commerce Platform Mock".to_string(),
119        author: "community-user".to_string(),
120        author_avatar: None,
121        description: "Complete e-commerce API mock with shopping carts, orders, and payments"
122            .to_string(),
123        category: "ecommerce".to_string(),
124        tags: vec![
125            "ecommerce".to_string(),
126            "shopping-cart".to_string(),
127            "payments".to_string(),
128        ],
129        featured: true,
130        screenshot: Some("https://example.com/screenshot.png".to_string()),
131        demo_url: Some("https://demo.mockforge.dev/ecommerce".to_string()),
132        source_url: Some("https://github.com/user/ecommerce-mock".to_string()),
133        template_id: Some("ecommerce-store@1.0.0".to_string()),
134        scenario_id: Some("ecommerce-scenario@1.0.0".to_string()),
135        stats: ShowcaseStats {
136            downloads: 1250,
137            stars: 45,
138            forks: 12,
139            rating: 4.8,
140        },
141        testimonials: vec![Testimonial {
142            author: "John Doe".to_string(),
143            company: Some("Acme Corp".to_string()),
144            text: "This template saved us weeks of development time!".to_string(),
145        }],
146        created_at: Utc::now(),
147        updated_at: Utc::now(),
148    }];
149
150    Json(ApiResponse {
151        success: true,
152        data: Some(projects),
153        error: None,
154        timestamp: Utc::now(),
155    })
156}
157
158/// Get showcase project by ID
159pub async fn get_showcase_project(
160    Path(project_id): Path<String>,
161) -> Json<ApiResponse<ShowcaseProject>> {
162    // Note: Requires database integration (see list_showcase_projects comment)
163    // For now, returns mock data
164    let project = ShowcaseProject {
165        id: project_id.clone(),
166        title: "E-commerce Platform Mock".to_string(),
167        author: "community-user".to_string(),
168        author_avatar: None,
169        description: "Complete e-commerce API mock".to_string(),
170        category: "ecommerce".to_string(),
171        tags: vec!["ecommerce".to_string()],
172        featured: true,
173        screenshot: None,
174        demo_url: None,
175        source_url: None,
176        template_id: None,
177        scenario_id: None,
178        stats: ShowcaseStats {
179            downloads: 0,
180            stars: 0,
181            forks: 0,
182            rating: 0.0,
183        },
184        testimonials: vec![],
185        created_at: Utc::now(),
186        updated_at: Utc::now(),
187    };
188
189    Json(ApiResponse {
190        success: true,
191        data: Some(project),
192        error: None,
193        timestamp: Utc::now(),
194    })
195}
196
197/// Get showcase categories
198pub async fn get_showcase_categories() -> Json<ApiResponse<Vec<String>>> {
199    let categories = vec![
200        "ecommerce".to_string(),
201        "finance".to_string(),
202        "healthcare".to_string(),
203        "social".to_string(),
204        "iot".to_string(),
205        "gaming".to_string(),
206        "education".to_string(),
207        "other".to_string(),
208    ];
209
210    Json(ApiResponse {
211        success: true,
212        data: Some(categories),
213        error: None,
214        timestamp: Utc::now(),
215    })
216}
217
218/// Get success stories
219pub async fn get_success_stories(
220    Query(params): Query<HashMap<String, String>>,
221) -> Json<ApiResponse<Vec<SuccessStory>>> {
222    let featured = params.get("featured").map(|s| s == "true");
223    let limit = params.get("limit").and_then(|s| s.parse::<usize>().ok()).unwrap_or(10);
224
225    // Note: Requires database integration (see list_showcase_projects comment)
226    // For now, returns mock data
227    let stories = vec![
228        SuccessStory {
229            id: "acme-corp".to_string(),
230            title: "Acme Corp: Accelerating API Development".to_string(),
231            company: "Acme Corporation".to_string(),
232            industry: "E-commerce".to_string(),
233            author: "Jane Smith".to_string(),
234            role: "Lead API Developer".to_string(),
235            date: Utc::now(),
236            challenge: "Acme Corp needed to develop a new payment API but couldn't wait for backend services to be ready.".to_string(),
237            solution: "Used MockForge to create realistic payment mocks with various scenarios (success, failure, retry).".to_string(),
238            results: vec![
239                "Reduced development time by 60%".to_string(),
240                "Enabled parallel frontend/backend development".to_string(),
241                "Improved test coverage with edge cases".to_string(),
242            ],
243            featured: true,
244        },
245    ];
246
247    Json(ApiResponse {
248        success: true,
249        data: Some(stories),
250        error: None,
251        timestamp: Utc::now(),
252    })
253}
254
255/// Get learning resources
256pub async fn get_learning_resources(
257    Query(params): Query<HashMap<String, String>>,
258) -> Json<ApiResponse<Vec<LearningResource>>> {
259    let category = params.get("category");
260    let resource_type = params.get("type");
261    let difficulty = params.get("difficulty");
262    let limit = params.get("limit").and_then(|s| s.parse::<usize>().ok()).unwrap_or(20);
263
264    // Note: Requires database integration (see list_showcase_projects comment)
265    // For now, returns mock data or content storage
266    let resources = vec![LearningResource {
267        id: "getting-started".to_string(),
268        title: "Getting Started with MockForge".to_string(),
269        description: "Learn how to create your first mock API in minutes".to_string(),
270        category: "tutorial".to_string(),
271        resource_type: "tutorial".to_string(),
272        difficulty: "beginner".to_string(),
273        tags: vec!["getting-started".to_string(), "tutorial".to_string()],
274        content_url: Some("/docs/getting-started".to_string()),
275        video_url: None,
276        code_examples: vec![CodeExample {
277            title: "Basic REST API".to_string(),
278            language: "yaml".to_string(),
279            code: "http:\n  port: 3000\n  routes:\n    - path: /users\n      method: GET"
280                .to_string(),
281            description: Some("Simple REST endpoint".to_string()),
282        }],
283        author: "MockForge Team".to_string(),
284        views: 1500,
285        rating: 4.9,
286        created_at: Utc::now(),
287        updated_at: Utc::now(),
288    }];
289
290    Json(ApiResponse {
291        success: true,
292        data: Some(resources),
293        error: None,
294        timestamp: Utc::now(),
295    })
296}
297
298/// Get learning resource by ID
299pub async fn get_learning_resource(
300    Path(resource_id): Path<String>,
301) -> Json<ApiResponse<LearningResource>> {
302    // Note: Requires database integration (see list_showcase_projects comment)
303    // For now, returns mock data
304    let resource = LearningResource {
305        id: resource_id.clone(),
306        title: "Getting Started with MockForge".to_string(),
307        description: "Learn how to create your first mock API".to_string(),
308        category: "tutorial".to_string(),
309        resource_type: "tutorial".to_string(),
310        difficulty: "beginner".to_string(),
311        tags: vec!["getting-started".to_string()],
312        content_url: None,
313        video_url: None,
314        code_examples: vec![],
315        author: "MockForge Team".to_string(),
316        views: 0,
317        rating: 0.0,
318        created_at: Utc::now(),
319        updated_at: Utc::now(),
320    };
321
322    Json(ApiResponse {
323        success: true,
324        data: Some(resource),
325        error: None,
326        timestamp: Utc::now(),
327    })
328}
329
330/// Get learning resource categories
331pub async fn get_learning_categories() -> Json<ApiResponse<Vec<String>>> {
332    let categories = vec![
333        "getting-started".to_string(),
334        "advanced-features".to_string(),
335        "integration".to_string(),
336        "best-practices".to_string(),
337    ];
338
339    Json(ApiResponse {
340        success: true,
341        data: Some(categories),
342        error: None,
343        timestamp: Utc::now(),
344    })
345}
346
347/// Submit a project for showcase
348pub async fn submit_showcase_project(
349    State(_state): State<AdminState>,
350    Json(payload): Json<serde_json::Value>,
351) -> Result<Json<ApiResponse<String>>, StatusCode> {
352    // Note: Project submission requires database integration
353    // To enable:
354    // 1. Validate project data (title, description, files, etc.)
355    // 2. Store project metadata in database
356    // 3. Store project files in object storage (S3, etc.)
357    // 4. Return created project with ID
358    // For now, returns success response with mock project
359    // Requires authentication and validation
360
361    Ok(Json(ApiResponse {
362        success: true,
363        data: Some("Project submitted successfully".to_string()),
364        error: None,
365        timestamp: Utc::now(),
366    }))
367}