Compare commits

...
Author SHA1 Message Date
egagofur 638b64b715 fix: validation by query param 2025-04-04 16:42:27 +07:00
egagofur d61cb4a215 fix: split zod validation register modal 2025-04-04 16:38:21 +07:00
Maulana SodiqinandGitHub 272db82c8c Merge pull request #18 from IMPHNEN/feat/register-integeration
feat: integeration register modal
2025-04-04 06:03:13 +07:00
egagofur 9108ddb742 fix: unused field 2025-04-03 23:39:41 +07:00
egagofur 628e168342 fix: form type 2025-04-03 23:39:09 +07:00
egagofur 9aff9e2dc8 feat: integeration register modal 2025-04-03 23:32:44 +07:00
Maulana SodiqinandGitHub 585ad482f2 Merge pull request #16 from IMPHNEN/feat/backoffice-login
Backoffice login
2025-04-03 23:02:36 +08:00
Hafid Nur 988476b409 refactor auth and logout
- Pindah hard-code API version ke environment variable
- Gunakan helper untuk logout
2025-04-03 21:59:31 +07:00
Hafid Nur 38e6d4643b Merge branch 'develop' into feat/backoffice-login 2025-04-03 21:51:00 +07:00
Maulana SodiqinandGitHub 79cb9d81fc Merge pull request #15 from IMPHNEN/docs
Update README
2025-04-03 22:25:19 +08:00
Maulana SodiqinandGitHub 64e702df5d Merge pull request #17 from IMPHNEN/feat/handle-state-modal
Feat/handle state modal
2025-04-03 21:01:05 +08:00
Hafid Nur 33c04707f8 feat: login backoffice 2025-04-03 16:28:54 +07:00
Hafid Nur f87dc4c782 Merge branch 'develop' into feat/backoffice-login 2025-04-03 15:45:23 +07:00
Hafid NurandGitHub b5428d8552 Update README.md 2025-04-03 14:15:52 +07:00
Hafid Nur 0a846b0512 docs: fix 2025-04-03 14:03:38 +07:00
Hafid Nur 3a950ea867 docs: align center logo 2025-04-03 14:01:14 +07:00
Hafid Nur 4693284b37 docs: Update README 2025-04-03 13:59:27 +07:00
Hafid Nur fe1928e8ef Merge branch 'develop' into feat/backoffice-login 2025-04-03 12:29:28 +07:00
Hafid Nur a9cc728ade feat(backoffice): implement login and logout
- Tambah fitur login dan logout pada Backoffice
- Ubah port development server dan atasi CORS
- Update type login response
- Update component Input agar toggle password berfungsi dengan benar
2025-04-03 06:10:07 +07:00
Hafid Nur 2602657201 feat(backoffice): send login request
- buat .env untuk menyimpan API dan memberikan .env.example juga untuk contohnya. Tambah .gitignore juga
- tambah fungsi login pada Backoffice
2025-04-02 22:42:02 +07:00
15 changed files with 406 additions and 156 deletions
+1
View File
@@ -0,0 +1 @@
VITE_API_URL=https://api.example.com
+89 -73
View File
@@ -1,101 +1,117 @@
# ImphnenFrontendService
# IMPHNEN Frontend Service
<a alt="Nx logo" href="https://nx.dev" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-logo.png" width="45"></a>
<p align="center">
<img src="docs/logo.svg" alt="IMPHNEN">
</p>
✨ Your new, shiny [Nx workspace](https://nx.dev) is ready ✨.
This repository is a **monorepo** for all frontend services of IMPHNEN. The monorepo includes three main applications:
[Learn more about this workspace setup and its capabilities](https://nx.dev/getting-started/tutorials/react-monorepo-tutorial?utm_source=nx_project&amp;utm_medium=readme&amp;utm_campaign=nx_projects) or run `npx nx graph` to visually explore what was created. Now, let's get you up to speed!
1. **Gacha** - Application for <a href="https://gacha.imphnen.dev/" target="_blank">gacha website</a>.
2. **Backoffice** - Application for internal management.
3. **Dimentorin** - Application for mentoring services.
## Run tasks
## How to install
To run the dev server for your app, use:
1. Clone this repository:
```sh
git clone https://github.com/IMPHNEN/imphnen-frontend-service.git
cd imphnen-frontend-service
```
2. Install all dependencies:
```sh
npm install
```
```sh
npx nx serve dimentorin
```
## How to run
To create a production bundle:
### Development
```sh
npx nx build dimentorin
```
Use the following commands to run in development mode:
To see all available targets to run for a project, run:
- **Gacha**:
```sh
npm run gacha:dev
```
- **Backoffice**:
```sh
npm run backoffice:dev
```
- **Dimentorin**:
```sh
npm run dimentorin:dev
```
```sh
npx nx show project dimentorin
```
### Build
These targets are either [inferred automatically](https://nx.dev/concepts/inferred-tasks?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) or defined in the `project.json` or `package.json` files.
Use the following commands to build the applications:
[More about running tasks in the docs &raquo;](https://nx.dev/features/run-tasks?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
- **Gacha**:
```sh
npm run gacha:build
```
- **Backoffice**:
```sh
npm run backoffice:build
```
- **Dimentorin**:
```sh
npm run dimentorin:build
```
## Add new projects
### Production
While you could add new projects to your workspace manually, you might want to leverage [Nx plugins](https://nx.dev/concepts/nx-plugins?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) and their [code generation](https://nx.dev/features/generate-code?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) feature.
Use the following commands to run the applications in production mode:
Use the plugin's generator to create new projects.
- **Gacha**:
```sh
npm run gacha:prod
```
- **Backoffice**:
```sh
npm run backoffice:prod
```
- **Dimentorin**:
```sh
npm run dimentorin:prod
```
To generate a new application, use:
### Storybook
```sh
npx nx g @nx/react:app demo
```
This repository uses Storybook to develop, test, and document UI components in an isolated and interactive environment. Below are the commands to work with Storybook:
To generate a new library, use:
- **Run Storybook**
This command starts Storybook in development mode, allowing you to view and test UI components interactively.
```sh
npx nx g @nx/react:lib mylib
```
```sh
npm run ui:storybook
```
You can use `npx nx list` to get a list of installed plugins. Then, run `npx nx list <plugin-name>` to learn about more specific capabilities of a particular plugin. Alternatively, [install Nx Console](https://nx.dev/getting-started/editor-setup?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) to browse plugins and generators in your IDE.
- **Run Unit Test**
[Learn more about Nx plugins &raquo;](https://nx.dev/concepts/nx-plugins?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) | [Browse the plugin registry &raquo;](https://nx.dev/plugin-registry?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
This command runs unit tests for the UI components to ensure they function as expected.
## Set up CI!
```sh
npm run ui:test
```
### Step 1
- **Build Components**
To connect to Nx Cloud, run the following command:
This command generates a static build of Storybook, which can be deployed for sharing and documentation purposes.
```sh
npx nx connect
```
```sh
npm run ui:build
```
Connecting to Nx Cloud ensures a [fast and scalable CI](https://nx.dev/ci/intro/why-nx-cloud?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) pipeline. It includes features such as:
## How to contribute
- [Remote caching](https://nx.dev/ci/features/remote-cache?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
- [Task distribution across multiple machines](https://nx.dev/ci/features/distribute-task-execution?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
- [Automated e2e test splitting](https://nx.dev/ci/features/split-e2e-tasks?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
- [Task flakiness detection and rerunning](https://nx.dev/ci/features/flaky-tasks?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
1. Fork the repository and clone it locally.
2. Create a new branch for a new feature or fix:
```sh
git checkout -b feat/feature-name
```
3. Make changes, commit, and push to your forked repository.
4. Create a pull request to this repository `develop` branch.
### Step 2
Use the following command to configure a CI workflow for your workspace:
```sh
npx nx g ci-workflow
```
[Learn more about Nx on CI](https://nx.dev/ci/intro/ci-with-nx#ready-get-started-with-your-provider?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
## Install Nx Console
Nx Console is an editor extension that enriches your developer experience. It lets you run tasks, generate code, and improves code autocompletion in your IDE. It is available for VSCode and IntelliJ.
[Install Nx Console &raquo;](https://nx.dev/getting-started/editor-setup?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
## Useful links
Learn more:
- [Learn more about this workspace setup](https://nx.dev/getting-started/tutorials/react-monorepo-tutorial?utm_source=nx_project&amp;utm_medium=readme&amp;utm_campaign=nx_projects)
- [Learn about Nx on CI](https://nx.dev/ci/intro/ci-with-nx?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
- [Releasing Packages with Nx release](https://nx.dev/features/manage-releases?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
- [What are Nx plugins?](https://nx.dev/concepts/nx-plugins?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
And join the Nx community:
- [Discord](https://go.nx.dev/community)
- [Follow us on X](https://twitter.com/nxdevtools) or [LinkedIn](https://www.linkedin.com/company/nrwl)
- [Our Youtube channel](https://www.youtube.com/@nxdevtools)
- [Our blog](https://nx.dev/blog?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
If you encounter any issues or problems, feel free to create a new Issue.
+1 -1
View File
@@ -6,7 +6,7 @@
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="icon" type="image/x-icon" href="/logos/simple.svg" />
<link rel="stylesheet" href="/src/styles.css" />
</head>
<body>
@@ -0,0 +1,31 @@
import { useForm } from 'react-hook-form';
import {
authLoginSchema,
TLoginRequest,
usePostLogin,
} from '@imphnen-frontend-service/service';
import { zodResolver } from '@hookform/resolvers/zod';
import { useNavigate } from 'react-router-dom';
export const useLogin = () => {
const postLogin = usePostLogin();
const form = useForm<TLoginRequest>({
resolver: zodResolver(authLoginSchema),
mode: 'all',
});
const navigate = useNavigate();
const onSubmit = form.handleSubmit((data) => {
postLogin.mutate(data, {
onSuccess: () => {
console.log('Success Login');
navigate('/dashboard');
},
});
});
return {
form,
onSubmit,
};
};
+26 -18
View File
@@ -1,10 +1,10 @@
import { FC, ReactElement } from 'react';
import { useNavigate } from 'react-router';
import { Button } from '@imphnen-frontend-service/ui/atoms';
import { InputField } from '@imphnen-frontend-service/ui/molecules';
import { useLogin } from './_hooks/use-login';
import { ControlledInputField } from '@imphnen-frontend-service/ui/organisms';
export const Components: FC = (): ReactElement => {
const navigate = useNavigate();
const { form, onSubmit } = useLogin();
return (
<div className="flex justify-center items-center min-h-screen">
@@ -13,21 +13,29 @@ export const Components: FC = (): ReactElement => {
<h1 className="text-primary-500 text-p1 font-semibold">
Welcome to IMPHNEN Backoffice
</h1>
<InputField
label="Email"
placeholder="Masukkan Email"
type="email"
size="lg"
className="w-full"
/>
<InputField
label="Password"
placeholder="Masukkan password"
type="password"
size="lg"
className="w-full"
/>
<Button onClick={() => navigate('/dashboard')}>Login</Button>
<form onSubmit={onSubmit} className="flex flex-col gap-8">
<ControlledInputField
control={form.control}
label="Email"
placeholder="Masukkan Email"
type="email"
name="email"
size="lg"
className="w-full"
/>
<ControlledInputField
control={form.control}
label="Password"
placeholder="Masukkan Password"
type="password"
name="password"
size="lg"
className="w-full"
/>
<Button type="submit" size="md" className="w-full">
Login
</Button>
</form>
</div>
</div>
);
+2 -2
View File
@@ -8,11 +8,11 @@ export default defineConfig(() => ({
root: __dirname,
cacheDir: '../../node_modules/.vite/apps/backoffice',
server: {
port: 4200,
port: 3000,
host: 'localhost',
},
preview: {
port: 4300,
port: 3001,
host: 'localhost',
},
plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
@@ -1,6 +1,11 @@
import { Button } from '@imphnen-frontend-service/ui/atoms';
import { InputField, Modal } from '@imphnen-frontend-service/ui/molecules';
import { Modal } from '@imphnen-frontend-service/ui/molecules';
import { useQueryState } from '@imphnen-frontend-service/utils';
import { useRegister } from '../../_hooks/use-register';
import { ControlledInputField } from '@imphnen-frontend-service/ui/organisms';
import { UseFormReturn } from 'react-hook-form';
import { TRegisterRequest } from '@imphnen-frontend-service/service';
import { useEffect } from 'react';
interface IModalFormRegisterProps {
isOpen: boolean;
@@ -8,6 +13,8 @@ interface IModalFormRegisterProps {
}
const ModalFormRegister = ({ isOpen, onClose }: IModalFormRegisterProps) => {
const { form, onSubmit, isStepOneValid } = useRegister();
const {
step: currentStep,
nextStep,
@@ -19,6 +26,12 @@ const ModalFormRegister = ({ isOpen, onClose }: IModalFormRegisterProps) => {
minValue: 1,
});
useEffect(() => {
if (!isStepOneValid && currentStep === 2) {
prevStep();
}
}, [isStepOneValid, currentStep, prevStep]);
return (
<Modal
className="py-[45px] min-w-[400px] lg:min-w-[455px] px-7"
@@ -37,74 +50,118 @@ const ModalFormRegister = ({ isOpen, onClose }: IModalFormRegisterProps) => {
{currentStep === 1 ? 'Info Akun' : 'Informasi Pengiriman Hadiah'}
</h2>
</Modal.Header>
<Modal.Content className="space-y-6">
{currentStep === 1 && <StepOne nextStep={nextStep} onClose={onClose} />}
{currentStep === 2 && (
<StepTwo nextStep={nextStep} prevStep={prevStep} />
)}
<Modal.Content>
<form onSubmit={onSubmit} className="space-y-6">
{currentStep === 1 && (
<StepOne
form={form}
nextStep={nextStep}
isStepOneValid={isStepOneValid}
/>
)}
{currentStep === 2 && <StepTwo form={form} prevStep={prevStep} />}
</form>
</Modal.Content>
</Modal>
);
};
interface IStepOneProps {
form: UseFormReturn<TRegisterRequest, any, TRegisterRequest>;
nextStep: () => void;
onClose: () => void;
isStepOneValid: boolean;
}
const StepOne = ({ nextStep, onClose }: IStepOneProps) => (
<>
<InputField
label="Nama Lengkap"
placeholder="Masukkan Nama Lengkap"
type="text"
size="lg"
className="w-full"
/>
<InputField
label="Email"
placeholder="Masukkan Email Anda"
type="email"
size="lg"
className="w-full"
/>
<InputField
label="Password"
placeholder="Masukkan Password"
type="password"
size="lg"
className="w-full"
/>
<InputField
label="Ulangi Password"
placeholder="Masukkan Ulang Password"
type="password"
size="lg"
className="w-full"
/>
<Button size="md" className="w-full" onClick={nextStep}>
Selanjutnya
</Button>
</>
);
const StepOne = ({ form, nextStep, isStepOneValid }: IStepOneProps) => {
return (
<>
<ControlledInputField
control={form.control}
name="fullname"
label="Nama Lengkap"
placeholder="Masukkan Nama Lengkap"
type="text"
size="lg"
className="w-full"
/>
<ControlledInputField
control={form.control}
label="Email"
placeholder="Masukkan Email Anda"
type="email"
name="email"
size="lg"
className="w-full"
/>
<ControlledInputField
control={form.control}
name="password"
label="Password"
placeholder="Masukkan Password"
type="password"
size="lg"
className="w-full"
/>
<ControlledInputField
control={form.control}
name="confirm_password"
label="Ulangi Password"
placeholder="Masukkan Ulang Password"
type="password"
size="lg"
className="w-full"
/>
<Button
size="md"
className="w-full"
onClick={nextStep}
disabled={!isStepOneValid}
>
Selanjutnya
</Button>
</>
);
};
interface IStepTwoProps {
nextStep: () => void;
form: UseFormReturn<TRegisterRequest, any, TRegisterRequest>;
prevStep: () => void;
}
const StepTwo = ({ nextStep, prevStep }: IStepTwoProps) => (
const StepTwo = ({ form, prevStep }: IStepTwoProps) => (
<>
<InputField
<ControlledInputField
control={form.control}
name="phone_number"
label="Nomor Telepon"
placeholder="Masukkan Nomor Telepon Aktif"
type="text"
size="lg"
className="w-full"
/>
<InputField
label="Alamat Pengiriman"
placeholder="Masukkan Alamat Pengiriman"
<ControlledInputField
control={form.control}
name="referral_code"
label="Kode Referral"
placeholder="Masukkan Kode Referral"
type="text"
size="lg"
className="w-full"
/>
<ControlledInputField
control={form.control}
name="referred_by"
label="Referral By"
placeholder="Masukkan Referral By"
type="text"
size="lg"
className="w-full"
/>
<ControlledInputField
control={form.control}
name="student_type"
label="Tipe Pelajar"
placeholder="Masukkan Tipe Pelajar"
type="text"
size="lg"
className="w-full"
@@ -113,7 +170,7 @@ const StepTwo = ({ nextStep, prevStep }: IStepTwoProps) => (
<Button size="md" variant="text" className="w-[40%]" onClick={prevStep}>
Kembali
</Button>
<Button size="md" className="w-full" onClick={nextStep}>
<Button size="md" className="w-full" type="submit">
Gacha Sekarang
</Button>
</div>
+58
View File
@@ -0,0 +1,58 @@
import { useForm } from 'react-hook-form';
import {
authRegisterSchema,
stepOneRegisterSchema,
TRegisterRequest,
usePostRegister,
} from '@imphnen-frontend-service/service';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import { toast } from 'sonner';
import { useEffect, useState } from 'react';
export const useRegister = () => {
const postRegister = usePostRegister();
const form = useForm<TRegisterRequest>({
resolver: zodResolver(authRegisterSchema),
mode: 'all',
defaultValues: {
fullname: '',
email: '',
password: '',
confirm_password: '',
phone_number: '',
referral_code: '',
referred_by: '',
student_type: '',
},
});
const [stepValid, setStepValid] = useState(false);
useEffect(() => {
const subscription = form.watch(() => {
const { fullname, email, password, confirm_password } = form.getValues();
const valid = stepOneRegisterSchema.safeParse({
fullname,
email,
password,
confirm_password,
}).success;
setStepValid(valid);
});
return () => subscription.unsubscribe();
}, [form]);
const onSubmit = form.handleSubmit((data) => {
postRegister.mutate(data, {
onSuccess: (data) => toast.success(data.message),
onError: (error) => toast.error(error.message),
});
});
return {
form,
onSubmit,
isStepOneValid: stepValid,
};
};
+2
View File
@@ -8,6 +8,7 @@ import {
convertPagesToRoute,
QueryProvider,
} from '@imphnen-frontend-service/utils';
import { Toaster } from 'sonner';
import './index.css';
const files = import.meta.glob('./app/**/*(page|layout).tsx');
@@ -34,6 +35,7 @@ if (!rootElement) throw new Error('Failed to find the root element');
createRoot(rootElement).render(
<StrictMode>
<QueryProvider>
<Toaster />
<RouterProvider router={router} />
</QueryProvider>
</StrictMode>
+9
View File
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 351 KiB

+61
View File
@@ -15,3 +15,64 @@ export const authLoginSchema = z.object({
})
.min(1, 'Password tidak boleh kosong'),
});
export const stepOneRegisterSchema = z
.object({
email: z
.string({
required_error: 'Email tidak boleh kosong',
invalid_type_error: 'Email harus berupa string',
})
.min(1, 'Email tidak boleh kosong')
.email('Email harus valid'),
fullname: z
.string({
required_error: 'Nama tidak boleh kosong',
invalid_type_error: 'Nama harus berupa string',
})
.min(1, 'Nama tidak boleh kosong')
.max(50, 'Nama tidak boleh lebih dari 50 karakter'),
password: z
.string({
required_error: 'Password tidak boleh kosong',
invalid_type_error: 'Password harus berupa string',
})
.min(1, 'Password tidak boleh kosong')
.min(8, 'Password harus lebih dari 8 karakter')
.max(50, 'Password tidak boleh lebih dari 50 karakter'),
confirm_password: z
.string({
required_error: 'Konfirmasi password tidak boleh kosong',
})
.min(1, 'Konfirmasi password tidak boleh kosong')
.max(50, 'Konfirmasi password tidak boleh lebih dari 50 karakter'),
})
.refine((data) => data.password === data.confirm_password, {
message: 'Password dan Konfirmasi Password harus sama',
path: ['confirm_password'],
});
const stepTwoRegisterSchema = z.object({
phone_number: z
.string({
required_error: 'Nomor telepon tidak boleh kosong',
invalid_type_error: 'Nomor telepon harus berupa string',
})
.min(1, 'Nomor telepon tidak boleh kosong')
.max(15, 'Nomor telepon tidak boleh lebih dari 15 karakter'),
referral_code: z
.string()
.min(1, 'Kode referral tidak boleh kosong')
.max(4, 'Kode referral tidak boleh lebih dari 4 karakter')
.optional(),
referred_by: z
.string()
.min(1, 'Kode referral tidak boleh kosong')
.max(50, 'Kode referral tidak boleh lebih dari 50 karakter')
.optional(),
student_type: z.string(),
});
export const authRegisterSchema = stepOneRegisterSchema.and(
stepTwoRegisterSchema
);
+6 -1
View File
@@ -16,9 +16,14 @@ export type TLoginResponse = {
};
export type TRegisterRequest = {
fullname: string;
email: string;
fullname: string;
password: string;
phone_number: string;
referral_code?: string;
referred_by?: string;
student_type: string;
confirm_password: string;
};
export type TVerifyEmailRequest = {
+3 -1
View File
@@ -40,7 +40,8 @@ export const Input: FC<TInputProps> = ({
}): ReactElement => {
const [showPassword, setShowPassword] = useState(false); // State for password visibility
const togglePasswordVisibility = () => {
const togglePasswordVisibility = (e: React.FormEvent) => {
e.preventDefault();
if (!disabled) setShowPassword((prev) => !prev);
};
@@ -63,6 +64,7 @@ export const Input: FC<TInputProps> = ({
{type === 'password' && (
<div className="absolute end-0 px-[12px] h-full flex items-center">
<Button
type="button"
variant="text"
size={size}
onClick={togglePasswordVisibility}
@@ -75,10 +75,15 @@ export const InputField: FC<TInputFieldProps> = ({
{...rest}
/>
{error ? (
<p className="text-danger-500 text-xl mt-1">{error}</p>
<p className="text-danger-500 text-label1 text-left">{error}</p>
) : (
helperText && (
<p className={cn('text-cs mt-1', sizeClasses[size].helperText)}>
<p
className={cn(
'text-label2 text-left',
sizeClasses[size].helperText
)}
>
{helperText}
</p>
)
@@ -7,20 +7,19 @@ import {
} from '@ant-design/icons';
import { Button } from '../../atoms';
import { FC, ReactElement } from 'react';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';
import { useSession } from '@imphnen-frontend-service/utils';
export const BackofficeSidebar: FC = (): ReactElement => {
const { signOut } = useSession();
const location = useLocation();
const navigate = useNavigate();
const isActive = (path: string) => location.pathname.includes(path);
return (
<aside className="sticky top-0 left-0 w-[280px] bg-white min-h-screen py-[60px] px-[28px] shadow-xl flex flex-col justify-between">
<div className="flex flex-col gap-20 justify-between items-center">
{/* Logo */}
<img src="/logos/simple.svg" alt="IMPHNEN Logo" className="w-[150px]" />
{/* Navigation Menu */}
<nav className="flex flex-col gap-4 w-full">
<Link
to="/dashboard"
@@ -72,14 +71,10 @@ export const BackofficeSidebar: FC = (): ReactElement => {
</nav>
</div>
{/* Log Out Button */}
<div className="w-full">
<hr className="mb-5 border-primary-200" />
<Button
onClick={() => {
navigate('/');
}}
onClick={signOut}
variant="text"
className="items-start justify-start gap-3 px-[8px] py-[10px] text-gray-700 hover:text-red-500 transition-colors w-full"
>