336 lines
15 KiB
TypeScript
336 lines
15 KiB
TypeScript
'use client';
|
||
import React, { Fragment, useEffect, useState } from 'react';
|
||
import { Dialog, Transition, TransitionChild, DialogPanel } 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);
|
||
|
||
const defaultParams = {
|
||
userid: null,
|
||
name: '',
|
||
email: '',
|
||
password: '',
|
||
phonenumber: '',
|
||
role: 'customer',
|
||
};
|
||
const [params, setParams] = useState<any>({ ...defaultParams });
|
||
|
||
// ✅ Fetch users
|
||
const fetchUsers = async () => {
|
||
try {
|
||
const res = await axios.get('https://ebay.backend.data4autos.com/api/auth/users');
|
||
setUsers(res.data?.users || []);
|
||
setFilteredUsers(res.data?.users || []);
|
||
} catch (err) {
|
||
console.error(err);
|
||
showMessage('Failed to load users', 'error');
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
fetchUsers();
|
||
}, []);
|
||
|
||
// ✅ Search filter
|
||
useEffect(() => {
|
||
if (!search) {
|
||
setFilteredUsers(users);
|
||
} else {
|
||
setFilteredUsers(
|
||
users.filter((u) => u.name.toLowerCase().includes(search.toLowerCase()))
|
||
);
|
||
}
|
||
}, [search, users]);
|
||
|
||
const showMessage = (msg = '', type = 'success') => {
|
||
const toast: any = Swal.mixin({
|
||
toast: true,
|
||
position: 'top',
|
||
showConfirmButton: false,
|
||
timer: 2500,
|
||
customClass: { container: 'toast' },
|
||
});
|
||
toast.fire({
|
||
icon: type,
|
||
title: msg,
|
||
padding: '10px 20px',
|
||
});
|
||
};
|
||
|
||
const changeValue = (e: any) => {
|
||
const { id, value } = e.target;
|
||
setParams({ ...params, [id]: value });
|
||
};
|
||
|
||
// ✅ Add / Update User
|
||
const saveUser = async () => {
|
||
if (!params.name || !params.email || !params.phonenumber || !params.role) {
|
||
showMessage('Please fill all required fields', 'error');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
if (params.userid) {
|
||
// UPDATE
|
||
await axios.put(`https://ebay.backend.data4autos.com/api/auth/users/${params.userid}`, params);
|
||
showMessage('User updated successfully');
|
||
} else {
|
||
// ADD
|
||
await axios.post('https://ebay.backend.data4autos.com/api/auth/users/add', params);
|
||
showMessage('User added successfully');
|
||
}
|
||
setAddUserModal(false);
|
||
fetchUsers();
|
||
} catch (err: any) {
|
||
console.error(err);
|
||
showMessage('Error saving user', 'error');
|
||
}
|
||
};
|
||
|
||
// ✅ Edit User
|
||
const editUser = (user: any) => {
|
||
setParams({ ...user, password: '' }); // don’t show password in edit
|
||
setAddUserModal(true);
|
||
};
|
||
|
||
// ✅ Delete User
|
||
const deleteUser = async (user: any) => {
|
||
Swal.fire({
|
||
title: 'Are you sure?',
|
||
text: `Delete user ${user.name}?`,
|
||
icon: 'warning',
|
||
showCancelButton: true,
|
||
confirmButtonText: 'Yes, delete it!',
|
||
cancelButtonText: 'Cancel',
|
||
}).then(async (result) => {
|
||
if (result.isConfirmed) {
|
||
try {
|
||
await axios.delete(`https://ebay.backend.data4autos.com/api/auth/users/${user.userid}`);
|
||
showMessage('User deleted successfully');
|
||
fetchUsers();
|
||
} catch (err) {
|
||
console.error(err);
|
||
showMessage('Failed to delete user', 'error');
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
return (
|
||
<div>
|
||
{/* Header */}
|
||
<div className="flex flex-wrap items-center justify-between gap-4">
|
||
<h2 className="text-xl font-semibold">Users</h2>
|
||
<div className="flex gap-3 items-center">
|
||
<button
|
||
type="button"
|
||
className="btn btn-primary"
|
||
onClick={() => {
|
||
setParams(defaultParams);
|
||
setAddUserModal(true);
|
||
}}
|
||
>
|
||
<IconUserPlus className="ltr:mr-2 rtl:ml-2" /> Add User
|
||
</button>
|
||
|
||
<div className="relative">
|
||
<input
|
||
type="text"
|
||
placeholder="Search User"
|
||
className="form-input py-2 ltr:pr-10 rtl:pl-10"
|
||
value={search}
|
||
onChange={(e) => setSearch(e.target.value)}
|
||
/>
|
||
<button
|
||
type="button"
|
||
className="absolute top-1/2 -translate-y-1/2 ltr:right-[11px] rtl:left-[11px]"
|
||
>
|
||
<IconSearch />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* ✅ Table */}
|
||
<div className="panel mt-5 overflow-hidden border-0 p-0">
|
||
<div className="table-responsive">
|
||
<table className="table-striped table-hover">
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Email</th>
|
||
<th>Phone</th>
|
||
<th>Role</th>
|
||
<th className="!text-center">Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{filteredUsers.map((user: any) => (
|
||
<tr key={user.userid}>
|
||
<td>{user.name}</td>
|
||
<td>{user.email}</td>
|
||
<td>{user.phonenumber}</td>
|
||
<td>{user.role}</td>
|
||
<td className="text-center">
|
||
<div className="flex justify-center gap-2">
|
||
<button
|
||
className="btn btn-sm btn-outline-primary"
|
||
onClick={() => editUser(user)}
|
||
>
|
||
Edit
|
||
</button>
|
||
<button
|
||
className="btn btn-sm btn-outline-danger"
|
||
onClick={() => deleteUser(user)}
|
||
>
|
||
Delete
|
||
</button>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
{/* ✅ Add / Edit Popup */}
|
||
<Transition appear show={addUserModal} as={Fragment}>
|
||
<Dialog
|
||
as="div"
|
||
open={addUserModal}
|
||
onClose={() => setAddUserModal(false)}
|
||
className="relative z-50"
|
||
>
|
||
<TransitionChild
|
||
as={Fragment}
|
||
enter="ease-out duration-300"
|
||
enterFrom="opacity-0"
|
||
enterTo="opacity-100"
|
||
leave="ease-in duration-200"
|
||
leaveFrom="opacity-100"
|
||
leaveTo="opacity-0"
|
||
>
|
||
<div className="fixed inset-0 bg-[black]/60" />
|
||
</TransitionChild>
|
||
<div className="fixed inset-0 overflow-y-auto">
|
||
<div className="flex min-h-full items-center justify-center px-4 py-8">
|
||
<TransitionChild
|
||
as={Fragment}
|
||
enter="ease-out duration-300"
|
||
enterFrom="opacity-0 scale-95"
|
||
enterTo="opacity-100 scale-100"
|
||
leave="ease-in duration-200"
|
||
leaveFrom="opacity-100 scale-100"
|
||
leaveTo="opacity-0 scale-95"
|
||
>
|
||
<DialogPanel className="panel w-full max-w-lg overflow-hidden rounded-lg border-0 p-0 text-black dark:text-white-dark">
|
||
<button
|
||
type="button"
|
||
onClick={() => setAddUserModal(false)}
|
||
className="absolute top-4 text-gray-400 hover:text-gray-800 ltr:right-4 rtl:left-4"
|
||
>
|
||
<IconX />
|
||
</button>
|
||
<div className="bg-[#fbfbfb] py-3 text-lg font-medium ltr:pl-5 rtl:pr-5 dark:bg-[#121c2c]">
|
||
{params.userid ? 'Edit User' : 'Add User'}
|
||
</div>
|
||
<div className="p-5">
|
||
<form>
|
||
<div className="mb-4">
|
||
<label>Name</label>
|
||
<input
|
||
id="name"
|
||
type="text"
|
||
className="form-input"
|
||
value={params.name}
|
||
onChange={changeValue}
|
||
placeholder="Enter name"
|
||
/>
|
||
</div>
|
||
<div className="mb-4">
|
||
<label>Email</label>
|
||
<input
|
||
id="email"
|
||
type="email"
|
||
className="form-input"
|
||
value={params.email}
|
||
onChange={changeValue}
|
||
placeholder="Enter email"
|
||
/>
|
||
</div>
|
||
{!params.userid && (
|
||
<div className="mb-4">
|
||
<label>Password</label>
|
||
<input
|
||
id="password"
|
||
type="password"
|
||
className="form-input"
|
||
value={params.password}
|
||
onChange={changeValue}
|
||
placeholder="Enter password"
|
||
/>
|
||
</div>
|
||
)}
|
||
<div className="mb-4">
|
||
<label>Phone Number</label>
|
||
<input
|
||
id="phonenumber"
|
||
type="text"
|
||
className="form-input"
|
||
value={params.phonenumber}
|
||
onChange={changeValue}
|
||
placeholder="Enter phone number"
|
||
/>
|
||
</div>
|
||
<div className="mb-4">
|
||
<label>Role</label>
|
||
<select
|
||
id="role"
|
||
className="form-select"
|
||
value={params.role}
|
||
onChange={changeValue}
|
||
>
|
||
<option value="admin">Admin</option>
|
||
<option value="partner">Partner</option>
|
||
<option value="customer">Customer</option>
|
||
</select>
|
||
</div>
|
||
<div className="mt-6 flex justify-end">
|
||
<button
|
||
type="button"
|
||
className="btn btn-outline-danger"
|
||
onClick={() => setAddUserModal(false)}
|
||
>
|
||
Cancel
|
||
</button>
|
||
<button
|
||
type="button"
|
||
className="btn btn-primary ltr:ml-4 rtl:mr-4"
|
||
onClick={saveUser}
|
||
>
|
||
{params.userid ? 'Update' : 'Add'}
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</DialogPanel>
|
||
</TransitionChild>
|
||
</div>
|
||
</div>
|
||
</Dialog>
|
||
</Transition>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default UserModule;
|