123
124export function getAvatarUrl(name: string): string {
125 return `https://ui-avatars.com/api/?name=${encodeURIComponent(name)}&background=6366f1&color=fff&size=40`;
126}
127
131}
132
133export function capitalizeFirst(text: string): string {
134 return text.charAt(0).toUpperCase() + text.slice(1);
135}
1/** @jsxImportSource https://esm.sh/react@18.2.0 */
2import React, { useState } from "https://esm.sh/react@18.2.0";
3import type { Course, User, ApiResponse } from "../../shared/types.ts";
4
5interface CourseListProps {
23 setEnrolling(courseId);
24 try {
25 const response = await fetch(`/api/courses/${courseId}/enroll`, {
26 method: "POST"
27 });
28
29 const data: ApiResponse = await response.json();
30
31 if (data.success) {
128}
129
130export interface ApiResponse<T = any> {
131 success: boolean;
132 data?: T;
53
54## Getting Started
551. The backend serves the frontend and API endpoints
562. SQLite database is automatically initialized
573. Sample data is seeded on first run
584. Access the site at the HTTP endpoint URL
59
60## API Endpoints
61- `GET /` - Homepage
62- `GET /api/products` - Product listing with filters
63- `GET /api/products/:id` - Product details
64- `POST /api/auth/login` - User authentication
65- `POST /api/cart` - Cart operations
66- `POST /api/orders` - Order placement
67- `GET /admin/*` - Admin dashboard (protected)
1/** @jsxImportSource https://esm.sh/react@18.2.0 */
2import React, { useState, useEffect } from "https://esm.sh/react@18.2.0";
3import type { User, Course, Exam, ApiResponse } from "../../shared/types.ts";
4import CourseList from "./CourseList.tsx";
5import CourseDetail from "./CourseDetail.tsx";
32 const loadCourses = async () => {
33 try {
34 const response = await fetch("/api/courses");
35 const data: ApiResponse<Course[]> = await response.json();
36
37 if (data.success && data.data) {
72}
73
74// API Response types
75export interface ApiResponse<T> {
76 success: boolean;
77 data?: T;
41## Tech Stack
42
43- **Backend**: Hono (TypeScript API framework)
44- **Frontend**: React with TypeScript
45- **Database**: SQLite
1/** @jsxImportSource https://esm.sh/react@18.2.0 */
2import React, { useState } from "https://esm.sh/react@18.2.0";
3import type { User, ApiResponse, LoginRequest, RegisterRequest } from "../../shared/types.ts";
4
5interface LoginProps {
25
26 try {
27 const endpoint = isLogin ? "/api/auth/login" : "/api/auth/register";
28 const payload = isLogin
29 ? { email: formData.email, password: formData.password } as LoginRequest
36 });
37
38 const data: ApiResponse<User> = await response.json();
39
40 if (data.success && data.data) {
1/** @jsxImportSource https://esm.sh/react@18.2.0 */
2import React, { useState, useEffect } from "https://esm.sh/react@18.2.0";
3import type { User, ApiResponse } from "../../shared/types.ts";
4import Dashboard from "./Dashboard.tsx";
5import Login from "./Login.tsx";
16 const checkAuth = async () => {
17 try {
18 const response = await fetch("/api/auth/me");
19 const data: ApiResponse<User> = await response.json();
20
21 if (data.success && data.data) {
36 const handleLogout = async () => {
37 try {
38 await fetch("/api/auth/logout", { method: "POST" });
39 setUser(null);
40 } catch (err) {
17await runMigrations();
18
19// API routes
20app.route("/api/auth", auth);
21app.route("/api/courses", courses);
22app.route("/api", exams);
23app.route("/api", submissions);
24
25// Static file serving and frontend routes
27
28// Health check endpoint
29app.get("/api/health", (c) => {
30 return c.json({
31 status: "healthy",
32 timestamp: new Date().toISOString(),
33 service: "LMS API"
34 });
35});
36
37// 404 handler for API routes
38app.notFound((c) => {
39 if (c.req.path.startsWith("/api/")) {
40 return c.json({ success: false, error: "Endpoint not found" }, 404);
41 }
42 // For non-API routes, serve the main app (SPA routing)
43 return staticRoutes.fetch(new Request(new URL("/", c.req.url)));
44});