feat: Forgot Password Slicing, fix: webp image

This commit is contained in:
boboiazumi
2025-03-29 20:42:54 +07:00
parent a3887519f9
commit ecdaa4a82e
22 changed files with 246 additions and 5 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 MiB

@@ -0,0 +1,12 @@
import { FC, ReactElement } from 'react';
import { Outlet } from 'react-router-dom';
export const AppLayout: FC = (): ReactElement => {
return (
<main className="bg-primary-50 min-h-screen">
<Outlet />
</main>
);
};
export default AppLayout;
@@ -0,0 +1,12 @@
import { FC, ReactElement } from 'react';
import { Outlet } from 'react-router-dom';
export const AppLayout: FC = (): ReactElement => {
return (
<main className="bg-primary-50 min-h-screen">
<Outlet />
</main>
);
};
export default AppLayout;
@@ -0,0 +1,23 @@
import { FC, ReactElement } from 'react';
import { RegisterResetBanner } from "@imphnen-frontend-service/ui/organisms";
import { ForgotStep, OtpForm } from "@imphnen-frontend-service/ui/molecules";
import { Button, Input } from '@imphnen-frontend-service/ui/atoms';
export const Components: FC = (): ReactElement => {
return (
<div className='flex flex-col justify-center items-center min-h-screen py-[60px] px-[80px]'>
<div className='bg-white min-w-[1120px] min-h-[712px] p-10 rounded-2xl shadow-md flex gap-6'>
<RegisterResetBanner />
<div className='border-2 border-primary-500/50 w-[596px] rounded-lg py-[70px] px-[48px] flex flex-col justify-center'>
<ForgotStep step={2} />
<h3 className='mt-5 text-3xl font-semibold text-primary-500'>Forgot Password</h3>
<h5 className='mt-2 text-xl font-medium text-primary-500'>Yeay~! Pesan dari dunia lain (a.k.a email-mu) sudah tiba!</h5>
<OtpForm />
<Button>Summon Password Baru!</Button>
</div>
</div>
</div>
);
};
export default Components;
@@ -0,0 +1,24 @@
import { FC, ReactElement } from 'react';
import { RegisterResetBanner } from "@imphnen-frontend-service/ui/organisms";
import { ForgotStep } from "@imphnen-frontend-service/ui/molecules";
import { Button, Input } from '@imphnen-frontend-service/ui/atoms';
export const Components: FC = (): ReactElement => {
return (
<div className='flex flex-col justify-center items-center min-h-screen py-[60px] px-[80px]'>
<div className='bg-white min-w-[1120px] min-h-[712px] p-10 rounded-2xl shadow-md flex gap-6'>
<RegisterResetBanner />
<div className='border-2 border-primary-500/50 w-[596px] rounded-lg py-[70px] px-[48px] flex flex-col justify-center'>
<ForgotStep step={1} />
<h3 className='mt-5 text-3xl font-semibold text-primary-500'>Forgot Password</h3>
<h5 className='mt-2 text-xl font-medium text-primary-500'>Masukkan email-mu, dan biarkan kami memanggil password-mu kembali dari dunia lain!</h5>
<h6 className='text-gray-600 mt-8'>Email</h6>
<Input type='email' size='lg' placeholder='Contoh: yourname@mail.com'></Input>
<Button variant='primary' className='mt-7'>Kirim Kode OTP ^^</Button>
</div>
</div>
</div>
);
};
export default Components;
@@ -0,0 +1,12 @@
import { FC, ReactElement } from 'react';
import { Outlet } from 'react-router-dom';
export const AppLayout: FC = (): ReactElement => {
return (
<main className="bg-primary-50 min-h-screen">
<Outlet />
</main>
);
};
export default AppLayout;
@@ -0,0 +1,34 @@
import { FC, ReactElement } from 'react';
import { RegisterResetBanner } from "@imphnen-frontend-service/ui/organisms";
import { ForgotStep } from "@imphnen-frontend-service/ui/molecules";
import { Button, Input } from '@imphnen-frontend-service/ui/atoms';
export const Components: FC = (): ReactElement => {
return (
<div className='flex flex-col justify-center items-center min-h-screen py-[60px] px-[80px]'>
<div className='bg-white min-w-[1120px] min-h-[712px] p-10 rounded-2xl shadow-md flex gap-6'>
<RegisterResetBanner />
<div className='border-2 border-primary-500/50 w-[596px] rounded-lg py-[70px] px-[48px] flex flex-col justify-center'>
<ForgotStep step={3} />
<h3 className='mt-5 text-3xl font-semibold text-primary-500'>Forgot Password</h3>
<h5 className='mt-2 text-md font-medium text-primary-500'>Sekarang, buatlah password baru yang lebih kuat, seperti jurus andalanmu!<span role="img" aria-label='emoji'>🔥</span></h5>
<div className='my-4'>
<h6 className='text-gray-700'>Password Baru</h6>
<Input className='w-full' type="password" placeholder='Buat password sekokoh armor legendary!'/>
<h6 className='text-gray-700 mt-5'>Ulang Password Baru</h6>
<Input className='w-full' type="password" placeholder='Pastikan Cocok! Jangan sampai ada typo, Senpai~!'/>
</div>
<div className='mb-4 text-gray-700 flex flex-col gap-3 text-sm'>
<h6><span role='img' aria-label='emoji'>💡</span> Tips dari kami:</h6>
<h6><span role='img' aria-label='emoji'> </span> Buat kombinasi huruf besar, kecil, angka, dan simbol untuk kekuatan maksimal!</h6>
<h6><span role='img' aria-label='emoji'></span> Pastikan kamu ingat password-mu atau simpan di tempat aman~</h6>
<h6>Masukkan password baru dan bersiaplah untuk kembali bertualang!</h6>
</div>
<Button className='mt-5'>Summon Password Baru!</Button>
</div>
</div>
</div>
);
};
export default Components;
+1 -1
View File
@@ -34,7 +34,7 @@ export const Components: FC = (): ReactElement => {
</div> </div>
<Button className='w-full my-3 text-gray-500 gap-2' variant='secondary'> <Button className='w-full my-3 text-gray-500 gap-2' variant='secondary'>
<p>Log In With Google</p> <p>Log In With Google</p>
<img src="/image/33978ce5bed2da9bf9d73acad802182a.png" alt="Google Icon" width={24}/> <img src="/image/33978ce5bed2da9bf9d73acad802182a.webp" alt="Google Icon" width={24}/>
</Button> </Button>
</div> </div>
</div> </div>
@@ -0,0 +1,12 @@
import { FC, ReactElement } from 'react';
import { Outlet } from 'react-router-dom';
export const AppLayout: FC = (): ReactElement => {
return (
<main className="bg-primary-50 min-h-screen">
<Outlet />
</main>
);
};
export default AppLayout;
@@ -0,0 +1,19 @@
import { FC, ReactElement } from 'react';
import { RegisterResetBanner } from "@imphnen-frontend-service/ui/organisms";
import { Button, Input } from '@imphnen-frontend-service/ui/atoms';
export const Components: FC = (): ReactElement => {
return (
<div className='flex flex-col justify-center items-center min-h-screen py-[60px] px-[80px]'>
<div className='bg-white min-w-[1120px] min-h-[712px] p-10 rounded-2xl shadow-md flex gap-6'>
<RegisterResetBanner />
<div className='border-2 border-primary-500/50 w-[596px] rounded-lg py-[70px] px-[96px] flex justify-center'>
<div className='w-[404px]'>
</div>
</div>
</div>
</div>
);
};
export default Components;
+1 -1
View File
@@ -6,7 +6,7 @@ import {
} from 'react'; } from 'react';
import { cn } from '@imphnen-frontend-service/utils'; import { cn } from '@imphnen-frontend-service/utils';
type TInputType = 'text' | 'email'; type TInputType = 'text' | 'email' | 'password';
type TInputSize = 'sm' | 'md' | 'lg'; type TInputSize = 'sm' | 'md' | 'lg';
type TInputProps = Omit< type TInputProps = Omit<
@@ -0,0 +1,30 @@
import { DetailedHTMLProps, FC, InputHTMLAttributes, ReactElement } from "react"
type TForgotStepProps = Omit<
DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>,
'size' | 'type'
> & {
step: number
};
export const ForgotStep: FC<TForgotStepProps> = ({
step = 1,
...rest
}): ReactElement => {
return (
<div className="w-full grid grid-cols-3 gap-3">
<div className="flex flex-col items-center justify-center gap-2">
<div className={`${step === 1 ? "bg-primary-500" : "bg-primary-200"} px-5 py-2 rounded-md w-full`}></div>
<h4 className={`${step === 1 ? "text-primary-500" : "text-gray-500"} text-xs`}>Masukkan Email</h4>
</div>
<div className="flex flex-col items-center justify-center gap-2">
<div className={`${step === 2 ? "bg-primary-500" : "bg-primary-200"} px-5 py-2 rounded-md w-full`}></div>
<h4 className={`${step === 2 ? "text-primary-500" : "text-gray-500"} text-xs`}>Verifikasi OTP</h4>
</div>
<div className="flex flex-col items-center justify-center gap-2">
<div className={`${step === 3 ? "bg-primary-500" : "bg-primary-200"} px-5 py-2 rounded-md w-full`}></div>
<h4 className={`${step === 3 ? "text-primary-500" : "text-gray-500"} text-xs`}>Summon Password ^^</h4>
</div>
</div>
)
}
@@ -0,0 +1 @@
export * from './forgot-step';
+2 -1
View File
@@ -1 +1,2 @@
export * from "./forgot-step"; export * from "./forgot-step";
export * from "./otp-form";
+1
View File
@@ -0,0 +1 @@
export * from "./otp-form"
@@ -0,0 +1,60 @@
// eslint-disable-next-line @nx/enforce-module-boundaries
import { Input } from "@imphnen-frontend-service/ui/atoms";
import { ChangeEvent, DetailedHTMLProps, FC, InputHTMLAttributes, ReactElement, useRef, useState } from "react"
type TForgotStepProps = Omit<
DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>,
'size' | 'type'
> & {
};
export const OtpForm: FC<TForgotStepProps> = ({
step = 1,
...rest
}): ReactElement => {
const [cell, setCell] = useState(new Array(6).fill(""))
const inputRef = useRef([])
const handleChange = (index: number, e: ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
if (!(/^[0-9]?$/.test(value))) return;
const newCell = [...cell]
newCell[index] = value
setCell(newCell)
if(value && index < 6 - 1){
(inputRef.current[index + 1] as HTMLInputElement).focus()
}
}
const handleKeyDown = (index: number, e: React.KeyboardEvent<HTMLInputElement>) => {
if(e.key === "Backspace" && !cell[index] && index > 0){
const newCell = [...cell]
newCell[index] = ""
setCell(newCell)
const ref = (inputRef.current[index - 1] as HTMLInputElement)
ref.focus()
}
}
return (
<div className="w-full grid grid-cols-6 gap-3 my-10">
{cell.map((v, i) => (
<Input
placeholder=""
ref={(el) => {
inputRef.current.push(el as never)
return inputRef.current[i]
}}
onChange={(e) => handleChange(i, e)}
onKeyDown={(e) => handleKeyDown(i, e)}
size="lg"
value={cell[i]}
/>
))}
</div>
)
}
@@ -6,7 +6,7 @@ import { FC, ReactElement } from "react";
function AuthBanner({text, href}: {text: string, href: string}){ function AuthBanner({text, href}: {text: string, href: string}){
return ( return (
<div className='relative rounded-md'> <div className='relative rounded-md'>
<img src="/image/95319c4f9953dfe6180200e529dfcea5.jpeg" alt="Banner" className='w-[420px] h-[632px] object-[80%] object-cover rounded-lg' /> <img src="/image/95319c4f9953dfe6180200e529dfcea5.webp" alt="Banner" className='w-[420px] h-[632px] object-[80%] object-cover rounded-lg' />
<div className='absolute top-0 bg-gradient-to-b from-primary-500 to-transparent w-full rounded-t-lg h-[163px] py-5 px-5'> <div className='absolute top-0 bg-gradient-to-b from-primary-500 to-transparent w-full rounded-t-lg h-[163px] py-5 px-5'>
<Button variant='secondary' className='gap-2' onClick={() => document.location.href = `${href}`}> <Button variant='secondary' className='gap-2' onClick={() => document.location.href = `${href}`}>
<ArrowLeftOutlined /> <ArrowLeftOutlined />
@@ -14,7 +14,7 @@ function AuthBanner({text, href}: {text: string, href: string}){
</Button> </Button>
</div> </div>
<div className='absolute bottom-0 bg-gradient-to-t from-primary-500 to-transparent w-full rounded-b-lg h-[263px] flex flex-col items-center justify-center'> <div className='absolute bottom-0 bg-gradient-to-t from-primary-500 to-transparent w-full rounded-b-lg h-[263px] flex flex-col items-center justify-center'>
<img src="/image/9261045e09137f3fcb925a78c55b6ddb.png" alt="Logo" width={317} /> <img src="/image/9261045e09137f3fcb925a78c55b6ddb.webp" alt="Logo" width={317} />
</div> </div>
</div> </div>
) )