Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add pages for saved, completed and favorited lessons #312

Merged
merged 7 commits into from
Jul 22, 2024
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
3 changes: 2 additions & 1 deletion app/components/navigation-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ export function NavigateBar({userData}: {readonly userData: TypeUserSession | un
<NavigationMenu.Content className='data-[motion=from-start]:animate-enterFromLeft data-[motion=from-end]:animate-enterFromRight data-[motion=to-start]:animate-exitToLeft data-[motion=to-end]:animate-exitToRight px-4 py-2 bg-mauve-3 dark:bg-mauvedark-3 absolute top-0 right-0 rounded-md max-xs:w-[calc(100vw_-_calc(100vw_*_5_/_100))] w-72'>
<ul className='grid grid-cols-2 gap-3'>
{pathname !== '/' && <NavigateLink to='/'>Home</NavigateLink>}
{userData?.roles?.includes('admin') && pathname !== '/admin' && <NavigateLink to='/admin'>Admin</NavigateLink>}
{userData?.roles?.includes('admin') && !pathname.startsWith('/admin') && <NavigateLink to='/admin'>Admin</NavigateLink>}
{pathname !== '/courses' && <NavigateLink to='/courses'>Cursos</NavigateLink>}
{!pathname.startsWith('/profile') && userData?.id && <NavigateLink to='/profile'>Minha Área</NavigateLink>}
{userData?.id && pathname !== '/logout' && <NavigateLink to='/logout'>Sair</NavigateLink>}
</ul>
</NavigationMenu.Content>
Expand Down
4 changes: 2 additions & 2 deletions app/routes/admin.comments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export const loader = defineLoader(({request}: LoaderFunctionArgs) => ({

export default function Comments() {
return (
<main>
<div>
<h1>Comments</h1>
</main>
</div>
);
}
4 changes: 2 additions & 2 deletions app/routes/admin.courses_.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export default function Courses() {
} = useLoaderData<typeof loader>();

return (
<main>
<>
<SuccessOrErrorMessage success={success} error={error}/>

<div className='flex items-center gap-5'>
Expand All @@ -105,6 +105,6 @@ export default function Courses() {
<AdminEntityCard key={course?.id} course={course ?? {}} to={`./${course?.slug}`}/>
))}
</div>
</main>
</>
);
}
6 changes: 3 additions & 3 deletions app/routes/courses.$course-slug.$module-slug.$lesson-slug.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -169,7 +169,7 @@ export default function Lesson() {

<Tooltip.Portal>
<Tooltip.Content sideOffset={5} className='bg-mauve-5 dark:bg-mauvedark-5 px-4 py-3 rounded-2xl shadow-sm shadow-mauve-8 dark:shadow-mauvedark-8'>
{userLessonActivity.data?.completed ? <p>Remover Aula Salva</p> : <p>Salvar Aula</p>}
{userLessonActivity.data?.saved ? <p>Remover Aula Salva</p> : <p>Salvar Aula</p>}
<Tooltip.Arrow className='fill-mauve-5 stroke-none'/>
</Tooltip.Content>
</Tooltip.Portal>
Expand All @@ -186,7 +186,7 @@ export default function Lesson() {

<Tooltip.Portal>
<Tooltip.Content sideOffset={5} className='bg-mauve-5 dark:bg-mauvedark-5 px-4 py-3 rounded-2xl shadow-sm shadow-mauve-8 dark:shadow-mauvedark-8'>
{userLessonActivity.data?.completed ? <p>Remover Aula Favorita</p> : <p>Favoritar Aula</p>}
{userLessonActivity.data?.favorited ? <p>Remover Aula Favorita</p> : <p>Favoritar Aula</p>}
<Tooltip.Arrow className='fill-mauve-5 stroke-none'/>
</Tooltip.Content>
</Tooltip.Portal>
Expand Down
1 change: 1 addition & 0 deletions app/routes/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export default function Login() {
return (
<>
<NavigateBar userData={userData}/>

<main className='flex flex-col flex-grow-[0.6] mt-20'>
<div className='my-auto'>
<RadixForm.Root asChild method='post'>
Expand Down
64 changes: 64 additions & 0 deletions app/routes/profile.completed-lessons.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof loader>) => [
{title: 'Yoga em Movimento - Área Pessoal - Aulas Assistidas'},
{name: 'description', content: 'Aulas assistidas 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 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<typeof loader>();

if (completedLessonsWithActivity.length === 0) {
return (
<div>
<h1>Aulas Assistidas</h1>
<p>{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.</p>
</div>
);
}

return (
<div>
<h1>Aulas Assistidas</h1>

<div className='flex gap-4 my-4 flex-wrap'>
{completedLessonsWithActivity.map(lesson => (
<LessonEntityCard key={lesson.id} course={lesson.lesson} to={lesson.link} activity={lesson.activity}/>
))}
</div>
</div>
);
}
64 changes: 64 additions & 0 deletions app/routes/profile.favorited-lessons.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof loader>) => [
{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<typeof loader>();

if (favoritedLessonsWithActivity.length === 0) {
return (
<div>
<h1>Aulas Favoritas</h1>
<p>{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.</p>
</div>
);
}

return (
<div>
<h1>Aulas Favoritas</h1>

<div className='flex gap-4 my-4 flex-wrap'>
{favoritedLessonsWithActivity.map(lesson => (
<LessonEntityCard key={lesson.id} course={lesson.lesson} to={lesson.link} activity={lesson.activity}/>
))}
</div>
</div>
);
}
64 changes: 64 additions & 0 deletions app/routes/profile.saved-lessons.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof loader>) => [
{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<typeof loader>();

if (savedLessonsWithActivity.length === 0) {
return (
<div>
<h1>Aulas Salvadas</h1>
<p>{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.</p>
</div>
);
}

return (
<div>
<h1>Aulas Salvadas</h1>

<div className='flex gap-4 my-4 flex-wrap'>
{savedLessonsWithActivity.map(lesson => (
<LessonEntityCard key={lesson.id} course={lesson.lesson} to={lesson.link} activity={lesson.activity}/>
))}
</div>
</div>
);
}
64 changes: 64 additions & 0 deletions app/routes/profile.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof loader>) => [
{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<typeof loader>();
const {pathname} = useLocation();

return userData?.id && (
<>
<NavigateBar userData={userData}/>

<div className='flex max-w-[95%] w-full mx-auto flex-col sm:flex-row gap-4'>
<aside className='w-64 p-3 bg-mauve-3 dark:bg-mauvedark-3 shadow-sm shadow-mauve-11 dark:shadow-mauvedark-3 rounded-lg h-fit flex flex-col gap-3'>
<Link to='/profile/completed-lessons'>
<p>Aulas Assistidas</p>
</Link>
<Link to='/profile/saved-lessons'>
<p>Aulas Salvadas</p>
</Link>
<Link to='/profile/favorited-lessons'>
<p>Aulas Favoritadas</p>
</Link>
</aside>
<main className='flex-grow flex-shrink p-3'>
{(pathname === '/profile' || pathname === '/profile/') && (
<p>Selecione a opção no menu ao lado</p>
)}
<Outlet/>
</main>
</div>
</>
);
}
Loading