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