Merge pull request #17 from IMPHNEN/feat/handle-state-modal
Feat/handle state modal
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
|
||||
|
||||
@@ -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)}>
|
||||
|
||||
@@ -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,2 +1,3 @@
|
||||
export * from './use-query-state';
|
||||
export * from './use-session';
|
||||
export * from './use-modal-login';
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
Generated
+11
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user