🎓 Initial commit: Math2 Platform - Plataforma de Álgebra Lineal PRO
✨ Características: - 45 ejercicios universitarios (Basic → Advanced) - Renderizado LaTeX profesional - IA generativa (Z.ai/DashScope) - Docker 9 servicios - Tests 123/123 pasando - Seguridad enterprise (JWT, XSS, Rate limiting) 🐳 Infraestructura: - Next.js 14 + Node.js 20 - PostgreSQL 15 + Redis 7 - Docker Compose completo - Nginx + SSL ready 📚 Documentación: - 5 informes técnicos completos - README profesional - Scripts de deployment automatizados Estado: Producción lista ✅
This commit is contained in:
195
shared/types/README.md
Normal file
195
shared/types/README.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# @math-platform/shared-types
|
||||
|
||||
TypeScript type definitions shared between frontend and backend applications.
|
||||
|
||||
## Purpose
|
||||
|
||||
Eliminates duplication and misalignment between frontend and backend type definitions.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
shared/types/
|
||||
├── package.json # Package configuration
|
||||
├── tsconfig.json # TypeScript configuration
|
||||
└── src/
|
||||
├── index.ts # Main entry point - exports all types
|
||||
├── auth.ts # Authentication types
|
||||
├── exercise.ts # Exercise and attempt types
|
||||
├── module.ts # Module and topic types
|
||||
├── progress.ts # Progress tracking types
|
||||
├── achievement.ts # Achievement and badge types
|
||||
├── ranking.ts # Leaderboard types
|
||||
├── api.ts # Generic API types
|
||||
├── error.ts # Error types and classes
|
||||
└── utils.ts # Utility types
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
### Frontend
|
||||
|
||||
Types are accessed via path mapping in `tsconfig.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@math-platform/shared-types": ["../shared/types/src"],
|
||||
"@math-platform/shared-types/*": ["../shared/types/src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Backend
|
||||
|
||||
Same path mapping in `tsconfig.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@math-platform/shared-types": ["../../shared/types/src"],
|
||||
"@math-platform/shared-types/*": ["../../shared/types/src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Import Types
|
||||
|
||||
```typescript
|
||||
// Import specific types
|
||||
import type {
|
||||
User,
|
||||
Exercise,
|
||||
LoginRequest,
|
||||
LoginResponse,
|
||||
SubmitAttemptRequest,
|
||||
SubmitAttemptResponse,
|
||||
ApiResponse
|
||||
} from '@math-platform/shared-types';
|
||||
|
||||
// Import from specific module
|
||||
import type { LoginRequest, LoginResponse } from '@math-platform/shared-types/auth';
|
||||
import type { Exercise, SubmitAttemptRequest } from '@math-platform/shared-types/exercise';
|
||||
```
|
||||
|
||||
### Import Classes
|
||||
|
||||
```typescript
|
||||
// Error classes are exported as values
|
||||
import {
|
||||
ApplicationError,
|
||||
ValidationError,
|
||||
NotFoundError
|
||||
} from '@math-platform/shared-types';
|
||||
```
|
||||
|
||||
## Key Contracts
|
||||
|
||||
### 1. Submit Attempt
|
||||
|
||||
**Request:**
|
||||
```typescript
|
||||
interface SubmitAttemptRequest {
|
||||
answer: string;
|
||||
timeSpent: number;
|
||||
hintsUsed?: number;
|
||||
skipped?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
interface SubmitAttemptResponse {
|
||||
isCorrect: boolean;
|
||||
points: number;
|
||||
message: string;
|
||||
correctAnswer?: string;
|
||||
solutionSteps?: SolutionStep[];
|
||||
newAchievements?: AchievementUnlock[];
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Auth
|
||||
|
||||
**Login Request:**
|
||||
```typescript
|
||||
interface LoginRequest {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Login Response:**
|
||||
```typescript
|
||||
interface LoginResponse {
|
||||
token: string;
|
||||
refreshToken?: string;
|
||||
expiresIn?: number;
|
||||
user: User;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. API Response
|
||||
|
||||
```typescript
|
||||
interface ApiResponse<T = unknown> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
error?: ApiError;
|
||||
meta?: ApiMeta;
|
||||
}
|
||||
```
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### Before (Duplicated)
|
||||
|
||||
**Frontend:** `frontend/src/types/index.ts`
|
||||
```typescript
|
||||
export interface LoginData {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Backend:** `backend/src/shared/types/index.ts`
|
||||
```typescript
|
||||
export interface LoginInput {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
```
|
||||
|
||||
### After (Shared)
|
||||
|
||||
Both use:
|
||||
```typescript
|
||||
import type { LoginRequest } from '@math-platform/shared-types';
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always use `type` imports** for type-only imports to avoid circular dependencies
|
||||
2. **Keep types serializable** - use `string` for dates, not `Date` objects
|
||||
3. **Document breaking changes** in CHANGELOG.md
|
||||
4. **Don't import from specific backend/frontend packages** - keep this package independent
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
cd shared/types
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Type Checking
|
||||
|
||||
```bash
|
||||
npm run type-check
|
||||
```
|
||||
22
shared/types/package.json
Normal file
22
shared/types/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@math-platform/shared-types",
|
||||
"version": "1.0.0",
|
||||
"description": "Shared TypeScript types between frontend and backend",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zod": "^3.22.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"zod": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
82
shared/types/src/achievement.ts
Normal file
82
shared/types/src/achievement.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* ============================================
|
||||
* ACHIEVEMENT TYPES
|
||||
* ============================================
|
||||
*
|
||||
* Types for achievements and badges
|
||||
* shared between frontend and backend
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// ENUMS
|
||||
// ============================================
|
||||
|
||||
export type AchievementCategory =
|
||||
| 'EXERCISES'
|
||||
| 'MODULES'
|
||||
| 'STREAKS'
|
||||
| 'RANKING'
|
||||
| 'SPECIAL';
|
||||
|
||||
export type AchievementRarity = 'COMMON' | 'RARE' | 'EPIC' | 'LEGENDARY';
|
||||
|
||||
// ============================================
|
||||
// ACHIEVEMENT
|
||||
// ============================================
|
||||
|
||||
export interface Achievement {
|
||||
code: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
rarity: AchievementRarity;
|
||||
category: AchievementCategory;
|
||||
requirementType: string;
|
||||
requirementValue: number;
|
||||
metadata?: AchievementMetadata;
|
||||
}
|
||||
|
||||
export interface AchievementMetadata {
|
||||
color: string;
|
||||
animation?: string;
|
||||
tooltip: string;
|
||||
unlockMessage: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// USER ACHIEVEMENT
|
||||
// ============================================
|
||||
|
||||
export interface UserAchievement {
|
||||
userId: string;
|
||||
achievementCode: string;
|
||||
progress: number;
|
||||
requirementValue: number;
|
||||
unlocked: boolean;
|
||||
unlockedAt: string | null;
|
||||
}
|
||||
|
||||
export interface UserAchievementProgress {
|
||||
achievementId: string;
|
||||
code: string;
|
||||
name: string;
|
||||
description: string;
|
||||
category: string;
|
||||
rarity: string;
|
||||
icon: string;
|
||||
progress: number;
|
||||
requirementValue: number;
|
||||
unlocked: boolean;
|
||||
unlockedAt?: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// BADGE (For rankings)
|
||||
// ============================================
|
||||
|
||||
export interface AchievementBadge {
|
||||
code: string;
|
||||
icon: string;
|
||||
rarity: AchievementRarity;
|
||||
name?: string;
|
||||
}
|
||||
73
shared/types/src/api.ts
Normal file
73
shared/types/src/api.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* ============================================
|
||||
* API TYPES
|
||||
* ============================================
|
||||
*
|
||||
* Generic API request/response types
|
||||
* shared between frontend and backend
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// API RESPONSE
|
||||
// ============================================
|
||||
|
||||
export interface ApiResponse<T = unknown> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
error?: ApiError;
|
||||
meta?: ApiMeta;
|
||||
}
|
||||
|
||||
export interface ApiError {
|
||||
code: string;
|
||||
message: string;
|
||||
details?: unknown;
|
||||
}
|
||||
|
||||
export interface ApiMeta {
|
||||
timestamp: string;
|
||||
requestId?: string;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// PAGINATION
|
||||
// ============================================
|
||||
|
||||
export interface PaginationParams {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
sortBy?: string;
|
||||
sortOrder?: 'asc' | 'desc';
|
||||
}
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
data: T[];
|
||||
pagination: PaginationInfo;
|
||||
}
|
||||
|
||||
export interface PaginationInfo {
|
||||
page: number;
|
||||
limit: number;
|
||||
total: number;
|
||||
totalPages: number;
|
||||
hasNext: boolean;
|
||||
hasPrev: boolean;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// FILTERS & SORTING
|
||||
// ============================================
|
||||
|
||||
export interface FilterOptions {
|
||||
search?: string;
|
||||
difficulty?: string[];
|
||||
type?: string[];
|
||||
moduleId?: string[];
|
||||
topicId?: string[];
|
||||
}
|
||||
|
||||
export interface SortOptions {
|
||||
field: 'createdAt' | 'points' | 'difficulty' | 'name';
|
||||
order: 'asc' | 'desc';
|
||||
}
|
||||
93
shared/types/src/auth.ts
Normal file
93
shared/types/src/auth.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* ============================================
|
||||
* AUTHENTICATION TYPES
|
||||
* ============================================
|
||||
*
|
||||
* Types for authentication requests and responses
|
||||
* shared between frontend and backend
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// REQUEST TYPES
|
||||
// ============================================
|
||||
|
||||
export interface LoginRequest {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface RegisterRequest {
|
||||
email: string;
|
||||
username: string;
|
||||
password: string;
|
||||
confirmPassword?: string;
|
||||
}
|
||||
|
||||
export interface RefreshTokenRequest {
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
export interface ChangePasswordRequest {
|
||||
currentPassword: string;
|
||||
newPassword: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// RESPONSE TYPES
|
||||
// ============================================
|
||||
|
||||
export interface LoginResponse {
|
||||
token: string;
|
||||
refreshToken?: string;
|
||||
expiresIn?: number;
|
||||
user: User;
|
||||
}
|
||||
|
||||
export interface RegisterResponse {
|
||||
token: string;
|
||||
user: User;
|
||||
}
|
||||
|
||||
export interface RefreshTokenResponse {
|
||||
token: string;
|
||||
refreshToken: string;
|
||||
expiresIn: number;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// USER TYPE (Shared subset)
|
||||
// ============================================
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
username: string;
|
||||
role?: UserRole;
|
||||
createdAt: string;
|
||||
lastLoginAt?: string;
|
||||
}
|
||||
|
||||
export type UserRole = 'STUDENT' | 'ADMIN' | 'INSTRUCTOR';
|
||||
|
||||
// ============================================
|
||||
// JWT PAYLOAD (Backend usage mainly)
|
||||
// ============================================
|
||||
|
||||
export interface JwtPayload {
|
||||
userId: string;
|
||||
email: string;
|
||||
username: string;
|
||||
role: string;
|
||||
iat?: number;
|
||||
exp?: number;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// AUTH TOKENS
|
||||
// ============================================
|
||||
|
||||
export interface AuthTokens {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
expiresIn: number;
|
||||
}
|
||||
118
shared/types/src/error.ts
Normal file
118
shared/types/src/error.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* ============================================
|
||||
* ERROR TYPES
|
||||
* ============================================
|
||||
*
|
||||
* Error types and classes
|
||||
* Primarily used by backend, but shared for type consistency
|
||||
*/
|
||||
|
||||
// Extend ErrorConstructor for Node.js captureStackTrace
|
||||
declare global {
|
||||
interface ErrorConstructor {
|
||||
captureStackTrace?(targetObject: object, constructorOpt?: Function): void;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ERROR CODES
|
||||
// ============================================
|
||||
|
||||
export type ErrorCode =
|
||||
| 'INTERNAL_ERROR'
|
||||
| 'VALIDATION_ERROR'
|
||||
| 'AUTHENTICATION_ERROR'
|
||||
| 'AUTHORIZATION_ERROR'
|
||||
| 'NOT_FOUND'
|
||||
| 'CONFLICT_ERROR'
|
||||
| 'RATE_LIMIT_ERROR'
|
||||
| 'BAD_REQUEST'
|
||||
| 'SERVICE_UNAVAILABLE';
|
||||
|
||||
// ============================================
|
||||
// APP ERROR INTERFACE
|
||||
// ============================================
|
||||
|
||||
export interface AppError extends Error {
|
||||
code: ErrorCode;
|
||||
statusCode: number;
|
||||
isOperational: boolean;
|
||||
details?: unknown;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ERROR CLASSES (Backend usage)
|
||||
// ============================================
|
||||
|
||||
export class ApplicationError extends Error implements AppError {
|
||||
code: ErrorCode;
|
||||
statusCode: number;
|
||||
isOperational: boolean;
|
||||
details?: unknown;
|
||||
|
||||
constructor(
|
||||
message: string,
|
||||
code: ErrorCode = 'INTERNAL_ERROR',
|
||||
statusCode: number = 500,
|
||||
isOperational: boolean = true,
|
||||
details?: unknown
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'ApplicationError';
|
||||
this.code = code;
|
||||
this.statusCode = statusCode;
|
||||
this.isOperational = isOperational;
|
||||
this.details = details;
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ValidationError extends ApplicationError {
|
||||
constructor(message: string, details?: unknown) {
|
||||
super(message, 'VALIDATION_ERROR', 400, true, details);
|
||||
this.name = 'ValidationError';
|
||||
}
|
||||
}
|
||||
|
||||
export class AuthenticationError extends ApplicationError {
|
||||
constructor(message: string = 'Authentication failed') {
|
||||
super(message, 'AUTHENTICATION_ERROR', 401, true);
|
||||
this.name = 'AuthenticationError';
|
||||
}
|
||||
}
|
||||
|
||||
export class AuthorizationError extends ApplicationError {
|
||||
constructor(message: string = 'Insufficient permissions') {
|
||||
super(message, 'AUTHORIZATION_ERROR', 403, true);
|
||||
this.name = 'AuthorizationError';
|
||||
}
|
||||
}
|
||||
|
||||
export class NotFoundError extends ApplicationError {
|
||||
constructor(resource: string) {
|
||||
super(`${resource} not found`, 'NOT_FOUND', 404, true);
|
||||
this.name = 'NotFoundError';
|
||||
}
|
||||
}
|
||||
|
||||
export class ConflictError extends ApplicationError {
|
||||
constructor(message: string, details?: unknown) {
|
||||
super(message, 'CONFLICT_ERROR', 409, true, details);
|
||||
this.name = 'ConflictError';
|
||||
}
|
||||
}
|
||||
|
||||
export class RateLimitError extends ApplicationError {
|
||||
constructor(retryAfter?: number) {
|
||||
super(
|
||||
'Too many requests',
|
||||
'RATE_LIMIT_ERROR',
|
||||
429,
|
||||
true,
|
||||
{ retryAfter }
|
||||
);
|
||||
this.name = 'RateLimitError';
|
||||
}
|
||||
}
|
||||
145
shared/types/src/exercise.ts
Normal file
145
shared/types/src/exercise.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* ============================================
|
||||
* EXERCISE TYPES
|
||||
* ============================================
|
||||
*
|
||||
* Types for exercises, attempts, and solutions
|
||||
* shared between frontend and backend
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// ENUMS
|
||||
// ============================================
|
||||
|
||||
export type ExerciseType =
|
||||
| 'MULTIPLE_CHOICE'
|
||||
| 'OPEN_ENDED'
|
||||
| 'TRUE_FALSE'
|
||||
| 'CALCULATION';
|
||||
|
||||
export type ExerciseDifficulty = 'EASY' | 'MEDIUM' | 'HARD';
|
||||
|
||||
export type ExerciseStatus = 'CORRECT' | 'INCORRECT' | 'PARTIAL' | 'SKIPPED';
|
||||
|
||||
export type TopicType =
|
||||
| 'VECTORES'
|
||||
| 'MATRICES'
|
||||
| 'SISTEMAS'
|
||||
| 'ESPACIOS_VECTORIALES'
|
||||
| 'PROGRAMACION_LINEAL';
|
||||
|
||||
export type ModuleType = 'FUNDAMENTOS' | 'SISTEMAS' | 'APLICACIONES';
|
||||
|
||||
// ============================================
|
||||
// CORE EXERCISE TYPES
|
||||
// ============================================
|
||||
|
||||
export interface Exercise {
|
||||
id: string;
|
||||
moduleId: string;
|
||||
topicId?: string;
|
||||
type: ExerciseType;
|
||||
difficulty: ExerciseDifficulty;
|
||||
statement: string;
|
||||
correctAnswer: string;
|
||||
solutionSteps: SolutionStep[];
|
||||
formulas?: string[];
|
||||
hints?: string[];
|
||||
options?: ExerciseOption[];
|
||||
points: number;
|
||||
isAIGenerated?: boolean;
|
||||
}
|
||||
|
||||
export interface ExerciseOption {
|
||||
id: string;
|
||||
text: string;
|
||||
isCorrect: boolean;
|
||||
}
|
||||
|
||||
export interface SolutionStep {
|
||||
step: number;
|
||||
description: string;
|
||||
formula?: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ATTEMPT SUBMISSION
|
||||
// ============================================
|
||||
|
||||
export interface SubmitAttemptRequest {
|
||||
answer: string;
|
||||
timeSpent: number;
|
||||
hintsUsed?: number;
|
||||
skipped?: boolean;
|
||||
}
|
||||
|
||||
export interface SubmitAttemptResponse {
|
||||
isCorrect: boolean;
|
||||
points: number;
|
||||
message: string;
|
||||
correctAnswer?: string;
|
||||
solutionSteps?: SolutionStep[];
|
||||
newAchievements?: AchievementUnlock[];
|
||||
}
|
||||
|
||||
export interface AchievementUnlock {
|
||||
code: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// EXERCISE ATTEMPT (History)
|
||||
// ============================================
|
||||
|
||||
export interface ExerciseAttempt {
|
||||
id: string;
|
||||
userId: string;
|
||||
exerciseId: string;
|
||||
userAnswer: string;
|
||||
status: ExerciseStatus;
|
||||
pointsEarned: number;
|
||||
timeSpentSeconds: number;
|
||||
attemptedAt: string;
|
||||
hintsUsed?: number;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// EXERCISE CONTENT (Detailed)
|
||||
// ============================================
|
||||
|
||||
export interface ExerciseContent {
|
||||
statement: string;
|
||||
correctAnswer: string;
|
||||
solutionSteps?: SolutionStep[];
|
||||
formulas?: LatexFormula[];
|
||||
hints?: ExerciseHint[];
|
||||
multipleChoiceOptions?: MultipleChoiceOption[];
|
||||
calculationSteps?: CalculationStep[];
|
||||
}
|
||||
|
||||
export interface LatexFormula {
|
||||
name: string;
|
||||
latex: string;
|
||||
description: string;
|
||||
category?: string;
|
||||
}
|
||||
|
||||
export interface ExerciseHint {
|
||||
hint: string;
|
||||
cost: number;
|
||||
}
|
||||
|
||||
export interface MultipleChoiceOption {
|
||||
option: string;
|
||||
isCorrect: boolean;
|
||||
explanation?: string;
|
||||
}
|
||||
|
||||
export interface CalculationStep {
|
||||
step: number;
|
||||
operation: string;
|
||||
result: string;
|
||||
latexFormula?: string;
|
||||
}
|
||||
158
shared/types/src/index.ts
Normal file
158
shared/types/src/index.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* ============================================
|
||||
* SHARED TYPES PACKAGE
|
||||
* ============================================
|
||||
*
|
||||
* TypeScript type definitions shared between
|
||||
* frontend and backend applications
|
||||
*
|
||||
* Usage:
|
||||
* import type { User, Exercise, ApiResponse } from '@math-platform/shared-types';
|
||||
* import { ApplicationError } from '@math-platform/shared-types';
|
||||
*
|
||||
* Or import specific modules:
|
||||
* import type { LoginRequest, LoginResponse } from '@math-platform/shared-types/auth';
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// AUTH
|
||||
// ============================================
|
||||
export type {
|
||||
LoginRequest,
|
||||
LoginResponse,
|
||||
RegisterRequest,
|
||||
RegisterResponse,
|
||||
RefreshTokenRequest,
|
||||
RefreshTokenResponse,
|
||||
ChangePasswordRequest,
|
||||
User,
|
||||
UserRole,
|
||||
JwtPayload,
|
||||
AuthTokens,
|
||||
} from './auth';
|
||||
|
||||
// ============================================
|
||||
// EXERCISE
|
||||
// ============================================
|
||||
export type {
|
||||
ExerciseType,
|
||||
ExerciseDifficulty,
|
||||
ExerciseStatus,
|
||||
TopicType,
|
||||
ModuleType,
|
||||
Exercise,
|
||||
ExerciseOption,
|
||||
SolutionStep,
|
||||
SubmitAttemptRequest,
|
||||
SubmitAttemptResponse,
|
||||
AchievementUnlock,
|
||||
ExerciseAttempt,
|
||||
ExerciseContent,
|
||||
LatexFormula,
|
||||
ExerciseHint,
|
||||
MultipleChoiceOption,
|
||||
CalculationStep,
|
||||
} from './exercise';
|
||||
|
||||
// ============================================
|
||||
// MODULE
|
||||
// ============================================
|
||||
export type {
|
||||
Module,
|
||||
ModuleExample,
|
||||
ModuleExerciseRef,
|
||||
ModuleAnswer,
|
||||
Topic,
|
||||
TheoryContent,
|
||||
TheorySection,
|
||||
Formula,
|
||||
TheoryContentDetailed,
|
||||
KeyPoint,
|
||||
CommonMistake,
|
||||
} from './module';
|
||||
|
||||
// ============================================
|
||||
// PROGRESS
|
||||
// ============================================
|
||||
export type {
|
||||
UserProgress,
|
||||
ModuleProgress,
|
||||
ProgressMetrics,
|
||||
UserStatistics,
|
||||
Activity,
|
||||
ActivityType,
|
||||
} from './progress';
|
||||
|
||||
// ============================================
|
||||
// ACHIEVEMENT
|
||||
// ============================================
|
||||
export type {
|
||||
AchievementCategory,
|
||||
AchievementRarity,
|
||||
Achievement,
|
||||
AchievementMetadata,
|
||||
UserAchievement,
|
||||
UserAchievementProgress,
|
||||
AchievementBadge,
|
||||
} from './achievement';
|
||||
|
||||
// ============================================
|
||||
// RANKING
|
||||
// ============================================
|
||||
export type {
|
||||
RankingEntry,
|
||||
GlobalRanking,
|
||||
UserRankingPosition,
|
||||
RankingFilters,
|
||||
} from './ranking';
|
||||
|
||||
// ============================================
|
||||
// API
|
||||
// ============================================
|
||||
export type {
|
||||
ApiResponse,
|
||||
ApiError,
|
||||
ApiMeta,
|
||||
PaginationParams,
|
||||
PaginatedResponse,
|
||||
PaginationInfo,
|
||||
FilterOptions,
|
||||
SortOptions,
|
||||
} from './api';
|
||||
|
||||
// ============================================
|
||||
// ERROR
|
||||
// ============================================
|
||||
export type {
|
||||
ErrorCode,
|
||||
AppError,
|
||||
} from './error';
|
||||
|
||||
// Export error classes as values (not types)
|
||||
export {
|
||||
ApplicationError,
|
||||
ValidationError,
|
||||
AuthenticationError,
|
||||
AuthorizationError,
|
||||
NotFoundError,
|
||||
ConflictError,
|
||||
RateLimitError,
|
||||
} from './error';
|
||||
|
||||
// ============================================
|
||||
// UTILS
|
||||
// ============================================
|
||||
export type {
|
||||
WithId,
|
||||
WithTimestamps,
|
||||
PartialBy,
|
||||
Nullable,
|
||||
Optional,
|
||||
UUID,
|
||||
ISO8601Date,
|
||||
Email,
|
||||
AsyncReturnType,
|
||||
NullableKeys,
|
||||
EmptyObject,
|
||||
StringRecord,
|
||||
} from './utils';
|
||||
114
shared/types/src/module.ts
Normal file
114
shared/types/src/module.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* ============================================
|
||||
* MODULE TYPES
|
||||
* ============================================
|
||||
*
|
||||
* Types for course modules and topics
|
||||
* shared between frontend and backend
|
||||
*/
|
||||
|
||||
import { TopicType, ModuleType } from './exercise';
|
||||
|
||||
// ============================================
|
||||
// MODULE
|
||||
// ============================================
|
||||
|
||||
export interface Module {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
type: ModuleType;
|
||||
order: number;
|
||||
introduction?: string;
|
||||
examples?: ModuleExample[];
|
||||
exercises?: ModuleExerciseRef[];
|
||||
answers?: ModuleAnswer[];
|
||||
}
|
||||
|
||||
export interface ModuleExample {
|
||||
title: string;
|
||||
content: string;
|
||||
latexFormula?: string;
|
||||
explanation: string;
|
||||
difficulty?: 'EASY' | 'MEDIUM' | 'HARD';
|
||||
}
|
||||
|
||||
export interface ModuleExerciseRef {
|
||||
id: string;
|
||||
section: string;
|
||||
content: string;
|
||||
problems: {
|
||||
id: string;
|
||||
statement: string;
|
||||
type: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface ModuleAnswer {
|
||||
id: string;
|
||||
exerciseId: string;
|
||||
content: string;
|
||||
steps: string[];
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// TOPIC
|
||||
// ============================================
|
||||
|
||||
export interface Topic {
|
||||
id: string;
|
||||
moduleId: string;
|
||||
name: string;
|
||||
type: TopicType;
|
||||
theoryContent?: TheoryContent;
|
||||
formulas?: Formula[];
|
||||
}
|
||||
|
||||
export interface TheoryContent {
|
||||
title: string;
|
||||
description: string;
|
||||
sections: TheorySection[];
|
||||
}
|
||||
|
||||
export interface TheorySection {
|
||||
title: string;
|
||||
content: string;
|
||||
formulas?: string[];
|
||||
}
|
||||
|
||||
export interface Formula {
|
||||
id: string;
|
||||
name: string;
|
||||
latex: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// THEORY CONTENT (Detailed)
|
||||
// ============================================
|
||||
|
||||
export interface TheoryContentDetailed {
|
||||
concept: string;
|
||||
explanation: string;
|
||||
formulas: LatexFormula[];
|
||||
examples: ModuleExample[];
|
||||
}
|
||||
|
||||
export interface LatexFormula {
|
||||
name: string;
|
||||
latex: string;
|
||||
description: string;
|
||||
category?: string;
|
||||
}
|
||||
|
||||
export interface KeyPoint {
|
||||
title: string;
|
||||
explanation: string;
|
||||
latex?: string;
|
||||
}
|
||||
|
||||
export interface CommonMistake {
|
||||
mistake: string;
|
||||
correction: string;
|
||||
explanation: string;
|
||||
}
|
||||
88
shared/types/src/progress.ts
Normal file
88
shared/types/src/progress.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* ============================================
|
||||
* PROGRESS TYPES
|
||||
* ============================================
|
||||
*
|
||||
* Types for user progress tracking
|
||||
* shared between frontend and backend
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// USER PROGRESS
|
||||
// ============================================
|
||||
|
||||
export interface UserProgress {
|
||||
userId: string;
|
||||
moduleId: string;
|
||||
exercisesCompleted: number;
|
||||
totalExercises: number;
|
||||
points: number;
|
||||
percentage: number;
|
||||
isStarted: boolean;
|
||||
isCompleted: boolean;
|
||||
lastActivityAt: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// MODULE PROGRESS
|
||||
// ============================================
|
||||
|
||||
export interface ModuleProgress {
|
||||
moduleId: string;
|
||||
moduleName: string;
|
||||
isStarted: boolean;
|
||||
isCompleted: boolean;
|
||||
startedAt?: string;
|
||||
completedAt?: string;
|
||||
lastAccessedAt?: string;
|
||||
metrics: ProgressMetrics;
|
||||
}
|
||||
|
||||
export interface ProgressMetrics {
|
||||
exercisesCompleted: number;
|
||||
totalExercises: number;
|
||||
points: number;
|
||||
percentage: number;
|
||||
averageScore?: number;
|
||||
totalTimeSpent: number;
|
||||
perfectExercises: number;
|
||||
attemptsCount: number;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// USER STATISTICS
|
||||
// ============================================
|
||||
|
||||
export interface UserStatistics {
|
||||
totalExercisesCompleted: number;
|
||||
totalPoints: number;
|
||||
averageScore: number;
|
||||
totalStudyTime: number;
|
||||
currentStreak: number;
|
||||
longestStreak: number;
|
||||
modulesCompleted: number;
|
||||
achievementsUnlocked: number;
|
||||
rankGlobal: number | null;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ACTIVITY
|
||||
// ============================================
|
||||
|
||||
export interface Activity {
|
||||
id: string;
|
||||
userId: string;
|
||||
type: ActivityType;
|
||||
description: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export type ActivityType =
|
||||
| 'EXERCISE_COMPLETED'
|
||||
| 'EXERCISE_ATTEMPTED'
|
||||
| 'MODULE_STARTED'
|
||||
| 'MODULE_COMPLETED'
|
||||
| 'ACHIEVEMENT_UNLOCKED'
|
||||
| 'LOGIN'
|
||||
| 'LOGOUT';
|
||||
64
shared/types/src/ranking.ts
Normal file
64
shared/types/src/ranking.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* ============================================
|
||||
* RANKING TYPES
|
||||
* ============================================
|
||||
*
|
||||
* Types for leaderboards and rankings
|
||||
* shared between frontend and backend
|
||||
*/
|
||||
|
||||
import { AchievementBadge } from './achievement';
|
||||
|
||||
// ============================================
|
||||
// RANKING ENTRY
|
||||
// ============================================
|
||||
|
||||
export interface RankingEntry {
|
||||
position: number;
|
||||
userId?: string;
|
||||
username?: string;
|
||||
points: number;
|
||||
exercisesCompleted: number;
|
||||
streak: number;
|
||||
perfectExercises?: number;
|
||||
averageScore?: number;
|
||||
achievementsUnlocked: number;
|
||||
badges?: AchievementBadge[];
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// GLOBAL RANKING
|
||||
// ============================================
|
||||
|
||||
export interface GlobalRanking {
|
||||
global: RankingEntry[];
|
||||
byModule: Record<string, RankingEntry[]>;
|
||||
myPosition: {
|
||||
global: RankingEntry | null;
|
||||
byModule: Record<string, RankingEntry | null>;
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// USER RANKING POSITION
|
||||
// ============================================
|
||||
|
||||
export interface UserRankingPosition {
|
||||
global: RankingEntry;
|
||||
byModule: {
|
||||
moduleId: string;
|
||||
moduleName: string;
|
||||
position: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// RANKING FILTERS
|
||||
// ============================================
|
||||
|
||||
export interface RankingFilters {
|
||||
moduleId?: string;
|
||||
timeRange?: 'ALL_TIME' | 'WEEKLY' | 'MONTHLY';
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}
|
||||
53
shared/types/src/utils.ts
Normal file
53
shared/types/src/utils.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* ============================================
|
||||
* UTILITY TYPES
|
||||
* ============================================
|
||||
*
|
||||
* Generic utility types used across the application
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// BASIC UTILITIES
|
||||
// ============================================
|
||||
|
||||
export type WithId<T> = T & { id: string };
|
||||
|
||||
export type WithTimestamps<T> = T & {
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||
|
||||
export type Nullable<T> = T | null;
|
||||
|
||||
export type Optional<T> = T | undefined;
|
||||
|
||||
// ============================================
|
||||
// STRING UTILITIES
|
||||
// ============================================
|
||||
|
||||
export type UUID = string;
|
||||
|
||||
export type ISO8601Date = string;
|
||||
|
||||
export type Email = string;
|
||||
|
||||
// ============================================
|
||||
// FUNCTION UTILITIES
|
||||
// ============================================
|
||||
|
||||
export type AsyncReturnType<T extends (...args: any[]) => Promise<any>> =
|
||||
T extends (...args: any[]) => Promise<infer R> ? R : never;
|
||||
|
||||
export type NullableKeys<T, K extends keyof T> = Omit<T, K> & {
|
||||
[P in K]: T[P] | null;
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// API UTILITIES
|
||||
// ============================================
|
||||
|
||||
export type EmptyObject = Record<string, never>;
|
||||
|
||||
export type StringRecord = Record<string, string>;
|
||||
22
shared/types/tsconfig.json
Normal file
22
shared/types/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["ES2022"],
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": false
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user