update payment method, remove stripe, paypal, amanapay
Build & Push / Pipeline Tests (push) Failing after 59s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Failing after 48s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Carplace Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
Build & Push / Pipeline Tests (push) Failing after 59s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Failing after 48s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Carplace Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
This commit is contained in:
@@ -8,7 +8,7 @@ import { apiFetch } from '@/lib/api'
|
||||
import { useDashboardI18n } from '@/components/I18nProvider'
|
||||
|
||||
type BillingPaymentType = 'CHARGE' | 'DEPOSIT'
|
||||
type ManualPaymentMethod = 'CASH' | 'CHECK' | 'BANK_TRANSFER' | 'CARD' | 'PAYPAL' | 'OTHER'
|
||||
type ManualPaymentMethod = 'CHECK' | 'BANK_TRANSFER'
|
||||
|
||||
type BillingPayment = {
|
||||
id: string
|
||||
@@ -92,7 +92,7 @@ export default function BillingPage() {
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [paymentInvoice, setPaymentInvoice] = useState<BillingInvoice | null>(null)
|
||||
const [paymentType, setPaymentType] = useState<BillingPaymentType>('CHARGE')
|
||||
const [paymentMethod, setPaymentMethod] = useState<ManualPaymentMethod>('CASH')
|
||||
const [paymentMethod, setPaymentMethod] = useState<ManualPaymentMethod>('BANK_TRANSFER')
|
||||
const [paymentAmount, setPaymentAmount] = useState('')
|
||||
const [receivedAt, setReceivedAt] = useState('')
|
||||
const [reference, setReference] = useState('')
|
||||
@@ -153,7 +153,7 @@ export default function BillingPage() {
|
||||
invalidAmount: 'Enter a valid amount within the selected remaining balance.',
|
||||
paymentStatusLabels: { ALL: 'All', UNPAID: 'Unpaid', PARTIAL: 'Partial', PAID: 'Paid' } as Record<string, string>,
|
||||
depositStatusLabels: { NOT_REQUIRED: 'Not required', OUTSTANDING: 'Outstanding', PARTIALLY_COLLECTED: 'Partially collected', HELD: 'Held' } as Record<string, string>,
|
||||
paymentMethodLabels: { CASH: 'Cash', CHECK: 'Check', BANK_TRANSFER: 'Bank transfer', CARD: 'Card', PAYPAL: 'PayPal', OTHER: 'Other' } as Record<string, string>,
|
||||
paymentMethodLabels: { CHECK: 'Check', BANK_TRANSFER: 'Bank transfer' } as Record<string, string>,
|
||||
},
|
||||
fr: {
|
||||
heading: 'Facturation clients',
|
||||
@@ -207,7 +207,7 @@ export default function BillingPage() {
|
||||
invalidAmount: 'Saisissez un montant valide dans la limite du solde sélectionné.',
|
||||
paymentStatusLabels: { ALL: 'Tous', UNPAID: 'Non payé', PARTIAL: 'Partiel', PAID: 'Payé' } as Record<string, string>,
|
||||
depositStatusLabels: { NOT_REQUIRED: 'Non requis', OUTSTANDING: 'À collecter', PARTIALLY_COLLECTED: 'Partiellement collecté', HELD: 'Détenu' } as Record<string, string>,
|
||||
paymentMethodLabels: { CASH: 'Espèces', CHECK: 'Chèque', BANK_TRANSFER: 'Virement bancaire', CARD: 'Carte', PAYPAL: 'PayPal', OTHER: 'Autre' } as Record<string, string>,
|
||||
paymentMethodLabels: { CHECK: 'Chèque', BANK_TRANSFER: 'Virement bancaire' } as Record<string, string>,
|
||||
},
|
||||
ar: {
|
||||
heading: 'فوترة العملاء',
|
||||
@@ -261,7 +261,7 @@ export default function BillingPage() {
|
||||
invalidAmount: 'أدخل مبلغاً صحيحاً ضمن الرصيد المحدد.',
|
||||
paymentStatusLabels: { ALL: 'الكل', UNPAID: 'غير مدفوع', PARTIAL: 'جزئي', PAID: 'مدفوع' } as Record<string, string>,
|
||||
depositStatusLabels: { NOT_REQUIRED: 'غير مطلوب', OUTSTANDING: 'مستحق', PARTIALLY_COLLECTED: 'محصل جزئياً', HELD: 'محتجز' } as Record<string, string>,
|
||||
paymentMethodLabels: { CASH: 'نقداً', CHECK: 'شيك', BANK_TRANSFER: 'تحويل بنكي', CARD: 'بطاقة', PAYPAL: 'PayPal', OTHER: 'أخرى' } as Record<string, string>,
|
||||
paymentMethodLabels: { CHECK: 'شيك', BANK_TRANSFER: 'تحويل بنكي' } as Record<string, string>,
|
||||
},
|
||||
}[language]), [language])
|
||||
|
||||
@@ -331,7 +331,7 @@ export default function BillingPage() {
|
||||
const remaining = nextType === 'DEPOSIT' ? invoice.depositOutstanding : invoice.invoiceBalanceDue
|
||||
setPaymentInvoice(invoice)
|
||||
setPaymentType(nextType)
|
||||
setPaymentMethod('CASH')
|
||||
setPaymentMethod('BANK_TRANSFER')
|
||||
setPaymentAmount((remaining / 100).toFixed(2))
|
||||
setReceivedAt(new Date().toISOString().slice(0, 16))
|
||||
setReference('')
|
||||
|
||||
@@ -524,7 +524,7 @@ export default function ContractDetailPage() {
|
||||
damageTypeLabels: { SCRATCH: 'Scratch', DENT: 'Dent', CRACK: 'Crack', CHIP: 'Chip', MISSING: 'Missing', STAIN: 'Stain', OTHER: 'Other' },
|
||||
severityLabels: { MINOR: 'Minor', MODERATE: 'Moderate', MAJOR: 'Major' },
|
||||
fuelLevelLabels: { FULL: 'Full', SEVEN_EIGHTHS: '7/8', THREE_QUARTERS: '3/4', FIVE_EIGHTHS: '5/8', HALF: '1/2', THREE_EIGHTHS: '3/8', QUARTER: '1/4', ONE_EIGHTH: '1/8', EMPTY: 'Empty' },
|
||||
paymentProviderLabels: { CASH: 'Cash', STRIPE: 'Stripe', BANK_TRANSFER: 'Bank transfer', CHECK: 'Check', PAYPAL: 'PayPal', CREDIT_CARD: 'Credit card', DEBIT_CARD: 'Debit card' },
|
||||
paymentProviderLabels: { BANK_TRANSFER: 'Bank transfer', CHECK: 'Check' },
|
||||
},
|
||||
fr: {
|
||||
back: 'Retour aux contrats',
|
||||
@@ -656,7 +656,7 @@ export default function ContractDetailPage() {
|
||||
damageTypeLabels: { SCRATCH: 'Rayure', DENT: 'Bosse', CRACK: 'Fissure', CHIP: 'Éclat', MISSING: 'Manquant', STAIN: 'Tache', OTHER: 'Autre' },
|
||||
severityLabels: { MINOR: 'Léger', MODERATE: 'Modéré', MAJOR: 'Majeur' },
|
||||
fuelLevelLabels: { FULL: 'Plein', SEVEN_EIGHTHS: '7/8', THREE_QUARTERS: '3/4', FIVE_EIGHTHS: '5/8', HALF: '1/2', THREE_EIGHTHS: '3/8', QUARTER: '1/4', ONE_EIGHTH: '1/8', EMPTY: 'Vide' },
|
||||
paymentProviderLabels: { CASH: 'Espèces', STRIPE: 'Stripe', BANK_TRANSFER: 'Virement bancaire', CHECK: 'Chèque', PAYPAL: 'PayPal', CREDIT_CARD: 'Carte de crédit', DEBIT_CARD: 'Carte de débit' },
|
||||
paymentProviderLabels: { BANK_TRANSFER: 'Virement bancaire', CHECK: 'Chèque' },
|
||||
},
|
||||
ar: {
|
||||
back: 'العودة إلى العقود',
|
||||
@@ -788,7 +788,7 @@ export default function ContractDetailPage() {
|
||||
damageTypeLabels: { SCRATCH: 'خدش', DENT: 'انبعاج', CRACK: 'تشقق', CHIP: 'تقشر', MISSING: 'مفقود', STAIN: 'بقعة', OTHER: 'أخرى' },
|
||||
severityLabels: { MINOR: 'طفيف', MODERATE: 'متوسط', MAJOR: 'شديد' },
|
||||
fuelLevelLabels: { FULL: 'ممتلئ', SEVEN_EIGHTHS: '7/8', THREE_QUARTERS: '3/4', FIVE_EIGHTHS: '5/8', HALF: '1/2', THREE_EIGHTHS: '3/8', QUARTER: '1/4', ONE_EIGHTH: '1/8', EMPTY: 'فارغ' },
|
||||
paymentProviderLabels: { CASH: 'نقداً', STRIPE: 'Stripe', BANK_TRANSFER: 'تحويل بنكي', CHECK: 'شيك', PAYPAL: 'PayPal', CREDIT_CARD: 'بطاقة ائتمان', DEBIT_CARD: 'بطاقة خصم' },
|
||||
paymentProviderLabels: { BANK_TRANSFER: 'تحويل بنكي', CHECK: 'شيك' },
|
||||
},
|
||||
} as const
|
||||
|
||||
|
||||
@@ -108,6 +108,13 @@ interface ReservationBilling {
|
||||
|
||||
type EditMode = 'booking' | 'return' | null
|
||||
|
||||
const RENTAL_PAYMENT_MODES = ['BANK_TRANSFER', 'CHECK'] as const
|
||||
type RentalPaymentMode = (typeof RENTAL_PAYMENT_MODES)[number]
|
||||
|
||||
function isRentalPaymentMode(value: string): value is RentalPaymentMode {
|
||||
return (RENTAL_PAYMENT_MODES as readonly string[]).includes(value)
|
||||
}
|
||||
|
||||
type ReservationFormState = {
|
||||
startDate: string
|
||||
endDate: string
|
||||
@@ -233,6 +240,7 @@ const detailCopy = {
|
||||
returnLabel: 'Return location',
|
||||
depositLabel: 'Deposit',
|
||||
paymentModeLabel: 'Payment mode',
|
||||
paymentModeLabels: { BANK_TRANSFER: 'Bank transfer', CHECK: 'Check' } as Record<string, string>,
|
||||
spareWheelLabel: 'Spare wheel',
|
||||
radioCdLabel: 'Radio and CD',
|
||||
bookingNotesLabel: 'Booking notes',
|
||||
@@ -348,6 +356,7 @@ const detailCopy = {
|
||||
returnLabel: 'Lieu de retour',
|
||||
depositLabel: 'Dépôt',
|
||||
paymentModeLabel: 'Mode de paiement',
|
||||
paymentModeLabels: { BANK_TRANSFER: 'Virement bancaire', CHECK: 'Chèque' } as Record<string, string>,
|
||||
spareWheelLabel: 'Roue de secours',
|
||||
radioCdLabel: 'Poste radio et CD',
|
||||
bookingNotesLabel: 'Notes de réservation',
|
||||
@@ -463,6 +472,7 @@ const detailCopy = {
|
||||
returnLabel: 'موقع الإرجاع',
|
||||
depositLabel: 'العربون',
|
||||
paymentModeLabel: 'طريقة الدفع',
|
||||
paymentModeLabels: { BANK_TRANSFER: 'تحويل بنكي', CHECK: 'شيك' } as Record<string, string>,
|
||||
spareWheelLabel: 'العجلة الاحتياطية',
|
||||
radioCdLabel: 'الراديو و CD',
|
||||
bookingNotesLabel: 'ملاحظات الحجز',
|
||||
@@ -691,7 +701,11 @@ export default function ReservationDetailPage() {
|
||||
pickupLocation: form.pickupLocation || null,
|
||||
returnLocation: form.returnLocation || null,
|
||||
depositAmount: Number(form.depositAmount || 0),
|
||||
paymentMode: form.paymentMode || null,
|
||||
paymentMode: isRentalPaymentMode(form.paymentMode)
|
||||
? form.paymentMode
|
||||
: form.paymentMode
|
||||
? undefined
|
||||
: null,
|
||||
spareWheel: form.spareWheel,
|
||||
radioCd: form.radioCd,
|
||||
contractFields: form.contractFields,
|
||||
@@ -972,7 +986,7 @@ export default function ReservationDetailPage() {
|
||||
<p className="font-medium text-slate-900">{reservation.vehicle.make} {reservation.vehicle.model}</p>
|
||||
<p>{reservation.vehicle.licensePlate}</p>
|
||||
<p>{formatDate(reservation.startDate)} - {formatDate(reservation.endDate)}</p>
|
||||
<p>{copy.paymentModeLabel}: <span className="font-medium text-slate-900">{reservation.paymentMode || copy.paymentModeEmpty}</span></p>
|
||||
<p>{copy.paymentModeLabel}: <span className="font-medium text-slate-900">{reservation.paymentMode ? (copy.paymentModeLabels[reservation.paymentMode] ?? reservation.paymentMode) : copy.paymentModeEmpty}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1033,12 +1047,20 @@ export default function ReservationDetailPage() {
|
||||
</div>
|
||||
<div>
|
||||
<label className="mb-1.5 block text-sm font-medium text-slate-700">{copy.paymentModeLabel}</label>
|
||||
<input
|
||||
<select
|
||||
className="input-field disabled:cursor-not-allowed disabled:bg-slate-100 disabled:text-slate-500"
|
||||
value={form.paymentMode}
|
||||
disabled={bookingInputsDisabled}
|
||||
onChange={(e) => setForm((current) => current ? { ...current, paymentMode: e.target.value } : current)}
|
||||
/>
|
||||
>
|
||||
<option value="">{copy.paymentModeEmpty}</option>
|
||||
{form.paymentMode && !isRentalPaymentMode(form.paymentMode) && (
|
||||
<option value={form.paymentMode}>{form.paymentMode}</option>
|
||||
)}
|
||||
{RENTAL_PAYMENT_MODES.map((mode) => (
|
||||
<option key={mode} value={mode}>{copy.paymentModeLabels[mode]}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<label className="flex items-center gap-3 rounded-2xl border border-slate-200 px-4 py-3 text-sm font-medium text-slate-700">
|
||||
<input
|
||||
|
||||
@@ -77,9 +77,6 @@ interface BrandSettings {
|
||||
heroImageUrl?: string | null
|
||||
websiteUrl?: string | null
|
||||
whatsappNumber?: string | null
|
||||
paypalEmail: string | null
|
||||
amanpayMerchantId: string | null
|
||||
amanpaySecretKey?: string | null
|
||||
defaultLocale?: string
|
||||
defaultCurrency?: string
|
||||
isListedOnCarplace: boolean
|
||||
@@ -196,15 +193,14 @@ function SettingsPageContent() {
|
||||
lockedTitle: 'This settings area is not included in your current plan.', lockedBody: 'Your saved configuration is preserved and becomes editable again after upgrade.',
|
||||
readOnly: 'This section is read-only while your subscription access is restricted.', companyHint: 'Default language affects Carplace text and generated contracts when available.',
|
||||
publicProfile: 'Public profile', carplaceBasics: 'Carplace basics', premiumBranding: 'Available on GROWTH',
|
||||
paymentsBody: 'Configure renter payment providers. Secret values are never shown after saving.',
|
||||
paymentsBody: 'Rental payments are recorded as bank transfer or check. Online checkout is not available.',
|
||||
policies: 'Fuel, driver, and damage policies', additionalDriver: 'Additional-driver automation', insuranceNew: 'New insurance policy',
|
||||
pricingNew: 'New pricing rule', accountingSchedule: 'Scheduled delivery is available on PRO.', noItems: 'No records configured yet.',
|
||||
carplace: 'Listed on Carplace', logo: 'Logo', hero: 'Hero image', upload: 'Upload', active: 'Active', inactive: 'Inactive',
|
||||
labels: {
|
||||
displayName: 'Display name', tagline: 'Tagline', publicEmail: 'Public email', publicPhone: 'Public phone', whatsapp: 'WhatsApp number',
|
||||
city: 'City', country: 'Country', websiteUrl: 'Website URL', defaultLocale: 'Default language', defaultCurrency: 'Default currency',
|
||||
primaryColor: 'Primary color', accentColor: 'Accent color', amanpayMerchantId: 'AmanPay merchant ID', amanpaySecretKey: 'AmanPay secret key',
|
||||
paypalEmail: 'PayPal business email', fuelPolicy: 'Fuel policy', fuelPolicyType: 'Fuel policy type', fuelPolicyNote: 'Fuel policy note',
|
||||
primaryColor: 'Primary color', accentColor: 'Accent color', fuelPolicy: 'Fuel policy', fuelPolicyType: 'Fuel policy type', fuelPolicyNote: 'Fuel policy note',
|
||||
damagePolicy: 'Damage policy', additionalDriverPolicy: 'Driver policy', additionalDriverCharge: 'Additional driver charge',
|
||||
dailyRate: 'Daily rate', flatRate: 'Flat rate', accountantName: 'Accountant name',
|
||||
accountantEmail: 'Accountant email', reportingPeriod: 'Reporting period', reportFormat: 'Report format', autoSend: 'Auto-send reports',
|
||||
@@ -218,15 +214,14 @@ function SettingsPageContent() {
|
||||
lockedTitle: 'Cette section n’est pas incluse dans votre plan actuel.', lockedBody: 'La configuration enregistrée est conservée et redevient modifiable après mise à niveau.',
|
||||
readOnly: 'Cette section est en lecture seule pendant la restriction d’accès.', companyHint: 'La langue par défaut affecte la vitrine et les contrats générés si disponibles.',
|
||||
publicProfile: 'Profil public', carplaceBasics: 'Paramètres Carplace', premiumBranding: 'Disponible avec GROWTH',
|
||||
paymentsBody: 'Configurez les prestataires de paiement. Les secrets ne sont jamais affichés après enregistrement.',
|
||||
paymentsBody: 'Les paiements de location sont enregistrés par virement bancaire ou chèque. Le paiement en ligne n’est pas disponible.',
|
||||
policies: 'Carburant, conducteur et dommages', additionalDriver: 'Automatisation conducteur additionnel', insuranceNew: 'Nouvelle police',
|
||||
pricingNew: 'Nouvelle règle', accountingSchedule: 'L’envoi planifié est disponible avec PRO.', noItems: 'Aucun enregistrement.',
|
||||
carplace: 'Publié sur Carplace', logo: 'Logo', hero: 'Image principale', upload: 'Téléverser', active: 'Actif', inactive: 'Inactif',
|
||||
labels: {
|
||||
displayName: 'Nom affiché', tagline: 'Slogan', publicEmail: 'E-mail public', publicPhone: 'Téléphone public', whatsapp: 'Numéro WhatsApp',
|
||||
city: 'Ville', country: 'Pays', websiteUrl: 'Site web', defaultLocale: 'Langue par défaut', defaultCurrency: 'Devise par défaut',
|
||||
primaryColor: 'Couleur principale', accentColor: 'Couleur secondaire', amanpayMerchantId: 'ID marchand AmanPay', amanpaySecretKey: 'Clé secrète AmanPay',
|
||||
paypalEmail: 'Email PayPal business', fuelPolicy: 'Politique carburant', fuelPolicyType: 'Type de politique carburant', fuelPolicyNote: 'Note carburant',
|
||||
primaryColor: 'Couleur principale', accentColor: 'Couleur secondaire', fuelPolicy: 'Politique carburant', fuelPolicyType: 'Type de politique carburant', fuelPolicyNote: 'Note carburant',
|
||||
damagePolicy: 'Politique dommages', additionalDriverPolicy: 'Politique conducteur', additionalDriverCharge: 'Supplément conducteur',
|
||||
dailyRate: 'Tarif journalier', flatRate: 'Tarif fixe', accountantName: 'Nom du comptable',
|
||||
accountantEmail: 'E-mail du comptable', reportingPeriod: 'Période', reportFormat: 'Format', autoSend: 'Envoi automatique',
|
||||
@@ -240,15 +235,14 @@ function SettingsPageContent() {
|
||||
lockedTitle: 'هذا القسم غير مشمول في خطتك الحالية.', lockedBody: 'يتم الاحتفاظ بالإعدادات المحفوظة وتعود قابلة للتعديل بعد الترقية.',
|
||||
readOnly: 'هذا القسم للقراءة فقط أثناء تقييد الوصول.', companyHint: 'تؤثر اللغة الافتراضية على الواجهة والعقود عند توفرها.',
|
||||
publicProfile: 'الملف العام', carplaceBasics: 'أساسيات الواجهة', premiumBranding: 'متاح في GROWTH',
|
||||
paymentsBody: 'إعداد مزودي دفع المستأجرين. لا يتم عرض المفاتيح السرية بعد الحفظ.',
|
||||
paymentsBody: 'يتم تسجيل مدفوعات الكراء بالتحويل البنكي أو الشيك. الدفع الإلكتروني غير متاح.',
|
||||
policies: 'سياسات الوقود والسائق والأضرار', additionalDriver: 'أتمتة السائق الإضافي', insuranceNew: 'سياسة تأمين جديدة',
|
||||
pricingNew: 'قاعدة تسعير جديدة', accountingSchedule: 'الإرسال المجدول متاح في PRO.', noItems: 'لا توجد سجلات بعد.',
|
||||
carplace: 'مدرج على Carplace', logo: 'الشعار', hero: 'صورة الواجهة', upload: 'رفع', active: 'نشط', inactive: 'غير نشط',
|
||||
labels: {
|
||||
displayName: 'اسم العرض', tagline: 'الشعار', publicEmail: 'البريد العام', publicPhone: 'الهاتف العام', whatsapp: 'رقم واتساب',
|
||||
city: 'المدينة', country: 'الدولة', websiteUrl: 'الموقع', defaultLocale: 'اللغة الافتراضية', defaultCurrency: 'العملة الافتراضية',
|
||||
primaryColor: 'اللون الأساسي', accentColor: 'لون التمييز', amanpayMerchantId: 'معرف تاجر AmanPay', amanpaySecretKey: 'مفتاح AmanPay السري',
|
||||
paypalEmail: 'بريد PayPal التجاري', fuelPolicy: 'سياسة الوقود', fuelPolicyType: 'نوع سياسة الوقود', fuelPolicyNote: 'ملاحظة الوقود',
|
||||
primaryColor: 'اللون الأساسي', accentColor: 'لون التمييز', fuelPolicy: 'سياسة الوقود', fuelPolicyType: 'نوع سياسة الوقود', fuelPolicyNote: 'ملاحظة الوقود',
|
||||
damagePolicy: 'سياسة الأضرار', additionalDriverPolicy: 'سياسة السائق', additionalDriverCharge: 'رسوم السائق الإضافي',
|
||||
dailyRate: 'السعر اليومي', flatRate: 'السعر الثابت', accountantName: 'اسم المحاسب',
|
||||
accountantEmail: 'بريد المحاسب', reportingPeriod: 'فترة التقرير', reportFormat: 'تنسيق التقرير', autoSend: 'إرسال تلقائي',
|
||||
@@ -349,9 +343,6 @@ function SettingsPageContent() {
|
||||
websiteUrl: brand.websiteUrl || undefined, defaultLocale: brand.defaultLocale || undefined,
|
||||
whatsappNumber: brand.whatsappNumber || undefined, defaultCurrency: brand.defaultCurrency || undefined,
|
||||
isListedOnCarplace: brand.isListedOnCarplace,
|
||||
amanpayMerchantId: canEdit('settings.renter_payments') ? brand.amanpayMerchantId || undefined : undefined,
|
||||
amanpaySecretKey: canEdit('settings.renter_payments') ? brand.amanpaySecretKey || undefined : undefined,
|
||||
paypalEmail: canEdit('settings.renter_payments') ? brand.paypalEmail || undefined : undefined,
|
||||
}),
|
||||
})
|
||||
setBrand(updated); setMessage(copy.saved)
|
||||
@@ -547,12 +538,7 @@ function SettingsPageContent() {
|
||||
|
||||
{activeSection === 'payments' && brand && (
|
||||
<SectionCard onSave={saveBrand} saving={saving} copy={copy} disabled={!canEdit('settings.renter_payments')}>
|
||||
<p className="mb-4 text-sm text-slate-500">{copy.paymentsBody}</p>
|
||||
<div className="grid gap-4 lg:grid-cols-2">
|
||||
<Input label={copy.labels.amanpayMerchantId} value={brand.amanpayMerchantId ?? ''} disabled={!canEdit('settings.renter_payments')} onChange={(v) => setBrand({ ...brand, amanpayMerchantId: v })} />
|
||||
<Input label={copy.labels.amanpaySecretKey} type="password" value={brand.amanpaySecretKey ?? ''} disabled={!canEdit('settings.renter_payments')} onChange={(v) => setBrand({ ...brand, amanpaySecretKey: v })} />
|
||||
<Input label={copy.labels.paypalEmail} type="email" value={brand.paypalEmail ?? ''} disabled={!canEdit('settings.renter_payments')} onChange={(v) => setBrand({ ...brand, paypalEmail: v })} />
|
||||
</div>
|
||||
<p className="text-sm text-slate-600">{copy.paymentsBody}</p>
|
||||
</SectionCard>
|
||||
)}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { buildHomepageSignInPath } from '@/lib/dashboardPaths'
|
||||
|
||||
type Plan = 'STARTER' | 'GROWTH' | 'PRO' | 'ENTERPRISE'
|
||||
type BillingPeriod = 'MONTHLY' | 'ANNUAL'
|
||||
type CollectionMethod = 'STRIPE' | 'BANK_TRANSFER' | 'CHECK'
|
||||
type CollectionMethod = 'BANK_TRANSFER' | 'CHECK'
|
||||
|
||||
interface Subscription {
|
||||
id: string
|
||||
@@ -592,7 +592,7 @@ export default function SubscriptionPage() {
|
||||
.then(([sub, inv, options, settings]) => {
|
||||
setPaymentOptions(options.methods ?? [])
|
||||
setCommunicationSettings(settings)
|
||||
const firstEnabled = options.methods?.find((option) => option.enabled && option.method !== 'STRIPE')
|
||||
const firstEnabled = options.methods?.find((option) => option.enabled)
|
||||
if (firstEnabled) setSelectedMethod(firstEnabled.method)
|
||||
if (sub) {
|
||||
setSubscription(sub)
|
||||
@@ -601,7 +601,7 @@ export default function SubscriptionPage() {
|
||||
// currency is always MAD
|
||||
}
|
||||
setInvoices(inv ?? [])
|
||||
const latestManual = inv?.find((invoice) => invoice.collectionMethod !== 'STRIPE' && ['OPEN', 'PAYMENT_PENDING', 'PAST_DUE'].includes(invoice.status))
|
||||
const latestManual = inv?.find((invoice) => ['OPEN', 'PAYMENT_PENDING', 'PAST_DUE'].includes(invoice.status))
|
||||
if (latestManual) {
|
||||
const option = options.methods?.find((item) => item.method === latestManual.collectionMethod)
|
||||
setSelectedMethod(latestManual.collectionMethod)
|
||||
@@ -975,7 +975,7 @@ export default function SubscriptionPage() {
|
||||
|
||||
{/* Plan selector + checkout */}
|
||||
<div className="card p-6 space-y-6">
|
||||
{!paymentOptions.some((option) => option.enabled && option.method !== 'STRIPE') ? (
|
||||
{!paymentOptions.some((option) => option.enabled) ? (
|
||||
<div className="rounded-xl border border-orange-200 bg-orange-50 px-4 py-3 text-sm text-orange-700">
|
||||
{copy.noProviderConfigured}
|
||||
</div>
|
||||
@@ -1081,7 +1081,7 @@ export default function SubscriptionPage() {
|
||||
<div>
|
||||
<p className="text-sm font-medium text-slate-700 mb-2 dark:text-zinc-300">{copy.paymentProvider}</p>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{paymentOptions.filter((option) => option.enabled && option.method !== 'STRIPE').map((option) => (
|
||||
{paymentOptions.filter((option) => option.enabled).map((option) => (
|
||||
<button
|
||||
type="button"
|
||||
key={option.method}
|
||||
@@ -1123,7 +1123,7 @@ export default function SubscriptionPage() {
|
||||
disabled={
|
||||
paying
|
||||
|| loading
|
||||
|| !paymentOptions.some((option) => option.method === selectedMethod && option.enabled && option.method !== 'STRIPE')
|
||||
|| !paymentOptions.some((option) => option.method === selectedMethod && option.enabled)
|
||||
|| isInvalidActiveUpgradeSelection
|
||||
|| (isManualMethod && (paymentReference.trim().length < 3 || evidenceFiles.length === 0))
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ export default function OnboardingPage() {
|
||||
const [step, setStep] = useState<Step>(1)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [showSecretKey, setShowSecretKey] = useState(false)
|
||||
|
||||
const [company, setCompany] = useState({ name: '', slug: '' })
|
||||
const [brand, setBrand] = useState({
|
||||
displayName: '',
|
||||
@@ -25,9 +23,6 @@ export default function OnboardingPage() {
|
||||
publicCountry: '',
|
||||
})
|
||||
const [payments, setPayments] = useState({
|
||||
amanpayMerchantId: '',
|
||||
amanpaySecretKey: '',
|
||||
paypalEmail: '',
|
||||
isListedOnCarplace: true,
|
||||
})
|
||||
|
||||
@@ -78,9 +73,6 @@ export default function OnboardingPage() {
|
||||
await apiFetch('/companies/me/brand', {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({
|
||||
amanpayMerchantId: payments.amanpayMerchantId || undefined,
|
||||
amanpaySecretKey: payments.amanpaySecretKey || undefined,
|
||||
paypalEmail: payments.paypalEmail || undefined,
|
||||
isListedOnCarplace: payments.isListedOnCarplace,
|
||||
}),
|
||||
})
|
||||
@@ -213,56 +205,8 @@ export default function OnboardingPage() {
|
||||
{step === 3 && (
|
||||
<div className="space-y-5">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-slate-900">Payment setup</h2>
|
||||
<p className="mt-1 text-sm text-slate-500">Connect payment providers so renters can book online. You can skip this for now.</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1">AmanPay Merchant ID <span className="text-slate-400">(optional)</span></label>
|
||||
<input
|
||||
className="input-field"
|
||||
placeholder="your-merchant-id"
|
||||
value={payments.amanpayMerchantId}
|
||||
onChange={(e) => setPayments({ ...payments, amanpayMerchantId: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1">AmanPay Secret Key <span className="text-slate-400">(optional)</span></label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type={showSecretKey ? 'text' : 'password'}
|
||||
className="input-field pr-10"
|
||||
placeholder="your-secret-key"
|
||||
value={payments.amanpaySecretKey}
|
||||
onChange={(e) => setPayments({ ...payments, amanpaySecretKey: e.target.value })}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowSecretKey((v) => !v)}
|
||||
className="absolute inset-y-0 right-3 flex items-center text-slate-400 hover:text-slate-600"
|
||||
tabIndex={-1}
|
||||
>
|
||||
{showSecretKey ? (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1">PayPal email <span className="text-slate-400">(optional)</span></label>
|
||||
<input
|
||||
type="email"
|
||||
className="input-field"
|
||||
placeholder="payments@yourcompany.com"
|
||||
value={payments.paypalEmail}
|
||||
onChange={(e) => setPayments({ ...payments, paypalEmail: e.target.value })}
|
||||
/>
|
||||
<h2 className="text-lg font-semibold text-slate-900">Finish setup</h2>
|
||||
<p className="mt-1 text-sm text-slate-500">Rental payments are recorded manually in the dashboard. Choose whether to list your company on Carplace.</p>
|
||||
</div>
|
||||
<label className="flex items-center gap-3 cursor-pointer">
|
||||
<input
|
||||
|
||||
Reference in New Issue
Block a user