From ee5fa17e882df427c7c214cd3e3723d06d0bb62c Mon Sep 17 00:00:00 2001 From: Claus Haas Date: Mon, 22 Jul 2024 12:11:56 -0300 Subject: [PATCH 1/4] add services for retrieving saved, favorited and completed lessons by user --- app/services/lesson.service.server.ts | 195 +++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 2 deletions(-) diff --git a/app/services/lesson.service.server.ts b/app/services/lesson.service.server.ts index c0a83b6..dd2baf4 100644 --- a/app/services/lesson.service.server.ts +++ b/app/services/lesson.service.server.ts @@ -16,6 +16,7 @@ import {logger} from '~/utils/logger.util.js'; import {type TLessonDataForCache} from '~/cache/populate-lessons-to-cache.js'; import {memoryCache} from '~/cache/memory-cache.js'; import {type TTag} from '~/types/tag.type.js'; +import {type TypeUserSession} from '~/types/user-session.type'; export class LessonService { private static cache: typeof memoryCache; @@ -504,6 +505,198 @@ export class LessonService { } } + public async getCompletedLessonsByUser(user: TypeUserSession): Promise & {link: string}>>> { + const lessons = await this._model.lesson.findMany({ + where: { + completedBy: { + some: { + userId: user.id, + }, + }, + }, + include: { + modules: { + include: { + module: { + include: { + courses: { + include: { + course: { + include: { + delegateAuthTo: { + include: { + subscriptions: { + where: { + userId: user.id, + expiresAt: { + gte: new Date(), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }); + + const lessonsWithLinks = lessons.map(lesson => { + const hasActiveSubscription = lesson.modules.some(lessonToModule => + lessonToModule.module.courses.some(course => + course.course.delegateAuthTo.some(delegateAuthTo => + delegateAuthTo.subscriptions.some(subscription => subscription.expiresAt >= new Date()), // eslint-disable-line max-nested-callbacks + ), + ), + ); + + return { + ...lesson, + content: hasActiveSubscription ? lesson.content : lesson.marketingContent, + videoSourceUrl: hasActiveSubscription ? lesson.videoSourceUrl : lesson.marketingVideoUrl, + link: `/courses/${lesson.modules[0].module.courses[0].course.slug}/${lesson.modules[0].module.slug}/${lesson.slug}`, + }; + }); + + return { + status: 'SUCCESSFUL', + data: lessonsWithLinks, + }; + } + + public async getSavedLessonsByUser(user: TypeUserSession): Promise & {link: string}>>> { + const lessons = await this._model.lesson.findMany({ + where: { + savedBy: { + some: { + userId: user.id, + }, + }, + }, + include: { + modules: { + include: { + module: { + include: { + courses: { + include: { + course: { + include: { + delegateAuthTo: { + include: { + subscriptions: { + where: { + userId: user.id, + expiresAt: { + gte: new Date(), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }); + + const lessonsWithLinks = lessons.map(lesson => { + const hasActiveSubscription = lesson.modules.some(lessonToModule => + lessonToModule.module.courses.some(course => + course.course.delegateAuthTo.some(delegateAuthTo => + delegateAuthTo.subscriptions.some(subscription => subscription.expiresAt >= new Date()), // eslint-disable-line max-nested-callbacks + ), + ), + ); + + return { + ...lesson, + content: hasActiveSubscription ? lesson.content : lesson.marketingContent, + videoSourceUrl: hasActiveSubscription ? lesson.videoSourceUrl : lesson.marketingVideoUrl, + link: `/courses/${lesson.modules[0].module.courses[0].course.slug}/${lesson.modules[0].module.slug}/${lesson.slug}`, + }; + }); + + return { + status: 'SUCCESSFUL', + data: lessonsWithLinks, + }; + } + + public async getFavoritedLessonsByUser(user: TypeUserSession): Promise & {link: string}>>> { + const lessons = await this._model.lesson.findMany({ + where: { + favoritedBy: { + some: { + userId: user.id, + }, + }, + }, + include: { + modules: { + include: { + module: { + include: { + courses: { + include: { + course: { + include: { + delegateAuthTo: { + include: { + subscriptions: { + where: { + userId: user.id, + expiresAt: { + gte: new Date(), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }); + + const lessonsWithLinks = lessons.map(lesson => { + const hasActiveSubscription = lesson.modules.some(lessonToModule => + lessonToModule.module.courses.some(course => + course.course.delegateAuthTo.some(delegateAuthTo => + delegateAuthTo.subscriptions.some(subscription => subscription.expiresAt >= new Date()), // eslint-disable-line max-nested-callbacks + ), + ), + ); + + return { + ...lesson, + content: hasActiveSubscription ? lesson.content : lesson.marketingContent, + videoSourceUrl: hasActiveSubscription ? lesson.videoSourceUrl : lesson.marketingVideoUrl, + link: `/courses/${lesson.modules[0].module.courses[0].course.slug}/${lesson.modules[0].module.slug}/${lesson.slug}`, + }; + }); + + return { + status: 'SUCCESSFUL', + data: lessonsWithLinks, + }; + } + private _hasActiveSubscription(user: TUser | undefined, lessonToModule: TPrismaPayloadGetLessonById): boolean { const isAdmin = user?.roles?.includes('admin'); const hasActiveSubscription = lessonToModule.module.courses.some( @@ -517,5 +710,3 @@ export class LessonService { return isAdmin || hasActiveSubscription; } } - -type test = Prisma.LessonToModuleCreateManyLessonInputEnvelope; From a0e982e2d9cb6c79b24dd1615c0454dcee6e004d Mon Sep 17 00:00:00 2001 From: Claus Haas Date: Mon, 22 Jul 2024 17:40:04 -0300 Subject: [PATCH 2/4] add profile page and completed lessons page --- app/components/navigation-bar.tsx | 3 +- app/routes/admin.comments.tsx | 4 +- app/routes/admin.courses_.tsx | 4 +- app/routes/login.tsx | 1 + app/routes/profile.completed-lessons.tsx | 64 ++++++++++++++++++ app/routes/profile.favorited-lessons.tsx | 0 app/routes/profile.saved-lessons.tsx | 0 app/routes/profile.tsx | 64 ++++++++++++++++++ app/services/lesson.service.server.ts | 82 ++++++++++++------------ app/types/lesson.type.ts | 40 ++++++++++++ 10 files changed, 217 insertions(+), 45 deletions(-) create mode 100644 app/routes/profile.completed-lessons.tsx create mode 100644 app/routes/profile.favorited-lessons.tsx create mode 100644 app/routes/profile.saved-lessons.tsx create mode 100644 app/routes/profile.tsx diff --git a/app/components/navigation-bar.tsx b/app/components/navigation-bar.tsx index d84a513..b5a7c54 100644 --- a/app/components/navigation-bar.tsx +++ b/app/components/navigation-bar.tsx @@ -65,8 +65,9 @@ export function NavigateBar({userData}: {readonly userData: TypeUserSession | un
    {pathname !== '/' && Home} - {userData?.roles?.includes('admin') && pathname !== '/admin' && Admin} + {userData?.roles?.includes('admin') && !pathname.startsWith('/admin') && Admin} {pathname !== '/courses' && Cursos} + {!pathname.startsWith('/profile') && userData?.id && Minha Área} {userData?.id && pathname !== '/logout' && Sair}
diff --git a/app/routes/admin.comments.tsx b/app/routes/admin.comments.tsx index 83e907b..64063d3 100644 --- a/app/routes/admin.comments.tsx +++ b/app/routes/admin.comments.tsx @@ -14,8 +14,8 @@ export const loader = defineLoader(({request}: LoaderFunctionArgs) => ({ export default function Comments() { return ( -
+

Comments

-
+ ); } diff --git a/app/routes/admin.courses_.tsx b/app/routes/admin.courses_.tsx index 8471465..30d556b 100644 --- a/app/routes/admin.courses_.tsx +++ b/app/routes/admin.courses_.tsx @@ -92,7 +92,7 @@ export default function Courses() { } = useLoaderData(); return ( -
+ <>
@@ -105,6 +105,6 @@ export default function Courses() { ))}
-
+ ); } diff --git a/app/routes/login.tsx b/app/routes/login.tsx index 98c32b4..e90805e 100644 --- a/app/routes/login.tsx +++ b/app/routes/login.tsx @@ -96,6 +96,7 @@ export default function Login() { return ( <> +
diff --git a/app/routes/profile.completed-lessons.tsx b/app/routes/profile.completed-lessons.tsx new file mode 100644 index 0000000..5d0db47 --- /dev/null +++ b/app/routes/profile.completed-lessons.tsx @@ -0,0 +1,64 @@ +import {type LoaderFunctionArgs, unstable_defineLoader as defineLoader} from '@remix-run/node'; +import {useLoaderData, type MetaArgs_SingleFetch} from '@remix-run/react'; +import {LessonEntityCard} from '~/components/entities-cards'; +import {LessonActivityService} from '~/services/lesson-activity.service.server'; +import {LessonService} from '~/services/lesson.service.server'; +import {type TypeUserSession} from '~/types/user-session.type'; +import {getUserSession} from '~/utils/session.server'; + +export const meta = ({data}: MetaArgs_SingleFetch) => [ + {title: 'Yoga em Movimento - Área Pessoal'}, + {name: 'description', content: 'Área pessoal de cada aluno dentro do site da Yoga em Movimento.'}, + {name: 'robots', content: 'noindex, nofollow'}, + ...data!.meta, +]; + +export const loader = defineLoader(async ({request, response}: LoaderFunctionArgs) => { + const userSession = await getUserSession(request.headers.get('Cookie')); + const userData = userSession.data as TypeUserSession; + + if (!userData.id) { + response!.headers.set('Location', '/'); + response!.status = 303; + + throw response; // eslint-disable-line @typescript-eslint/only-throw-error + } + + const completedLessons = await new LessonService().getCompletedLessonsByUser(userData); + + const completedLessonsWithActivity = completedLessons.data.map(lesson => ({ + ...lesson, + activity: new LessonActivityService().getLessonActivityForUser(lesson.lessonSlug, userData.id), + })); + + return { + meta: [{tagName: 'link', rel: 'canonical', href: new URL('/profile/completed-lessons', request.url).toString()}], + userData, + completedLessonsWithActivity, + }; +}); + +export default function CompletedLessons() { + const {completedLessonsWithActivity, userData} = useLoaderData(); + + if (completedLessonsWithActivity.length === 0) { + return ( +
+

Aulas Assistidas

+

{userData.firstName}, você ainda não completou nenhuma aula. Conforme assistir as aulas e marcá-las como completas, a lista de suas aulas assistidas aparecerá aqui.

+
+ ); + } + + return ( +
+

Aulas Assistidas

+ +
+ {completedLessonsWithActivity.map(lesson => ( + + ))} +
+
+ ); +} diff --git a/app/routes/profile.favorited-lessons.tsx b/app/routes/profile.favorited-lessons.tsx new file mode 100644 index 0000000..e69de29 diff --git a/app/routes/profile.saved-lessons.tsx b/app/routes/profile.saved-lessons.tsx new file mode 100644 index 0000000..e69de29 diff --git a/app/routes/profile.tsx b/app/routes/profile.tsx new file mode 100644 index 0000000..5be2e6e --- /dev/null +++ b/app/routes/profile.tsx @@ -0,0 +1,64 @@ +import {type LoaderFunctionArgs, unstable_defineLoader as defineLoader} from '@remix-run/node'; +import { + Link, Outlet, useLocation, + type MetaArgs_SingleFetch, + useLoaderData, +} from '@remix-run/react'; +import {NavigateBar} from '~/components/navigation-bar.js'; +import {type TypeUserSession} from '~/types/user-session.type'; +import {getUserSession} from '~/utils/session.server'; + +export const meta = ({data}: MetaArgs_SingleFetch) => [ + {title: 'Yoga em Movimento - Área Pessoal'}, + {name: 'description', content: 'Área pessoal de cada aluno dentro do site da Yoga em Movimento.'}, + {name: 'robots', content: 'noindex, nofollow'}, + ...data!.meta, +]; + +export const loader = defineLoader(async ({request, response}: LoaderFunctionArgs) => { + const userSession = await getUserSession(request.headers.get('Cookie')); + const data = userSession.data as TypeUserSession; + + if (!data.id) { + response!.headers.set('Location', '/'); + response!.status = 303; + + throw response; // eslint-disable-line @typescript-eslint/only-throw-error + } + + return { + meta: [{tagName: 'link', rel: 'canonical', href: new URL('/profile', request.url).toString()}], + userData: data, + }; +}); + +export default function Profile() { + const {userData} = useLoaderData(); + const {pathname} = useLocation(); + + return userData?.id && ( + <> + + +
+ +
+ {(pathname === '/profile' || pathname === '/profile/') && ( +

Selecione a opção no menu ao lado

+ )} + +
+
+ + ); +} diff --git a/app/services/lesson.service.server.ts b/app/services/lesson.service.server.ts index dd2baf4..039f71e 100644 --- a/app/services/lesson.service.server.ts +++ b/app/services/lesson.service.server.ts @@ -8,6 +8,7 @@ import { type TLesson, type TPrismaPayloadGetLessonList, type TPrismaPayloadGetLessonById, + type TPrismaPayloadGetCompletedLessons, } from '../types/lesson.type'; import {Lesson} from '../entities/lesson.entity.server'; import {type TServiceReturn} from '../types/service-return.type'; @@ -505,31 +506,44 @@ export class LessonService { } } - public async getCompletedLessonsByUser(user: TypeUserSession): Promise & {link: string}>>> { - const lessons = await this._model.lesson.findMany({ + public async getCompletedLessonsByUser(user: TypeUserSession): Promise> { + const completedLessons = await this._model.completedLessons.findMany({ where: { - completedBy: { - some: { - userId: user.id, - }, - }, + userId: user.id, }, - include: { - modules: { - include: { - module: { - include: { - courses: { - include: { - course: { - include: { - delegateAuthTo: { - include: { - subscriptions: { - where: { - userId: user.id, - expiresAt: { - gte: new Date(), + orderBy: { + updatedAt: 'desc', + }, + select: { + lessonSlug: true, + userId: true, + updatedAt: true, + id: true, + lesson: { + select: { + name: true, + slug: true, + thumbnailUrl: true, + description: true, + modules: { + select: { + module: { + select: { + slug: true, + courses: { + select: { + course: { + select: { + slug: true, + delegateAuthTo: { + select: { + subscriptions: { + where: { + userId: user.id, + expiresAt: { + gte: new Date(), + }, + }, }, }, }, @@ -546,26 +560,14 @@ export class LessonService { }, }); - const lessonsWithLinks = lessons.map(lesson => { - const hasActiveSubscription = lesson.modules.some(lessonToModule => - lessonToModule.module.courses.some(course => - course.course.delegateAuthTo.some(delegateAuthTo => - delegateAuthTo.subscriptions.some(subscription => subscription.expiresAt >= new Date()), // eslint-disable-line max-nested-callbacks - ), - ), - ); - - return { - ...lesson, - content: hasActiveSubscription ? lesson.content : lesson.marketingContent, - videoSourceUrl: hasActiveSubscription ? lesson.videoSourceUrl : lesson.marketingVideoUrl, - link: `/courses/${lesson.modules[0].module.courses[0].course.slug}/${lesson.modules[0].module.slug}/${lesson.slug}`, - }; - }); + const completedLessonsWithLinks = completedLessons.map(lesson => ({ + ...lesson, + link: `/courses/${lesson.lesson.modules[0].module.courses[0].course.slug}/${lesson.lesson.modules[0].module.slug}/${lesson.lesson.slug}`, + })); return { status: 'SUCCESSFUL', - data: lessonsWithLinks, + data: completedLessonsWithLinks, }; } diff --git a/app/types/lesson.type.ts b/app/types/lesson.type.ts index 164aba7..d16987b 100644 --- a/app/types/lesson.type.ts +++ b/app/types/lesson.type.ts @@ -93,3 +93,43 @@ export type TPrismaPayloadGetLessonById = Prisma.LessonToModuleGetPayload<{ }; }; }>; + +export type TPrismaPayloadGetCompletedLessons = Array & {link: string}>; From 7460de0a36351dc1f6f880aa48b38d4a68dd3e2f Mon Sep 17 00:00:00 2001 From: Claus Haas Date: Mon, 22 Jul 2024 17:48:29 -0300 Subject: [PATCH 3/4] fix icons tooltips --- .../courses.$course-slug.$module-slug.$lesson-slug.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/routes/courses.$course-slug.$module-slug.$lesson-slug.tsx b/app/routes/courses.$course-slug.$module-slug.$lesson-slug.tsx index e5543d5..6a6da15 100644 --- a/app/routes/courses.$course-slug.$module-slug.$lesson-slug.tsx +++ b/app/routes/courses.$course-slug.$module-slug.$lesson-slug.tsx @@ -47,7 +47,7 @@ export const loader = defineLoader(async ({request, params}: LoaderFunctionArgs) const userId = (userSession.data as TUser).id; const {data: lesson} = new LessonService().getBySlugFromCache(moduleSlug!, lessonSlug!, userSession.data as TUser); - const {data: module} = new ModuleService().getBySlugFromCache(courseSlug!, moduleSlug!, userSession.data as TUser); + const {data: module} = new ModuleService().getBySlugFromCache(courseSlug!, moduleSlug!, userSession.data as TUser, []); const {data: course} = new CourseService().getBySlugFromCache(courseSlug!, userSession.data as TUser); const userLessonActivity = new LessonActivityService().getLessonActivityForUser(lessonSlug!, userId); @@ -169,7 +169,7 @@ export default function Lesson() { - {userLessonActivity.data?.completed ?

Remover Aula Salva

:

Salvar Aula

} + {userLessonActivity.data?.saved ?

Remover Aula Salva

:

Salvar Aula

}
@@ -186,7 +186,7 @@ export default function Lesson() { - {userLessonActivity.data?.completed ?

Remover Aula Favorita

:

Favoritar Aula

} + {userLessonActivity.data?.favorited ?

Remover Aula Favorita

:

Favoritar Aula

}
From 99f20ee6ef5eeeb2e4f5363d3702865aa77e65dd Mon Sep 17 00:00:00 2001 From: Claus Haas Date: Mon, 22 Jul 2024 18:57:31 -0300 Subject: [PATCH 4/4] add pages for favorited and saved lessons --- app/routes/profile.completed-lessons.tsx | 4 +- app/routes/profile.favorited-lessons.tsx | 64 +++++++++ app/routes/profile.saved-lessons.tsx | 64 +++++++++ app/routes/profile.tsx | 4 +- app/services/lesson.service.server.ts | 167 ++++++++++++----------- app/types/lesson.type.ts | 80 +++++++++++ 6 files changed, 299 insertions(+), 84 deletions(-) diff --git a/app/routes/profile.completed-lessons.tsx b/app/routes/profile.completed-lessons.tsx index 5d0db47..d4c995c 100644 --- a/app/routes/profile.completed-lessons.tsx +++ b/app/routes/profile.completed-lessons.tsx @@ -7,8 +7,8 @@ import {type TypeUserSession} from '~/types/user-session.type'; import {getUserSession} from '~/utils/session.server'; export const meta = ({data}: MetaArgs_SingleFetch) => [ - {title: 'Yoga em Movimento - Área Pessoal'}, - {name: 'description', content: 'Área pessoal de cada aluno dentro do site da Yoga em Movimento.'}, + {title: 'Yoga em Movimento - Área Pessoal - Aulas Assistidas'}, + {name: 'description', content: 'Aulas assistidas por cada aluno.'}, {name: 'robots', content: 'noindex, nofollow'}, ...data!.meta, ]; diff --git a/app/routes/profile.favorited-lessons.tsx b/app/routes/profile.favorited-lessons.tsx index e69de29..14b2855 100644 --- a/app/routes/profile.favorited-lessons.tsx +++ b/app/routes/profile.favorited-lessons.tsx @@ -0,0 +1,64 @@ +import {type LoaderFunctionArgs, unstable_defineLoader as defineLoader} from '@remix-run/node'; +import {useLoaderData, type MetaArgs_SingleFetch} from '@remix-run/react'; +import {LessonEntityCard} from '~/components/entities-cards'; +import {LessonActivityService} from '~/services/lesson-activity.service.server'; +import {LessonService} from '~/services/lesson.service.server'; +import {type TypeUserSession} from '~/types/user-session.type'; +import {getUserSession} from '~/utils/session.server'; + +export const meta = ({data}: MetaArgs_SingleFetch) => [ + {title: 'Yoga em Movimento - Área Pessoal - Aulas Favoritadas'}, + {name: 'description', content: 'Aulas favoritadas de cada aluno.'}, + {name: 'robots', content: 'noindex, nofollow'}, + ...data!.meta, +]; + +export const loader = defineLoader(async ({request, response}: LoaderFunctionArgs) => { + const userSession = await getUserSession(request.headers.get('Cookie')); + const userData = userSession.data as TypeUserSession; + + if (!userData.id) { + response!.headers.set('Location', '/'); + response!.status = 303; + + throw response; // eslint-disable-line @typescript-eslint/only-throw-error + } + + const favoritedLessons = await new LessonService().getFavoritedLessonsByUser(userData); + + const favoritedLessonsWithActivity = favoritedLessons.data.map(lesson => ({ + ...lesson, + activity: new LessonActivityService().getLessonActivityForUser(lesson.lessonSlug, userData.id), + })); + + return { + meta: [{tagName: 'link', rel: 'canonical', href: new URL('/profile/completed-lessons', request.url).toString()}], + userData, + favoritedLessonsWithActivity, + }; +}); + +export default function FavoritedLessons() { + const {favoritedLessonsWithActivity, userData} = useLoaderData(); + + if (favoritedLessonsWithActivity.length === 0) { + return ( +
+

Aulas Favoritas

+

{userData.firstName}, você ainda não favoritou nenhuma aula. Conforme assistir as aulas e marcá-las como favoritas, a lista de suas aulas favoritas aparecerá aqui.

+
+ ); + } + + return ( +
+

Aulas Favoritas

+ +
+ {favoritedLessonsWithActivity.map(lesson => ( + + ))} +
+
+ ); +} diff --git a/app/routes/profile.saved-lessons.tsx b/app/routes/profile.saved-lessons.tsx index e69de29..e2c68bf 100644 --- a/app/routes/profile.saved-lessons.tsx +++ b/app/routes/profile.saved-lessons.tsx @@ -0,0 +1,64 @@ +import {type LoaderFunctionArgs, unstable_defineLoader as defineLoader} from '@remix-run/node'; +import {useLoaderData, type MetaArgs_SingleFetch} from '@remix-run/react'; +import {LessonEntityCard} from '~/components/entities-cards'; +import {LessonActivityService} from '~/services/lesson-activity.service.server'; +import {LessonService} from '~/services/lesson.service.server'; +import {type TypeUserSession} from '~/types/user-session.type'; +import {getUserSession} from '~/utils/session.server'; + +export const meta = ({data}: MetaArgs_SingleFetch) => [ + {title: 'Yoga em Movimento - Área Pessoal - Aulas salvas'}, + {name: 'description', content: 'Aulas salvadas por cada aluno.'}, + {name: 'robots', content: 'noindex, nofollow'}, + ...data!.meta, +]; + +export const loader = defineLoader(async ({request, response}: LoaderFunctionArgs) => { + const userSession = await getUserSession(request.headers.get('Cookie')); + const userData = userSession.data as TypeUserSession; + + if (!userData.id) { + response!.headers.set('Location', '/'); + response!.status = 303; + + throw response; // eslint-disable-line @typescript-eslint/only-throw-error + } + + const savedLessons = await new LessonService().getSavedLessonsByUser(userData); + + const savedLessonsWithActivity = savedLessons.data.map(lesson => ({ + ...lesson, + activity: new LessonActivityService().getLessonActivityForUser(lesson.lessonSlug, userData.id), + })); + + return { + meta: [{tagName: 'link', rel: 'canonical', href: new URL('/profile/completed-lessons', request.url).toString()}], + userData, + savedLessonsWithActivity, + }; +}); + +export default function CompletedLessons() { + const {savedLessonsWithActivity, userData} = useLoaderData(); + + if (savedLessonsWithActivity.length === 0) { + return ( +
+

Aulas Salvadas

+

{userData.firstName}, você ainda não salcou nenhuma aula. Conforme assistir as aulas e marcá-las como salvas, a lista de suas aulas salvadas aparecerá aqui.

+
+ ); + } + + return ( +
+

Aulas Salvadas

+ +
+ {savedLessonsWithActivity.map(lesson => ( + + ))} +
+
+ ); +} diff --git a/app/routes/profile.tsx b/app/routes/profile.tsx index 5be2e6e..f767242 100644 --- a/app/routes/profile.tsx +++ b/app/routes/profile.tsx @@ -46,10 +46,10 @@ export default function Profile() {

Aulas Assistidas

-

Aulas salvas

+

Aulas Salvadas

-

Aulas favoritadas

+

Aulas Favoritadas

diff --git a/app/services/lesson.service.server.ts b/app/services/lesson.service.server.ts index 039f71e..faac8ab 100644 --- a/app/services/lesson.service.server.ts +++ b/app/services/lesson.service.server.ts @@ -9,6 +9,8 @@ import { type TPrismaPayloadGetLessonList, type TPrismaPayloadGetLessonById, type TPrismaPayloadGetCompletedLessons, + type TPrismaPayloadGetSavedLessons, + type TPrismaPayloadGetFavoritedLessons, } from '../types/lesson.type'; import {Lesson} from '../entities/lesson.entity.server'; import {type TServiceReturn} from '../types/service-return.type'; @@ -510,6 +512,7 @@ export class LessonService { const completedLessons = await this._model.completedLessons.findMany({ where: { userId: user.id, + isCompleted: true, }, orderBy: { updatedAt: 'desc', @@ -571,31 +574,45 @@ export class LessonService { }; } - public async getSavedLessonsByUser(user: TypeUserSession): Promise & {link: string}>>> { - const lessons = await this._model.lesson.findMany({ + public async getSavedLessonsByUser(user: TypeUserSession): Promise> { + const savedLessons = await this._model.savedLessons.findMany({ where: { - savedBy: { - some: { - userId: user.id, - }, - }, + userId: user.id, + isSaved: true, }, - include: { - modules: { - include: { - module: { - include: { - courses: { - include: { - course: { - include: { - delegateAuthTo: { - include: { - subscriptions: { - where: { - userId: user.id, - expiresAt: { - gte: new Date(), + orderBy: { + updatedAt: 'desc', + }, + select: { + lessonSlug: true, + userId: true, + updatedAt: true, + id: true, + lesson: { + select: { + name: true, + slug: true, + thumbnailUrl: true, + description: true, + modules: { + select: { + module: { + select: { + slug: true, + courses: { + select: { + course: { + select: { + slug: true, + delegateAuthTo: { + select: { + subscriptions: { + where: { + userId: user.id, + expiresAt: { + gte: new Date(), + }, + }, }, }, }, @@ -612,54 +629,56 @@ export class LessonService { }, }); - const lessonsWithLinks = lessons.map(lesson => { - const hasActiveSubscription = lesson.modules.some(lessonToModule => - lessonToModule.module.courses.some(course => - course.course.delegateAuthTo.some(delegateAuthTo => - delegateAuthTo.subscriptions.some(subscription => subscription.expiresAt >= new Date()), // eslint-disable-line max-nested-callbacks - ), - ), - ); - - return { - ...lesson, - content: hasActiveSubscription ? lesson.content : lesson.marketingContent, - videoSourceUrl: hasActiveSubscription ? lesson.videoSourceUrl : lesson.marketingVideoUrl, - link: `/courses/${lesson.modules[0].module.courses[0].course.slug}/${lesson.modules[0].module.slug}/${lesson.slug}`, - }; - }); + const savedLessonsWithLinks = savedLessons.map(lesson => ({ + ...lesson, + link: `/courses/${lesson.lesson.modules[0].module.courses[0].course.slug}/${lesson.lesson.modules[0].module.slug}/${lesson.lesson.slug}`, + })); return { status: 'SUCCESSFUL', - data: lessonsWithLinks, + data: savedLessonsWithLinks, }; } - public async getFavoritedLessonsByUser(user: TypeUserSession): Promise & {link: string}>>> { - const lessons = await this._model.lesson.findMany({ + public async getFavoritedLessonsByUser(user: TypeUserSession): Promise> { + const favoritedLessons = await this._model.favoritedLessons.findMany({ where: { - favoritedBy: { - some: { - userId: user.id, - }, - }, + userId: user.id, + isFavorited: true, }, - include: { - modules: { - include: { - module: { - include: { - courses: { - include: { - course: { - include: { - delegateAuthTo: { - include: { - subscriptions: { - where: { - userId: user.id, - expiresAt: { - gte: new Date(), + orderBy: { + updatedAt: 'desc', + }, + select: { + lessonSlug: true, + userId: true, + updatedAt: true, + id: true, + lesson: { + select: { + name: true, + slug: true, + thumbnailUrl: true, + description: true, + modules: { + select: { + module: { + select: { + slug: true, + courses: { + select: { + course: { + select: { + slug: true, + delegateAuthTo: { + select: { + subscriptions: { + where: { + userId: user.id, + expiresAt: { + gte: new Date(), + }, + }, }, }, }, @@ -676,26 +695,14 @@ export class LessonService { }, }); - const lessonsWithLinks = lessons.map(lesson => { - const hasActiveSubscription = lesson.modules.some(lessonToModule => - lessonToModule.module.courses.some(course => - course.course.delegateAuthTo.some(delegateAuthTo => - delegateAuthTo.subscriptions.some(subscription => subscription.expiresAt >= new Date()), // eslint-disable-line max-nested-callbacks - ), - ), - ); - - return { - ...lesson, - content: hasActiveSubscription ? lesson.content : lesson.marketingContent, - videoSourceUrl: hasActiveSubscription ? lesson.videoSourceUrl : lesson.marketingVideoUrl, - link: `/courses/${lesson.modules[0].module.courses[0].course.slug}/${lesson.modules[0].module.slug}/${lesson.slug}`, - }; - }); + const favoritedLessonsWithLinks = favoritedLessons.map(lesson => ({ + ...lesson, + link: `/courses/${lesson.lesson.modules[0].module.courses[0].course.slug}/${lesson.lesson.modules[0].module.slug}/${lesson.lesson.slug}`, + })); return { status: 'SUCCESSFUL', - data: lessonsWithLinks, + data: favoritedLessonsWithLinks, }; } diff --git a/app/types/lesson.type.ts b/app/types/lesson.type.ts index d16987b..a0ee82b 100644 --- a/app/types/lesson.type.ts +++ b/app/types/lesson.type.ts @@ -133,3 +133,83 @@ export type TPrismaPayloadGetCompletedLessons = Array & {link: string}>; + +export type TPrismaPayloadGetSavedLessons = Array & {link: string}>; + +export type TPrismaPayloadGetFavoritedLessons = Array & {link: string}>;