Created
January 15, 2025 13:41
-
-
Save Usamaliaquat123/4fcb854047b28396d8fe1a655483d50a to your computer and use it in GitHub Desktop.
tablecomponent
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, useEffect } from 'react'; | |
import { | |
Box, | |
CircularProgress, | |
Menu, | |
MenuItem, | |
IconButton, | |
TextField, | |
Button, | |
} from '@mui/material'; | |
import MoreVertIcon from '@mui/icons-material/MoreVert'; | |
import SearchIcon from '@mui/icons-material/Search'; | |
import PageLoader from 'layouts/components/common/PageLoader'; | |
import JobDetailsModal from './components/JobDetailsModal'; | |
import { fetchStaffsByDepartment } from 'layouts/TasksBoard/functions/firestoreUtils'; | |
import { | |
doc, | |
updateDoc, | |
collection, | |
query, | |
where, | |
getDocs, | |
orderBy, | |
startAfter, | |
limit, | |
getCountFromServer, | |
onSnapshot, | |
Timestamp, | |
} from 'firebase/firestore'; | |
import ReasonModal from './components/ReasonModal'; | |
import CreateTechniciaJobModal from './components/CreateJobModal'; | |
import UserDetailsSidebar from './UserDetailsSidebar'; | |
import { db } from 'Utils/firebase'; | |
import { toast } from 'react-toastify'; | |
import TechnicianAssignModal from './components/TechnicianAssignModal'; | |
export default function TasksTable({ title }) { | |
const [loading, setLoading] = useState(true); | |
const [tasks, setTasks] = useState([]); | |
const [tabsTotalCount, setTabsTotalCount] = useState({ | |
new: 0, | |
followUp: 0, | |
dispatched: 0, | |
rejected: 0, | |
accepted: 0, | |
userCalled: 0, | |
onRoute: 0, | |
arrived: 0, | |
jobStarted: 0, | |
jobDone: 0, | |
declined: 0, | |
unfinished: 0, | |
}); | |
const [showLoader, setShowLoader] = useState(false); | |
const [lastDocs, setLastDocs] = useState({}); | |
const [anchorEl, setAnchorEl] = useState(null); | |
const [openMenuId, setOpenMenuId] = useState(null); | |
const [openModal, setOpenModal] = useState(false); | |
const [selectedJob, setSelectedJob] = useState(null); | |
const [openReasonModal, setOpenReasonUpModal] = useState(false); | |
const [actionForReasonModal, setActionForReasonModal] = useState(''); | |
const [openAssignModal, setOpenAssignModal] = useState(false); | |
const [taskDetails, setTaskDetails] = useState(null); | |
const [staffs, setStaffs] = useState([]); | |
const [editModal, setEditModal] = useState(false); | |
const [searchTerm, setSearchTerm] = useState(''); | |
const [selectedUserData, setSelectedUserData] = useState(null); | |
const [sidebarOpen, setSidebarOpen] = useState(false); | |
const [filteredTasks, setFilteredTasks] = useState([]); | |
const [isSearching, setIsSearching] = useState(false); | |
const [tab, setTab] = useState(0); | |
const [subTab, setSubTab] = useState(0); | |
const [currentPage, setCurrentPage] = useState(1); | |
const [number, setNumber] = useState(0); | |
const recordsPerPage = 15; | |
const [lastRec, setLastRec] = useState(null); | |
const [allRealtimeTasks, setAllRealtimeTasks] = useState([]); | |
const [sucessFullyAssigned, setSucessFullyAssigned] = useState(false); | |
const tasksCol = collection(db, 'TechnicianJobs'); | |
const subTabs = { | |
0: [ | |
{ index: 0, label: 'Progress track', key: 'new' }, | |
{ index: 1, label: 'Follow Up', key: 'followUp' }, | |
{ index: 2, label: 'Dispatched', key: 'dispatched' }, | |
{ index: 3, label: 'Rejected', key: 'rejected' }, | |
{ index: 4, label: 'Accepted', key: 'accepted' }, | |
{ index: 5, label: 'User Called', key: 'userCalled' }, | |
{ index: 6, label: 'On Route', key: 'onRoute' }, | |
{ index: 7, label: 'Arrived', key: 'arrived' }, | |
{ index: 8, label: 'Job Started', key: 'jobStarted' }, | |
{ index: 9, label: 'Done', key: 'jobDone' }, | |
{ index: 10, label: 'Declined', key: 'declined' }, | |
], | |
1: [{ index: 0, label: 'Job Done', key: 'jobDone-History' }], | |
3: [{ index: 0, label: 'Declined', key: 'declined-History' }], | |
2: [{ index: 0, label: 'Unfinished', key: 'unfinished' }], | |
}; | |
// Helper function to get timestamp for 2 days ago | |
const get24HoursAgo = () => { | |
const twoDaysAgo = new Date(); | |
twoDaysAgo.setDate(twoDaysAgo.getDate() - 1); | |
return Timestamp.fromDate(twoDaysAgo); | |
}; | |
useEffect(() => { | |
const fetchUnfinishedCount = async () => { | |
try { | |
const twoDaysAgo = get24HoursAgo(); | |
const unfinishedQuery = query( | |
tasksCol, | |
where('dateTimestamp', '<', twoDaysAgo), | |
where('statuses', 'array-contains-any', ['unFinished']), | |
orderBy('dateTimestamp', 'desc'), | |
); | |
const countSnapshot = await getCountFromServer(unfinishedQuery); | |
console.log('---?', unfinishedQuery, countSnapshot.data()); | |
setTabsTotalCount(prev => ({ | |
...prev, | |
unfinished: countSnapshot.data().count, | |
})); | |
} catch (error) { | |
console.error('Error fetching unfinished count:', error); | |
} | |
}; | |
fetchUnfinishedCount(); | |
}, []); | |
// Fetch historical counts only once or when needed | |
useEffect(() => { | |
const fetchHistoricalCounts = async () => { | |
try { | |
// Remove jobDone and declined from historical counts since they're now in real-time | |
const historicalStatuses = ['jobDone-History', 'declined-History']; | |
const counts = {}; | |
for (const status of historicalStatuses) { | |
const countQuery = query(tasksCol, where('status', '==', `${status.split('-')[0]}`)); | |
const snapshot = await getCountFromServer(countQuery); | |
counts[status] = snapshot.data().count; | |
} | |
setTabsTotalCount(prev => ({ | |
...prev, | |
...counts, | |
})); | |
setLoading(false); | |
} catch (error) { | |
setLoading(false); | |
console.error('Error fetching historical counts:', error); | |
} | |
}; | |
fetchHistoricalCounts(); | |
}, []); | |
useEffect(() => { | |
const twoDaysAgo = get24HoursAgo(); | |
const recentTasksQuery = query( | |
tasksCol, | |
where('dateTimestamp', '>=', twoDaysAgo), | |
orderBy('dateTimestamp', 'desc'), | |
); | |
const unsubscribe = onSnapshot(recentTasksQuery, snapshot => { | |
const newCounts = { | |
new: 0, | |
followUp: 0, | |
dispatched: 0, | |
rejected: 0, | |
accepted: 0, | |
userCalled: 0, | |
onRoute: 0, | |
arrived: 0, | |
jobStarted: 0, | |
jobDone: 0, | |
declined: 0, | |
}; | |
const allTasks = snapshot.docs.map(doc => { | |
const data = doc.data(); | |
if (newCounts.hasOwnProperty(data.status)) { | |
newCounts[data.status]++; | |
} | |
return { id: doc.id, data }; | |
}); | |
setAllRealtimeTasks(allTasks); | |
setTabsTotalCount(prev => ({ | |
...prev, | |
...newCounts, | |
})); | |
if (tab === 0) { | |
const filteredTasks = allTasks.filter(task => task.data.status === subTabs[0][subTab].key); | |
setNumber(filteredTasks.length); | |
setTasks(filteredTasks); | |
setFilteredTasks( | |
filteredTasks.slice((currentPage - 1) * recordsPerPage, currentPage * recordsPerPage), | |
); | |
} | |
}); | |
return () => unsubscribe(); | |
}, [tab, currentPage]); | |
// Handle tab changes | |
const handleTabChange = async (newTab, newSubTab = 0) => { | |
setShowLoader(true); | |
try { | |
setTab(newTab); | |
setSubTab(newSubTab); | |
setCurrentPage(1); | |
setIsSearching(false); | |
if (newTab === 0) { | |
// For progress track tabs, filter from real-time data | |
const filteredTasks = allRealtimeTasks.filter( | |
task => task.data.status === subTabs[0][newSubTab].key, | |
); | |
setTasks(filteredTasks); | |
setNumber(filteredTasks.length); | |
setFilteredTasks(filteredTasks.slice(0, recordsPerPage)); | |
} else { | |
// For historical tabs, fetch from Firestore | |
const statusKey = subTabs[newTab][newSubTab].key; | |
const historicalQuery = query( | |
tasksCol, | |
where('status', '==', `${statusKey.split('-')[0]}`), | |
orderBy('dateTimestamp', 'desc'), | |
limit(recordsPerPage), | |
); | |
const snapshot = await getDocs(historicalQuery); | |
const historicalTasks = snapshot.docs.map(doc => ({ | |
id: doc.id, | |
data: doc.data(), | |
})); | |
setTasks(historicalTasks); | |
setFilteredTasks(historicalTasks); | |
setNumber(tabsTotalCount[statusKey] || 0); | |
if (snapshot.docs.length > 0) { | |
setLastRec(snapshot.docs[snapshot.docs.length - 1]); | |
} | |
} | |
} catch (error) { | |
console.error('Error changing tab:', error); | |
toast.error('Error loading data'); | |
} finally { | |
setShowLoader(false); | |
} | |
}; | |
// Handle pagination | |
const handleNextPage = async () => { | |
if (currentPage * recordsPerPage >= number) return; | |
setShowLoader(true); | |
try { | |
if (tab === 0) { | |
// For progress track tabs, paginate from allRealtimeTasks | |
const filteredTasks = allRealtimeTasks.filter( | |
task => task.data.status === subTabs[0][subTab].key, | |
); | |
setFilteredTasks( | |
filteredTasks.slice(currentPage * recordsPerPage, (currentPage + 1) * recordsPerPage), | |
); | |
} else { | |
// For historical tabs, fetch next page from Firestore | |
const statusKey = subTabs[tab][subTab].key; | |
const paginationQuery = query( | |
tasksCol, | |
where('status', '==', `${statusKey.split('-')[0]}`), | |
orderBy('dateTimestamp', 'desc'), | |
startAfter(lastRec), | |
limit(recordsPerPage), | |
); | |
const snapshot = await getDocs(paginationQuery); | |
const newTasks = snapshot.docs.map(doc => ({ | |
id: doc.id, | |
data: doc.data(), | |
})); | |
setTasks(prevTasks => [...prevTasks, ...newTasks]); | |
setFilteredTasks(prevFiltered => [...prevFiltered, ...newTasks]); | |
if (snapshot.docs.length > 0) { | |
setLastRec(snapshot.docs[snapshot.docs.length - 1]); | |
} | |
} | |
setCurrentPage(prev => prev + 1); | |
} catch (error) { | |
console.error('Error fetching next page:', error); | |
} finally { | |
setShowLoader(false); | |
} | |
}; | |
const handlePrevPage = () => { | |
if (currentPage === 1) return; | |
const newPage = currentPage - 1; | |
setCurrentPage(newPage); | |
if (tab === 0) { | |
// For progress track tabs, paginate from allRealtimeTasks | |
const filteredTasks = allRealtimeTasks.filter( | |
task => task.data.status === subTabs[0][subTab].key, | |
); | |
setFilteredTasks( | |
filteredTasks.slice((newPage - 1) * recordsPerPage, newPage * recordsPerPage), | |
); | |
} else { | |
// For historical tabs, show previous page from already fetched tasks | |
setFilteredTasks(tasks.slice((newPage - 1) * recordsPerPage, newPage * recordsPerPage)); | |
} | |
}; | |
const handleClick = (event, id) => { | |
setAnchorEl(event.currentTarget); | |
setOpenMenuId(id); | |
}; | |
const handleCloseMenu = () => { | |
setAnchorEl(null); | |
setOpenMenuId(null); | |
}; | |
const handleUserClick = async phoneNumber => { | |
try { | |
setShowLoader(true); | |
const q = query(collection(db, 'Users'), where('phoneNumber', '==', phoneNumber)); | |
const userQuery = await getDocs(q); | |
if (!userQuery.empty) { | |
const user = userQuery.docs[0]; | |
setSelectedUserData(user.data()); | |
setSidebarOpen(true); | |
} else { | |
toast.error('User not found'); | |
} | |
} catch (error) { | |
console.error('Failed to fetch user details:', error); | |
} finally { | |
setShowLoader(false); | |
} | |
}; | |
const handleCloseSidebar = () => { | |
setSidebarOpen(false); | |
setSelectedUserData(null); | |
}; | |
const handleOpenModal = jobData => { | |
setSelectedJob(jobData); | |
setOpenModal(true); | |
}; | |
const handleOpenEditModal = jobData => { | |
setSelectedJob(jobData); | |
setEditModal(true); | |
}; | |
const handleCloseModal = () => { | |
setOpenModal(false); | |
setSelectedJob(null); | |
}; | |
const handleReasonModal = (action, task) => { | |
setTaskDetails(task); | |
setOpenReasonUpModal(true); | |
setActionForReasonModal(action === 'followUp' ? 'followUp' : 'decline'); | |
}; | |
const handleCloseFollowUpModal = () => { | |
setOpenReasonUpModal(false); | |
}; | |
const handleConfirmFollowUp = async (reason, taskId) => { | |
try { | |
if (actionForReasonModal === 'followUp') { | |
await updateDoc(doc(db, 'TechnicianJobs', taskId), { | |
status: 'followUp', | |
followUpReason: reason, | |
}); | |
} else { | |
await updateDoc(doc(db, 'TechnicianJobs', taskId), { | |
status: 'declined', | |
declineReason: reason, | |
assignedTechnicianID: '', | |
assignedTechnician: '', | |
}); | |
} | |
} catch (error) { | |
console.error('Failed to update document:', error); | |
} | |
}; | |
const handleAssignTechnician = async taskDetails => { | |
setOpenAssignModal(true); | |
setTaskDetails(taskDetails); | |
console.log('Assigning technicians..., quuried'); | |
try { | |
if (staffs?.length > 0) { | |
return; | |
} | |
setStaffs([]); | |
const staffArr = await fetchStaffsByDepartment('Technicians'); | |
setStaffs(staffArr); | |
} catch (error) { | |
console.error('Error fetching technicians:', error.message); | |
} | |
}; | |
const handleSearch = async () => { | |
if (!searchTerm.trim()) { | |
setFilteredTasks(tasks.slice(0, recordsPerPage)); | |
setIsSearching(false); | |
return; | |
} | |
setShowLoader(true); | |
setIsSearching(true); | |
try { | |
const searchQuery = query(tasksCol, where('phoneNumber', '==', searchTerm)); | |
const querySnapshot = await getDocs(searchQuery); | |
const searchResults = querySnapshot.docs.map(doc => ({ | |
id: doc.id, | |
data: doc.data(), | |
})); | |
setFilteredTasks(searchResults); | |
} catch (error) { | |
console.error('Error performing search:', error.message); | |
} finally { | |
setShowLoader(false); | |
} | |
}; | |
const handleResetSearch = () => { | |
setSearchTerm(''); | |
setFilteredTasks(tasks.slice(0, recordsPerPage)); | |
setIsSearching(false); | |
}; | |
// Update filtered tasks when tasks or pagination changes | |
useEffect(() => { | |
if (!isSearching) { | |
setFilteredTasks( | |
tasks.slice((currentPage - 1) * recordsPerPage, currentPage * recordsPerPage), | |
); | |
} | |
}, [tasks, currentPage, isSearching]); | |
return loading ? ( | |
<PageLoader /> | |
) : ( | |
<div className="mt-1"> | |
{/* Main Tabs */} | |
{!isSearching && ( | |
<div className="mb-4 mb-9 border-b border-gray-200 pt-1"> | |
<ul className="-mb-px flex w-full flex-wrap justify-between text-center text-sm font-medium text-gray-500"> | |
{Object.keys(subTabs).map(mainTabKey => ( | |
<li key={mainTabKey} className="flex-1"> | |
<a | |
href="#" | |
className={`${ | |
tab === parseInt(mainTabKey) | |
? 'bg-white-100 border-b-2 border-gray-800 text-gray-800' | |
: 'border-transparent bg-white text-gray-500' | |
} group inline-flex w-full items-center justify-center py-3`} | |
onClick={() => handleTabChange(parseInt(mainTabKey))} | |
> | |
{subTabs[mainTabKey][0].label} ({tabsTotalCount[subTabs[mainTabKey][0].key] || 0}) | |
</a> | |
</li> | |
))} | |
</ul> | |
</div> | |
)} | |
{/* Search Bar */} | |
<div className={`relative mb-4 mr-2 flex justify-center ${isSearching ? 'mt-15' : ''}`}> | |
<TextField | |
label="Search by Phone Number" | |
variant="outlined" | |
value={searchTerm} | |
onChange={e => setSearchTerm(e.target.value)} | |
onKeyPress={e => { | |
if (e.key === 'Enter') handleSearch(); | |
}} | |
InputProps={{ | |
style: { | |
backgroundColor: 'white', | |
fontWeight: 'bold', | |
}, | |
endAdornment: ( | |
<> | |
<IconButton onClick={handleSearch}> | |
<SearchIcon /> | |
</IconButton> | |
<Button | |
onClick={handleResetSearch} | |
variant="outlined" | |
size="small" | |
style={{ marginLeft: '10px', color: '#000' }} | |
> | |
Reset | |
</Button> | |
</> | |
), | |
}} | |
InputLabelProps={{ | |
style: { | |
fontWeight: 'bold', | |
}, | |
}} | |
className={`bg-white p-2 text-gray-900 shadow-md ${isSearching ? 'mb-8' : ''}`} | |
style={{ | |
position: 'absolute', | |
top: '-1rem', | |
zIndex: 1, | |
right: 35, | |
}} | |
/> | |
</div> | |
<div className="relative mb-9 mr-2 rounded-lg bg-white"> | |
<div className="absolute -top-9 left-0 right-0 mx-7 rounded-md bg-red-500 py-3 pl-3 text-white"> | |
{isSearching ? `Search Results for: ${searchTerm}` : title} | |
</div> | |
<div className="mb-2 py-4"></div> | |
{/* Sub Tabs */} | |
{!isSearching && ( | |
<div className="mb-4 border-b border-gray-200 bg-slate-300 pt-2"> | |
<ul className="-mb-px flex flex-wrap text-center text-sm font-medium text-gray-500"> | |
{(subTabs[tab] || []).map(({ index, label, key }) => ( | |
<li key={index} className="me-2"> | |
<a | |
href="#" | |
className={`${subTab === index ? 'border-b-2 border-red-500 text-red-800' : 'text-gray-950'} group inline-flex items-center justify-center rounded-t-lg p-4`} | |
onClick={() => handleTabChange(tab, index)} | |
> | |
{label} ({tabsTotalCount[key] || 0}) | |
</a> | |
</li> | |
))} | |
</ul> | |
</div> | |
)} | |
{/* Table */} | |
<div className="overflow-x-auto pb-6 pl-4 pr-8 shadow-xl"> | |
<table className={`min-w-full ${isSearching ? 'mt-8' : ''}`}> | |
<thead className="border-b bg-slate-100"> | |
<tr> | |
<th className="px-4 py-2 text-left text-base font-medium text-gray-900"> | |
Customer | |
</th> | |
<th className="px-4 py-2 text-left text-base font-medium text-gray-900">Mobile</th> | |
<th className="px-4 py-2 text-left text-base font-medium text-gray-900">Service</th> | |
<th className="px-4 py-2 text-left text-base font-medium text-gray-900">Branch</th> | |
<th className="px-4 py-2 text-left text-base font-medium text-gray-900"> | |
Date/Time | |
</th> | |
<th className="px-4 py-2 text-left text-base font-medium text-gray-900"> | |
Created By | |
</th> | |
<th className="px-4 py-2 text-left text-base font-medium text-gray-900"> | |
Assigned To | |
</th> | |
<th className="px-4 py-2 text-left text-base font-medium text-gray-900">Status</th> | |
<th className="px-4 py-2 text-left text-base font-medium text-gray-900">Actions</th> | |
</tr> | |
</thead> | |
<tbody> | |
{filteredTasks.length > 0 ? ( | |
filteredTasks.map(task => ( | |
<tr | |
key={task.id} | |
className="border-b bg-white transition duration-300 ease-in-out hover:cursor-pointer hover:bg-gray-100" | |
> | |
<td onClick={() => handleUserClick(task?.data?.phoneNumber)}> | |
<p className="whitespace-nowrap px-4 py-2 text-base text-blue-800"> | |
{task?.data?.customerName || 'N/A'} | |
</p> | |
</td> | |
<td className="whitespace-nowrap px-4 py-2 text-base text-gray-900"> | |
{task?.data?.phoneNumber || 'N/A'} | |
</td> | |
<td className="whitespace-nowrap px-4 py-2 text-base text-gray-900"> | |
{task?.data?.service || 'N/A'} | |
</td> | |
<td className="whitespace-nowrap px-4 py-2 text-base text-gray-900"> | |
{task?.data?.branch || 'N/A'} | |
</td> | |
<td className="whitespace-nowrap px-4 py-2 text-base text-gray-900">{`${task?.data?.bookedTime} - ${task?.data?.bookedDate}`}</td> | |
<td className="whitespace-nowrap px-4 py-2 text-base text-gray-900"> | |
{task?.data?.createdBy?.displayName || 'N/A'} | |
</td> | |
<td className="whitespace-nowrap px-4 py-2 text-base text-gray-900"> | |
{task?.data?.assignedTechnician || 'N/A'} | |
</td> | |
<td className="whitespace-nowrap px-4 py-2 text-base text-gray-900"> | |
{task?.data?.status || 'N/A'} | |
</td> | |
<td className="flex items-center justify-center gap-2 pt-2"> | |
<IconButton | |
aria-label="more" | |
aria-controls={`long-menu-${task.id}`} | |
aria-haspopup="true" | |
onClick={event => handleClick(event, task.id)} | |
> | |
<MoreVertIcon /> | |
</IconButton> | |
{openMenuId === task.id && ( | |
<Menu | |
id={`long-menu-${task.id}`} | |
anchorEl={anchorEl} | |
keepMounted | |
open={Boolean(anchorEl) && openMenuId === task.id} | |
onClose={handleCloseMenu} | |
> | |
<MenuItem | |
onClick={() => { | |
handleCloseMenu(); | |
handleOpenModal(task.data); | |
}} | |
> | |
View Details | |
</MenuItem> | |
<MenuItem | |
onClick={() => { | |
handleCloseMenu(); | |
handleOpenEditModal({ ...task.data, taskId: task.id }); | |
}} | |
> | |
Edit Details | |
</MenuItem> | |
{task?.data?.status === 'jobStarted' || | |
task?.data?.status === 'jobDone' ? null : ( | |
<MenuItem | |
onClick={() => { | |
handleCloseMenu(); | |
handleAssignTechnician({ ...task.data, taskId: task.id }); | |
}} | |
> | |
Assign Technician | |
</MenuItem> | |
)} | |
<MenuItem | |
onClick={() => | |
handleReasonModal('followUp', { ...task.data, taskId: task.id }) | |
} | |
> | |
Add to Follow Up | |
</MenuItem> | |
<MenuItem | |
onClick={() => | |
handleReasonModal('decline', { ...task.data, taskId: task.id }) | |
} | |
> | |
Decline | |
</MenuItem> | |
</Menu> | |
)} | |
</td> | |
</tr> | |
)) | |
) : ( | |
<tr> | |
<td colSpan="9" className="py-4 text-center text-gray-500"> | |
No Tasks Available | |
</td> | |
</tr> | |
)} | |
</tbody> | |
</table> | |
{showLoader && ( | |
<Box sx={{ display: 'flex', position: 'absolute', top: '50%', left: '50%' }}> | |
<CircularProgress /> | |
</Box> | |
)} | |
</div> | |
</div> | |
{!isSearching && ( | |
<div className="mr-36 mt-12 flex items-center justify-end space-x-4"> | |
<div> | |
<span className="text-base"> | |
Showing items {(currentPage - 1) * recordsPerPage + 1} - | |
{currentPage * recordsPerPage > number ? number : currentPage * recordsPerPage} out of{' '} | |
{number} | |
</span> | |
</div> | |
<div className="flex items-center gap-x-4 rounded-lg bg-blue-500 px-2 py-2 text-base text-white"> | |
<button | |
type="button" | |
onClick={handlePrevPage} | |
disabled={currentPage === 1} | |
className={`${currentPage === 1 ? 'opacity-50 hover:cursor-not-allowed' : ''} flex items-center hover:cursor-pointer hover:text-white/80`} | |
> | |
<svg | |
className="mr-0 h-5 w-5" | |
fill="currentColor" | |
viewBox="0 0 20 20" | |
xmlns="http://www.w3.org/2000/svg" | |
> | |
<path | |
fillRule="evenodd" | |
d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" | |
clipRule="evenodd" | |
/> | |
</svg> | |
<span>Previous</span> | |
</button> | |
<button | |
type="button" | |
onClick={handleNextPage} | |
disabled={currentPage * recordsPerPage >= number} | |
className={`${currentPage * recordsPerPage >= number ? 'opacity-50 hover:cursor-not-allowed' : 'hover:bg-blue-500'} flex items-center hover:cursor-pointer hover:text-white/80`} | |
> | |
<span>Next</span> | |
<svg | |
className="h-5 w-5" | |
fill="currentColor" | |
viewBox="0 0 20 20" | |
xmlns="http://www.w3.org/2000/svg" | |
> | |
<path | |
fillRule="evenodd" | |
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" | |
clipRule="evenodd" | |
/> | |
</svg> | |
</button> | |
</div> | |
</div> | |
)} | |
{/* Modals and Sidebars */} | |
{sidebarOpen && selectedUserData && ( | |
<UserDetailsSidebar userData={selectedUserData} onClose={handleCloseSidebar} /> | |
)} | |
{openAssignModal && ( | |
<TechnicianAssignModal | |
staffs={staffs} | |
taskId={taskDetails?.taskId} | |
closeStaffModal={() => setOpenAssignModal(false)} | |
role={'Technicians'} | |
setSucessFullyAssigned={setSucessFullyAssigned} | |
taskDetails={taskDetails} | |
/> | |
)} | |
<ReasonModal | |
open={openReasonModal} | |
handleClose={handleCloseFollowUpModal} | |
onConfirm={handleConfirmFollowUp} | |
heading={`Reason for ${actionForReasonModal === 'followUp' ? 'Follow Up' : 'Decline'}`} | |
actionForReasonModal={actionForReasonModal} | |
taskId={taskDetails?.taskId} | |
/> | |
{editModal && ( | |
<CreateTechniciaJobModal | |
openModal={editModal} | |
setOpenModal={setEditModal} | |
dontReload={true} | |
job={selectedJob} | |
/> | |
)} | |
<JobDetailsModal open={openModal} handleClose={handleCloseModal} jobData={selectedJob} /> | |
</div> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment