main page
This commit is contained in:
47
src/pages/AssetsPage.jsx
Normal file
47
src/pages/AssetsPage.jsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { useState } from 'react'
|
||||
import { FaServer, FaDesktop, FaPrint, FaNetworkWired } from 'react-icons/fa6'
|
||||
|
||||
export default function AssetsPage() {
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
|
||||
// Placeholder data - would come from Appwrite in production
|
||||
const assetCategories = [
|
||||
{ icon: FaServer, name: 'Servers', count: 0 },
|
||||
{ icon: FaDesktop, name: 'Workstations', count: 0 },
|
||||
{ icon: FaPrint, name: 'Printers', count: 0 },
|
||||
{ icon: FaNetworkWired, name: 'Network Devices', count: 0 }
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="main-content">
|
||||
<header className="text-center mb-2">
|
||||
<h2>Assets Management</h2>
|
||||
</header>
|
||||
|
||||
<div className="search-bar mb-2">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search assets..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="input"
|
||||
style={{ width: '100%', maxWidth: '400px' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="assets-grid">
|
||||
{assetCategories.map(({ icon: Icon, name, count }) => (
|
||||
<div key={name} className="asset-card">
|
||||
<Icon size={48} className="text-teal" />
|
||||
<h4>{name}</h4>
|
||||
<span className="asset-count">{count}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="text-center mt-4 text-grey">
|
||||
<p>Asset management module - Connect to Appwrite to manage your IT assets.</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
73
src/pages/DashboardPage.jsx
Normal file
73
src/pages/DashboardPage.jsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { databases, DATABASE_ID, COLLECTIONS, Query } from '../lib/appwrite'
|
||||
import { FaTicket, FaClipboardList, FaClock, FaCircleCheck } from 'react-icons/fa6'
|
||||
|
||||
export default function DashboardPage() {
|
||||
const [stats, setStats] = useState({
|
||||
totalTickets: 0,
|
||||
openTickets: 0,
|
||||
closedTickets: 0,
|
||||
pendingTickets: 0
|
||||
})
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchStats() {
|
||||
try {
|
||||
const [total, open, closed, pending] = await Promise.all([
|
||||
databases.listDocuments(DATABASE_ID, COLLECTIONS.WORKORDERS, [Query.limit(1)]),
|
||||
databases.listDocuments(DATABASE_ID, COLLECTIONS.WORKORDERS, [Query.equal('status', 'Open'), Query.limit(1)]),
|
||||
databases.listDocuments(DATABASE_ID, COLLECTIONS.WORKORDERS, [Query.equal('status', 'Closed'), Query.limit(1)]),
|
||||
databases.listDocuments(DATABASE_ID, COLLECTIONS.WORKORDERS, [Query.equal('status', 'Awaiting'), Query.limit(1)])
|
||||
])
|
||||
|
||||
setStats({
|
||||
totalTickets: total.total,
|
||||
openTickets: open.total,
|
||||
closedTickets: closed.total,
|
||||
pendingTickets: pending.total
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('Error fetching stats:', err)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
fetchStats()
|
||||
}, [])
|
||||
|
||||
const StatCard = ({ icon: Icon, label, value, color }) => (
|
||||
<div className="stat-card" style={{ borderLeft: `4px solid ${color}` }}>
|
||||
<div className="stat-icon" style={{ color }}>
|
||||
<Icon size={32} />
|
||||
</div>
|
||||
<div className="stat-info">
|
||||
<span className="stat-value">{loading ? '...' : value}</span>
|
||||
<span className="stat-label">{label}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="main-content">
|
||||
<header className="text-center mb-2">
|
||||
<h2>Dashboard</h2>
|
||||
</header>
|
||||
|
||||
<div className="stats-grid">
|
||||
<StatCard icon={FaTicket} label="Total Tickets" value={stats.totalTickets} color="#2196F3" />
|
||||
<StatCard icon={FaClipboardList} label="Open Tickets" value={stats.openTickets} color="#4CAF50" />
|
||||
<StatCard icon={FaClock} label="Pending" value={stats.pendingTickets} color="#FF9800" />
|
||||
<StatCard icon={FaCircleCheck} label="Closed" value={stats.closedTickets} color="#9E9E9E" />
|
||||
</div>
|
||||
|
||||
<div className="dashboard-section mt-4">
|
||||
<h3>Quick Actions</h3>
|
||||
<div className="quick-actions">
|
||||
<a href="/tickets" className="btn btn-teal">View All Tickets</a>
|
||||
<a href="/reports" className="btn btn-dark">Generate Report</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
66
src/pages/DocsPage.jsx
Normal file
66
src/pages/DocsPage.jsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { FaBook, FaCircleQuestion, FaKeyboard } from 'react-icons/fa6'
|
||||
|
||||
export default function DocsPage() {
|
||||
const sections = [
|
||||
{
|
||||
icon: FaBook,
|
||||
title: 'Getting Started',
|
||||
content: 'Learn how to create and manage work orders in WOMS 2.0.'
|
||||
},
|
||||
{
|
||||
icon: FaCircleQuestion,
|
||||
title: 'FAQ',
|
||||
content: 'Frequently asked questions about the system.'
|
||||
},
|
||||
{
|
||||
icon: FaKeyboard,
|
||||
title: 'Keyboard Shortcuts',
|
||||
content: 'Speed up your workflow with keyboard shortcuts.'
|
||||
}
|
||||
]
|
||||
|
||||
const shortcuts = [
|
||||
{ key: 'N', action: 'New Ticket' },
|
||||
{ key: 'F', action: 'Focus Search' },
|
||||
{ key: 'R', action: 'Refresh List' },
|
||||
{ key: 'Esc', action: 'Close Modal' }
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="main-content">
|
||||
<header className="text-center mb-2">
|
||||
<h2>Documentation</h2>
|
||||
</header>
|
||||
|
||||
<div className="docs-grid">
|
||||
{sections.map(({ icon: Icon, title, content }) => (
|
||||
<div key={title} className="doc-card">
|
||||
<Icon size={32} className="text-teal" />
|
||||
<h4>{title}</h4>
|
||||
<p className="text-grey">{content}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="shortcuts-section mt-4">
|
||||
<h3 className="text-center">Keyboard Shortcuts</h3>
|
||||
<table className="table" style={{ maxWidth: '400px', margin: '0 auto' }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{shortcuts.map(({ key, action }) => (
|
||||
<tr key={key}>
|
||||
<td><kbd>{key}</kbd></td>
|
||||
<td>{action}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
85
src/pages/LoginPage.jsx
Normal file
85
src/pages/LoginPage.jsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useAuth } from '../context/AuthContext'
|
||||
|
||||
export default function LoginPage() {
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [error, setError] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const { login } = useAuth()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
setError('')
|
||||
setLoading(true)
|
||||
|
||||
const result = await login(email, password)
|
||||
|
||||
if (result.success) {
|
||||
navigate('/tickets')
|
||||
} else {
|
||||
setError(result.error || 'Login failed')
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
minHeight: '100vh',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background: '#f1f1f1'
|
||||
}}>
|
||||
<div className="card" style={{ width: '400px' }}>
|
||||
<div className="card-header text-center">
|
||||
<h2>NetWEB Systems WOMS 2.0</h2>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<form onSubmit={handleSubmit}>
|
||||
{error && (
|
||||
<div className="bg-red text-white p-1 mb-2" style={{ borderRadius: '4px' }}>
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
className="form-control"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="form-label">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
className="form-control"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-green"
|
||||
style={{ width: '100%' }}
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? 'Logging in...' : 'Login'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
46
src/pages/PlanboardPage.jsx
Normal file
46
src/pages/PlanboardPage.jsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { useState } from 'react'
|
||||
import { format, addDays, startOfWeek } from 'date-fns'
|
||||
|
||||
export default function PlanboardPage() {
|
||||
const [currentWeek, setCurrentWeek] = useState(startOfWeek(new Date(), { weekStartsOn: 1 }))
|
||||
|
||||
const weekDays = Array.from({ length: 7 }, (_, i) => addDays(currentWeek, i))
|
||||
|
||||
const navigateWeek = (direction) => {
|
||||
setCurrentWeek(prev => addDays(prev, direction * 7))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="main-content">
|
||||
<header className="text-center mb-2">
|
||||
<h2>Planboard</h2>
|
||||
</header>
|
||||
|
||||
<div className="planboard-nav text-center mb-2">
|
||||
<button className="btn btn-dark" onClick={() => navigateWeek(-1)}>← Previous Week</button>
|
||||
<span className="mx-2">
|
||||
{format(currentWeek, 'dd.MM.yyyy')} - {format(addDays(currentWeek, 6), 'dd.MM.yyyy')}
|
||||
</span>
|
||||
<button className="btn btn-dark" onClick={() => navigateWeek(1)}>Next Week →</button>
|
||||
</div>
|
||||
|
||||
<div className="planboard-grid">
|
||||
{weekDays.map(day => (
|
||||
<div key={day.toISOString()} className="planboard-day">
|
||||
<div className="day-header">
|
||||
<strong>{format(day, 'EEEE')}</strong>
|
||||
<span>{format(day, 'dd.MM')}</span>
|
||||
</div>
|
||||
<div className="day-content">
|
||||
<p className="text-grey text-small">No tasks scheduled</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="text-center mt-4 text-grey">
|
||||
<p>Drag and drop tickets to schedule them on the planboard.</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
39
src/pages/ProjectsPage.jsx
Normal file
39
src/pages/ProjectsPage.jsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { useState } from 'react'
|
||||
import { FaFolder, FaPlus } from 'react-icons/fa6'
|
||||
|
||||
export default function ProjectsPage() {
|
||||
const [projects] = useState([])
|
||||
|
||||
return (
|
||||
<div className="main-content">
|
||||
<header className="text-center mb-2">
|
||||
<h2>Projects</h2>
|
||||
</header>
|
||||
|
||||
<div className="text-center mb-2">
|
||||
<button className="btn btn-teal">
|
||||
<FaPlus /> New Project
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{projects.length === 0 ? (
|
||||
<div className="text-center p-4">
|
||||
<FaFolder size={64} className="text-grey" />
|
||||
<p className="text-grey mt-2">No projects yet. Create your first project to get started.</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="projects-grid">
|
||||
{projects.map(project => (
|
||||
<div key={project.$id} className="project-card">
|
||||
<h4>{project.name}</h4>
|
||||
<p className="text-grey">{project.description}</p>
|
||||
<div className="project-meta">
|
||||
<span>{project.ticketCount || 0} tickets</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
82
src/pages/ReportsPage.jsx
Normal file
82
src/pages/ReportsPage.jsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import { useState } from 'react'
|
||||
import { format, subDays } from 'date-fns'
|
||||
import { FaFileExport, FaChartBar } from 'react-icons/fa6'
|
||||
|
||||
export default function ReportsPage() {
|
||||
const [dateRange, setDateRange] = useState({
|
||||
from: format(subDays(new Date(), 30), 'yyyy-MM-dd'),
|
||||
to: format(new Date(), 'yyyy-MM-dd')
|
||||
})
|
||||
const [reportType, setReportType] = useState('tickets')
|
||||
|
||||
const reportTypes = [
|
||||
{ value: 'tickets', label: 'Ticket Summary' },
|
||||
{ value: 'performance', label: 'Performance Report' },
|
||||
{ value: 'customer', label: 'Customer Report' },
|
||||
{ value: 'technician', label: 'Technician Report' }
|
||||
]
|
||||
|
||||
const handleGenerateReport = () => {
|
||||
// Would generate report from Appwrite data
|
||||
alert(`Generating ${reportType} report from ${dateRange.from} to ${dateRange.to}`)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="main-content">
|
||||
<header className="text-center mb-2">
|
||||
<h2>Reports</h2>
|
||||
</header>
|
||||
|
||||
<div className="report-filters card p-2">
|
||||
<div className="filter-row">
|
||||
<label>
|
||||
Report Type:
|
||||
<select
|
||||
value={reportType}
|
||||
onChange={(e) => setReportType(e.target.value)}
|
||||
className="input ml-1"
|
||||
>
|
||||
{reportTypes.map(type => (
|
||||
<option key={type.value} value={type.value}>{type.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="filter-row mt-2">
|
||||
<label>
|
||||
From:
|
||||
<input
|
||||
type="date"
|
||||
value={dateRange.from}
|
||||
onChange={(e) => setDateRange(prev => ({ ...prev, from: e.target.value }))}
|
||||
className="input ml-1"
|
||||
/>
|
||||
</label>
|
||||
<label className="ml-2">
|
||||
To:
|
||||
<input
|
||||
type="date"
|
||||
value={dateRange.to}
|
||||
onChange={(e) => setDateRange(prev => ({ ...prev, to: e.target.value }))}
|
||||
className="input ml-1"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="text-center mt-2">
|
||||
<button className="btn btn-teal" onClick={handleGenerateReport}>
|
||||
<FaChartBar /> Generate Report
|
||||
</button>
|
||||
<button className="btn btn-dark ml-1">
|
||||
<FaFileExport /> Export PDF
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center mt-4 text-grey">
|
||||
<p>Select report parameters and click Generate to view your report.</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
216
src/pages/TicketsPage.jsx
Normal file
216
src/pages/TicketsPage.jsx
Normal file
@@ -0,0 +1,216 @@
|
||||
import { useState } from 'react'
|
||||
import { format } from 'date-fns'
|
||||
import { FaAngleDown, FaSpinner } from 'react-icons/fa6'
|
||||
import { useWorkorders } from '../hooks/useWorkorders'
|
||||
import TicketRow from '../components/TicketRow'
|
||||
import TicketFilters from '../components/TicketFilters'
|
||||
import CreateTicketModal from '../components/CreateTicketModal'
|
||||
import QuickOverviewModal from '../components/QuickOverviewModal'
|
||||
|
||||
export default function TicketsPage() {
|
||||
const [limit, setLimit] = useState(10)
|
||||
const [filters, setFilters] = useState({
|
||||
status: ['Open', 'Occupied', 'Assigned', 'Awaiting', 'Added Info'],
|
||||
type: [],
|
||||
priority: [],
|
||||
limit: 10
|
||||
})
|
||||
|
||||
const { workorders, loading, error, refresh, updateWorkorder, createWorkorder } = useWorkorders(filters)
|
||||
|
||||
const [showCreateModal, setShowCreateModal] = useState(false)
|
||||
const [showOverviewModal, setShowOverviewModal] = useState(false)
|
||||
|
||||
const handleFilterChange = (newFilters) => {
|
||||
setFilters({ ...newFilters, limit })
|
||||
}
|
||||
|
||||
const handleApplyFilters = () => {
|
||||
refresh()
|
||||
}
|
||||
|
||||
const handleLimitChange = (e) => {
|
||||
const newLimit = parseInt(e.target.value)
|
||||
setLimit(newLimit)
|
||||
setFilters(prev => ({ ...prev, limit: newLimit }))
|
||||
}
|
||||
|
||||
const handleUpdate = async (id, data) => {
|
||||
await updateWorkorder(id, data)
|
||||
}
|
||||
|
||||
const handleCreate = async (data) => {
|
||||
const result = await createWorkorder(data)
|
||||
if (result.success) {
|
||||
setShowCreateModal(false)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const handleLoadMore = () => {
|
||||
setLimit(prev => prev + 10)
|
||||
setFilters(prev => ({ ...prev, limit: prev.limit + 10 }))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="main-content">
|
||||
<header className="text-center mb-2">
|
||||
<h2>Active Tickets Overview</h2>
|
||||
</header>
|
||||
|
||||
<p className="text-center text-grey">
|
||||
Work Order loading limit is set to <span className="text-xlarge">{limit}</span>.
|
||||
Reduce value to increase reload speed.
|
||||
</p>
|
||||
<p className="text-center text-grey text-small">
|
||||
Last page reload: {format(new Date(), 'dd.MM.yyyy, HH:mm:ss')}
|
||||
</p>
|
||||
|
||||
<hr className="mb-2" />
|
||||
|
||||
<table className="table table-hover">
|
||||
<tbody>
|
||||
<TicketFilters
|
||||
filters={filters}
|
||||
onChange={handleFilterChange}
|
||||
onApply={handleApplyFilters}
|
||||
/>
|
||||
|
||||
<tr>
|
||||
<td colSpan={10} className="bg-dark-grey">
|
||||
<div className="slider-container text-center">
|
||||
<input
|
||||
type="range"
|
||||
min="5"
|
||||
max="50"
|
||||
value={limit}
|
||||
className="slider"
|
||||
onChange={handleLimitChange}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colSpan={10} className="text-center p-1">
|
||||
<button
|
||||
className="btn btn-teal"
|
||||
onClick={() => setFilters(prev => ({ ...prev, type: ['Procurement'] }))}
|
||||
>
|
||||
Procurements
|
||||
</button>
|
||||
{' '}
|
||||
<button
|
||||
className="btn btn-teal"
|
||||
onClick={() => setFilters(prev => ({ ...prev, priority: [4] }))}
|
||||
>
|
||||
Criticals
|
||||
</button>
|
||||
{' '}
|
||||
<button
|
||||
className="btn btn-teal"
|
||||
onClick={() => setFilters(prev => ({ ...prev, priority: [3] }))}
|
||||
>
|
||||
Highs
|
||||
</button>
|
||||
{' '}
|
||||
<button
|
||||
className="btn btn-teal"
|
||||
onClick={() => { setLimit(10); setFilters(prev => ({ ...prev, limit: 10 })) }}
|
||||
>
|
||||
10
|
||||
</button>
|
||||
{' '}
|
||||
<button
|
||||
className="btn btn-teal"
|
||||
onClick={() => { setLimit(25); setFilters(prev => ({ ...prev, limit: 25 })) }}
|
||||
>
|
||||
25
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colSpan={10} className="text-center p-1">
|
||||
<button
|
||||
className="btn btn-dark"
|
||||
onClick={() => setShowCreateModal(true)}
|
||||
>
|
||||
CREATE NEW TICKET
|
||||
</button>
|
||||
{' '}
|
||||
<button
|
||||
className="btn btn-dark"
|
||||
onClick={() => setShowOverviewModal(true)}
|
||||
>
|
||||
QUICK OVERVIEW
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr className="spacer">
|
||||
<td colSpan={10} style={{ height: '16px' }}></td>
|
||||
</tr>
|
||||
|
||||
{loading ? (
|
||||
<tr>
|
||||
<td colSpan={10} className="text-center p-2">
|
||||
<FaSpinner className="spinner" size={32} />
|
||||
<p>Loading...</p>
|
||||
</td>
|
||||
</tr>
|
||||
) : error ? (
|
||||
<tr>
|
||||
<td colSpan={10} className="text-center p-2 text-red">
|
||||
Error: {error}
|
||||
</td>
|
||||
</tr>
|
||||
) : workorders.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={10} className="text-center p-2">
|
||||
No tickets found matching your filters.
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
workorders.map(ticket => (
|
||||
<TicketRow
|
||||
key={ticket.$id}
|
||||
ticket={ticket}
|
||||
onUpdate={handleUpdate}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{workorders.length > 0 && workorders.length >= limit && (
|
||||
<div className="text-center mt-2">
|
||||
<button
|
||||
className="btn"
|
||||
style={{ background: 'none', border: 'none' }}
|
||||
onClick={handleLoadMore}
|
||||
>
|
||||
<FaAngleDown size={48} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="text-center text-grey mt-2">
|
||||
Summary: Listed a total of {workorders.length} Workorders.
|
||||
<br />EOL =)
|
||||
</p>
|
||||
|
||||
<CreateTicketModal
|
||||
isOpen={showCreateModal}
|
||||
onClose={() => setShowCreateModal(false)}
|
||||
onCreate={handleCreate}
|
||||
/>
|
||||
|
||||
<QuickOverviewModal
|
||||
isOpen={showOverviewModal}
|
||||
onClose={() => setShowOverviewModal(false)}
|
||||
workorders={workorders}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user