feat: Add dealer apply link to profile and agreement step to dealer application

- Add dealer program section to profile page with apply/view card button
- Add 2-step dealer application: privacy consent + obligations agreement before form
- Add all translations (en/mn/ru/ko) for new dealer agreement UI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
AutonetSellCar Deploy
2026-03-27 16:24:35 +09:00
parent 3f27297c4a
commit e274bc763d
3 changed files with 232 additions and 19 deletions

View File

@@ -35,6 +35,11 @@ export default function DealerApplyPage() {
const [existingApplication, setExistingApplication] = useState<DealerApplication | null>(null); const [existingApplication, setExistingApplication] = useState<DealerApplication | null>(null);
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null); const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
const [step, setStep] = useState<'agreement' | 'form'>('agreement');
const [privacyAgreed, setPrivacyAgreed] = useState(false);
const [obligationAgreed, setObligationAgreed] = useState(false);
const [agreeError, setAgreeError] = useState(false);
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
business_name: '', business_name: '',
business_number: '', business_number: '',
@@ -182,6 +187,138 @@ export default function DealerApplyPage() {
); );
} }
const handleProceedToForm = () => {
if (!privacyAgreed || !obligationAgreed) {
setAgreeError(true);
return;
}
setAgreeError(false);
setStep('form');
};
const handleAgreeAll = (checked: boolean) => {
setPrivacyAgreed(checked);
setObligationAgreed(checked);
if (checked) setAgreeError(false);
};
// Step 1: Agreement
if (step === 'agreement') {
return (
<div className="min-h-screen bg-gray-50 py-12">
<div className="container mx-auto px-4">
<div className="max-w-2xl mx-auto">
{/* Header */}
<div className="text-center mb-8">
<h1 className="text-3xl font-bold text-gray-800 mb-2">{t.dealerAgreementTitle}</h1>
<p className="text-gray-600">{t.dealerApplicationSubtitle}</p>
</div>
{/* Benefits Card */}
<div className="bg-gradient-to-r from-primary-600 to-primary-700 text-white rounded-xl p-6 mb-8">
<h2 className="text-xl font-semibold mb-4">
{language === 'ko' ? '딜러 혜택' : 'Dealer Benefits'}
</h2>
<ul className="space-y-2">
<li className="flex items-center gap-2">
<span>💰</span>
<span>{language === 'ko' ? '차량 판매 시 수수료 50% 수익' : '50% commission on vehicle sales'}</span>
</li>
<li className="flex items-center gap-2">
<span>🎫</span>
<span>{language === 'ko' ? '공식 딜러증 발급' : 'Official dealer card issued'}</span>
</li>
<li className="flex items-center gap-2">
<span>📈</span>
<span>{language === 'ko' ? '수익 관리 대시보드' : 'Earnings management dashboard'}</span>
</li>
</ul>
</div>
{/* Privacy Agreement */}
<div className="bg-white rounded-xl shadow-lg p-6 mb-4">
<div className="flex items-start gap-3 mb-3">
<input
type="checkbox"
id="privacy-agree"
checked={privacyAgreed}
onChange={(e) => {
setPrivacyAgreed(e.target.checked);
if (e.target.checked) setAgreeError(false);
}}
className="mt-1 w-5 h-5 text-primary-600 rounded border-gray-300 focus:ring-primary-500 cursor-pointer"
/>
<label htmlFor="privacy-agree" className="font-semibold text-gray-800 cursor-pointer">
{t.dealerPrivacyAgreement}
</label>
</div>
<div className="bg-gray-50 border border-gray-200 rounded-lg p-4 max-h-48 overflow-y-auto">
<p className="text-sm text-gray-700 whitespace-pre-line leading-relaxed">
{t.dealerPrivacyContent}
</p>
</div>
</div>
{/* Obligation Agreement */}
<div className="bg-white rounded-xl shadow-lg p-6 mb-4">
<div className="flex items-start gap-3 mb-3">
<input
type="checkbox"
id="obligation-agree"
checked={obligationAgreed}
onChange={(e) => {
setObligationAgreed(e.target.checked);
if (e.target.checked) setAgreeError(false);
}}
className="mt-1 w-5 h-5 text-primary-600 rounded border-gray-300 focus:ring-primary-500 cursor-pointer"
/>
<label htmlFor="obligation-agree" className="font-semibold text-gray-800 cursor-pointer">
{t.dealerObligationAgreement}
</label>
</div>
<div className="bg-gray-50 border border-gray-200 rounded-lg p-4 max-h-48 overflow-y-auto">
<p className="text-sm text-gray-700 whitespace-pre-line leading-relaxed">
{t.dealerObligationContent}
</p>
</div>
</div>
{/* Agree All + Proceed */}
<div className="bg-white rounded-xl shadow-lg p-6">
<label className="flex items-center gap-3 cursor-pointer mb-4">
<input
type="checkbox"
checked={privacyAgreed && obligationAgreed}
onChange={(e) => handleAgreeAll(e.target.checked)}
className="w-5 h-5 text-primary-600 rounded border-gray-300 focus:ring-primary-500"
/>
<span className="font-semibold text-gray-800">{t.dealerAgreeAll}</span>
</label>
{agreeError && (
<div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg">
<p className="text-sm text-red-600">{t.dealerMustAgree}</p>
</div>
)}
<button
onClick={handleProceedToForm}
className={`w-full px-4 py-4 rounded-lg font-semibold transition ${
privacyAgreed && obligationAgreed
? 'bg-primary-600 text-white hover:bg-primary-700'
: 'bg-gray-300 text-gray-500 cursor-not-allowed'
}`}
>
{t.dealerProceedToApply}
</button>
</div>
</div>
</div>
</div>
);
}
// Step 2: Application Form
return ( return (
<div className="min-h-screen bg-gray-50 py-12"> <div className="min-h-screen bg-gray-50 py-12">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
@@ -192,25 +329,17 @@ export default function DealerApplyPage() {
<p className="text-gray-600">{t.dealerApplicationSubtitle}</p> <p className="text-gray-600">{t.dealerApplicationSubtitle}</p>
</div> </div>
{/* Benefits Card */} {/* Step indicator */}
<div className="bg-gradient-to-r from-primary-600 to-primary-700 text-white rounded-xl p-6 mb-8"> <div className="flex items-center justify-center gap-3 mb-8">
<h2 className="text-xl font-semibold mb-4"> <div className="flex items-center gap-2 text-green-600">
{language === 'ko' ? '딜러 혜택' : 'Dealer Benefits'} <div className="w-8 h-8 rounded-full bg-green-600 text-white flex items-center justify-center text-sm font-bold"></div>
</h2> <span className="text-sm font-medium">{t.dealerAgreementTitle}</span>
<ul className="space-y-2"> </div>
<li className="flex items-center gap-2"> <div className="w-8 h-px bg-gray-300"></div>
<span>💰</span> <div className="flex items-center gap-2 text-primary-600">
<span>{language === 'ko' ? '차량 판매 시 수수료 50% 수익' : '50% commission on vehicle sales'}</span> <div className="w-8 h-8 rounded-full bg-primary-600 text-white flex items-center justify-center text-sm font-bold">2</div>
</li> <span className="text-sm font-medium">{t.dealerApplication}</span>
<li className="flex items-center gap-2"> </div>
<span>🎫</span>
<span>{language === 'ko' ? '공식 딜러증 발급' : 'Official dealer card issued'}</span>
</li>
<li className="flex items-center gap-2">
<span>📈</span>
<span>{language === 'ko' ? '수익 관리 대시보드' : 'Earnings management dashboard'}</span>
</li>
</ul>
</div> </div>
{/* Message */} {/* Message */}

View File

@@ -249,6 +249,30 @@ export default function ProfilePage() {
</div> </div>
</div> </div>
{/* Dealer Program Section */}
<div className="bg-white rounded-xl shadow-lg p-6 mb-6">
<h2 className="text-xl font-semibold text-gray-800 mb-4">{t.dealerSectionTitle}</h2>
<p className="text-gray-600 text-sm mb-4">{t.dealerSectionDesc}</p>
{user.is_dealer ? (
<a
href="/dealer/my-card"
className="w-full flex items-center justify-center gap-2 px-4 py-3 bg-gradient-to-r from-amber-500 to-amber-600 text-white rounded-lg hover:from-amber-600 hover:to-amber-700 transition font-semibold"
>
<span>🎫</span>
<span>{t.dealerViewCard}</span>
</a>
) : (
<a
href="/dealer/apply"
className="w-full flex items-center justify-center gap-2 px-4 py-3 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition font-semibold"
>
<span>🤝</span>
<span>{t.dealerApplyButton}</span>
</a>
)}
</div>
{/* Referral Code Card */} {/* Referral Code Card */}
<div className="bg-white rounded-xl shadow-lg p-6"> <div className="bg-white rounded-xl shadow-lg p-6">
<h2 className="text-xl font-semibold text-gray-800 mb-4">{t.myReferralCode}</h2> <h2 className="text-xl font-semibold text-gray-800 mb-4">{t.myReferralCode}</h2>

View File

@@ -381,6 +381,18 @@ export interface Translations {
pendingApplicationMessage: string; pendingApplicationMessage: string;
rejectedApplicationMessage: string; rejectedApplicationMessage: string;
rejectReason: string; rejectReason: string;
dealerSectionTitle: string;
dealerSectionDesc: string;
dealerApplyButton: string;
dealerViewCard: string;
dealerAgreementTitle: string;
dealerPrivacyAgreement: string;
dealerPrivacyContent: string;
dealerObligationAgreement: string;
dealerObligationContent: string;
dealerAgreeAll: string;
dealerMustAgree: string;
dealerProceedToApply: string;
// Vehicle Sharing // Vehicle Sharing
shareVehicle: string; shareVehicle: string;
@@ -860,6 +872,18 @@ const translations: Record<Language, Translations> = {
pendingApplicationMessage: 'Your application is being reviewed. We will notify you once approved.', pendingApplicationMessage: 'Your application is being reviewed. We will notify you once approved.',
rejectedApplicationMessage: 'Your application was rejected.', rejectedApplicationMessage: 'Your application was rejected.',
rejectReason: 'Rejection Reason', rejectReason: 'Rejection Reason',
dealerSectionTitle: 'Dealer Program',
dealerSectionDesc: 'Become a dealer and earn commissions by recommending vehicles to friends.',
dealerApplyButton: 'Apply to Become a Dealer',
dealerViewCard: 'View My Dealer Card',
dealerAgreementTitle: 'Dealer Agreement',
dealerPrivacyAgreement: 'Consent to Personal Information Collection and Use',
dealerPrivacyContent: `By applying for the Dealer Program, you agree to the collection and use of the following personal information:\n\n1. Information Collected: Full name, phone number, ID/passport number, bank account details, business registration number\n2. Purpose of Collection: Dealer identity verification, commission payment processing, tax reporting\n3. Retention Period: Retained for the duration of the dealer agreement and 5 years after termination (as required by law)\n4. Right to Refuse: You may refuse to provide personal information; however, this will prevent dealer registration.\n\nYour personal information will be handled in accordance with applicable privacy laws and will not be shared with third parties without your consent, except as required by law.`,
dealerObligationAgreement: 'Dealer Obligations Agreement',
dealerObligationContent: `By becoming a dealer, you agree to the following obligations:\n\n1. Accurate Information: You must provide truthful and accurate vehicle information to customers.\n2. Fair Pricing: You must not add excessive markups or engage in deceptive pricing practices.\n3. Customer Service: You are responsible for responding to customer inquiries promptly and professionally.\n4. Compliance: You must comply with all applicable local laws and regulations regarding vehicle sales.\n5. Platform Rules: You must follow AutonetSellCar platform policies, including commission structures and dispute resolution procedures.\n6. Prohibited Activities: Misrepresentation of vehicle condition, unauthorized use of platform branding, and soliciting customers outside the platform are strictly prohibited.\n7. Termination: Violation of these obligations may result in immediate termination of your dealer status without prior notice.\n\nAutonetSellCar reserves the right to modify these obligations with reasonable notice.`,
dealerAgreeAll: 'I agree to all terms above',
dealerMustAgree: 'You must agree to all terms before proceeding.',
dealerProceedToApply: 'Proceed to Application',
// Vehicle Sharing // Vehicle Sharing
shareVehicle: 'Share Vehicle', shareVehicle: 'Share Vehicle',
@@ -1337,6 +1361,18 @@ const translations: Record<Language, Translations> = {
pendingApplicationMessage: 'Таны хүсэлт хянагдаж байна. Зөвшөөрөгдсөний дараа мэдэгдэх болно.', pendingApplicationMessage: 'Таны хүсэлт хянагдаж байна. Зөвшөөрөгдсөний дараа мэдэгдэх болно.',
rejectedApplicationMessage: 'Таны хүсэлт татгалзагдлаа.', rejectedApplicationMessage: 'Таны хүсэлт татгалзагдлаа.',
rejectReason: 'Татгалзсан шалтгаан', rejectReason: 'Татгалзсан шалтгаан',
dealerSectionTitle: 'Дилерийн хөтөлбөр',
dealerSectionDesc: 'Дилер болж, найзууддаа машин санал болгож комисс аваарай.',
dealerApplyButton: 'Дилер болох хүсэлт гаргах',
dealerViewCard: 'Миний дилерийн карт харах',
dealerAgreementTitle: 'Дилерийн гэрээ',
dealerPrivacyAgreement: 'Хувийн мэдээлэл цуглуулах, ашиглахыг зөвшөөрөх',
dealerPrivacyContent: `Дилерийн хөтөлбөрт бүртгүүлснээр та дараах хувийн мэдээлэл цуглуулах, ашиглахыг зөвшөөрч байна:\n\n1. Цуглуулах мэдээлэл: Бүтэн нэр, утасны дугаар, иргэний үнэмлэх/паспортын дугаар, банкны дансны мэдээлэл, бизнесийн бүртгэлийн дугаар\n2. Цуглуулах зорилго: Дилерийн таньж мэдэх, комисс төлбөр хийх, татварын тайлан\n3. Хадгалах хугацаа: Дилерийн гэрээний хугацаанд болон дуусгавар болсноос хойш 5 жил (хуулийн шаардлагаар)\n4. Татгалзах эрх: Та хувийн мэдээлэл өгөхөөс татгалзаж болно, гэхдээ энэ нь дилерийн бүртгэлийг боломжгүй болгоно.`,
dealerObligationAgreement: 'Дилерийн үүргийн гэрээ',
dealerObligationContent: `Дилер болсноор та дараах үүргүүдийг хүлээн зөвшөөрч байна:\n\n1. Үнэн зөв мэдээлэл: Та худалдан авагчдад машины талаар үнэн зөв мэдээлэл өгөх ёстой.\n2. Шударга үнэ: Хэт өндөр нэмэлт үнэ тогтоох, хууран мэхлэх үнийн бодлого явуулахыг хориглоно.\n3. Хэрэглэгчийн үйлчилгээ: Та хэрэглэгчийн асуултад шуурхай, мэргэжлийн түвшинд хариулах үүрэгтэй.\n4. Хууль дүрэм: Машин худалдаатай холбоотой бүх холбогдох хууль тогтоомжийг дагаж мөрдөх ёстой.\n5. Платформын дүрэм: AutonetSellCar платформын бодлогыг дагах ёстой.\n6. Хориотой үйлдэл: Машины байдлыг буруу мэдээлэх, платформын бренд зөвшөөрөлгүй ашиглах зэрэг нь хатуу хориотой.\n7. Цуцлах: Эдгээр үүргийг зөрчсөн тохиолдолд дилерийн статусыг нэн даруй цуцалж болно.`,
dealerAgreeAll: 'Дээрх бүх нөхцлийг зөвшөөрч байна',
dealerMustAgree: 'Үргэлжлүүлэхийн өмнө бүх нөхцлийг зөвшөөрөх шаардлагатай.',
dealerProceedToApply: 'Хүсэлт гаргах руу үргэлжлүүлэх',
// Vehicle Sharing // Vehicle Sharing
shareVehicle: 'Машин хуваалцах', shareVehicle: 'Машин хуваалцах',
@@ -1814,6 +1850,18 @@ const translations: Record<Language, Translations> = {
pendingApplicationMessage: 'Ваша заявка рассматривается. Мы уведомим вас после одобрения.', pendingApplicationMessage: 'Ваша заявка рассматривается. Мы уведомим вас после одобрения.',
rejectedApplicationMessage: 'Ваша заявка была отклонена.', rejectedApplicationMessage: 'Ваша заявка была отклонена.',
rejectReason: 'Причина отклонения', rejectReason: 'Причина отклонения',
dealerSectionTitle: 'Дилерская программа',
dealerSectionDesc: 'Станьте дилером и зарабатывайте комиссионные, рекомендуя автомобили друзьям.',
dealerApplyButton: 'Подать заявку на дилера',
dealerViewCard: 'Просмотр карты дилера',
dealerAgreementTitle: 'Соглашение дилера',
dealerPrivacyAgreement: 'Согласие на сбор и использование персональных данных',
dealerPrivacyContent: `Подавая заявку на участие в дилерской программе, вы соглашаетесь на сбор и использование следующих персональных данных:\n\n1. Собираемая информация: ФИО, номер телефона, номер паспорта/удостоверения, банковские реквизиты, регистрационный номер бизнеса\n2. Цель сбора: Верификация дилера, обработка комиссионных выплат, налоговая отчётность\n3. Срок хранения: На протяжении действия дилерского соглашения и 5 лет после его прекращения (по требованию закона)\n4. Право на отказ: Вы можете отказаться от предоставления персональных данных, однако это сделает регистрацию дилера невозможной.`,
dealerObligationAgreement: 'Соглашение об обязанностях дилера',
dealerObligationContent: `Становясь дилером, вы соглашаетесь с следующими обязанностями:\n\n1. Достоверная информация: Вы обязаны предоставлять клиентам правдивую и точную информацию об автомобилях.\n2. Справедливое ценообразование: Запрещается устанавливать чрезмерные наценки или вводить в заблуждение относительно цен.\n3. Обслуживание клиентов: Вы обязаны оперативно и профессионально отвечать на запросы клиентов.\n4. Соблюдение законов: Вы обязаны соблюдать все применимые законы и правила, касающиеся продажи автомобилей.\n5. Правила платформы: Вы обязаны следовать политике платформы AutonetSellCar.\n6. Запрещённые действия: Искажение состояния автомобиля, несанкционированное использование бренда платформы строго запрещены.\n7. Прекращение: Нарушение данных обязанностей может привести к немедленному прекращению статуса дилера.`,
dealerAgreeAll: 'Я согласен со всеми вышеуказанными условиями',
dealerMustAgree: 'Для продолжения необходимо согласиться со всеми условиями.',
dealerProceedToApply: 'Перейти к заявке',
// Vehicle Sharing // Vehicle Sharing
shareVehicle: 'Поделиться авто', shareVehicle: 'Поделиться авто',
@@ -2291,6 +2339,18 @@ const translations: Record<Language, Translations> = {
pendingApplicationMessage: '신청서가 검토 중입니다. 승인되면 알려드리겠습니다.', pendingApplicationMessage: '신청서가 검토 중입니다. 승인되면 알려드리겠습니다.',
rejectedApplicationMessage: '신청이 거부되었습니다.', rejectedApplicationMessage: '신청이 거부되었습니다.',
rejectReason: '거부 사유', rejectReason: '거부 사유',
dealerSectionTitle: '딜러 프로그램',
dealerSectionDesc: '딜러가 되어 친구에게 차량을 추천하고 수수료를 받으세요.',
dealerApplyButton: '딜러 신청하기',
dealerViewCard: '내 딜러증 보기',
dealerAgreementTitle: '딜러 약관 동의',
dealerPrivacyAgreement: '개인정보 수집 및 이용 동의',
dealerPrivacyContent: `딜러 프로그램 신청 시 아래의 개인정보 수집 및 이용에 동의하게 됩니다.\n\n1. 수집 항목: 성명, 전화번호, 주민등록번호(또는 여권번호), 은행 계좌 정보, 사업자등록번호\n2. 수집 목적: 딜러 본인 확인, 수수료 지급 처리, 세금 신고\n3. 보유 기간: 딜러 계약 기간 및 종료 후 5년간 보유 (관계 법령에 따름)\n4. 거부 권리: 개인정보 제공을 거부할 수 있으나, 이 경우 딜러 등록이 불가합니다.\n\n귀하의 개인정보는 관련 법률에 따라 처리되며, 법령에 의한 경우를 제외하고 본인의 동의 없이 제3자에게 제공되지 않습니다.`,
dealerObligationAgreement: '딜러 의무 수행 동의',
dealerObligationContent: `딜러가 됨으로써 아래의 의무에 동의하게 됩니다.\n\n1. 정확한 정보 제공: 고객에게 차량에 대한 정확하고 진실된 정보를 제공해야 합니다.\n2. 공정한 가격: 과도한 추가 금액을 부과하거나 기만적인 가격 정책을 사용할 수 없습니다.\n3. 고객 응대: 고객 문의에 신속하고 전문적으로 응답할 의무가 있습니다.\n4. 법규 준수: 차량 판매와 관련된 모든 관련 법률 및 규정을 준수해야 합니다.\n5. 플랫폼 규칙: AutonetSellCar 플랫폼 정책(수수료 구조, 분쟁 해결 절차 등)을 따라야 합니다.\n6. 금지 행위: 차량 상태 허위 표시, 플랫폼 브랜드 무단 사용, 플랫폼 외부에서의 고객 유인 행위는 엄격히 금지됩니다.\n7. 자격 박탈: 위 의무를 위반할 경우 사전 통보 없이 딜러 자격이 즉시 박탈될 수 있습니다.\n\nAutonetSellCar는 합리적인 사전 고지 후 본 의무 사항을 변경할 권리를 보유합니다.`,
dealerAgreeAll: '위의 모든 약관에 동의합니다',
dealerMustAgree: '계속하려면 모든 약관에 동의해야 합니다.',
dealerProceedToApply: '신청서 작성으로 이동',
// Vehicle Sharing // Vehicle Sharing
shareVehicle: '차량 공유', shareVehicle: '차량 공유',