1use 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#[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#[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#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct Testimonial {
51 pub author: String,
52 pub company: Option<String>,
53 pub text: String,
54}
55
56#[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#[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, pub difficulty: String, 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#[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
101pub 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 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
158pub async fn get_showcase_project(
160 Path(project_id): Path<String>,
161) -> Json<ApiResponse<ShowcaseProject>> {
162 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
197pub 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
218pub 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 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
255pub 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 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
298pub async fn get_learning_resource(
300 Path(resource_id): Path<String>,
301) -> Json<ApiResponse<LearningResource>> {
302 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
330pub 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
347pub async fn submit_showcase_project(
349 State(_state): State<AdminState>,
350 Json(payload): Json<serde_json::Value>,
351) -> Result<Json<ApiResponse<String>>, StatusCode> {
352 Ok(Json(ApiResponse {
362 success: true,
363 data: Some("Project submitted successfully".to_string()),
364 error: None,
365 timestamp: Utc::now(),
366 }))
367}