admin/components/auth/AdminAuthContent.tsx
alisaza f9a9afc697 feat: add layout components for various admin dashboard pages
- Introduced layout components for multiple admin dashboard sections including events, audit logs, bank accounts, blog articles, bookings, chat oversight, cities, contact messages, dashboard, discount codes, event categories, guest lists, identity verifications, manage events, manual notifications, notification rules, payments, provinces, push deliveries, reviews, settlements, sms messages, support tickets, user reports, and users.
- Updated metadata for the auth layout to include the admin page title.
- Modified branch protection documentation to clarify deployment notifications.
2026-09-05 14:29:43 +03:30

216 lines
7.8 KiB
TypeScript

'use client'
import Link from 'next/link'
import { FormProvider } from 'react-hook-form'
import { Progress } from '@/components/heroui/Progress'
import Button from '@/components/formElements/Button'
import Checkbox from '@/components/formElements/Checkbox'
import Input from '@/components/formElements/Input'
import ConsumerInput from '@/components/consumer/ConsumerInput'
import { texts, format } from '@/texts'
import { useAuthFlow } from '@/features/auth/useAuthFlow'
import { useDiscoveryCitiesQuery } from '@/queries/consumer/useDiscoveryCitiesQuery'
import { showFormValidationToast } from '@/lib/formValidationToast'
import { OTP_LENGTH } from '@/validation/auth'
const GENDER_OPTIONS = [
{ name: texts.common.genderMale, selectKey: 'male' },
{ name: texts.common.genderFemale, selectKey: 'female' },
]
export function AdminAuthContent() {
const flow = useAuthFlow({})
const citiesQuery = useDiscoveryCitiesQuery(flow.step === 'profile')
const cityOptions = (citiesQuery.data ?? []).map((city) => ({ id: String(city.id), name: city.name }))
const title = flow.step === 'otp' ? texts.auth.otpTitle : flow.step === 'profile' ? texts.auth.profileTitle : null
return (
<div className="mx-auto flex w-full max-w-xs flex-col">
{title ? <div className="mb-5 text-center text-lg font-bold text-tertiary">{title}</div> : null}
{flow.accountBlockMessage ? (
<div className="rounded-xl border border-fourth-100 bg-fourth-100 px-4 py-3 text-center text-sm text-fourth-900">
{flow.accountBlockMessage}
</div>
) : null}
{flow.step === 'mobile' ? (
<FormProvider {...flow.sendOtpForm}>
<form onSubmit={flow.sendOtpForm.handleSubmit(flow.sendOtp, showFormValidationToast)}>
<ConsumerInput
generalType="input"
inputType="tel"
label={texts.auth.mobileLabel}
name="mobile"
placeholder={texts.auth.mobilePlaceholder}
/>
<Button
fullWidth
className="mt-4"
isLoading={flow.loading}
type="submit"
>
{texts.common.continue}
</Button>
</form>
</FormProvider>
) : null}
{flow.step === 'otp' ? (
<>
<FormProvider {...flow.checkOtpForm}>
<form onSubmit={flow.checkOtpForm.handleSubmit(flow.checkOtp, showFormValidationToast)}>
<ConsumerInput
autoFocus
className="px-2"
generalType="otp"
name="code"
otpLength={OTP_LENGTH}
/>
{flow.otpError ? <p className="mt-2 text-center text-sm text-fourth-900">{flow.otpError}</p> : null}
{flow.isRegistration ? (
<div className="mt-5">
<Checkbox
className="items-start gap-3 rounded-2xl border border-primary-100 bg-primary-50/60 p-4 data-[selected=true]:border-primary-100 data-[selected=true]:bg-primary-50/60"
isSelected={flow.termsAccepted}
onValueChange={flow.updateTermsAccepted}
>
<span
aria-describedby={flow.termsError ? 'terms-error' : undefined}
className="text-sm leading-7 text-tertiary-400"
>
{texts.auth.termsConfirmPrefix}{' '}
<Link
className="font-bold text-primary underline underline-offset-4"
href="/terms"
target="_blank"
>
{texts.auth.termsLink}
</Link>{' '}
{texts.auth.termsConfirmSuffix}
</span>
</Checkbox>
{flow.termsError ? (
<p
className="mt-2 text-sm text-fourth-900"
id="terms-error"
>
{flow.termsError}
</p>
) : null}
</div>
) : null}
<Button
fullWidth
className="mt-6"
isLoading={flow.loading}
type="submit"
>
{flow.isRegistration ? texts.auth.confirmAndContinueRegister : texts.common.login}
</Button>
</form>
</FormProvider>
<div className="my-6 text-center text-sm text-secondary-20">
{flow.resendCodeTime > 0 ? (
<>
<Progress
aria-label={texts.auth.resendTimerAria}
className="mb-4 max-w-md [&>div]:bg-tertiary-50 [&>div>div]:bg-tertiary-100"
size="md"
value={((flow.otpTtl - flow.resendCodeTime) / flow.otpTtl) * 100}
/>
<p>{texts.auth.didNotReceiveCode}</p>
<p className="mt-1 text-tertiary-600">{format(texts.auth.resendInSeconds, { seconds: flow.resendCodeTime })}</p>
</>
) : (
<Button
className="text-primary"
variant="light"
onClick={() => flow.sendOtp({ mobile: flow.mobile })}
>
{texts.auth.resendCode}
</Button>
)}
</div>
<Button
fullWidth
color="warning"
variant="light"
onClick={flow.resetToMobile}
>
{texts.auth.changeMobile}
</Button>
</>
) : null}
{flow.step === 'profile' ? (
<FormProvider {...flow.completeProfileForm}>
<form
className="flex flex-col gap-5"
onSubmit={flow.completeProfileForm.handleSubmit(flow.submitProfile, showFormValidationToast)}
>
<div>
<ConsumerInput
required
generalType="input"
label={texts.common.firstName}
name="firstName"
placeholder={texts.common.firstNamePlaceholder}
/>
</div>
<div>
<ConsumerInput
required
generalType="input"
label={texts.common.lastName}
name="lastName"
placeholder={texts.common.lastNamePlaceholder}
/>
</div>
<div>
<ConsumerInput
required
generalType="radio"
label={texts.common.gender}
name="gender"
orientation="horizontal"
radioOptions={GENDER_OPTIONS}
selectKey="selectKey"
/>
</div>
<div>
<Input
required
generalType="birthDatePicker"
label={texts.common.birthDate}
name="dateOfBirth"
placeholder={texts.common.birthDatePlaceholder}
/>
</div>
<div>
<ConsumerInput
required
generalType="select"
label={texts.common.city}
name="cityId"
placeholder={citiesQuery.isPending ? texts.auth.loadingCities : texts.common.selectCity}
selectKey="id"
selectOptions={cityOptions}
selectValue="name"
/>
{citiesQuery.isError ? <p className="mt-2 text-sm text-fourth-900">{texts.auth.citiesLoadFailed}</p> : null}
</div>
<Button
fullWidth
isLoading={flow.loading}
type="submit"
>
{texts.auth.completeRegistration}
</Button>
</form>
</FormProvider>
) : null}
</div>
)
}