1use 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#[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#[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#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct Testimonial {
52 pub author: String,
53 pub company: Option<String>,
54 pub text: String,
55}
56
57#[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#[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, pub difficulty: String, 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#[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
102pub 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 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
155pub async fn get_showcase_project(
157 Path(project_id): Path<String>,
158) -> Json<ApiResponse<ShowcaseProject>> {
159 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
193pub 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
214pub 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 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
250pub 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 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
292pub async fn get_learning_resource(
294 Path(resource_id): Path<String>,
295) -> Json<ApiResponse<LearningResource>> {
296 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
323pub 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
340pub async fn submit_showcase_project(
342 State(_state): State<AdminState>,
343 Json(payload): Json<serde_json::Value>,
344) -> Result<Json<ApiResponse<String>>, StatusCode> {
345 Ok(Json(ApiResponse {
349 success: true,
350 data: Some("Project submitted successfully".to_string()),
351 error: None,
352 timestamp: Utc::now(),
353 }))
354}