SocialBuddyAdmin/components/user/user-module.tsx
2026-01-02 01:38:00 +05:30

300 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import React, { Fragment, useEffect, useState } from 'react';
import { Dialog, Transition } from '@headlessui/react';
import IconX from '@/components/icon/icon-x';
import IconUserPlus from '@/components/icon/icon-user-plus';
import IconSearch from '@/components/icon/icon-search';
import Swal from 'sweetalert2';
import axios from 'axios';
const UserModule = () => {
const [users, setUsers] = useState<any[]>([]);
const [filteredUsers, setFilteredUsers] = useState<any[]>([]);
const [search, setSearch] = useState('');
const [addUserModal, setAddUserModal] = useState(false);
// ✅ NEW: delete modal state (UI only)
const [deleteUserModal, setDeleteUserModal] = useState(false);
const [userToDelete, setUserToDelete] = useState<any>(null);
const isAdminUser = true;
const defaultParams = {
userid: null,
name: '',
email: '',
mobileNumber: '',
password: '',
role: 'customer',
};
const [params, setParams] = useState<any>({ ...defaultParams });
/* ================= LOGIC ================= */
const fetchUsers = async () => {
try {
const res = await axios.get('/api/users');
setUsers(res.data || []);
setFilteredUsers(res.data || []);
} catch {
showMessage('Failed to load users', 'error');
}
};
useEffect(() => {
fetchUsers();
},);
useEffect(() => {
setFilteredUsers(
search
? users.filter((u) =>
(u.name || '').toLowerCase().includes(search.toLowerCase())
)
: users
);
}, [search, users]);
const showMessage = (msg = '', type = 'success') => {
Swal.mixin({
toast: true,
position: 'top',
showConfirmButton: false,
timer: 2500,
}).fire({ icon: type as any, title: msg });
};
const changeValue = (e: any) => {
const { id, value } = e.target;
setParams({ ...params, [id]: value });
};
const saveUser = async () => {
if (!params.name || !params.email || !params.mobileNumber) {
showMessage('Name, email and mobile number are required', 'error');
return;
}
const payload = {
name: params.name,
email: params.email,
mobileNumber: params.mobileNumber,
password: params.password,
role: params.role,
};
try {
if (params.userid) {
await axios.put(`/api/users/${params.userid}`, payload);
showMessage('User updated successfully');
} else {
await axios.post('/api/users', payload);
showMessage('User created successfully');
}
setAddUserModal(false);
setParams(defaultParams);
fetchUsers();
} catch {
showMessage('Failed to save user', 'error');
}
};
const editUser = (user: any) => {
setParams({
userid: user.userid,
name: user.name,
email: user.email,
mobileNumber: user.mobileNumber || '',
role: user.role,
});
setAddUserModal(true);
};
return (
<div className="relative min-h-screen w-full bg-[#111111] p-6">
{/* BACKGROUND GLOWS */}
<div className="pointer-events-none absolute inset-0">
<div className="absolute top-[180px] left-52 w-[100px] h-[100px] bg-[#1d8be0] rounded-full blur-2xl opacity-[1.5]" />
<div className="absolute top-10 left-0 w-[60px] h-[60px] bg-[#6cb655] rounded-full blur-2xl opacity-[1.5]" />
<div className="absolute -left-[80px] bottom-[140px] w-[100px] h-[200px] bg-[#db21d9] blur-3xl opacity-1" />
<div className="absolute top-[100px] right-[260px] w-[100px] h-[100px] bg-[#f28f50] rounded-full blur-2xl opacity-80" />
</div>
{/* CONTENT */}
<div className="relative z-10 text-white w-full min-h-screen">
{/* HEADER */}
<div className="flex justify-between items-center mb-6">
<h2 className="text-xl font-semibold">Users</h2>
<div className="flex gap-3">
<button
className="btn btn-primary text-white bg-gradient-to-r from-blue-600 to-pink-500"
disabled={!isAdminUser}
onClick={() => {
setParams(defaultParams);
setAddUserModal(true);
}}
>
<IconUserPlus className="mr-2" />
Add User
</button>
<div className="relative">
<input
type="text"
placeholder="Search user"
value={search}
onChange={(e) => setSearch(e.target.value)}
className="px-3 py-2 rounded-lg bg-[rgba(7,13,30,0.7)] border border-white/15 text-white"
/>
<span className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400">
<IconSearch />
</span>
</div>
</div>
</div>
{/* TABLE */}
<div className="rounded-2xl overflow-hidden border border-white/10 backdrop-blur-xl">
<table className="w-full text-sm text-white">
<thead className="bg-gray text-white">
<tr className='bg-gray'>
<th className="px-5 py-3 text-left bg-[#242424]">Name</th>
<th className='bg-[#242424]'>Email</th>
<th className='bg-[#242424]'>Mobile Number</th>
<th className='bg-[#242424]'>Role</th>
<th className="text-center bg-[#242424]">Actions</th>
</tr>
</thead>
<tbody>
{filteredUsers.map((user) => (
<tr key={user.userid} className="border-t border-white/10 hover:bg-white/5">
<td className="px-5 py-4">{user.name}</td>
<td>{user.email}</td>
<td>{user.mobileNumber}</td>
<td>
<span className="px-3 py-1 rounded-full text-xs bg-blue-500/20 text-blue-400 capitalize">
{user.role}
</span>
</td>
<td className="text-center">
<div className="flex justify-center gap-2">
<button
onClick={() => editUser(user)}
className="btn btn-sm btn-outline-primary"
>
Edit
</button>
<button
onClick={() => {
setUserToDelete(user);
setDeleteUserModal(true);
}}
className="btn btn-sm btn-outline-danger"
>
Delete
</button>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
{/* ADD / EDIT MODAL */}
<Transition appear show={addUserModal} as={Fragment}>
<Dialog as="div" open={addUserModal} onClose={() => setAddUserModal(false)} className="relative z-50">
<div className="fixed inset-0 bg-black/70" />
<div className="fixed inset-0 flex items-center justify-center px-4">
<Dialog.Panel className="w-full max-w-lg rounded-xl bg-[#0b0d1c] p-6 border border-white/20 text-white">
<button onClick={() => setAddUserModal(false)} className="absolute top-4 right-4 text-gray-400">
<IconX />
</button>
<h3 className="mb-4 text-lg font-semibold">
{params.userid ? 'Edit User' : 'Add User'}
</h3>
<div className="space-y-3">
<input id="name" value={params.name} onChange={changeValue} placeholder="Name"
className="w-full px-3 py-2 rounded-lg bg-black/40 border border-white/15 text-white" />
<input id="email" value={params.email} onChange={changeValue} placeholder="Email"
className="w-full px-3 py-2 rounded-lg bg-black/40 border border-white/15 text-white" />
<input id="mobileNumber" value={params.mobileNumber} onChange={changeValue} placeholder="Mobile"
className="w-full px-3 py-2 rounded-lg bg-black/40 border border-white/15 text-white" />
{!params.userid && (
<input id="password" type="password" value={params.password} onChange={changeValue}
placeholder="Password"
className="w-full px-3 py-2 rounded-lg bg-black/40 border border-white/15 text-white" />
)}
<select id="role" value={params.role} onChange={changeValue}
className="w-full px-3 py-2 rounded-lg bg-black/40 border border-white/15 text-white">
<option value="admin">Admin</option>
<option value="partner">Partner</option>
<option value="customer">Customer</option>
</select>
<div className="flex justify-end gap-3 pt-4">
<button className="btn btn-outline-danger" onClick={() => setAddUserModal(false)}>
Cancel
</button>
<button className="btn btn-primary bg-gradient-to-r from-blue-600 to-pink-500" onClick={saveUser}>
{params.userid ? 'Update' : 'Add'}
</button>
</div>
</div>
</Dialog.Panel>
</div>
</Dialog>
</Transition>
{/* DELETE MODAL SAME CARD UI */}
<Transition appear show={deleteUserModal} as={Fragment}>
<Dialog as="div" open={deleteUserModal} onClose={() => setDeleteUserModal(false)} className="relative z-50">
<div className="fixed inset-0 bg-black/70" />
<div className="fixed inset-0 flex items-center justify-center px-4">
<Dialog.Panel className="w-full max-w-lg rounded-xl bg-[#0b0d1c] p-6 border border-white/20 text-white">
<button onClick={() => setDeleteUserModal(false)} className="absolute top-4 right-4 text-gray-400">
<IconX />
</button>
<h3 className="mb-2 text-lg font-semibold">Delete User</h3>
<p className="text-sm text-gray-400 mb-6">
Are you sure you want to delete <span className="text-white">{userToDelete?.email}</span>?
</p>
<div className="flex justify-end gap-3">
<button className="btn btn-outline-danger" onClick={() => setDeleteUserModal(false)}>
Cancel
</button>
<button
className="btn btn-primary bg-gradient-to-r from-blue-600 to-pink-500"
onClick={async () => {
await axios.delete(`/api/users/${userToDelete.userid}`);
showMessage('User deleted');
setDeleteUserModal(false);
fetchUsers();
}}
>
Delete
</button>
</div>
</Dialog.Panel>
</div>
</Dialog>
</Transition>
</div>
);
};
export default UserModule;