first commit

This commit is contained in:
AD2025
2025-12-27 22:00:37 +02:00
commit 41e3d43129
179 changed files with 46444 additions and 0 deletions

View File

@@ -0,0 +1,320 @@
/**
* Admin Statistics Models
* Type definitions for admin statistics and analytics
*/
/**
* User growth data point for chart
*/
export interface UserGrowthData {
date: string;
newUsers: number;
}
/**
* Category popularity data for chart
*/
export interface CategoryPopularity {
id: string;
name: string;
slug: string;
icon: any;
color: string;
quizCount: number;
averageScore: number;
}
/**
* System-wide statistics response
*/
export interface AdminStatistics {
users: AdminStatisticsUsers;
quizzes: AdminStatisticsQuizzes;
content: AdminStatisticsContent;
quizActivity: QuizActivity[];
userGrowth: UserGrowthData[];
popularCategories: CategoryPopularity[];
}
export interface AdminStatisticsContent {
totalCategories: number;
totalQuestions: number;
questionsByDifficulty: {
easy: number;
medium: number;
hard: number;
};
}
export interface AdminStatisticsQuizzes {
totalSessions: number;
averageScore: number;
averageScorePercentage: number;
passRate: number;
passedQuizzes: number;
failedQuizzes: number;
}
export interface AdminStatisticsUsers {
total: number;
active: number;
inactiveLast7Days: number;
}
/**
* API response wrapper for statistics
*/
export interface AdminStatisticsResponse {
success: boolean;
data: AdminStatistics;
message?: string;
}
export interface QuizActivity {
date: string;
quizzesCompleted: number;
}
/**
* Date range filter for statistics
*/
export interface DateRangeFilter {
startDate: Date | null;
endDate: Date | null;
}
/**
* Cache entry for admin data
*/
export interface AdminCacheEntry<T> {
data: T;
timestamp: number;
expiresAt: number;
}
/**
* Guest session timeline data point
*/
// export interface GuestSessionTimelineData {
// date: string;
// activeSessions: number;
// newSessions: number;
// convertedSessions: number;
// }
/**
* Conversion funnel stage
*/
// export interface ConversionFunnelStage {
// stage: string;
// count: number;
// percentage: number;
// dropoff?: number;
// }
/**
* Guest analytics data
*/
export interface GuestAnalyticsOverview {
totalGuestSessions: number;
activeGuestSessions: number;
expiredGuestSessions: number;
convertedGuestSessions: number;
conversionRate: number;
}
export interface GuestAnalyticsQuizActivity {
totalGuestQuizzes: number;
completedGuestQuizzes: number;
guestQuizCompletionRate: number;
avgQuizzesPerGuest: number;
avgQuizzesBeforeConversion: number;
}
export interface GuestAnalyticsBehavior {
bounceRate: number;
avgSessionDurationMinutes: number;
}
export interface GuestAnalyticsRecentActivity {
last30Days: {
newGuestSessions: number;
conversions: number;
};
}
export interface GuestAnalytics {
overview: GuestAnalyticsOverview;
quizActivity: GuestAnalyticsQuizActivity;
behavior: GuestAnalyticsBehavior;
recentActivity: GuestAnalyticsRecentActivity;
}
/**
* API response wrapper for guest analytics
*/
export interface GuestAnalyticsResponse {
success: boolean;
data: GuestAnalytics;
message?: string;
}
/**
* Guest access settings
*/
export interface GuestSettings {
guestAccessEnabled: boolean;
maxQuizzesPerDay: number;
maxQuestionsPerQuiz: number;
sessionExpiryHours: number;
upgradePromptMessage: string;
allowedCategories?: string[];
features?: {
canBookmark: boolean;
canViewHistory: boolean;
canExportResults: boolean;
};
}
/**
* API response wrapper for guest settings
*/
export interface GuestSettingsResponse {
success: boolean;
data: GuestSettings;
message?: string;
}
/**
* Admin user data
*/
export interface AdminUser {
id: string;
username: string;
email: string;
role: 'user' | 'admin';
isActive: boolean;
createdAt: string;
lastLoginAt?: string;
profilePicture?: string | null;
quizzesTaken?: number;
averageScore?: number;
}
/**
* User list query parameters
*/
export interface UserListParams {
page?: number;
limit?: number;
role?: 'all' | 'user' | 'admin';
isActive?: 'all' | 'active' | 'inactive';
sortBy?: 'username' | 'email' | 'createdAt' | 'lastLoginAt';
sortOrder?: 'asc' | 'desc';
search?: string;
}
/**
* Paginated user list response
*/
export interface AdminUserListResponse {
success: boolean;
data: {
users: AdminUser[];
pagination: {
currentPage: number;
totalPages: number;
totalItems: number;
itemsPerPage: number;
hasNextPage: boolean;
hasPreviousPage: boolean;
};
};
message?: string;
}
/**
* User activity entry
*/
export interface UserActivity {
id: string;
type:
| 'login'
| 'quiz_start'
| 'quiz_complete'
| 'bookmark'
| 'profile_update'
| 'role_change';
description: string;
timestamp: string;
metadata?: {
categoryName?: string;
score?: number;
questionCount?: number;
oldRole?: string;
newRole?: string;
};
}
/**
* Quiz history entry for user detail
*/
export interface UserQuizHistoryEntry {
id: string;
categoryId: string;
categoryName: string;
score: number;
totalQuestions: number;
percentage: number;
timeTaken: number; // seconds
completedAt: string;
}
/**
* User statistics for detail view
*/
export interface UserStatistics {
totalQuizzes: number;
averageScore: number;
totalQuestionsAnswered: number;
correctAnswers: number;
accuracy: number;
currentStreak: number;
longestStreak: number;
totalTimeSpent: number; // seconds
favoriteCategory?: {
id: string;
name: string;
quizCount: number;
};
recentActivity: {
lastQuizDate?: string;
lastLoginDate?: string;
quizzesThisWeek: number;
quizzesThisMonth: number;
};
}
/**
* Detailed user profile
*/
export interface AdminUserDetail {
id: string;
username: string;
email: string;
role: 'user' | 'admin';
isActive: boolean;
createdAt: string;
lastLoginAt?: string;
statistics: UserStatistics;
quizHistory: UserQuizHistoryEntry[];
activityTimeline: UserActivity[];
metadata?: {
ipAddress?: string;
userAgent?: string;
registrationMethod?: 'direct' | 'guest_conversion';
guestSessionId?: string;
};
}
/**
* API response wrapper for user detail
*/
export interface AdminUserDetailResponse {
success: boolean;
data: AdminUserDetail;
message?: string;
}

