diff --git a/pages/Admin/Announcements.tsx b/pages/Admin/Announcements.tsx index 0d8feca..a17e94a 100644 --- a/pages/Admin/Announcements.tsx +++ b/pages/Admin/Announcements.tsx @@ -1,11 +1,11 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState } from 'react'; import { Plus, Edit2, Trash2, X, Save, Loader2, Megaphone } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; import { supabase } from '../../lib/supabase'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; export const Announcements = () => { - const [announcements, setAnnouncements] = useState([]); - const [loading, setLoading] = useState(true); + const queryClient = useQueryClient(); const [isEditing, setIsEditing] = useState(false); const [formData, setFormData] = useState({ title: '', @@ -16,45 +16,32 @@ export const Announcements = () => { category: 'General', status: 'Baru', color: 'from-purple-500 to-blue-500', - highlights: '', // comma separated for input + highlights: '', is_active: true }); - const fetchAnnouncements = async () => { - setLoading(true); - try { + // Query for Announcements + const { data: announcements = [], isLoading } = useQuery({ + queryKey: ['announcements'], + queryFn: async () => { const { data, error } = await supabase .from('announcements') - .select('id, title, subtitle, description, date, type, status, color, is_active') + .select('id, title, subtitle, description, date, type, status, color, is_active, content, category, highlights') .order('date', { ascending: false }); if (error) throw error; - setAnnouncements(data || []); - } catch (error) { - console.error('Error fetching announcements:', error); - } finally { - setLoading(false); - } - }; - - useEffect(() => { - fetchAnnouncements(); - }, []); + return data; + }, + }); - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - try { - const payload = { - ...formData, - highlights: formData.highlights.split(',').map((h: string) => h.trim()).filter(Boolean), - date: new Date().toISOString() - }; - - if (formData.id) { + // Mutation for Save/Update + const saveMutation = useMutation({ + mutationFn: async (payload: any) => { + if (payload.id) { const { error } = await supabase .from('announcements') .update(payload) - .eq('id', formData.id); + .eq('id', payload.id); if (error) throw error; } else { const { error } = await supabase @@ -62,25 +49,48 @@ export const Announcements = () => { .insert([payload]); if (error) throw error; } - + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['announcements'] }); setIsEditing(false); - fetchAnnouncements(); resetForm(); - } catch (error) { + }, + onError: (error) => { console.error('Error saving announcement:', error); - alert('Failed to save announcement.'); + alert('Gagal menyimpan pengumuman.'); } - }; + }); - const handleDelete = async (id: string) => { - if (!window.confirm('Delete this announcement?')) return; - try { + // Mutation for Delete + const deleteMutation = useMutation({ + mutationFn: async (id: string) => { const { error } = await supabase.from('announcements').delete().eq('id', id); if (error) throw error; - setAnnouncements(announcements.filter(a => a.id !== id)); - } catch (error) { + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['announcements'] }); + }, + onError: (error) => { console.error('Error deleting:', error); + alert('Gagal menghapus pengumuman.'); } + }); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + const payload = { + ...formData, + highlights: typeof formData.highlights === 'string' + ? formData.highlights.split(',').map((h: string) => h.trim()).filter(Boolean) + : formData.highlights, + date: formData.id ? formData.date : new Date().toISOString() + }; + saveMutation.mutate(payload); + }; + + const handleDelete = (id: string) => { + if (!window.confirm('Hapus pengumuman ini?')) return; + deleteMutation.mutate(id); }; const handleEdit = (item: any) => { @@ -116,7 +126,7 @@ export const Announcements = () => { {/* List - Zen Refinement */}
- {loading ? ( + {isLoading ? (

Menarik data...

@@ -127,7 +137,7 @@ export const Announcements = () => {
) : (
- {announcements.map((item, index) => ( + {announcements.map((item: any, index: number) => ( { -
@@ -178,13 +192,13 @@ export const Announcements = () => { initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} - className="fixed inset-0 bg-black/90 backdrop-blur-xl z-[100] flex items-center justify-center p-6" + className="fixed inset-0 bg-black/90 backdrop-blur-xl z-[100] flex items-center justify-center p-6 overflow-y-auto" >
@@ -238,8 +252,13 @@ export const Announcements = () => {
-
diff --git a/pages/Admin/Users.tsx b/pages/Admin/Users.tsx index a7a3c95..cc5f84b 100644 --- a/pages/Admin/Users.tsx +++ b/pages/Admin/Users.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; -import { Search, Filter, MoreVertical, Check, X, Shield, User, Loader2 } from 'lucide-react'; +import { Search, Filter, MoreVertical, Check, X, Shield, User, Loader2, Trash2 } from 'lucide-react'; import { supabase } from '../../lib/supabase'; import { useQuery, useMutation, useQueryClient, keepPreviousData } from '@tanstack/react-query'; import { Pagination } from '../../components/Pagination'; @@ -74,7 +74,31 @@ export const Users = () => { } }); - const handleAction = (userId: string, action: 'approve' | 'reject' | 'make_admin' | 'remove_admin') => { + const deleteMutation = useMutation({ + mutationFn: async (userId: string) => { + const { error } = await supabase + .from('profiles') + .delete() + .eq('id', userId); + if (error) throw error; + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['users'] }); + alert('Pengguna berhasil dihapus.'); + }, + onError: (error) => { + console.error('Delete failed:', error); + alert('Gagal menghapus pengguna.'); + } + }); + + const handleAction = (userId: string, action: 'approve' | 'reject' | 'make_admin' | 'remove_admin' | 'delete') => { + if (action === 'delete') { + if (window.confirm('Apakah Anda yakin ingin menghapus pengguna ini secara permanen? Semua data profil akan hilang.')) { + deleteMutation.mutate(userId); + } + return; + } let updates = {}; if (action === 'approve') updates = { is_approved: true }; if (action === 'reject') updates = { is_approved: false }; @@ -217,6 +241,14 @@ export const Users = () => { > {user.role === 'admin' ? : } +
@@ -232,18 +264,20 @@ export const Users = () => {
- {users.length > 0 && ( -
- setPage(newPage)} - loading={isPlaceholderData} - /> -
- )} -
-
+ { + users.length > 0 && ( +
+ setPage(newPage)} + loading={isPlaceholderData} + /> +
+ ) + } + +
); };