Skip to content

Commit

Permalink
refactor: page component now includes loader & action like in remix, …
Browse files Browse the repository at this point in the history
…keep files in folders flat
  • Loading branch information
rifandani committed Mar 15, 2024
1 parent f5922e2 commit 2a96218
Show file tree
Hide file tree
Showing 23 changed files with 251 additions and 258 deletions.
90 changes: 90 additions & 0 deletions src/modules/auth/pages/login.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import reactjs from '#assets/images/reactjs.svg';
import { authApi, loginSchema } from '#auth/apis/auth.api';
import { LoginForm } from '#auth/components/login-form';
import { useUserStore } from '#auth/hooks/use-user-store.hook';
import { homePath } from '#home/routes';
import { useI18n } from '#shared/hooks/use-i18n/use-i18n.hook';
import { checkAuthUser } from '#shared/utils/checker.util';
import { Link } from 'react-aria-components';
import { unstable_batchedUpdates } from 'react-dom';
import type { ActionFunctionArgs, LoaderFunction } from 'react-router-dom';
import { json, redirect } from 'react-router-dom';
import { toast } from 'react-toastify';

export async function action({ request }: ActionFunctionArgs) {
if (request.method === 'POST') {
const payload = Object.fromEntries(await request.formData());

// if `payload` is not correct, return error object
const parsed = loginSchema.safeParse(payload);
if (!parsed.success) return json(parsed.error, { status: 400 });

// will throw if `login` returns 500 error, therefore `errorElement` will be rendered
const loginResponse = await authApi.login(parsed.data);

if ('message' in loginResponse)
// on 400 error
return json(loginResponse);

// see https://docs.pmnd.rs/zustand/recipes/recipes#calling-actions-outside-a-react-event-handler
unstable_batchedUpdates(() => {
useUserStore.getState().setUser(loginResponse); // set user data to store
});

return redirect(homePath.root);
}

toast.warning('Not Implemented');
return new Response('Not Implemented', { status: 501 });
}

export const loader: LoaderFunction = () => {
const authed = checkAuthUser();

// redirect auth user to home
if (authed) {
toast.info('Already Logged In');
return redirect(homePath.root);
}

return null;
};

export function Element() {
const [t] = useI18n();

return (
<div id="LoginPage" className="min-h-screen w-full flex">
{/* form */}
<section className="min-h-screen w-full flex flex-col justify-center px-10 xl:px-20 md:w-1/2">
<h1 className="text-center text-3xl text-primary">{t('welcome')}</h1>

<LoginForm />

<p className="py-12 text-center">
{t('noAccount')}{' '}
<Link
aria-label={t('registerHere')}
className="link link-primary"
href="/does-not-exists"
>
{t('registerHere')}
</Link>
</p>
</section>

{/* image */}
<section className="hidden md:block w-1/2 shadow-2xl">
<span className="relative h-screen w-full md:flex md:items-center md:justify-center">
<img
src={reactjs}
alt="cool react logo with rainbow shadow"
loading="lazy"
className="h-full object-cover"
aria-label="cool react logo"
/>
</span>
</section>
</div>
);
}
27 changes: 0 additions & 27 deletions src/modules/auth/pages/login/action.ts

This file was deleted.

17 changes: 0 additions & 17 deletions src/modules/auth/pages/login/loader.ts

This file was deleted.

43 changes: 0 additions & 43 deletions src/modules/auth/pages/login/page.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useColorMode } from '#shared/hooks/use-color-mode.hook';
import { useI18n } from '#shared/hooks/use-i18n/use-i18n.hook';
import { Link } from 'react-aria-components';