View File

@@ -0,0 +1,57 @@
/**
* Bookmark Interface
* Represents a bookmarked question
*/
export interface Bookmark {
id: string;
userId: string;
questionId: string;
question: BookmarkedQuestion;
createdAt: string;
}
/**
* Bookmarked Question Details
*/
export interface BookmarkedQuestion {
id: string;
questionText: string;
questionType: 'multiple-choice' | 'true-false' | 'written';
difficulty: 'easy' | 'medium' | 'hard';
categoryId: string;
categoryName: string;
options?: string[];
correctAnswer: string;
explanation?: string;
points: number;
tags?: string[];
}
/**
* Bookmarks Response
*/
export interface BookmarksResponse {
success: boolean;
data: {
bookmarks: Bookmark[];
total: number;
};
}
/**
* Add Bookmark Request
*/
export interface AddBookmarkRequest {
questionId: string;
}
/**
* Add Bookmark Response
*/
export interface AddBookmarkResponse {
success: boolean;
data: {
bookmark: Bookmark;
};
message: string;
}

View File

@@ -0,0 +1,82 @@
/**
* 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;
difficultyBreakdown?: {
easy: number;
medium: number;
hard: number;
};
}
/**
* Category Statistics
*/
export interface CategoryStats {
totalQuestions: number;
questionsByDifficulty: {
easy: number;
medium: number;
hard: number;
};
totalAttempts: number;
totalCorrect: number;
averageAccuracy: number;
averageScore?: 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' | 'trueFalse' | '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;
}

View File

@@ -0,0 +1,246 @@
import { User } from './user.model';
import { QuizSession, QuizSessionHistory } from './quiz.model';
/**
* User Dashboard Response
*/
export interface UserDataDashboard {
id: string;
username: string;
email: string;
role: string;
profileImage: string | null;
memberSince: string;
}
export interface StatsDashboard {
totalQuizzes: number
quizzesPassed: number
passRate: number
totalQuestionsAnswered: number
correctAnswers: number
overallAccuracy: number
currentStreak: number
longestStreak: number
streakStatus: string;
lastActiveDate: string | null
}
export interface RecentSessionsScoreDashboard {
earned: number
total: number
percentage: number
}
export interface RecentSessionsCategoryDashboard {
id: string
name: string
slug: string
icon: any
color: string
}
export interface RecentSessionsDashboard {
id: string
category: RecentSessionsCategoryDashboard
quizType: string
difficulty: string
status: string
score: RecentSessionsScoreDashboard
isPassed: boolean
questionsAnswered: number
correctAnswers: number
accuracy: number
timeSpent: number
completedAt: string
}
export interface CategoryPerformanceStats {
quizzesTaken: number
quizzesPassed: number
passRate: number
averageScore: number
totalQuestions: number
correctAnswers: number
accuracy: number
}
export interface CategoryPerformanceDashboard {
category: RecentSessionsCategoryDashboard
stats: CategoryPerformanceStats
lastAttempt: string
}
export interface RecentActivityDashboard {
date: string
quizzesCompleted: number
}
export interface UserDashboardResponse {
success: boolean;
data: UserDashboard
}
export interface UserDashboard {
user: UserDataDashboard;
stats: StatsDashboard;
recentSessions: RecentSessionsDashboard[]
categoryPerformance: CategoryPerformanceDashboard[]
recentActivity: RecentActivityDashboard[]
// 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;
data: {
sessions: QuizSessionHistory[];
pagination: PaginationInfo;
filters: {
category: null,
status: null,
startDate: null,
endDate: null
}
sorting: {
sortBy: string
sortOrder: string
}
};
}
/**
* 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;
}
/**
* User Profile Update Response
*/
export interface UserProfileUpdateResponse {
success: boolean;
data: {
user: User;
};
message: 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;
}

