diff --git a/src/pages/admin/AdminMembers.tsx b/src/pages/admin/AdminMembers.tsx index 37d2dcf..7c6382f 100644 --- a/src/pages/admin/AdminMembers.tsx +++ b/src/pages/admin/AdminMembers.tsx @@ -9,8 +9,10 @@ import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Skeleton } from "@/components/ui/skeleton"; +import { Input } from "@/components/ui/input"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { formatDateTime } from "@/lib/format"; -import { Eye, Shield, ShieldOff } from "lucide-react"; +import { Eye, Shield, ShieldOff, Search } from "lucide-react"; import { toast } from "@/hooks/use-toast"; interface Member { @@ -36,6 +38,8 @@ export default function AdminMembers() { const [selectedMember, setSelectedMember] = useState(null); const [memberAccess, setMemberAccess] = useState([]); const [dialogOpen, setDialogOpen] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + const [filterRole, setFilterRole] = useState('all'); useEffect(() => { if (!authLoading) { @@ -60,6 +64,20 @@ export default function AdminMembers() { setLoading(false); }; + // Filter members based on search and role + const filteredMembers = members.filter((member) => { + const matchesSearch = + member.name?.toLowerCase().includes(searchQuery.toLowerCase()) || + member.email?.toLowerCase().includes(searchQuery.toLowerCase()); + + const matchesRole = + filterRole === 'all' || + (filterRole === 'admin' && adminIds.has(member.id)) || + (filterRole === 'member' && !adminIds.has(member.id)); + + return matchesSearch && matchesRole; + }); + const viewMemberDetails = async (member: Member) => { setSelectedMember(member); const { data } = await supabase.from("user_access").select("*, product:products(title)").eq("user_id", member.id); @@ -102,7 +120,49 @@ export default function AdminMembers() {

Manajemen Member

Kelola semua pengguna

- + {/* Search & Filter */} + + +
+
+ + setSearchQuery(e.target.value)} + className="pl-10 border-2" + /> +
+ +
+
+ Menampilkan {filteredMembers.length} dari {members.length} member +
+
+
+ + {filteredMembers.length === 0 ? ( + + +

+ {searchQuery || filterRole !== 'all' + ? 'Tidak ada member yang cocok dengan filter' + : 'Belum ada member'} +

+
+
+ ) : ( + <> + {/* Desktop Table */}
@@ -117,7 +177,7 @@ export default function AdminMembers() { - {members.map((member) => ( + {filteredMembers.map((member) => ( {member.email || "-"} {member.name || "-"} @@ -144,13 +204,6 @@ export default function AdminMembers() { ))} - {members.length === 0 && ( - - - Belum ada member - - - )}
@@ -159,7 +212,7 @@ export default function AdminMembers() { {/* Mobile Card Layout */}
- {members.map((member) => ( + {filteredMembers.map((member) => (
@@ -198,12 +251,9 @@ export default function AdminMembers() {
))} - {members.length === 0 && ( -
- Belum ada member -
- )}
+ + )} diff --git a/src/pages/admin/AdminOrders.tsx b/src/pages/admin/AdminOrders.tsx index b09a9d2..c6b6bd3 100644 --- a/src/pages/admin/AdminOrders.tsx +++ b/src/pages/admin/AdminOrders.tsx @@ -13,8 +13,9 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { formatIDR, formatDateTime } from "@/lib/format"; -import { Eye, CheckCircle, XCircle, Video, ExternalLink, Trash2, AlertTriangle, RefreshCw, Link as LinkIcon, Download } from "lucide-react"; +import { Eye, CheckCircle, XCircle, Video, ExternalLink, Trash2, AlertTriangle, RefreshCw, Link as LinkIcon, Download, Search } from "lucide-react"; import { toast } from "@/hooks/use-toast"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { getPaymentStatusLabel, getPaymentStatusColor, canRefundOrder, canCancelOrder, canMarkAsPaid } from "@/lib/statusHelpers"; import { convertToCSV, downloadCSV, formatExportDate, formatExportIDR } from "@/lib/exportCSV"; @@ -68,6 +69,8 @@ export default function AdminOrders() { const [newMeetLink, setNewMeetLink] = useState(""); const [creatingMeetLink, setCreatingMeetLink] = useState(false); const [exporting, setExporting] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + const [filterStatus, setFilterStatus] = useState('all'); useEffect(() => { if (!authLoading) { @@ -86,6 +89,21 @@ export default function AdminOrders() { setLoading(false); }; + // Filter orders based on search and status + const filteredOrders = orders.filter((order) => { + const matchesSearch = + order.id.toLowerCase().includes(searchQuery.toLowerCase()) || + order.profile?.email?.toLowerCase().includes(searchQuery.toLowerCase()); + + const matchesStatus = + filterStatus === 'all' || + (filterStatus === 'paid' && order.payment_status === 'paid') || + (filterStatus === 'pending' && order.payment_status === 'pending') || + (filterStatus === 'refunded' && order.refunded_at); + + return matchesSearch && matchesStatus; + }); + const viewOrderDetails = async (order: Order) => { setSelectedOrder(order); const { data: itemsData } = await supabase.from("order_items").select("*, product:products(title, type)").eq("order_id", order.id); @@ -374,7 +392,50 @@ export default function AdminOrders() {
- + {/* Search & Filter */} + + +
+
+ + setSearchQuery(e.target.value)} + className="pl-10 border-2" + /> +
+ +
+
+ Menampilkan {filteredOrders.length} dari {orders.length} order +
+
+
+ + {filteredOrders.length === 0 ? ( + + +

+ {searchQuery || filterStatus !== 'all' + ? 'Tidak ada order yang cocok dengan filter' + : 'Belum ada order'} +

+
+
+ ) : ( + <> + {/* Desktop Table */}
@@ -391,7 +452,7 @@ export default function AdminOrders() { - {orders.map((order) => ( + {filteredOrders.map((order) => ( {order.id.slice(0, 8)} {order.profile?.email || "-"} @@ -406,13 +467,6 @@ export default function AdminOrders() { ))} - {orders.length === 0 && ( - - - Belum ada order - - - )}
@@ -421,7 +475,7 @@ export default function AdminOrders() { {/* Mobile Card Layout */}
- {orders.map((order) => ( + {filteredOrders.map((order) => (
@@ -453,14 +507,11 @@ export default function AdminOrders() {
))} - {orders.length === 0 && ( -
- Belum ada order -
- )}
+ + )} - + Detail Order diff --git a/src/pages/admin/AdminReviews.tsx b/src/pages/admin/AdminReviews.tsx index 2568f75..6b232f1 100644 --- a/src/pages/admin/AdminReviews.tsx +++ b/src/pages/admin/AdminReviews.tsx @@ -11,7 +11,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from " import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Skeleton } from "@/components/ui/skeleton"; import { toast } from "@/hooks/use-toast"; -import { Star, Check, X, Edit, Trash2 } from "lucide-react"; +import { Star, Check, X, Edit, Trash2, Search } from "lucide-react"; interface Review { id: string; @@ -30,7 +30,7 @@ interface Review { export default function AdminReviews() { const [reviews, setReviews] = useState([]); const [loading, setLoading] = useState(true); - const [filter, setFilter] = useState({ type: "all", status: "all" }); + const [filter, setFilter] = useState({ type: "all", status: "all", search: "" }); const [editReview, setEditReview] = useState(null); const [editForm, setEditForm] = useState({ title: "", body: "" }); @@ -112,6 +112,16 @@ export default function AdminReviews() { if (filter.type !== "all" && r.type !== filter.type) return false; if (filter.status === "approved" && !r.is_approved) return false; if (filter.status === "pending" && r.is_approved) return false; + if (filter.search) { + const query = filter.search.toLowerCase(); + const matchesSearch = + r.title.toLowerCase().includes(query) || + r.body.toLowerCase().includes(query) || + r.profiles?.name?.toLowerCase().includes(query) || + r.profiles?.email?.toLowerCase().includes(query) || + r.products?.title.toLowerCase().includes(query); + if (!matchesSearch) return false; + } return true; }); @@ -162,31 +172,47 @@ export default function AdminReviews() {

Kelola ulasan dari member

-
- + + +
+
+ + setFilter({ ...filter, search: e.target.value })} + className="pl-10 border-2" + /> +
+ - -
+ +
+
+ Menampilkan {filteredReviews.length} dari {reviews.length} ulasan +
+
+
@@ -202,9 +228,15 @@ export default function AdminReviews() { -

Belum ada ulasan

+

+ {filter.search || filter.type !== 'all' || filter.status !== 'all' + ? 'Tidak ada ulasan yang cocok' + : 'Belum ada ulasan'} +

- Ulasan dari member akan muncul di sini setelah mereka mengirimkan ulasan. + {filter.search || filter.type !== 'all' || filter.status !== 'all' + ? 'Coba ubah filter atau kata kunci pencarian' + : 'Ulasan dari member akan muncul di sini setelah mereka mengirimkan ulasan.'}