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 { Button } from '@imphnen-frontend-service/ui/atoms';
|
||||||
import { Modal } from '@imphnen-frontend-service/ui/molecules';
|
import { Modal } from '@imphnen-frontend-service/ui/molecules';
|
||||||
import { ControlledInputField } from '@imphnen-frontend-service/ui/organisms';
|
import { ControlledInputField } from '@imphnen-frontend-service/ui/organisms';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { useLogin } from '../../_hooks/use-login';
|
import { useLogin } from '../../_hooks/use-login';
|
||||||
|
|
||||||
interface IModalFormLogin {
|
interface IModalFormLogin {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onForgotPassword: () => void;
|
onForgotPassword: () => void;
|
||||||
|
setIsOpenRegisterModal: (value: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ModalFormLogin = ({
|
const ModalFormLogin = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
onForgotPassword,
|
onForgotPassword,
|
||||||
|
setIsOpenRegisterModal,
|
||||||
}: IModalFormLogin) => {
|
}: IModalFormLogin) => {
|
||||||
const { form, onSubmit } = useLogin();
|
const { form, onSubmit } = useLogin();
|
||||||
|
|
||||||
@@ -59,12 +60,13 @@ const ModalFormLogin = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<div className="flex justify-center gap-2 pt-2.5 font-medium">
|
<div className="flex justify-center gap-2 pt-2.5 font-medium">
|
||||||
<p className="text-neutral-500">Belum punya akun?</p>
|
<p className="text-neutral-500">Belum punya akun?</p>
|
||||||
<Link
|
<Button
|
||||||
className="text-primary-500 hover:text-primary-600"
|
variant="text"
|
||||||
to="/register"
|
className="text-primary-500 hover:text-primary-600 m-0 p-0"
|
||||||
|
onClick={() => setIsOpenRegisterModal(true)}
|
||||||
>
|
>
|
||||||
Daftar
|
Daftar
|
||||||
</Link>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Modal.Content>
|
</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 { 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 => {
|
export const AppLayout: FC = (): ReactElement => {
|
||||||
return (
|
return (
|
||||||
<main className="bg-primary-50 min-h-screen">
|
<ModalLoginProvider>
|
||||||
<Navbar />
|
<main className="bg-primary-50 min-h-screen">
|
||||||
<Outlet />
|
<Navbar />
|
||||||
</main>
|
<Outlet />
|
||||||
|
</main>
|
||||||
|
</ModalLoginProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AppLayout;
|
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 ModalFormLogin from './_components/form/modal-form-login';
|
||||||
import ModalFormRegister from './_components/form/modal-form-register';
|
import ModalFormRegister from './_components/form/modal-form-register';
|
||||||
import { GachaItem } from './_components/item/gacha-item';
|
import { GachaItem } from './_components/item/gacha-item';
|
||||||
|
import { useModalLogin } from '@imphnen-frontend-service/utils';
|
||||||
|
|
||||||
export const Components: FC = (): ReactElement => {
|
export const Components: FC = (): ReactElement => {
|
||||||
|
const { showModalLogin, setShowModalLogin } = useModalLogin();
|
||||||
const [showModalForgotPassword, setShowModalForgotPassword] = useState(false);
|
const [showModalForgotPassword, setShowModalForgotPassword] = useState(false);
|
||||||
const [showModalLogin, setShowModalLogin] = useState(false);
|
|
||||||
const [showModalRegister, setShowModalRegister] = useState(false);
|
const [showModalRegister, setShowModalRegister] = useState(false);
|
||||||
|
|
||||||
const scrollToRoulette = () => {
|
const scrollToRoulette = () => {
|
||||||
@@ -176,18 +177,6 @@ export const Components: FC = (): ReactElement => {
|
|||||||
Spin Now
|
Spin Now
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</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>
|
</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>
|
<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}
|
isOpen={showModalLogin}
|
||||||
onClose={() => setShowModalLogin(false)}
|
onClose={() => setShowModalLogin(false)}
|
||||||
onForgotPassword={handleForgotPasswordClick}
|
onForgotPassword={handleForgotPasswordClick}
|
||||||
|
setIsOpenRegisterModal={setShowModalRegister}
|
||||||
key="login"
|
key="login"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,16 @@ import { z } from 'zod';
|
|||||||
|
|
||||||
export const authLoginSchema = z.object({
|
export const authLoginSchema = z.object({
|
||||||
email: z
|
email: z
|
||||||
.string()
|
.string({
|
||||||
.min(1, 'Email cannot be empty')
|
required_error: 'Email tidak boleh kosong',
|
||||||
.email('Email must be valid'),
|
invalid_type_error: 'Email harus berupa string',
|
||||||
password: z.string().min(1, 'Password cannot be empty'),
|
})
|
||||||
|
.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}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
{error ? (
|
{error ? (
|
||||||
<p className="text-danger-500 text-xs mt-1">{error}</p>
|
<p className="text-danger-500 text-xl mt-1">{error}</p>
|
||||||
) : (
|
) : (
|
||||||
helperText && (
|
helperText && (
|
||||||
<p className={cn('text-cs mt-1', sizeClasses[size].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 { FC, ReactElement, useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Button } from '../../atoms/button';
|
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 => {
|
export const Navbar: FC = (): ReactElement => {
|
||||||
const { session, signOut, isAuthenticated } = useSession();
|
const { session, signOut, isAuthenticated } = useSession();
|
||||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||||
|
const { setShowModalLogin } = useModalLogin();
|
||||||
|
|
||||||
return (
|
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">
|
<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>
|
</li>
|
||||||
{!isAuthenticated ? (
|
{!isAuthenticated ? (
|
||||||
<li>
|
<li>
|
||||||
<Button>Login</Button>
|
<Button onClick={() => setShowModalLogin(true)}>Login</Button>
|
||||||
</li>
|
</li>
|
||||||
) : (
|
) : (
|
||||||
<li className="flex gap-x-4">
|
<li className="flex gap-x-4">
|
||||||
@@ -88,12 +89,12 @@ export const Navbar: FC = (): ReactElement => {
|
|||||||
</li>
|
</li>
|
||||||
{!isAuthenticated ? (
|
{!isAuthenticated ? (
|
||||||
<li>
|
<li>
|
||||||
<Link
|
<Button
|
||||||
to="#"
|
onClick={() => setShowModalLogin(true)}
|
||||||
className="block text-gray-600 transition-colors px-4 py-2 text-center font-semibold"
|
className="block w-full text-gray-100 transition-colors px-4 py-2 text-center font-semibold"
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</Link>
|
</Button>
|
||||||
</li>
|
</li>
|
||||||
) : (
|
) : (
|
||||||
<li>{session.user?.fullname}</li>
|
<li>{session.user?.fullname}</li>
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from './use-query-state';
|
export * from './use-query-state';
|
||||||
export * from './use-session';
|
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-dom": "^19.0.0",
|
||||||
"react-hook-form": "^7.55.0",
|
"react-hook-form": "^7.55.0",
|
||||||
"react-router-dom": "^7.3.0",
|
"react-router-dom": "^7.3.0",
|
||||||
|
"sonner": "^2.0.3",
|
||||||
"tailwind-merge": "^3.0.2",
|
"tailwind-merge": "^3.0.2",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
},
|
},
|
||||||
@@ -24244,6 +24245,16 @@
|
|||||||
"atomic-sleep": "^1.0.0"
|
"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": {
|
"node_modules/sort-keys": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-hook-form": "^7.55.0",
|
"react-hook-form": "^7.55.0",
|
||||||
"react-router-dom": "^7.3.0",
|
"react-router-dom": "^7.3.0",
|
||||||
|
"sonner": "^2.0.3",
|
||||||
"tailwind-merge": "^3.0.2",
|
"tailwind-merge": "^3.0.2",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user