Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions src/API/fetchWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,14 @@ export const fetchWrapper = async (url, useToken = true, contentType = 'applicat
credentials: useToken ? 'include' : 'same-origin',
};

console.log(config);
console.log(body);
console.log(contentType);
console.log(method);
console.log(url);
console.log(useToken);

if (body && method !== 'GET') {
config.body = contentType === 'application/json' ? JSON.stringify(body) : body;
}

try {
const response = await fetch(url, config);

if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || `Request failed with status ${response.status}`);
Expand Down
1 change: 1 addition & 0 deletions src/domain/entity/ContentEntity.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class ContentEntity extends BaseEntity {
this.likesCount = props.likesCount || 0;
this.commentsCount = props.commentsCount || 0;
this.isLiked = !!props.isLiked;
this.isDraft = !!props.isDraft;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/domain/entity/CourseEntity.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export class CoursePreviewEntity extends ContentEntity {
this.duration = props.duration || 0;
this.completedLessonsCount = props.completedLessonsCount || 0;
this.lessonCount = props.lessonCount || 0;
this.course_types = props.course_types || [];
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/domain/entity/EventEntity.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export class EventEntity extends ContentEntity {
this.speakers = props.speakers || [];
this.activities = props.activities || [];
this.organizer = props.organizer;
this.scanners = props.scanners || [];
this.images = props.images || []; // all gallery images

// Interactions
Expand Down
39 changes: 29 additions & 10 deletions src/domain/mapper/EntityMapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class EntityMapper {
if (!data) return null;
console.log('[ProfileSync] Mapping toUser - Data:', data);
console.log('[ProfileSync] Mapping toUser - Stats Prop:', stats);

return new UserEntity({
id: data.id,
uid: data.documentId,
Expand Down Expand Up @@ -103,7 +103,8 @@ export class EntityMapper {
caption: dto.caption,
author: this.toUser(dto.author),
media: Array.from(dto.media?.values() || []).map(m => this.toMedia(m)),
article: dto.article ? this.toArticle(dto.article) : null
article: dto.article ? this.toArticle(dto.article) : null,
isDraft: !!dto.isDraft
});
}

Expand All @@ -129,7 +130,8 @@ export class EntityMapper {
video: this.toMedia(dto.video),
description: dto.description,
isPublic: dto.public,
instructor: this.toUser(dto.instructor)
instructor: this.toUser(dto.instructor),
isDraft: !!dto.isDraft
});
}

Expand Down Expand Up @@ -163,7 +165,8 @@ export class EntityMapper {
reviewsCount: dto.reviewsCount || 0,
duration: dto.duration || 0,
completedLessonsCount: dto.completedLessonsCount,
lessonCount: dto.lessonCount
lessonCount: dto.lessonCount,
isDraft: !!dto.isDraft
});
}

Expand Down Expand Up @@ -212,7 +215,17 @@ export class EntityMapper {
description: a.description,
time: a.time
})),
organizer: dto.organizer ? this.toUser(dto.organizer) : null
organizer: dto.organizer ? this.toUser(dto.organizer) : null,
scanners: Array.from(dto.scanners?.values() || []).map(s => ({
id: s.id,
documentId: s.documentId,
user: s.users_permissions_user ? {
id: s.users_permissions_user.id,
username: s.users_permissions_user.username,
email: s.users_permissions_user.email,
} : null
})),
isDraft: !!dto.isDraft
});
}

Expand Down Expand Up @@ -258,6 +271,7 @@ export class EntityMapper {
title: week.title,
lessons: Array.from(week.lessons?.values() || []).map(lesson => this.toLesson(lesson))
}));
console.log(dto, "dto")

return new CoursePreviewEntity({
id: dto.id,
Expand Down Expand Up @@ -285,7 +299,9 @@ export class EntityMapper {
reviewsCount: dto.reviewsCount || 0,
duration: dto.duration || 0,
completedLessonsCount: dto.completedLessonsCount,
lessonCount: dto.lessonCount
lessonCount: dto.lessonCount,
isDraft: dto.isDraft,
course_types: Array.from(dto.course_types?.values() || [])
});
}

Expand Down Expand Up @@ -338,7 +354,7 @@ export class EntityMapper {
memoryLimit: dto.memoryLimit,
submissionStatus: dto.submissionStatus || 'New',

// Nested relations
problemTypes: Array.from(dto.problem_types?.values?.() || []),
testCases: Array.from(dto.test_cases?.values?.() || []).map(tc => ({
id: tc.id,
documentId: tc.documentId,
Expand All @@ -365,7 +381,8 @@ export class EntityMapper {
commentsCount: dto.interactions?.commentsCount || 0,

availableLanguages: dto.availableLanguages,
points: dto.points
points: dto.points,
isDraft: !!dto.isDraft
});
}

Expand Down Expand Up @@ -410,8 +427,8 @@ export class EntityMapper {
description: dto.description,
image: dto.image ? this.toMedia(dto.image) : null,
author: dto.publisher ? this.toUser(dto.publisher) : null,
isDraft: !!dto.isDraft
});
return entity;
}

