feat: cafe owner registeration
This commit is contained in:
parent
cf26ec0b13
commit
c2382b23f1
5 changed files with 301 additions and 2 deletions
|
|
@ -3,6 +3,7 @@ import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom'
|
|||
import Home from './pages/Home'
|
||||
import CafeDetail from './pages/CafeDetail'
|
||||
import PaymentSuccess from './pages/PaymentSuccess'
|
||||
import CafeOwnerRegister from './pages/CafeOwnerRegister'
|
||||
|
||||
function ScrollToTop() {
|
||||
const { pathname } = useLocation()
|
||||
|
|
@ -22,6 +23,7 @@ export default function App() {
|
|||
<Route path="/" element={<Home />} />
|
||||
<Route path="/cafe/:id" element={<CafeDetail />} />
|
||||
<Route path="/payment/success" element={<PaymentSuccess />} />
|
||||
<Route path="/owner/register" element={<CafeOwnerRegister />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
|
|
|||
3
src/assets/arrow-right.svg
Normal file
3
src/assets/arrow-right.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.0898 4.07992L8.56984 10.5999C7.79984 11.3699 7.79984 12.6299 8.56984 13.3999L15.0898 19.9199" stroke="white" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 314 B |
|
|
@ -117,7 +117,7 @@ export default function ReservationModal({
|
|||
value={fullName}
|
||||
onChange={(e) => setFullName(e.target.value)}
|
||||
placeholder="نام و نام خانوادگی"
|
||||
className="w-full rounded-2xl border border-gray-200 px-4 py-3 text-right"
|
||||
className="w-full rounded-2xl border border-gray-200 px-4 py-3 text-right placeholder:font-light placeholder:text-[#999999]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -131,7 +131,7 @@ export default function ReservationModal({
|
|||
value={mockPhone}
|
||||
readOnly
|
||||
placeholder="مثال: ۰۹۱۲۳۴۵۶۷۸۹"
|
||||
className="w-full rounded-2xl border border-gray-200 bg-gray-50 px-4 py-3 text-right"
|
||||
className="w-full rounded-2xl border border-gray-200 bg-gray-50 px-4 py-3 text-right placeholder:font-light placeholder:text-[#999999]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
12
src/data/mockCafeFeatures.ts
Normal file
12
src/data/mockCafeFeatures.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export interface CafeFeature {
|
||||
id: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export const mockCafeFeatures: CafeFeature[] = [
|
||||
{ id: 'open-space', label: 'فضای باز' },
|
||||
{ id: 'family-friendly', label: 'فضای مناسب خانواده' },
|
||||
{ id: 'smoking-allowed', label: 'امکان کشیدن سیگار' },
|
||||
{ id: 'parking', label: 'پارکینگ' },
|
||||
{ id: 'delivery', label: 'امکان سفارش' },
|
||||
]
|
||||
282
src/pages/CafeOwnerRegister.tsx
Normal file
282
src/pages/CafeOwnerRegister.tsx
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
import { ReactNode, useState } from "react";
|
||||
import InfoIcon from "../components/InfoIcon";
|
||||
import { mockCafeFeatures } from "../data/mockCafeFeatures";
|
||||
|
||||
function ArrowIcon({ className = "" }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
className={className}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M15.0898 4.07992L8.56984 10.5999C7.79984 11.3699 7.79984 12.6299 8.56984 13.3999L15.0898 19.9199" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function CheckIcon() {
|
||||
return (
|
||||
<svg
|
||||
className="h-3 w-3 text-white"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M2.5 6.5L4.5 8.5L9.5 3.5" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function StepCircle({
|
||||
active,
|
||||
children,
|
||||
}: {
|
||||
active: boolean;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<span
|
||||
className={`mx-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-xs font-semibold ${
|
||||
active
|
||||
? "bg-[#223D5D] text-white"
|
||||
: "border border-gray-300 text-gray-400"
|
||||
}`}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function Stepper({ currentStep }: { currentStep: 1 | 2 }) {
|
||||
return (
|
||||
<div className="flex items-center px-4 py-7">
|
||||
<span
|
||||
className={`whitespace-nowrap ${currentStep === 1 ? "font-medium text-gray-900" : "text-gray-400"}`}
|
||||
>
|
||||
اطلاعات نماینده
|
||||
</span>
|
||||
<StepCircle active={currentStep === 1}>۱</StepCircle>
|
||||
<span className="mx-1 h-px flex-1 bg-gray-200" />
|
||||
<StepCircle active={currentStep === 2}>۲</StepCircle>
|
||||
<span
|
||||
className={`whitespace-nowrap ${currentStep === 2 ? "font-medium text-gray-900" : "text-gray-400"}`}
|
||||
>
|
||||
اطلاعات کافه
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FormField({
|
||||
label,
|
||||
placeholder,
|
||||
value,
|
||||
onChange,
|
||||
}: {
|
||||
label: string;
|
||||
placeholder: string;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="mt-5">
|
||||
<label className="mb-3 block text-right text-lg font-normal text-gray-900">
|
||||
{label}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
className="w-full rounded-2xl border border-gray-200 px-4 py-3 text-right h-14 placeholder:font-light placeholder:text-[#999999]"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function UploadBox({
|
||||
label,
|
||||
buttonLabel,
|
||||
}: {
|
||||
label: string;
|
||||
buttonLabel: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="mt-5">
|
||||
<label className="mb-3 block text-right text-lg font-normal text-gray-900">
|
||||
{label}
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-36 w-full items-center justify-center rounded-2xl border border-dashed border-gray-300 text-[#666666] bg-[#F8F8F8]"
|
||||
>
|
||||
{buttonLabel}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FeatureChip({
|
||||
label,
|
||||
checked,
|
||||
onToggle,
|
||||
}: {
|
||||
label: string;
|
||||
checked: boolean;
|
||||
onToggle: () => void;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggle}
|
||||
className={`flex items-center gap-2 whitespace-nowrap rounded-2xl border border-[#F2F2F2] px-3 py-2 text-sm text-gray-900 ${
|
||||
checked ? "bg-[#ECF2F8]" : "bg-transparent"
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`flex h-5 w-5 shrink-0 items-center justify-center rounded-md border border-[#223D5D] ${
|
||||
checked ? "bg-[#223D5D]" : "bg-transparent"
|
||||
}`}
|
||||
>
|
||||
{checked && <CheckIcon />}
|
||||
</span>
|
||||
<span>{label}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default function CafeOwnerRegister() {
|
||||
const [step, setStep] = useState<1 | 2>(1);
|
||||
|
||||
const [fullName, setFullName] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [nationalId, setNationalId] = useState("");
|
||||
const [cardNumber, setCardNumber] = useState("");
|
||||
|
||||
const [cafeName, setCafeName] = useState("");
|
||||
const [address, setAddress] = useState("");
|
||||
const [cafePhone, setCafePhone] = useState("");
|
||||
const [selectedFeatures, setSelectedFeatures] = useState<string[]>([]);
|
||||
|
||||
const toggleFeature = (feature: string) => {
|
||||
setSelectedFeatures((prev) =>
|
||||
prev.includes(feature)
|
||||
? prev.filter((f) => f !== feature)
|
||||
: [...prev, feature],
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white pb-24">
|
||||
<div className="h-16 w-full bg-gray-200" />
|
||||
|
||||
<Stepper currentStep={step} />
|
||||
|
||||
<div className="px-4">
|
||||
{step === 1 ? (
|
||||
<>
|
||||
<div className="flex items-center justify-end gap-2 rounded-2xl bg-[#ECF2F8] px-4 py-4">
|
||||
<InfoIcon />
|
||||
<p className="text-right text-md leading-relaxed text-gray-600">
|
||||
لطفاً یک{" "}
|
||||
<span className="font-semibold text-gray-900">نماینده</span>{" "}
|
||||
برای کافه معرفی کنید. نماینده باید در اپلیکیشن ثبتنام کند و
|
||||
پرداختها و واریز به کیف پول یا واریز میشود. شماره موبایل، کد
|
||||
ملی و کارت بانکی باید متعلق به یک نفر باشند.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
label="نام و نام خانوادگی"
|
||||
placeholder="نام و نام خانوادگی نماینده را وارد کنید"
|
||||
value={fullName}
|
||||
onChange={setFullName}
|
||||
/>
|
||||
<FormField
|
||||
label="شماره تماس نماینده"
|
||||
placeholder="مثال: ۰۹۱۲۳۴۵۶۷۸۹"
|
||||
value={phone}
|
||||
onChange={setPhone}
|
||||
/>
|
||||
<FormField
|
||||
label="کد ملی"
|
||||
placeholder="کد ملی نماینده را وارد کنید"
|
||||
value={nationalId}
|
||||
onChange={setNationalId}
|
||||
/>
|
||||
<FormField
|
||||
label="شماره کارت"
|
||||
placeholder="شماره کارت نماینده را وارد کنید"
|
||||
value={cardNumber}
|
||||
onChange={setCardNumber}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<FormField
|
||||
label="نام کافه"
|
||||
placeholder="نام کافه را وارد کنید"
|
||||
value={cafeName}
|
||||
onChange={setCafeName}
|
||||
/>
|
||||
<FormField
|
||||
label="آدرس"
|
||||
placeholder="موقعیت کافه را با جزئیات وارد کنید"
|
||||
value={address}
|
||||
onChange={setAddress}
|
||||
/>
|
||||
<FormField
|
||||
label="شماره تماس کافه (اختیاری)"
|
||||
placeholder="مثال: ۰۹۱۲۳۴۵۶۷۸۹"
|
||||
value={cafePhone}
|
||||
onChange={setCafePhone}
|
||||
/>
|
||||
|
||||
<UploadBox label="تصاویر (اختیاری)" buttonLabel="افزودن تصویر" />
|
||||
<UploadBox label="مجوز کسب و کار" buttonLabel="افزودن تصویر مجوز" />
|
||||
|
||||
<div className="mt-5">
|
||||
<label className="mb-3 block text-right text-lg font-normal text-gray-900">
|
||||
ویژگیهای کافه
|
||||
</label>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{mockCafeFeatures.map((feature) => (
|
||||
<FeatureChip
|
||||
key={feature.id}
|
||||
label={feature.label}
|
||||
checked={selectedFeatures.includes(feature.id)}
|
||||
onToggle={() => toggleFeature(feature.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="fixed inset-x-0 bottom-0 flex items-center justify-between gap-3 bg-white px-4 py-4">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-full bg-[#223D5D] px-5 py-3 font-semibold text-white"
|
||||
>
|
||||
پشتیبانی
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => step === 1 && setStep(2)}
|
||||
className="flex items-center gap-2 rounded-full bg-[#223D5D] px-6 py-3 font-semibold text-white"
|
||||
>
|
||||
ادامه
|
||||
<ArrowIcon className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue