add Middleware + fixed all pages and fixed add propertise
This commit is contained in:
@ -1,9 +1,9 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { FileText, Shield, CheckCircle, Languages, Loader2, AlertCircle } from 'lucide-react';
|
||||
import { getARTerms, getENTerms } from '../utils/api';
|
||||
import { useEffect, useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { FileText, Shield, CheckCircle, Languages, Loader2, AlertCircle } from "lucide-react";
|
||||
import { getARTerms, getENTerms } from "../utils/api";
|
||||
|
||||
const containerVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
@ -21,75 +21,63 @@ const itemVariants = {
|
||||
const FALLBACK_TERMS = {
|
||||
ar: [
|
||||
{
|
||||
title: 'مقدمة',
|
||||
description:
|
||||
'مرحباً بك في منصة SweetHome. باستخدامك للمنصة، فإنك توافق على الالتزام بشروط الاستخدام هذه. إذا كنت لا توافق على أي جزء من هذه الشروط، يرجى عدم استخدام المنصة.',
|
||||
title: "مقدمة",
|
||||
description: "مرحباً بك في منصة SweetHome. باستخدامك للمنصة، فإنك توافق على الالتزام بشروط الاستخدام هذه. إذا كنت لا توافق على أي جزء من هذه الشروط، يرجى عدم استخدام المنصة.",
|
||||
},
|
||||
{
|
||||
title: 'استخدام المنصة',
|
||||
description:
|
||||
'يُسمح باستخدام المنصة للأغراض المشروعة فقط. يلتزم المستخدم بعدم استخدام المنصة في أي نشاط غير قانوني.',
|
||||
title: "استخدام المنصة",
|
||||
description: "يُسمح باستخدام المنصة للأغراض المشروعة فقط. يلتزم المستخدم بعدم استخدام المنصة في أي نشاط غير قانوني.",
|
||||
},
|
||||
{
|
||||
title: 'حقوق ومسؤوليات المالك',
|
||||
description:
|
||||
'يتحمل المالك مسؤولية دقة المعلومات المقدمة عن العقار بما في ذلك الصور والوصف والسعر والتوفر.',
|
||||
title: "حقوق ومسؤوليات المالك",
|
||||
description: "يتحمل المالك مسؤولية دقة المعلومات المقدمة عن العقار بما في ذلك الصور والوصف والسعر والتوفر.",
|
||||
},
|
||||
{
|
||||
title: 'حقوق ومسؤوليات المستأجر',
|
||||
description:
|
||||
'يلتزم المستأجر باستخدام العقار بطريقة مسؤولة وعدم التسبب في أي ضرر للممتلكات.',
|
||||
title: "حقوق ومسؤوليات المستأجر",
|
||||
description: "يلتزم المستأجر باستخدام العقار بطريقة مسؤولة وعدم التسبب في أي ضرر للممتلكات.",
|
||||
},
|
||||
{
|
||||
title: 'الدفع والعمولات',
|
||||
description:
|
||||
'تتقاضى المنصة عمولة على كل حصة ناجحة وفقاً للنسبة المحددة في وقت الحجز.',
|
||||
title: "الدفع والعمولات",
|
||||
description: "تتقاضى المنصة عمولة على كل حصة ناجحة وفقاً للنسبة المحددة في وقت الحجز.",
|
||||
},
|
||||
{
|
||||
title: 'خصوصية البيانات',
|
||||
description:
|
||||
'نحن نأخذ خصوصية بياناتك على محمل الجد. يتم جمع واستخدام البيانات الشخصية وفقاً لسياسة الخصوصية الخاصة بنا.',
|
||||
title: "خصوصية البيانات",
|
||||
description: "نحن نأخذ خصوصية بياناتك على محمل الجد. يتم جمع واستخدام البيانات الشخصية وفقاً لسياسة الخصوصية الخاصة بنا.",
|
||||
},
|
||||
],
|
||||
en: [
|
||||
{
|
||||
title: 'Introduction',
|
||||
description:
|
||||
'Welcome to SweetHome. By using our platform, you agree to comply with these terms. If you do not agree, please do not use the platform.',
|
||||
title: "Introduction",
|
||||
description: "Welcome to SweetHome. By using our platform, you agree to comply with these terms. If you do not agree, please do not use the platform.",
|
||||
},
|
||||
{
|
||||
title: 'Platform Usage',
|
||||
description:
|
||||
'The platform may only be used for lawful purposes. Users must not engage in any illegal activity.',
|
||||
title: "Platform Usage",
|
||||
description: "The platform may only be used for lawful purposes. Users must not engage in any illegal activity.",
|
||||
},
|
||||
{
|
||||
title: 'Owner Rights & Responsibilities',
|
||||
description:
|
||||
'Owners are responsible for the accuracy of property information including images, description, price, and availability.',
|
||||
title: "Owner Rights & Responsibilities",
|
||||
description: "Owners are responsible for the accuracy of property information including images, description, price, and availability.",
|
||||
},
|
||||
{
|
||||
title: 'Tenant Rights & Responsibilities',
|
||||
description:
|
||||
'Tenants must use the property responsibly and not cause any damage to the property.',
|
||||
title: "Tenant Rights & Responsibilities",
|
||||
description: "Tenants must use the property responsibly and not cause any damage to the property.",
|
||||
},
|
||||
{
|
||||
title: 'Payment & Commissions',
|
||||
description:
|
||||
'The platform charges a commission on each successful booking according to the rate specified at the time of booking.',
|
||||
title: "Payment & Commissions",
|
||||
description: "The platform charges a commission on each successful booking according to the rate specified at the time of booking.",
|
||||
},
|
||||
{
|
||||
title: 'Data Privacy',
|
||||
description:
|
||||
'We take your data privacy seriously. Personal data is collected and used in accordance with our Privacy Policy.',
|
||||
title: "Data Privacy",
|
||||
description: "We take your data privacy seriously. Personal data is collected and used in accordance with our Privacy Policy.",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default function TermsPage() {
|
||||
const [terms, setTerms] = useState([]);
|
||||
const [language, setLanguage] = useState('ar');
|
||||
const [language, setLanguage] = useState("ar");
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
@ -97,9 +85,9 @@ export default function TermsPage() {
|
||||
const fetchTerms = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError('');
|
||||
setError("");
|
||||
|
||||
const fetcher = language === 'ar' ? getARTerms : getENTerms;
|
||||
const fetcher = language === "ar" ? getARTerms : getENTerms;
|
||||
const data = await fetcher();
|
||||
|
||||
if (!data) {
|
||||
@ -115,14 +103,14 @@ export default function TermsPage() {
|
||||
}
|
||||
|
||||
const mapped = raw.map((item) => ({
|
||||
title: item.title || item.name || '',
|
||||
description: item.description || item.content || item.body || item.text || '',
|
||||
title: item.title || item.name || "",
|
||||
description: item.description || item.content || item.body || item.text || "",
|
||||
}));
|
||||
|
||||
setTerms(mapped);
|
||||
} catch {
|
||||
setTerms(FALLBACK_TERMS[language]);
|
||||
setError('');
|
||||
setError("");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@ -134,102 +122,62 @@ export default function TermsPage() {
|
||||
}, [language]);
|
||||
|
||||
return (
|
||||
<div
|
||||
dir={language === 'ar' ? 'rtl' : 'ltr'}
|
||||
className="min-h-screen bg-gradient-to-b from-amber-50/50 to-white py-12"
|
||||
>
|
||||
<div dir={language === "ar" ? "rtl" : "ltr"} className="min-h-screen bg-gradient-to-b py-12">
|
||||
<div className="container mx-auto px-4 max-w-4xl">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="text-center mb-12"
|
||||
>
|
||||
<motion.div initial={{ opacity: 0, y: -20 }} animate={{ opacity: 1, y: 0 }} className="text-center mb-12">
|
||||
<div className="w-20 h-20 bg-amber-100 rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-lg shadow-amber-100">
|
||||
<FileText className="w-10 h-10 text-amber-600" />
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center gap-4 mb-4">
|
||||
<h1 className="text-4xl font-bold text-gray-900">
|
||||
{language === 'ar' ? 'شروط الاستخدام' : 'Terms of Use'}
|
||||
</h1>
|
||||
<h1 className="text-4xl font-bold text-gray-900">{language === "ar" ? "شروط الاستخدام" : "Terms of Use"}</h1>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setLanguage(language === 'ar' ? 'en' : 'ar')}
|
||||
onClick={() => setLanguage(language === "ar" ? "en" : "ar")}
|
||||
className="inline-flex items-center gap-2 rounded-full border border-amber-200 bg-white px-4 py-2 text-sm font-semibold text-gray-700 shadow-sm transition hover:shadow-md"
|
||||
>
|
||||
<Languages className="h-4 w-4" />
|
||||
{language === 'ar' ? 'English' : 'العربية'}
|
||||
{language === "ar" ? "English" : "العربية"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p className="text-lg text-gray-600 max-w-2xl mx-auto">
|
||||
{language === 'ar'
|
||||
? 'يرجى قراءة شروط الاستخدام التالية بعناية قبل استخدام المنصة'
|
||||
: 'Please read the following terms of use carefully before using the platform'}
|
||||
{language === "ar" ? "يرجى قراءة شروط الاستخدام التالية بعناية قبل استخدام المنصة" : "Please read the following terms of use carefully before using the platform"}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{loading && (
|
||||
<div className="flex items-center justify-center gap-3 mb-8 rounded-2xl border border-amber-200 bg-amber-50 px-4 py-4 text-amber-800">
|
||||
<Loader2 className="h-5 w-5 animate-spin" />
|
||||
<span>
|
||||
{language === 'ar'
|
||||
? 'جاري تحميل شروط الاستخدام...'
|
||||
: 'Loading terms of use...'}
|
||||
</span>
|
||||
<span>{language === "ar" ? "جاري تحميل شروط الاستخدام..." : "Loading terms of use..."}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="mb-8 rounded-2xl border border-red-200 bg-red-50 p-4 flex items-center gap-3 text-red-700"
|
||||
>
|
||||
<motion.div initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} className="mb-8 rounded-2xl border border-red-200 bg-red-50 p-4 flex items-center gap-3 text-red-700">
|
||||
<AlertCircle className="h-5 w-5 shrink-0" />
|
||||
<span>{error}</span>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{!loading && terms.length === 0 && !error && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="text-center py-16 text-gray-500"
|
||||
>
|
||||
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="text-center py-16 text-gray-500">
|
||||
<FileText className="h-16 w-16 mx-auto mb-4 text-gray-300" />
|
||||
<p className="text-xl font-medium">
|
||||
{language === 'ar'
|
||||
? 'لا توجد شروط استخدام متاحة حالياً'
|
||||
: 'No terms of use available'}
|
||||
</p>
|
||||
<p className="text-xl font-medium">{language === "ar" ? "لا توجد شروط استخدام متاحة حالياً" : "No terms of use available"}</p>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{terms.length > 0 && (
|
||||
<motion.div
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
className="space-y-6"
|
||||
>
|
||||
<motion.div variants={containerVariants} initial="hidden" animate="visible" className="space-y-6">
|
||||
{terms.map((term, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
variants={itemVariants}
|
||||
className="bg-white rounded-2xl shadow-sm border border-gray-200 p-6 hover:shadow-md transition-shadow"
|
||||
>
|
||||
<motion.div key={index} variants={itemVariants} className="bg-white rounded-2xl shadow-sm border border-gray-200 p-6 hover:shadow-md transition-shadow">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 bg-amber-100 rounded-xl flex items-center justify-center shrink-0 mt-1">
|
||||
<Shield className="w-5 h-5 text-amber-600" />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
{term.title && (
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3">{term.title}</h2>
|
||||
)}
|
||||
<p className="text-gray-600 leading-relaxed whitespace-pre-wrap">
|
||||
{term.description}
|
||||
</p>
|
||||
{term.title && <h2 className="text-xl font-bold text-gray-900 mb-3">{term.title}</h2>}
|
||||
<p className="text-gray-600 leading-relaxed whitespace-pre-wrap">{term.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
@ -237,21 +185,14 @@ export default function TermsPage() {
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.6 }}
|
||||
className="mt-8 bg-amber-50 rounded-2xl border border-amber-200 p-6 flex items-start gap-4"
|
||||
>
|
||||
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: 0.6 }} className="mt-8 bg-amber-50 rounded-2xl border border-amber-200 p-6 flex items-start gap-4">
|
||||
<CheckCircle className="w-6 h-6 text-amber-600 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="font-bold text-amber-800 mb-1">
|
||||
{language === 'ar' ? 'آخر تحديث' : 'Last Updated'}
|
||||
</p>
|
||||
<p className="font-bold text-amber-800 mb-1">{language === "ar" ? "آخر تحديث" : "Last Updated"}</p>
|
||||
<p className="text-amber-700">
|
||||
{language === 'ar'
|
||||
? 'تم آخر تحديث لشروط الاستخدام في 1 مايو 2026. يرجى مراجعة هذه الصفحة بشكل دوري للاطلاع على أي تغييرات.'
|
||||
: 'Last updated on May 1, 2026. Please review this page periodically for any changes.'}
|
||||
{language === "ar"
|
||||
? "تم آخر تحديث لشروط الاستخدام في 1 مايو 2026. يرجى مراجعة هذه الصفحة بشكل دوري للاطلاع على أي تغييرات."
|
||||
: "Last updated on May 1, 2026. Please review this page periodically for any changes."}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
Reference in New Issue
Block a user