/**
Expand All @@ -438,6 +455,7 @@ export class EntityMapper {
author: dto.author
? { username: dto.author.username, avatar: dto.author.avatar ? this.toMedia(dto.author.avatar) : null }
: null,
isDraft: !!dto.isDraft
});
}

Expand Down Expand Up @@ -480,7 +498,8 @@ export class EntityMapper {
flowData: dto.flowData,
color: dto.color,
icon: dto.icon,
author: this.toUser(dto.author)
author: this.toUser(dto.author),
isDraft: !!dto.isDraft
});
}

Expand Down
5 changes: 3 additions & 2 deletions src/domain/useCase/useCreateBlog.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const useCreateBlog = () => {
const [error, setError] = useState(null);
const [uploadProgress, setUploadProgress] = useState(0);

const createBlog = async ({ description, imageFile, tags }) => {
const createBlog = async ({ description, imageFile, tags, isDraft }) => {
setLoading(true);
setError(null);
setUploadProgress(0);
Expand All @@ -38,7 +38,8 @@ export const useCreateBlog = () => {
const blogData = {
description,
imageId, // Maps to `image` in BlogRequest
tagIds: tags // Maps to `tags` in BlogRequest
tagIds: tags, // Maps to `tags` in BlogRequest
isDraft
};

const response = await blogRepo.create(blogData);
Expand Down
28 changes: 28 additions & 0 deletions src/domain/useCase/useCreateCategorization.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useAsyncUseCase } from './useAsyncUseCase';
import { CategorizationRepository } from '../../infrastructure/repository/CategorizationRepository';
import { useMemo, useCallback } from 'react';

/**
* Use case hook for creating categorization tracks (Course or Problem types).
*/
export const useCreateCategorization = () => {
const repository = useMemo(() => new CategorizationRepository(), []);

const createCourseTypeLogic = useCallback(async (data) => {
return await repository.createCourseType(data);
}, [repository]);

const createProblemTypeLogic = useCallback(async (data) => {
return await repository.createProblemType(data);
}, [repository]);

const { execute: createCourseType, inProgress: isCreatingCourse, error: courseError } = useAsyncUseCase(createCourseTypeLogic);
const { execute: createProblemType, inProgress: isCreatingProblem, error: problemError } = useAsyncUseCase(createProblemTypeLogic);

return {
createCourseType,
createProblemType,
inProgress: isCreatingCourse || isCreatingProblem,
error: courseError || problemError
};
};
25 changes: 25 additions & 0 deletions src/domain/useCase/useCreateEventActivity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState, useMemo } from 'react';
import { EventRepository } from '@infrastructure/repository/EventRepository';
import { toast } from 'react-hot-toast';

export const useCreateEventActivity = () => {
const [inProgress, setInProgress] = useState(false);
const eventRepo = useMemo(() => new EventRepository(), []);

const createEventActivity = async (dtoData) => {
setInProgress(true);
try {
const response = await eventRepo.createActivity(dtoData);
return response;
} catch (error) {
console.error('[useCreateEventActivity] Error:', error);
const errorMessage = error.response?.data?.error?.message || error.message || "Failed to create activity";
toast.error(`Activity Creation Failed: ${errorMessage}`);
throw error;
} finally {
setInProgress(false);
}
};

return { createEventActivity, inProgress };
};
25 changes: 25 additions & 0 deletions src/domain/useCase/useCreateEventScanner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState, useMemo } from 'react';
import { EventRepository } from '@infrastructure/repository/EventRepository';
import { toast } from 'react-hot-toast';

export const useCreateEventScanner = () => {
const [inProgress, setInProgress] = useState(false);
const eventRepo = useMemo(() => new EventRepository(), []);

const createEventScanner = async (dtoData) => {
setInProgress(true);
try {
const response = await eventRepo.createScanner(dtoData);
return response;
} catch (error) {
console.error('[useCreateEventScanner] Error:', error);
const errorMessage = error.response?.data?.error?.message || error.message || "Failed to authorize scanner";
toast.error(`Auth Error: ${errorMessage}`);
throw error;
} finally {
setInProgress(false);
}
};

return { createEventScanner, inProgress };
};
25 changes: 25 additions & 0 deletions src/domain/useCase/useCreateEventSpeaker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState, useMemo } from 'react';
import { EventRepository } from '@infrastructure/repository/EventRepository';
import { toast } from 'react-hot-toast';

