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 Home from './pages/Home'
|
||||||
import CafeDetail from './pages/CafeDetail'
|
import CafeDetail from './pages/CafeDetail'
|
||||||
import PaymentSuccess from './pages/PaymentSuccess'
|
import PaymentSuccess from './pages/PaymentSuccess'
|
||||||
|
import CafeOwnerRegister from './pages/CafeOwnerRegister'
|
||||||
|
|
||||||
function ScrollToTop() {
|
function ScrollToTop() {
|
||||||
const { pathname } = useLocation()
|
const { pathname } = useLocation()
|
||||||
|
|
@ -22,6 +23,7 @@ export default function App() {
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
<Route path="/cafe/:id" element={<CafeDetail />} />
|
<Route path="/cafe/:id" element={<CafeDetail />} />
|
||||||
<Route path="/payment/success" element={<PaymentSuccess />} />
|
<Route path="/payment/success" element={<PaymentSuccess />} />
|
||||||
|
<Route path="/owner/register" element={<CafeOwnerRegister />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</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}
|
value={fullName}
|
||||||
onChange={(e) => setFullName(e.target.value)}
|
onChange={(e) => setFullName(e.target.value)}
|
||||||
placeholder="نام و نام خانوادگی"
|
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>
|
</div>
|
||||||
|
|
||||||
|
|
@ -131,7 +131,7 @@ export default function ReservationModal({
|
||||||
value={mockPhone}
|
value={mockPhone}
|
||||||
readOnly
|
readOnly
|
||||||
placeholder="مثال: ۰۹۱۲۳۴۵۶۷۸۹"
|
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>
|
</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