2025-11-04 21:41:22 +05:30

336 lines
15 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, 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: '' }); // dont 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;