Merge pull request #17 from IMPHNEN/feat/handle-state-modal

Feat/handle state modal
This commit is contained in:
Maulana Sodiqin
2025-04-03 21:01:05 +08:00
committed by GitHub
11 changed files with 80 additions and 36 deletions
@@ -1,19 +1,20 @@
import { Button } from '@imphnen-frontend-service/ui/atoms';
import { Modal } from '@imphnen-frontend-service/ui/molecules';
import { ControlledInputField } from '@imphnen-frontend-service/ui/organisms';
import { Link } from 'react-router-dom';
import { useLogin } from '../../_hooks/use-login';
interface IModalFormLogin {
isOpen: boolean;
onClose: () => void;
onForgotPassword: () => void;
setIsOpenRegisterModal: (value: boolean) => void;
}
const ModalFormLogin = ({
isOpen,
onClose,
onForgotPassword,
setIsOpenRegisterModal,
}: IModalFormLogin) => {
const { form, onSubmit } = useLogin();
@@ -59,12 +60,13 @@ const ModalFormLogin = ({
</Button>
<div className="flex justify-center gap-2 pt-2.5 font-medium">
<p className="text-neutral-500">Belum punya akun?</p>
<Link
className="text-primary-500 hover:text-primary-600"
to="/register"
<Button
variant="text"
className="text-primary-500 hover:text-primary-600 m-0 p-0"
onClick={() => setIsOpenRegisterModal(true)}
>
Daftar
</Link>
</Button>
</div>
</form>
</Modal.Content>
+9 -7
View File
@@ -1,14 +1,16 @@
import { FC, ReactElement } from 'react';
import { Outlet } from 'react-router-dom';
import { Navbar } from '@imphnen-frontend-service/ui/organisms';
import { Outlet } from 'react-router-dom';
import { FC, ReactElement } from 'react';
import { ModalLoginProvider } from '@imphnen-frontend-service/utils';
export const AppLayout: FC = (): ReactElement => {
return (
<main className="bg-primary-50 min-h-screen">
<Navbar />
<Outlet />
</main>
<ModalLoginProvider>
<main className="bg-primary-50 min-h-screen">
<Navbar />
<Outlet />
</main>
</ModalLoginProvider>
);
};
export default AppLayout;
+3 -13
View File
@@ -5,10 +5,11 @@ import ModalFormForgotPassword from './_components/form/modal-form-forgot-passwo
import ModalFormLogin from './_components/form/modal-form-login';
import ModalFormRegister from './_components/form/modal-form-register';
import { GachaItem } from './_components/item/gacha-item';
import { useModalLogin } from '@imphnen-frontend-service/utils';
export const Components: FC = (): ReactElement => {
const { showModalLogin, setShowModalLogin } = useModalLogin();
const [showModalForgotPassword, setShowModalForgotPassword] = useState(false);
const [showModalLogin, setShowModalLogin] = useState(false);
const [showModalRegister, setShowModalRegister] = useState(false);
const scrollToRoulette = () => {
@@ -176,18 +177,6 @@ export const Components: FC = (): ReactElement => {
Spin Now
</Button>
</div>
<div className="flex gap-4 col-span-4 md:col-span-8 lg:col-span-6 justify-center items-center">
<Button onClick={() => setShowModalLogin(true)}>
Open Modal Login
</Button>
<Button onClick={() => setShowModalForgotPassword(true)}>
Open Modal Forgot Password
</Button>
<Button onClick={() => setShowModalRegister(true)}>
Open Modal Register
</Button>
</div>
</section>
<div className="sticky bottom-0 h-[86px] md:h-[200px] bg-gradient-to-b from-primary-500/0 to-primary-500/50 to-80%"></div>
@@ -195,6 +184,7 @@ export const Components: FC = (): ReactElement => {
isOpen={showModalLogin}
onClose={() => setShowModalLogin(false)}
onForgotPassword={handleForgotPasswordClick}
setIsOpenRegisterModal={setShowModalRegister}
key="login"
/>
+12 -4
View File
@@ -2,8 +2,16 @@ import { z } from 'zod';
export const authLoginSchema = z.object({
email: z
.string()
.min(1, 'Email cannot be empty')
.email('Email must be valid'),
password: z.string().min(1, 'Password cannot be empty'),
.string({
required_error: 'Email tidak boleh kosong',
invalid_type_error: 'Email harus berupa string',
})
.min(1, 'Email tidak boleh kosong')
.email('Email harus valid'),
password: z
.string({
required_error: 'Password tidak boleh kosong',
invalid_type_error: 'Password harus berupa string',
})
.min(1, 'Password tidak boleh kosong'),
});
@@ -75,7 +75,7 @@ export const InputField: FC<TInputFieldProps> = ({
{...rest}
/>
{error ? (
<p className="text-danger-500 text-xs mt-1">{error}</p>
<p className="text-danger-500 text-xl mt-1">{error}</p>
) : (
helperText && (
<p className={cn('text-cs mt-1', sizeClasses[size].helperText)}>
+7 -6
View File
@@ -2,11 +2,12 @@ import { MenuOutlined } from '@ant-design/icons';
import { FC, ReactElement, useState } from 'react';
import { Link } from 'react-router-dom';
import { Button } from '../../atoms/button';
import { useSession } from '@imphnen-frontend-service/utils';
import { useModalLogin, useSession } from '@imphnen-frontend-service/utils';
export const Navbar: FC = (): ReactElement => {
const { session, signOut, isAuthenticated } = useSession();
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const { setShowModalLogin } = useModalLogin();
return (
<div className="bg-primary-50 w-full px-[32px] pt-[32px] md:px-[60px] md:pt-[60px] lg:px-[80px] sticky top-0 z-50">
@@ -44,7 +45,7 @@ export const Navbar: FC = (): ReactElement => {
</li>
{!isAuthenticated ? (
<li>
<Button>Login</Button>
<Button onClick={() => setShowModalLogin(true)}>Login</Button>
</li>
) : (
<li className="flex gap-x-4">
@@ -88,12 +89,12 @@ export const Navbar: FC = (): ReactElement => {
</li>
{!isAuthenticated ? (
<li>
<Link
to="#"
className="block text-gray-600 transition-colors px-4 py-2 text-center font-semibold"
<Button
onClick={() => setShowModalLogin(true)}
className="block w-full text-gray-100 transition-colors px-4 py-2 text-center font-semibold"
>
Login
</Link>
</Button>
</li>
) : (
<li>{session.user?.fullname}</li>
+1
View File
@@ -1,2 +1,3 @@
export * from './use-query-state';
export * from './use-session';
export * from './use-modal-login';
+28
View File
@@ -0,0 +1,28 @@
import { createContext, ReactNode, useContext, useState } from 'react';
interface ModalLoginContextType {
showModalLogin: boolean;
setShowModalLogin: (value: boolean) => void;
}
const ModalLoginContext = createContext<ModalLoginContextType | undefined>(
undefined
);
export const ModalLoginProvider = ({ children }: { children: ReactNode }) => {
const [showModalLogin, setShowModalLogin] = useState(false);
return (
<ModalLoginContext.Provider value={{ showModalLogin, setShowModalLogin }}>
{children}
</ModalLoginContext.Provider>
);
};
export const useModalLogin = () => {
const context = useContext(ModalLoginContext);
if (!context) {
throw new Error('useModalLogin must be used within an ModalLoginProvider');
}
return context;
};
+11
View File
@@ -21,6 +21,7 @@
"react-dom": "^19.0.0",
"react-hook-form": "^7.55.0",
"react-router-dom": "^7.3.0",
"sonner": "^2.0.3",
"tailwind-merge": "^3.0.2",
"zod": "^3.24.2"
},
@@ -24244,6 +24245,16 @@
"atomic-sleep": "^1.0.0"
}
},
"node_modules/sonner": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.3.tgz",
"integrity": "sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==",
"license": "MIT",
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
}
},
"node_modules/sort-keys": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
+1
View File
@@ -30,6 +30,7 @@
"react-dom": "^19.0.0",
"react-hook-form": "^7.55.0",
"react-router-dom": "^7.3.0",
"sonner": "^2.0.3",
"tailwind-merge": "^3.0.2",
"zod": "^3.24.2"
},