export function NotFoundPage() {
export function Element() {
const userStore = useUserStore();
const [t] = useI18n();
useColorMode({
Expand Down
14 changes: 6 additions & 8 deletions src/modules/auth/routes.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { loginAction } from '#auth/pages/login/action';
import { loginLoader } from '#auth/pages/login/loader';
import { RouteErrorBoundary } from '#shared/components/route-error-boundary';
import type { RouteObject } from 'react-router-dom';

Expand All @@ -17,12 +15,12 @@ export const loginRoute = {
id: authId.login,
path: authPath.login,
lazy: async () => {
const { LoginPage } = await import('./pages/login/page');
const login = await import('./pages/login.page');

return {
action: loginAction,
loader: loginLoader,
element: <LoginPage />,
action: login.action,
loader: login.loader,
element: <login.Element />,
errorElement: <RouteErrorBoundary />,
};
},
Expand All @@ -35,8 +33,8 @@ export const notFoundRoute = {
id: 'notFound',
path: '*',
lazy: async () => {
const { NotFoundPage } = await import('./pages/not-found/page');
const notFound = await import('./pages/not-found.page');

return { element: <NotFoundPage /> };
return { element: <notFound.Element /> };
},
} as const satisfies RouteObject;
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useNavigate } from 'react-router-dom';

const currentDate = new Date();

export function HomeClockSection() {
export function HomeClock() {
const [t] = useI18n();
const navigate = useNavigate();
const [parentRef] = useAutoAnimate();
Expand Down
35 changes: 35 additions & 0 deletions src/modules/home/pages/home.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { authPath } from '#auth/routes';
import { HomeClock } from '#home/components/home-clock';
import { useI18n } from '#shared/hooks/use-i18n/use-i18n.hook';
import { checkAuthUser } from '#shared/utils/checker.util';
import { useAutoAnimate } from '@formkit/auto-animate/react';
import { redirect, type LoaderFunction } from 'react-router-dom';
import { toast } from 'react-toastify';

export const loader: LoaderFunction = () => {
const authed = checkAuthUser();

// redirect NOT authed user to login
if (!authed) {
toast.error('Unauthorized');
return redirect(authPath.login);
}

return null;
};

export function Element() {
const [t] = useI18n();
const [parentRef] = useAutoAnimate();

return (
<div
ref={parentRef}
className="container mx-auto flex flex-col items-center py-24 duration-300"
>
<h1 className="text-3xl sm:text-4xl">{t('title')}</h1>

<HomeClock />
</div>
);
}
17 changes: 0 additions & 17 deletions src/modules/home/pages/loader.ts

This file was deleted.

19 changes: 0 additions & 19 deletions src/modules/home/pages/page.tsx

This file was deleted.

7 changes: 3 additions & 4 deletions src/modules/home/routes.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { homeLoader } from '#home/pages/loader';
import { PageWrapper } from '#shared/components/page-wrapper';
import { RouteErrorBoundary } from '#shared/components/route-error-boundary';
import type { RouteObject } from 'react-router-dom';
Expand All @@ -17,11 +16,11 @@ const homeIndexRoute = {
id: homeId.index,
index: true,
lazy: async () => {
const { HomePage } = await import('./pages/page');
const home = await import('./pages/home.page');

return {
loader: homeLoader,
element: <HomePage />,
loader: home.loader,
element: <home.Element />,
errorElement: <RouteErrorBoundary />,
};
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const tabClassName: ComponentPropsWithoutRef<typeof Tab>['className'] = ({
isDisabled,
}) => twJoin('tab', isSelected && 'tab-active', isDisabled && 'tab-disabled');

export function PlaygroundPage() {
export function Element() {
const [, setTheme] = useColorMode({
modes,
attribute: 'data-theme',
Expand Down
4 changes: 2 additions & 2 deletions src/modules/playground/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ export const playgroundRoute = {
id: playgroundId.root,
path: playgroundPath.root,
lazy: async () => {
const { PlaygroundPage } = await import('./pages/page');
const playground = await import('./pages/playground.page');

return {
element: <PlaygroundPage />,
element: <playground.Element />,
errorElement: <RouteErrorBoundary />,
};
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ export function TodosCreate() {
</DaisyModal>

<form
id="todos-create"
className="form-control mb-3 w-full duration-300 lg:flex-row"
onSubmit={form.handleSubmit((values) => {
const payload = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ import type { TodoSchema } from '#todo/apis/todo.api';
import { useTodoDelete } from '#todo/hooks/use-todo-delete.hook';
import { useTodoUpdate } from '#todo/hooks/use-todo-update.hook';
import { Button, Link } from 'react-aria-components';
import { useHref } from 'react-router-dom';
import { twJoin } from 'tailwind-merge';
import { match } from 'ts-pattern';

export function TodosItem({
todo,
}: {
interface Props {
todo: TodoSchema;
}) {
}

export function TodosListItem({ todo }: Props) {
const [t] = useI18n();
const { user } = useUserStore();
const updateTodoMutation = useTodoUpdate();
const deleteTodoMutation = useTodoDelete();
const href = useHref(todo.id.toString(), { relative: 'path' });

return (
<form
Expand Down Expand Up @@ -43,12 +45,12 @@ export function TodosItem({
/>

<Link
href={href}
aria-label={`link todo item with id: ${todo.id}`}
className={twJoin(
'ml-5 w-full text-left text-lg hover:font-bold',
todo.completed && 'line-through',
)}
href={todo.id.toString()}
>
{todo.todo}
</Link>
Expand Down
Loading

0 comments on commit 2a96218

Please sign in to comment.