Added translation to payments
All checks were successful
Build frontend / build (push) Successful in 1m43s
All checks were successful
Build frontend / build (push) Successful in 1m43s
This commit is contained in:
@ -60,6 +60,9 @@ const resources = {
|
|||||||
"termsOfUse": "Terms of Use",
|
"termsOfUse": "Terms of Use",
|
||||||
"privacyPolicy": "Privacy Policy",
|
"privacyPolicy": "Privacy Policy",
|
||||||
"invalidCredentials": "Invalid credentials",
|
"invalidCredentials": "Invalid credentials",
|
||||||
|
"serverError": "Server error, please try again later",
|
||||||
|
"apiNotFound": "Service temporarily unavailable",
|
||||||
|
"tooManyRequests": "Too many requests, please wait",
|
||||||
"connectionError": "A connection error occurred",
|
"connectionError": "A connection error occurred",
|
||||||
"phoneInvalid": "Invalid phone number",
|
"phoneInvalid": "Invalid phone number",
|
||||||
"resendSuccess": "Verification code resent",
|
"resendSuccess": "Verification code resent",
|
||||||
@ -1488,6 +1491,16 @@ const resources = {
|
|||||||
"payments.cashPendingDialogDesc2": "The platform will confirm the payment after receiving the amount.",
|
"payments.cashPendingDialogDesc2": "The platform will confirm the payment after receiving the amount.",
|
||||||
"payments.platformOfficeFullAddress": "Platform Office: Abu Rumaneh, Al-Malki Street, Damascus",
|
"payments.platformOfficeFullAddress": "Platform Office: Abu Rumaneh, Al-Malki Street, Damascus",
|
||||||
"payments.closeButton": "Close",
|
"payments.closeButton": "Close",
|
||||||
|
"payments.propertyLabel": "Property",
|
||||||
|
|
||||||
|
"payments.method.cash": "Cash Payment",
|
||||||
|
"payments.method.cashDesc": "Pay now with automatic booking confirmation",
|
||||||
|
"payments.method.office": "Cash",
|
||||||
|
"payments.method.officeDesc": "Pay at the platform office. Confirmed later.",
|
||||||
|
"payments.method.transfer": "Internal Transfer",
|
||||||
|
"payments.method.transferDesc": "Transfer from internal account then send reference",
|
||||||
|
"payments.method.electronic": "Electronic Payment",
|
||||||
|
"payments.method.electronicDesc": "Electronic payment will be activated later",
|
||||||
|
|
||||||
/* ─── Onboarding ─── */
|
/* ─── Onboarding ─── */
|
||||||
"onboarding.step1.title": "Welcome to SweetHome",
|
"onboarding.step1.title": "Welcome to SweetHome",
|
||||||
@ -1655,6 +1668,9 @@ const resources = {
|
|||||||
"termsOfUse": "شروط الاستخدام",
|
"termsOfUse": "شروط الاستخدام",
|
||||||
"privacyPolicy": "سياسة الخصوصية",
|
"privacyPolicy": "سياسة الخصوصية",
|
||||||
"invalidCredentials": "بيانات الدخول غير صحيحة",
|
"invalidCredentials": "بيانات الدخول غير صحيحة",
|
||||||
|
"serverError": "خطأ في الخادم، يرجى المحاولة لاحقاً",
|
||||||
|
"apiNotFound": "الخدمة غير متاحة مؤقتاً",
|
||||||
|
"tooManyRequests": "طلبات كثيرة جداً، يرجى الانتظار",
|
||||||
"connectionError": "حدث خطأ في الاتصال",
|
"connectionError": "حدث خطأ في الاتصال",
|
||||||
"phoneInvalid": "رقم الهاتف غير صالح",
|
"phoneInvalid": "رقم الهاتف غير صالح",
|
||||||
"resendSuccess": "تم إرسال رمز التحقق مجدداً",
|
"resendSuccess": "تم إرسال رمز التحقق مجدداً",
|
||||||
@ -2924,6 +2940,16 @@ const resources = {
|
|||||||
"payments.cashPendingDialogDesc2": "ستقوم المنصة بتأكيد الدفع بعد استلام المبلع.",
|
"payments.cashPendingDialogDesc2": "ستقوم المنصة بتأكيد الدفع بعد استلام المبلع.",
|
||||||
"payments.platformOfficeFullAddress": "مكتب المنصة: أبو رمانة، شارع المالكي، دمشق",
|
"payments.platformOfficeFullAddress": "مكتب المنصة: أبو رمانة، شارع المالكي، دمشق",
|
||||||
"payments.closeButton": "إغلاق",
|
"payments.closeButton": "إغلاق",
|
||||||
|
"payments.propertyLabel": "عقار",
|
||||||
|
|
||||||
|
"payments.method.cash": "الدفع النقدي",
|
||||||
|
"payments.method.cashDesc": "ادفع الآن عبر شام كاش مع تأكيد تلقائي للحجز",
|
||||||
|
"payments.method.office": "نقداً",
|
||||||
|
"payments.method.officeDesc": "ادفع في مكتب المنصة. يتم التأكيد لاحقاً",
|
||||||
|
"payments.method.transfer": "تحويل داخلي",
|
||||||
|
"payments.method.transferDesc": "حول من حساب داخلي ثم أرسل المرجع",
|
||||||
|
"payments.method.electronic": "دفع إلكتروني",
|
||||||
|
"payments.method.electronicDesc": "سيتم تفعيل الدفع الإلكتروني لاحقاً",
|
||||||
|
|
||||||
/* ─── Onboarding ─── */
|
/* ─── Onboarding ─── */
|
||||||
"onboarding.step1.title": "مرحباً بك في SweetHome",
|
"onboarding.step1.title": "مرحباً بك في SweetHome",
|
||||||
|
|||||||
@ -153,8 +153,21 @@ export default function LoginPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setStep("otp");
|
setStep("otp");
|
||||||
|
} else if (result.status >= 500) {
|
||||||
|
console.error("[Login] Server error:", result.status, result.data);
|
||||||
|
toast.error(t("serverError"), {
|
||||||
|
style: { background: "#fee2e2", color: "#991b1b" },
|
||||||
|
});
|
||||||
|
} else if (result.status === 404) {
|
||||||
|
console.error("[Login] API endpoint not found:", result.status);
|
||||||
|
toast.error(t("apiNotFound"), {
|
||||||
|
style: { background: "#fee2e2", color: "#991b1b" },
|
||||||
|
});
|
||||||
|
} else if (result.status === 429) {
|
||||||
|
toast.error(t("tooManyRequests"), {
|
||||||
|
style: { background: "#fee2e2", color: "#991b1b" },
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// Other error
|
|
||||||
console.error("[Login] Unexpected status:", result.status, result.data);
|
console.error("[Login] Unexpected status:", result.status, result.data);
|
||||||
toast.error(
|
toast.error(
|
||||||
result.data?.message || result.data || t("invalidCredentials"),
|
result.data?.message || result.data || t("invalidCredentials"),
|
||||||
|
|||||||
@ -30,47 +30,27 @@ import { payDeposit, getMyTransaction } from '@/app/utils/api';
|
|||||||
|
|
||||||
const STATUS_MAP = ['pending', 'ownerConfirmed', 'depositPaid', 'depositConfirmed', 'completed', 'cancelled'];
|
const STATUS_MAP = ['pending', 'ownerConfirmed', 'depositPaid', 'depositConfirmed', 'completed', 'cancelled'];
|
||||||
|
|
||||||
const STATUS_CONFIG = {
|
function getStatusConfig(t) {
|
||||||
pending: { label: 'قيد الانتظار', color: 'bg-yellow-100 text-yellow-800 border-yellow-300', icon: Clock },
|
return {
|
||||||
ownerConfirmed: { label: 'مؤكد من المالك', color: 'bg-blue-100 text-blue-800 border-blue-300', icon: ShieldCheck },
|
pending: { label: t('bookingStatus.pending'), color: 'bg-yellow-100 text-yellow-800 border-yellow-300', icon: Clock },
|
||||||
depositPaid: { label: 'تم دفع السلفة', color: 'bg-orange-100 text-orange-800 border-orange-300', icon: Wallet },
|
ownerConfirmed: { label: t('bookingStatus.ownerConfirmed'), color: 'bg-blue-100 text-blue-800 border-blue-300', icon: ShieldCheck },
|
||||||
depositConfirmed: { label: 'تم تأكيد الدفع', color: 'bg-green-100 text-green-800 border-green-300', icon: Check },
|
depositPaid: { label: t('bookingStatus.depositPaid'), color: 'bg-orange-100 text-orange-800 border-orange-300', icon: Wallet },
|
||||||
completed: { label: 'منتهي', color: 'bg-teal-100 text-teal-800 border-teal-300', icon: Check },
|
depositConfirmed: { label: t('bookingStatus.depositConfirmed'), color: 'bg-green-100 text-green-800 border-green-300', icon: Check },
|
||||||
cancelled: { label: 'ملغي', color: 'bg-red-100 text-red-800 border-red-300', icon: X },
|
completed: { label: t('bookingStatus.completed'), color: 'bg-teal-100 text-teal-800 border-teal-300', icon: Check },
|
||||||
|
cancelled: { label: t('bookingStatus.cancelled'), color: 'bg-red-100 text-red-800 border-red-300', icon: X },
|
||||||
};
|
};
|
||||||
|
|
||||||
const PAYMENT_METHODS = [
|
|
||||||
{
|
|
||||||
id: 'cash',
|
|
||||||
label: 'الدفع النقدي',
|
|
||||||
desc: 'ادفع الآن عبر شام كاش مع تأكيد تلقائي للحجز',
|
|
||||||
icon: Banknote,
|
|
||||||
active: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'office',
|
|
||||||
label: 'نقداً',
|
|
||||||
desc: 'ادفع في مكتب المنصة. يتم التأكيد لاحقاً',
|
|
||||||
icon: Building,
|
|
||||||
active: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'transfer',
|
|
||||||
label: 'تحويل داخلي',
|
|
||||||
desc: 'حول من حساب داخلي ثم أرسل المرجع',
|
|
||||||
icon: ArrowLeftRight,
|
|
||||||
active: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'electronic',
|
|
||||||
label: 'دفع إلكتروني',
|
|
||||||
desc: 'سيتم تفعيل الدفع الإلكتروني والتحويل لاحقاً',
|
|
||||||
icon: CreditCard,
|
|
||||||
active: false,
|
|
||||||
}
|
}
|
||||||
];
|
|
||||||
|
|
||||||
function formatCurrency(v, sign = 'ل.س') {
|
function getPaymentMethods(t) {
|
||||||
|
return [
|
||||||
|
{ id: 'cash', label: t('payments.method.cash'), desc: t('payments.method.cashDesc'), icon: Banknote, active: true },
|
||||||
|
{ id: 'office', label: t('payments.method.office'), desc: t('payments.method.officeDesc'), icon: Building, active: true },
|
||||||
|
{ id: 'transfer', label: t('payments.method.transfer'), desc: t('payments.method.transferDesc'), icon: ArrowLeftRight, active: false },
|
||||||
|
{ id: 'electronic', label: t('payments.method.electronic'), desc: t('payments.method.electronicDesc'), icon: CreditCard, active: false },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCurrency(v, sign = '') {
|
||||||
return `${sign} ${Number(v ?? 0).toLocaleString()}`;
|
return `${sign} ${Number(v ?? 0).toLocaleString()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +62,7 @@ function formatDate(date) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function PaymentsPage() {
|
export default function PaymentsPage() {
|
||||||
const { t } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const [reservations, setReservations] = useState([]);
|
const [reservations, setReservations] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [payingId, setPayingId] = useState(null);
|
const [payingId, setPayingId] = useState(null);
|
||||||
@ -109,10 +89,10 @@ export default function PaymentsPage() {
|
|||||||
endDate: reservation.endDate,
|
endDate: reservation.endDate,
|
||||||
totalPrice: reservation.totalPrice ?? transaction.amount ?? 0,
|
totalPrice: reservation.totalPrice ?? transaction.amount ?? 0,
|
||||||
depositAmount: transaction.amount ?? reservation.totalPrice ?? 0,
|
depositAmount: transaction.amount ?? reservation.totalPrice ?? 0,
|
||||||
currencySign: currency.sign || 'ل.س',
|
currencySign: currency.sign || t('currency.syp'),
|
||||||
currencyName: currency.name || '',
|
currencyName: currency.name || '',
|
||||||
currencyRate: currency.rate,
|
currencyRate: currency.rate,
|
||||||
propertyName: propertyInfo.name || propertyInfo.address || reservation.propertyName || `عقار #${reservation.id || ''}`,
|
propertyName: propertyInfo.name || propertyInfo.address || reservation.propertyName || `${t('payments.propertyLabel')} #${reservation.id || ''}`,
|
||||||
propertyAddress: propertyInfo.address || reservation.propertyAddress || '',
|
propertyAddress: propertyInfo.address || reservation.propertyAddress || '',
|
||||||
propertyCity: propertyInfo.city || reservation.city || '',
|
propertyCity: propertyInfo.city || reservation.city || '',
|
||||||
_deposit: deposit,
|
_deposit: deposit,
|
||||||
@ -127,7 +107,7 @@ export default function PaymentsPage() {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [t]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (AuthService.isGuest()) {
|
if (AuthService.isGuest()) {
|
||||||
@ -156,7 +136,7 @@ export default function PaymentsPage() {
|
|||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center" dir="rtl">
|
<div className="min-h-screen bg-gray-50 flex items-center justify-center" dir={i18n.dir()}>
|
||||||
<Loader2 className="w-12 h-12 text-amber-500 animate-spin" />
|
<Loader2 className="w-12 h-12 text-amber-500 animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -164,7 +144,7 @@ export default function PaymentsPage() {
|
|||||||
|
|
||||||
if (isGuest) {
|
if (isGuest) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gradient-to-b from-amber-50/50 to-white flex items-center justify-center p-4" dir="rtl">
|
<div className="min-h-screen bg-gradient-to-b from-amber-50/50 to-white flex items-center justify-center p-4" dir={i18n.dir()}>
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, scale: 0.95 }}
|
initial={{ opacity: 0, scale: 0.95 }}
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
@ -193,7 +173,7 @@ export default function PaymentsPage() {
|
|||||||
const others = reservations.filter((r) => !canPay(r.status));
|
const others = reservations.filter((r) => !canPay(r.status));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50 py-8" dir="rtl">
|
<div className="min-h-screen bg-gray-50 py-8" dir={i18n.dir()}>
|
||||||
<Toaster position="top-center" reverseOrder={false} />
|
<Toaster position="top-center" reverseOrder={false} />
|
||||||
<div className="container mx-auto px-4 max-w-4xl">
|
<div className="container mx-auto px-4 max-w-4xl">
|
||||||
<motion.div
|
<motion.div
|
||||||
@ -232,7 +212,7 @@ export default function PaymentsPage() {
|
|||||||
</h2>
|
</h2>
|
||||||
{others.map((r, i) => {
|
{others.map((r, i) => {
|
||||||
const statusKey = STATUS_MAP[r.status] || 'pending';
|
const statusKey = STATUS_MAP[r.status] || 'pending';
|
||||||
const cfg = STATUS_CONFIG[statusKey];
|
const cfg = getStatusConfig(t)[statusKey];
|
||||||
const Icon = cfg.icon;
|
const Icon = cfg.icon;
|
||||||
const amount = r.depositAmount || r.totalPrice || 0;
|
const amount = r.depositAmount || r.totalPrice || 0;
|
||||||
|
|
||||||
@ -352,7 +332,7 @@ export default function PaymentsPage() {
|
|||||||
<div className="px-6 py-5 border-b border-gray-100">
|
<div className="px-6 py-5 border-b border-gray-100">
|
||||||
<p className="text-sm font-bold text-gray-700 mb-3">{t('payments.paymentMethodLabel')}</p>
|
<p className="text-sm font-bold text-gray-700 mb-3">{t('payments.paymentMethodLabel')}</p>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||||
{PAYMENT_METHODS.map((method) => {
|
{getPaymentMethods(t).map((method) => {
|
||||||
const isSelected = selectedPayment === method.id;
|
const isSelected = selectedPayment === method.id;
|
||||||
const Icon = method.icon;
|
const Icon = method.icon;
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user