View 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;
}

View 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';

View File

@@ -0,0 +1,79 @@
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;
category?: {
id: string;
name: string;
slug?: string;
icon?: string;
color?: string;
guestAccessible?: boolean;
};
options?: string[] | { id: string; text: 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;
}

View File

@@ -0,0 +1,288 @@
import { Category } from './category.model';
import { Question } from './question.model';
export interface QuizSessionHistory {
time: {
spent: number,
limit: number | null,
percentage: number
},
createdAt: string;
id: string;
category?: {
id: string;
name: string;
slug: string;
icon: string;
color: string;
};
quizType: QuizType;
difficulty: string;
questions: {
answered: number,
total: number,
correct: number,
accuracy: number
};
score: {
earned: number
total: number
percentage: number
};
status: QuizStatus;
startedAt: string;
completedAt?: string;
isPassed?: boolean;
}
/**
* 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 {
success: true;
data: {
categoryId: string;
questionCount: number;
difficulty?: string; // 'easy', 'medium', 'hard', 'mixed'
quizType?: QuizType;
};
}
export interface QuizStartFormRequest {
categoryId: string;
questionCount: number;
difficulty?: string; // 'easy', 'medium', 'hard', 'mixed'
quizType?: QuizType;
}
/**
* Quiz Start Response
*/
export interface QuizStartResponse {
success: boolean;
data: {
sessionId: string;
questions: Question[];
totalQuestions: number;
message?: string;
};
}
/**
* Quiz Answer Submission
*/
export interface QuizAnswerSubmission {
questionId: string;
userAnswer: 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[];
}
// Response from /complete endpoint - questions are statistics
export interface CompletedQuizResult {
sessionId: string;
status: string;
category: {
id: string;
name: string;
slug: string;
icon: string;
color: string
},
quizType: string
difficulty: string
score: {
earned: number,
total: number,
percentage: number
},
questions: {
total: number,
answered: number,
correct: number,
incorrect: number,
unanswered: number
},
accuracy: number,
isPassed: boolean,
time: {
started: string,
completed: string,
taken: number,
limit: number,
isTimeout: boolean
}
}
export interface CompletedQuizResponse {
success: boolean
data: CompletedQuizResult
}
// Response from /review endpoint - questions are detailed array
export interface QuizReviewResult {
session: {
id: string;
status: string;
quizType: string;
difficulty: string;
category: {
id: string;
name: string;
slug: string;
icon: string;
color: string;
};
startedAt: string;
completedAt: string;
timeSpent: number;
};
summary: {
score: {
earned: number;
total: number;
percentage: number;
};
questions: {
total: number;
answered: number;
correct: number;
incorrect: number;
unanswered: number;
};
accuracy: number;
isPassed: boolean;
timeStatistics: {
totalTime: number;
averageTimePerQuestion: number;
timeLimit: number | null;
wasTimedOut: boolean;
};
};
questions: QuizQuestionResult[];
}
export interface QuizReviewResponse {
success: boolean;
data: QuizReviewResult;
message?: string;
}
/**
* Quiz Question Result
*/
export interface QuizQuestionResult {
id: string;
questionText: string;
questionType: string;
options: any;
difficulty: string;
points: number;
explanation: string;
tags: string[];
order: number;
correctAnswer: string | string[];
userAnswer: string | string[] | null;
isCorrect: boolean | null;
resultStatus: 'correct' | 'incorrect' | 'unanswered';
pointsEarned: number;
pointsPossible: number;
timeTaken: number | null;
answeredAt: string | null;
showExplanation: boolean;
wasAnswered: boolean;
// Legacy support
questionId?: string;
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[];
}

View File

@@ -0,0 +1,64 @@
/**
* 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;
data: {
user: User;
token: string;
};
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;
}