export const useCreateEventSpeaker = () => {
const [inProgress, setInProgress] = useState(false);
const eventRepo = useMemo(() => new EventRepository(), []);

const createEventSpeaker = async (dtoData) => {
setInProgress(true);
try {
const response = await eventRepo.createSpeaker(dtoData);
return response;
} catch (error) {
console.error('[useCreateEventSpeaker] Error:', error);
const errorMessage = error.response?.data?.error?.message || error.message || "Failed to organize speaker";
toast.error(`Speaker Orchestration Failed: ${errorMessage}`);
throw error;
} finally {
setInProgress(false);
}
};

return { createEventSpeaker, inProgress };
};
4 changes: 3 additions & 1 deletion src/domain/useCase/useFetchCoursePreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ export const useFetchCoursePreview = () => {

// Strapi v4/v5 deep populate for weeks and their lessons
// Use explicit populate settings to avoid 'related' key validation errors in some Strapi versions
const populateQuery = 'populate[picture]=true&populate[weeks][populate][lessons][populate][video]=true';
const populateQuery = 'populate[picture]=true&populate[weeks][populate][lessons][populate][video]=true&populate[course_types]=true';
const rawData = await repository.getPreview(`${documentId}?${populateQuery}`);
const dto = new CourseDTO(rawData);
// console.log(rawData, "raw data")
console.log("EntityMapper.toCoursePreview(dto)", EntityMapper.toCoursePreview(dto))
return EntityMapper.toCoursePreview(dto);
}, []);

Expand Down
2 changes: 1 addition & 1 deletion src/domain/useCase/useFetchEvent.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const useFetchEvent = () => {

const fetchLogic = useCallback(async (id) => {
if (!id) throw new Error("Event ID is required");
console.log("id",id);
console.log("id", id);

const rawData = await repository.getById(id);

Expand Down
13 changes: 11 additions & 2 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@

/* ── Radius & Shadows ── */
--radius-bento: 12px;
--shadow-halo: 0 0 0 1px var(--border-subtle), 0 4px 24px -2px rgba(0,0,0,0.05);
--shadow-whisper: rgba(0,0,0,0.05) 0 4px 24px;
--shadow-halo: 0 0 0 1px var(--border-subtle), 0 2px 12px -1px rgba(0,0,0,0.03);
--shadow-whisper: rgba(0,0,0,0.03) 0 2px 12px;
--shadow-ring: 0 0 0 1px var(--ring-warm);
}

Expand Down Expand Up @@ -220,6 +220,15 @@
.shimmer {
@apply relative overflow-hidden before:absolute before:inset-0 before:-translate-x-full before:animate-[shimmer_2s_infinite] before:bg-gradient-to-r before:from-transparent before:via-white/20 before:to-transparent;
}

.animation-fade-in {
animation: fadeIn 0.4s ease-out forwards;
}
}

@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}

@keyframes shimmer {
Expand Down
4 changes: 2 additions & 2 deletions src/infrastructure/DTO/BaseContentDTO.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class BaseContentDTO {
this.commentsCount = typeof data.commentsCount === 'number' ? data.commentsCount : (interactions.commentsCount || 0);
this.isLiked = !!(data.isLikedByMe || interactions.isLikedByMe);

// Custom Draft State
this.isDraft = data.isDraft ?? true; // Default to true if not provided
// Custom Draft State (Standardized across CMS)
this.isDraft = data.isDraft !== undefined ? !!data.isDraft : true;
}
}
2 changes: 1 addition & 1 deletion src/infrastructure/DTO/CourseDTO.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class CourseDTO extends BaseContentDTO {
this.duration = data.duration || 0; // {number} - total minutes
this.rating = data.interactions?.rating?.average || 0; // {number}
this.reviewsCount = data.interactions?.rating?.count || 0; // {number}

this.isDraft = data.isDraft || false; // {boolean}
// Detailed Entitlement
this.entitlement = data.entitlement ? new EntitlementDTO(data.entitlement) : null; // {EntitlementDTO | null}

Expand Down
4 changes: 2 additions & 2 deletions src/infrastructure/DTO/Request/ArticleRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ export class ArticleRequest extends BaseRequest {
return {
data: {
title: this.title,
content: this.contentBlocks,
contentBlocks: this.contentBlocks,
tags: this.tags,
isDraft: this.isDraft
isDraft: !!this.isDraft
}
};
}
Expand Down
10 changes: 10 additions & 0 deletions src/infrastructure/DTO/Request/BaseRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ export class BaseRequest {
return payload;
}

/**
* Standard Strapi payload wrapper.
* @returns {object}
*/
toPayload() {
return {
data: this.toJSON()
};
}

/**
* Abstract validation method.
* Includes security audit for all fields.
Expand Down
Loading
Loading