add changes
This commit is contained in:
76
frontend/src/app/core/models/category.model.ts
Normal file
76
frontend/src/app/core/models/category.model.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Category Interface
|
||||
* Represents a quiz category
|
||||
*/
|
||||
export interface Category {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description: string;
|
||||
icon?: string;
|
||||
color?: string;
|
||||
questionCount: number;
|
||||
displayOrder?: number;
|
||||
isActive: boolean;
|
||||
guestAccessible: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Category Detail with Stats
|
||||
*/
|
||||
export interface CategoryDetail extends Category {
|
||||
questionPreview?: QuestionPreview[];
|
||||
stats?: CategoryStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Category Statistics
|
||||
*/
|
||||
export interface CategoryStats {
|
||||
totalQuestions: number;
|
||||
questionsByDifficulty: {
|
||||
easy: number;
|
||||
medium: number;
|
||||
hard: number;
|
||||
};
|
||||
totalAttempts: number;
|
||||
totalCorrect: number;
|
||||
averageAccuracy: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Question Preview (limited info)
|
||||
*/
|
||||
export interface QuestionPreview {
|
||||
id: string;
|
||||
questionText: string;
|
||||
questionType: QuestionType;
|
||||
difficulty: Difficulty;
|
||||
points: number;
|
||||
accuracy?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Question Types
|
||||
*/
|
||||
export type QuestionType = 'multiple_choice' | 'true_false' | 'written';
|
||||
|
||||
/**
|
||||
* Difficulty Levels
|
||||
*/
|
||||
export type Difficulty = 'easy' | 'medium' | 'hard';
|
||||
|
||||
/**
|
||||
* Category Create/Update Request
|
||||
*/
|
||||
export interface CategoryFormData {
|
||||
name: string;
|
||||
slug?: string;
|
||||
description: string;
|
||||
icon?: string;
|
||||
color?: string;
|
||||
displayOrder?: number;
|
||||
guestAccessible: boolean;
|
||||
}
|
||||
148
frontend/src/app/core/models/dashboard.model.ts
Normal file
148
frontend/src/app/core/models/dashboard.model.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import { User } from './user.model';
|
||||
import { QuizSession } from './quiz.model';
|
||||
|
||||
/**
|
||||
* User Dashboard Response
|
||||
*/
|
||||
export interface UserDashboard {
|
||||
success: boolean;
|
||||
totalQuizzes: number;
|
||||
totalQuestionsAnswered: number;
|
||||
overallAccuracy: number;
|
||||
currentStreak: number;
|
||||
longestStreak: number;
|
||||
averageScore: number;
|
||||
recentQuizzes: QuizSession[];
|
||||
categoryPerformance: CategoryPerformance[];
|
||||
achievements?: Achievement[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Category Performance
|
||||
*/
|
||||
export interface CategoryPerformance {
|
||||
categoryId: string;
|
||||
categoryName: string;
|
||||
quizzesTaken: number;
|
||||
averageScore: number;
|
||||
accuracy: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* User Quiz History
|
||||
*/
|
||||
export interface QuizHistoryResponse {
|
||||
success: boolean;
|
||||
sessions: QuizSession[];
|
||||
pagination: PaginationInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pagination Info
|
||||
*/
|
||||
export interface PaginationInfo {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
totalItems: number;
|
||||
itemsPerPage: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Achievement
|
||||
*/
|
||||
export interface Achievement {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
earnedAt?: string;
|
||||
progress?: number;
|
||||
maxProgress?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* User Profile Update Request
|
||||
*/
|
||||
export interface UserProfileUpdate {
|
||||
username?: string;
|
||||
email?: string;
|
||||
currentPassword?: string;
|
||||
newPassword?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bookmark
|
||||
*/
|
||||
export interface Bookmark {
|
||||
id: string;
|
||||
userId: string;
|
||||
questionId: string;
|
||||
question?: any; // Will use Question type
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bookmarks Response
|
||||
*/
|
||||
export interface BookmarksResponse {
|
||||
success: boolean;
|
||||
bookmarks: any[]; // Will contain Question objects
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin Statistics
|
||||
*/
|
||||
export interface AdminStatistics {
|
||||
totalUsers: number;
|
||||
activeUsers: number;
|
||||
totalQuizSessions: number;
|
||||
totalQuestions: number;
|
||||
totalCategories: number;
|
||||
mostPopularCategories: PopularCategory[];
|
||||
averageQuizScore: number;
|
||||
userGrowth: UserGrowthData[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Popular Category
|
||||
*/
|
||||
export interface PopularCategory {
|
||||
categoryId: string;
|
||||
categoryName: string;
|
||||
quizzesTaken: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* User Growth Data
|
||||
*/
|
||||
export interface UserGrowthData {
|
||||
date: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin Users List Response
|
||||
*/
|
||||
export interface AdminUsersResponse {
|
||||
success: boolean;
|
||||
users: User[];
|
||||
pagination: PaginationInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin User Details
|
||||
*/
|
||||
export interface AdminUserDetails extends User {
|
||||
quizHistory?: QuizSession[];
|
||||
activityTimeline?: ActivityEvent[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity Event
|
||||
*/
|
||||
export interface ActivityEvent {
|
||||
id: string;
|
||||
type: 'quiz_completed' | 'achievement_earned' | 'profile_updated';
|
||||
description: string;
|
||||
timestamp: string;
|
||||
}
|
||||
104
frontend/src/app/core/models/guest.model.ts
Normal file
104
frontend/src/app/core/models/guest.model.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Guest Session Interface
|
||||
* Represents a temporary guest user session
|
||||
*/
|
||||
export interface GuestSession {
|
||||
guestId: string;
|
||||
sessionToken: string;
|
||||
deviceId?: string;
|
||||
quizzesTaken: number;
|
||||
maxQuizzes: number;
|
||||
remainingQuizzes: number;
|
||||
expiresAt: string;
|
||||
isConverted: boolean;
|
||||
convertedUserId?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest Session Start Response
|
||||
*/
|
||||
export interface GuestSessionStartResponse {
|
||||
success: boolean;
|
||||
sessionToken: string;
|
||||
guestId: string;
|
||||
expiresAt: string;
|
||||
maxQuizzes: number;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest Quiz Limit Response
|
||||
*/
|
||||
export interface GuestQuizLimitResponse {
|
||||
success: boolean;
|
||||
remainingQuizzes: number;
|
||||
maxQuizzes: number;
|
||||
quizzesTaken: number;
|
||||
expiresAt: string;
|
||||
upgradePrompt?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest to User Conversion Request
|
||||
*/
|
||||
export interface GuestConversionRequest {
|
||||
guestSessionId: string;
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest Settings (Admin)
|
||||
*/
|
||||
export interface GuestSettings {
|
||||
id: string;
|
||||
guestAccessEnabled: boolean;
|
||||
maxQuizzesPerDay: number;
|
||||
maxQuestionsPerQuiz: number;
|
||||
sessionExpiryHours: number;
|
||||
upgradePromptMessage: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest Analytics (Admin)
|
||||
*/
|
||||
export interface GuestAnalytics {
|
||||
totalGuestSessions: number;
|
||||
activeGuestSessions: number;
|
||||
guestToUserConversionRate: number;
|
||||
averageQuizzesPerGuest: number;
|
||||
totalGuestQuizzes: number;
|
||||
conversionFunnel?: {
|
||||
totalSessions: number;
|
||||
startedQuiz: number;
|
||||
completedQuiz: number;
|
||||
converted: number;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest Quiz Limit
|
||||
* Tracks remaining quiz attempts for guest
|
||||
*/
|
||||
export interface GuestLimit {
|
||||
maxQuizzes: number;
|
||||
quizzesTaken: number;
|
||||
quizzesRemaining: number;
|
||||
expiresAt?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest State (for signal management)
|
||||
*/
|
||||
export interface GuestState {
|
||||
session: GuestSession | null;
|
||||
isGuest: boolean;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
quizLimit: GuestLimit | null;
|
||||
}
|
||||
90
frontend/src/app/core/models/index.ts
Normal file
90
frontend/src/app/core/models/index.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* API Response Wrapper
|
||||
*/
|
||||
export interface ApiResponse<T> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* API Error Response
|
||||
*/
|
||||
export interface ApiError {
|
||||
message: string;
|
||||
error?: string;
|
||||
statusCode?: number;
|
||||
timestamp?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP Error
|
||||
*/
|
||||
export interface HttpError {
|
||||
status: number;
|
||||
statusText: string;
|
||||
message: string;
|
||||
error?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loading State
|
||||
*/
|
||||
export interface LoadingState {
|
||||
isLoading: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toast Notification
|
||||
*/
|
||||
export interface ToastNotification {
|
||||
id?: string;
|
||||
type: 'success' | 'error' | 'warning' | 'info';
|
||||
message: string;
|
||||
duration?: number;
|
||||
action?: ToastAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toast Action
|
||||
*/
|
||||
export interface ToastAction {
|
||||
label: string;
|
||||
callback: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort Options
|
||||
*/
|
||||
export interface SortOptions {
|
||||
sortBy: string;
|
||||
sortOrder: 'asc' | 'desc';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter Options
|
||||
*/
|
||||
export interface FilterOptions {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search Options
|
||||
*/
|
||||
export interface SearchOptions {
|
||||
query: string;
|
||||
filters?: FilterOptions;
|
||||
sort?: SortOptions;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
// Export all models
|
||||
export * from './user.model';
|
||||
export * from './category.model';
|
||||
export * from './question.model';
|
||||
export * from './quiz.model';
|
||||
export * from './guest.model';
|
||||
export * from './dashboard.model';
|
||||
71
frontend/src/app/core/models/question.model.ts
Normal file
71
frontend/src/app/core/models/question.model.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { QuestionType, Difficulty } from './category.model';
|
||||
|
||||
/**
|
||||
* Question Interface
|
||||
* Represents a quiz question
|
||||
*/
|
||||
export interface Question {
|
||||
id: string;
|
||||
questionText: string;
|
||||
questionType: QuestionType;
|
||||
difficulty: Difficulty;
|
||||
categoryId: string;
|
||||
categoryName?: string;
|
||||
options?: string[]; // For multiple choice
|
||||
correctAnswer: string | string[];
|
||||
explanation: string;
|
||||
points: number;
|
||||
timeLimit?: number; // in seconds
|
||||
tags?: string[];
|
||||
keywords?: string[];
|
||||
isActive: boolean;
|
||||
isPublic: boolean;
|
||||
timesAttempted?: number;
|
||||
timesCorrect?: number;
|
||||
accuracy?: number;
|
||||
createdBy?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Question Create/Update Request
|
||||
*/
|
||||
export interface QuestionFormData {
|
||||
questionText: string;
|
||||
questionType: QuestionType;
|
||||
difficulty: Difficulty;
|
||||
categoryId: string;
|
||||
options?: string[];
|
||||
correctAnswer: string | string[];
|
||||
explanation: string;
|
||||
points?: number;
|
||||
timeLimit?: number;
|
||||
tags?: string[];
|
||||
keywords?: string[];
|
||||
isPublic: boolean;
|
||||
isGuestAccessible: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Question Search Filters
|
||||
*/
|
||||
export interface QuestionSearchFilters {
|
||||
q?: string; // search query
|
||||
category?: string;
|
||||
difficulty?: Difficulty;
|
||||
questionType?: QuestionType;
|
||||
isPublic?: boolean;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Question Search Response
|
||||
*/
|
||||
export interface QuestionSearchResponse {
|
||||
results: Question[];
|
||||
totalCount: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
}
|
||||
134
frontend/src/app/core/models/quiz.model.ts
Normal file
134
frontend/src/app/core/models/quiz.model.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { Question } from './question.model';
|
||||
|
||||
/**
|
||||
* Quiz Session Interface
|
||||
* Represents an active or completed quiz session
|
||||
*/
|
||||
export interface QuizSession {
|
||||
id: string;
|
||||
userId?: string;
|
||||
guestSessionId?: string;
|
||||
categoryId: string;
|
||||
categoryName?: string;
|
||||
quizType: QuizType;
|
||||
difficulty: string;
|
||||
totalQuestions: number;
|
||||
currentQuestionIndex: number;
|
||||
score: number;
|
||||
correctAnswers: number;
|
||||
incorrectAnswers: number;
|
||||
skippedAnswers: number;
|
||||
status: QuizStatus;
|
||||
startedAt: string;
|
||||
completedAt?: string;
|
||||
timeSpent?: number; // in seconds
|
||||
isPassed?: boolean;
|
||||
passingScore?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quiz Types
|
||||
*/
|
||||
export type QuizType = 'practice' | 'timed' | 'exam';
|
||||
|
||||
/**
|
||||
* Quiz Status
|
||||
*/
|
||||
export type QuizStatus = 'in_progress' | 'completed' | 'abandoned';
|
||||
|
||||
/**
|
||||
* Quiz Start Request
|
||||
*/
|
||||
export interface QuizStartRequest {
|
||||
categoryId: string;
|
||||
questionCount: number;
|
||||
difficulty?: string; // 'easy', 'medium', 'hard', 'mixed'
|
||||
quizType?: QuizType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quiz Start Response
|
||||
*/
|
||||
export interface QuizStartResponse {
|
||||
success: boolean;
|
||||
sessionId: string;
|
||||
questions: Question[];
|
||||
totalQuestions: number;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quiz Answer Submission
|
||||
*/
|
||||
export interface QuizAnswerSubmission {
|
||||
questionId: string;
|
||||
answer: string | string[];
|
||||
quizSessionId: string;
|
||||
timeSpent?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quiz Answer Response
|
||||
*/
|
||||
export interface QuizAnswerResponse {
|
||||
success: boolean;
|
||||
isCorrect: boolean;
|
||||
correctAnswer: string | string[];
|
||||
explanation: string;
|
||||
points: number;
|
||||
score: number;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quiz Results
|
||||
*/
|
||||
export interface QuizResults {
|
||||
success: boolean;
|
||||
score: number;
|
||||
totalQuestions: number;
|
||||
correctAnswers: number;
|
||||
incorrectAnswers: number;
|
||||
skippedAnswers: number;
|
||||
percentage: number;
|
||||
timeSpent: number;
|
||||
isPassed: boolean;
|
||||
performanceMessage: string;
|
||||
questions: QuizQuestionResult[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Quiz Question Result
|
||||
*/
|
||||
export interface QuizQuestionResult {
|
||||
questionId: string;
|
||||
questionText: string;
|
||||
questionType: string;
|
||||
userAnswer: string | string[];
|
||||
correctAnswer: string | string[];
|
||||
isCorrect: boolean;
|
||||
explanation: string;
|
||||
points: number;
|
||||
timeSpent?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quiz Session State (for signal management)
|
||||
*/
|
||||
export interface QuizSessionState {
|
||||
session: QuizSession | null;
|
||||
questions: Question[];
|
||||
currentQuestionIndex: number;
|
||||
answers: Map<string, QuizAnswerResponse>;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quiz Review Response
|
||||
*/
|
||||
export interface QuizReviewResponse {
|
||||
success: boolean;
|
||||
session: QuizSession;
|
||||
questions: QuizQuestionResult[];
|
||||
}
|
||||
61
frontend/src/app/core/models/user.model.ts
Normal file
61
frontend/src/app/core/models/user.model.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* User Interface
|
||||
* Represents a registered user in the system
|
||||
*/
|
||||
export interface User {
|
||||
id: string;
|
||||
username: string;
|
||||
email: string;
|
||||
role: 'user' | 'admin';
|
||||
isActive: boolean;
|
||||
totalQuizzesTaken?: number;
|
||||
totalQuestionsAnswered?: number;
|
||||
totalCorrectAnswers?: number;
|
||||
currentStreak?: number;
|
||||
longestStreak?: number;
|
||||
averageScore?: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* User Registration Request
|
||||
*/
|
||||
export interface UserRegistration {
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
guestSessionId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* User Login Request
|
||||
*/
|
||||
export interface UserLogin {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth Response
|
||||
*/
|
||||
export interface AuthResponse {
|
||||
success: boolean;
|
||||
token: string;
|
||||
user: User;
|
||||
message?: string;
|
||||
migratedStats?: {
|
||||
quizzesTaken: number;
|
||||
score: number;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth State (for signal management)
|
||||
*/
|
||||
export interface AuthState {
|
||||
user: User | null;
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
Reference in New Issue
Block a user