Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/client/home/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export default function ClientHomePage() {
<div className="mt-8 p-4 rounded-2xl bg-[#0A0D1A]/60 backdrop-blur-xl border border-white/5">
<div className="flex items-center justify-between mb-4">
<h3 className="text-white text-xl font-bold">الخدمات الرئيسية</h3>
<Link href="/client/services/all" className="text-[#FFA504] text-xs font-medium">عرض الكل</Link>
<Link href="/client/services" className="text-[#FFA504] text-xs font-medium">عرض الكل</Link>
</div>
{loading ? (
<div className="grid grid-cols-4 gap-4">
Expand Down
163 changes: 163 additions & 0 deletions app/client/services/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
'use client'

import { useState, useEffect } from 'react'
import { useRouter } from 'next/navigation'
import Link from 'next/link'
import { ArrowLeft, Settings, Sparkles, Home, AlertTriangle, PenTool, Wrench, Zap, Droplets, Wind, Truck, Search } from 'lucide-react'
import { createClient } from '@/lib/supabase/client'

const iconMap: Record<string, React.ReactNode> = {
painting: <PenTool size={24} className="text-[#FFA504]" />,
carpentry: <Wrench size={24} className="text-[#FFA504]" />,
plumbing: <Droplets size={24} className="text-[#FFA504]" />,
electricity: <Zap size={24} className="text-[#FFA504]" />,
ac: <Wind size={24} className="text-[#FFA504]" />,
cleaning: <Sparkles size={24} className="text-[#FFA504]" />,
moving: <Truck size={24} className="text-[#FFA504]" />,
default: <Settings size={24} className="text-[#FFA504]" />
}

const TOPICS = [
{ id: 'maintenance', label: 'صيانة وتصليح', icon: <Wrench size={20} className="text-white" />, color: 'from-blue-600 to-cyan-600' },
{ id: 'construction', label: 'تشطيبات وديكور', icon: <Home size={20} className="text-white" />, color: 'from-purple-600 to-indigo-600' },
{ id: 'cleaning', label: 'نظافة وعناية', icon: <Sparkles size={20} className="text-white" />, color: 'from-teal-500 to-emerald-500' },
{ id: 'emergency', label: 'خدمات الطوارئ', icon: <AlertTriangle size={20} className="text-white" />, color: 'from-red-500 to-orange-500' },
]

const HARDCODED_CATEGORIES = [
{ id: 'painting', label_ar: 'دهان', icon: 'painting', topicId: 'construction' },
{ id: 'carpentry', label_ar: 'نجارة', icon: 'carpentry', topicId: 'maintenance' },
{ id: 'plumbing', label_ar: 'سباكة', icon: 'plumbing', topicId: 'maintenance' },
{ id: 'electricity', label_ar: 'كهرباء', icon: 'electricity', topicId: 'maintenance' },
{ id: 'ac', label_ar: 'تكييف', icon: 'ac', topicId: 'maintenance' },
{ id: 'cleaning', label_ar: 'تنظيف', icon: 'cleaning', topicId: 'cleaning' },
{ id: 'moving', label_ar: 'نقل عفش', icon: 'moving', topicId: 'general' },
]

export default function MainServicesPage() {
const router = useRouter()
const supabase = createClient()
const [categories, setCategories] = useState(HARDCODED_CATEGORIES)
const [loading, setLoading] = useState(true)
const [searchQuery, setSearchQuery] = useState('')
const [activeTopic, setActiveTopic] = useState<string | 'all'>('all')

useEffect(() => {
const fetchCategories = async () => {
setLoading(true)
const { data } = await supabase.from('categories').select('*').order('usage_count', { ascending: false })
if (data && data.length > 0) {
const merged = data.map(dbCat => {
const hc = HARDCODED_CATEGORIES.find(c => c.id === dbCat.id)
return { ...dbCat, topicId: hc?.topicId || 'general' }
})
setCategories(merged)
}
setLoading(false)
}
fetchCategories()
}, [supabase])
Comment on lines +39 to +59

const filteredCategories = categories.filter(c => {
const matchesSearch = c.label_ar.includes(searchQuery)
const matchesTopic = activeTopic === 'all' || c.topicId === activeTopic
return matchesSearch && matchesTopic
})
Comment on lines +61 to +65

return (
<div dir="rtl" className="min-h-screen bg-[#020617]">
<div className="sticky top-0 z-10 bg-[#020617]/80 backdrop-blur-xl border-b border-white/5">
<div className="flex items-center justify-between h-14 max-w-[512px] mx-auto px-4">
<button onClick={() => router.back()} className="flex items-center gap-2">
<ArrowLeft size={16} className="text-white" />
</button>
<h1 className="text-white text-xl font-semibold">الخدمات والتخصصات</h1>
<div className="w-6" />
</div>
</div>

<div className="max-w-[512px] mx-auto px-4 pb-32 pt-6">
<div className="relative mb-8">
<div className="w-full h-14 pr-12 pl-4 rounded-xl bg-[#0F172A]/60 border border-white/5 text-white flex items-center">
<input
type="text"
placeholder="ابحث عن خدمة..."
className="bg-transparent w-full h-full outline-none text-sm placeholder-[#C7C5CF]/50"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
<Search size={18} className="absolute right-4 top-1/2 -translate-y-1/2 text-[#C7C5CF]/70" />
</div>

<div className="mb-8">
<h2 className="text-white text-lg font-bold mb-4">التخصصات (Topics)</h2>
<div className="grid grid-cols-2 gap-3">
{TOPICS.map(topic => (
<button
key={topic.id}
onClick={() => setActiveTopic(activeTopic === topic.id ? 'all' : topic.id)}
className={`p-4 rounded-xl text-right relative overflow-hidden transition-all duration-300 ${
activeTopic === topic.id ? 'ring-2 ring-white scale-[0.98]' : ''
}`}
>
<div className={`absolute inset-0 bg-gradient-to-br ${topic.color} opacity-90`} />
<div className="absolute inset-0 bg-black/20" />
<div className="relative z-10 flex flex-col gap-3">
<div className="w-10 h-10 rounded-lg bg-white/20 backdrop-blur-md flex items-center justify-center">
{topic.icon}
</div>
<span className="text-white font-bold">{topic.label}</span>
</div>
</button>
))}
</div>
</div>

<div>
<div className="flex items-center justify-between mb-4">
<h2 className="text-white text-lg font-bold">
{activeTopic === 'all' ? 'جميع الخدمات' : TOPICS.find(t => t.id === activeTopic)?.label}
</h2>
{activeTopic !== 'all' && (
<button onClick={() => setActiveTopic('all')} className="text-[#FFA504] text-xs">
عرض الكل
</button>
)}
</div>

{loading ? (
<div className="grid grid-cols-3 gap-4">
{[1,2,3,4,5,6].map(i => (
<div key={i} className="flex flex-col items-center gap-3">
<div className="w-20 h-20 rounded-2xl bg-white/5 animate-pulse" />
<div className="w-14 h-3 rounded bg-white/5 animate-pulse" />
</div>
))}
</div>
) : filteredCategories.length === 0 ? (
<div className="text-center text-[#6B7A99] py-10 bg-[#0F172A]/40 rounded-2xl border border-white/5">
<Search size={40} className="mx-auto mb-3 opacity-20" />
<p>لا توجد خدمات مطابقة لبحثك</p>
</div>
) : (
<div className="grid grid-cols-3 gap-x-4 gap-y-6">
{filteredCategories.map(cat => {
const icon = iconMap[cat.icon || 'default'] || iconMap.default
return (
<Link key={cat.id} href={`/client/services/${cat.id}`} className="group flex flex-col items-center gap-3">
<div className="w-full aspect-square rounded-2xl bg-[#0F172A] border border-white/5 flex items-center justify-center group-hover:bg-[#1E293B] group-hover:border-[#FFA504]/30 transition-all duration-300 shadow-sm relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-[#FFA504]/0 to-[#FFA504]/5 opacity-0 group-hover:opacity-100 transition-opacity" />
{icon}
</div>
<span className="text-[#E4E1E5] text-xs font-medium text-center">{cat.label_ar}</span>
</Link>
)
})}
</div>
)}
</div>
</div>
</div>
)
}
Loading