woms 3.0
This commit is contained in:
1 parent
7fb446c53a
commit
0e19df6895
73 files changed
+7907
-32290
No files matched your search
+250
-102
@@ -2,8 +2,8 @@ import { useState } from 'react'
|
||||
import { format } from 'date-fns'
|
||||
import { FaAngleDown, FaSpinner } from 'react-icons/fa6'
|
||||
import { useWorkorders } from '../hooks/useWorkorders'
|
||||
import { useCustomers } from '../hooks/useCustomers'
|
||||
import TicketRow from '../components/TicketRow'
|
||||
import TicketFilters from '../components/TicketFilters'
|
||||
import CreateTicketModal from '../components/CreateTicketModal'
|
||||
import QuickOverviewModal from '../components/QuickOverviewModal'
|
||||
|
||||
@@ -17,6 +17,7 @@ export default function TicketsPage() {
|
||||
})
|
||||
|
||||
const { workorders, loading, error, refresh, updateWorkorder, createWorkorder } = useWorkorders(filters)
|
||||
const { customers } = useCustomers()
|
||||
|
||||
const [showCreateModal, setShowCreateModal] = useState(false)
|
||||
const [showOverviewModal, setShowOverviewModal] = useState(false)
|
||||
@@ -54,104 +55,230 @@ export default function TicketsPage() {
|
||||
|
||||
return (
|
||||
<div className="main-content">
|
||||
<header className="text-center mb-2">
|
||||
<h2>Active Tickets Overview</h2>
|
||||
</header>
|
||||
<div style={{
|
||||
background: 'rgba(45, 55, 72, 0.95)',
|
||||
borderRadius: '12px',
|
||||
border: '1px solid rgba(16, 185, 129, 0.2)',
|
||||
padding: '24px',
|
||||
marginBottom: '24px',
|
||||
textAlign: 'center'
|
||||
}}>
|
||||
<h2 style={{
|
||||
color: 'var(--dark-text)',
|
||||
marginBottom: '12px',
|
||||
fontSize: '28px',
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
Active Tickets Overview
|
||||
</h2>
|
||||
<p style={{ color: '#a0aec0', marginBottom: '8px' }}>
|
||||
Work Order loading limit is set to <span style={{
|
||||
fontSize: '24px',
|
||||
color: 'var(--green-primary)',
|
||||
fontWeight: 'bold'
|
||||
}}>{limit}</span>.
|
||||
Reduce value to increase reload speed.
|
||||
</p>
|
||||
<p style={{ color: '#718096', fontSize: '12px' }}>
|
||||
Last page reload: {format(new Date(), 'dd.MM.yyyy, HH:mm:ss')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
{/* Unified Control Panel - All in One */}
|
||||
<div style={{
|
||||
background: 'rgba(45, 55, 72, 0.95)',
|
||||
borderRadius: '12px',
|
||||
border: '1px solid rgba(16, 185, 129, 0.2)',
|
||||
overflow: 'hidden',
|
||||
marginBottom: '24px'
|
||||
}}>
|
||||
{/* Extended Filters + Quick Selection - TOP */}
|
||||
<div style={{
|
||||
padding: '20px',
|
||||
borderBottom: '1px solid rgba(16, 185, 129, 0.2)'
|
||||
}}>
|
||||
{/* Main Filter Row */}
|
||||
<div style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))',
|
||||
gap: '12px',
|
||||
alignItems: 'center',
|
||||
marginBottom: '16px'
|
||||
}}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="WOID"
|
||||
className="form-control"
|
||||
style={{ margin: 0 }}
|
||||
value={filters.woid || ''}
|
||||
onChange={(e) => setFilters({ ...filters, woid: e.target.value })}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Created Date"
|
||||
className="form-control"
|
||||
style={{ margin: 0 }}
|
||||
value={filters.createdDate || ''}
|
||||
onChange={(e) => setFilters({ ...filters, createdDate: e.target.value })}
|
||||
/>
|
||||
<select
|
||||
className="form-control"
|
||||
style={{ margin: 0 }}
|
||||
value={filters.type?.[0] || ''}
|
||||
onChange={(e) => setFilters({ ...filters, type: e.target.value ? [e.target.value] : [] })}
|
||||
>
|
||||
<option value="">Type / Location</option>
|
||||
<option>Home Office</option>
|
||||
<option>Holidays</option>
|
||||
<option>Trip</option>
|
||||
<option>Supportrequest</option>
|
||||
<option>Change Request</option>
|
||||
<option>Maintenance</option>
|
||||
<option>Project</option>
|
||||
<option>Procurement</option>
|
||||
<option>Emergency Call</option>
|
||||
</select>
|
||||
<select
|
||||
className="form-control"
|
||||
style={{ margin: 0 }}
|
||||
value={filters.system?.[0] || ''}
|
||||
onChange={(e) => setFilters({ ...filters, system: e.target.value ? [e.target.value] : [] })}
|
||||
>
|
||||
<option value="">System</option>
|
||||
<option>Client</option>
|
||||
<option>Server</option>
|
||||
<option>Network</option>
|
||||
<option>EDI</option>
|
||||
<option>TOS</option>
|
||||
<option>Reports</option>
|
||||
<option>n/a</option>
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Customer"
|
||||
className="form-control"
|
||||
style={{ margin: 0 }}
|
||||
value={filters.customer || ''}
|
||||
onChange={(e) => setFilters({ ...filters, customer: e.target.value })}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Topic / User"
|
||||
className="form-control"
|
||||
style={{ margin: 0 }}
|
||||
value={filters.userTopic || ''}
|
||||
onChange={(e) => setFilters({ ...filters, userTopic: e.target.value })}
|
||||
/>
|
||||
<select
|
||||
className="form-control"
|
||||
style={{ margin: 0 }}
|
||||
value={filters.priority?.[0] ?? ''}
|
||||
onChange={(e) => setFilters({ ...filters, priority: e.target.value ? [parseInt(e.target.value)] : [] })}
|
||||
>
|
||||
<option value="">Priority</option>
|
||||
<option value="0">None</option>
|
||||
<option value="1">Low</option>
|
||||
<option value="2">Medium</option>
|
||||
<option value="3">High</option>
|
||||
<option value="4">Critical</option>
|
||||
</select>
|
||||
<button
|
||||
className="btn btn-green"
|
||||
onClick={handleApplyFilters}
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
Apply!
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<hr className="mb-2" />
|
||||
{/* Quick Selection Buttons */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
gap: '8px',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap',
|
||||
paddingTop: '16px',
|
||||
borderTop: '1px solid rgba(16, 185, 129, 0.1)'
|
||||
}}>
|
||||
<button
|
||||
className="btn btn-green"
|
||||
onClick={() => { setFilters(prev => ({ ...prev, type: ['Procurement'] })); handleApplyFilters(); }}
|
||||
>
|
||||
Procurements
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-green"
|
||||
onClick={() => { setFilters(prev => ({ ...prev, priority: [4] })); handleApplyFilters(); }}
|
||||
>
|
||||
Criticals
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-green"
|
||||
onClick={() => { setFilters(prev => ({ ...prev, priority: [3] })); handleApplyFilters(); }}
|
||||
>
|
||||
Highs
|
||||
</button>
|
||||
<div style={{
|
||||
width: '1px',
|
||||
height: '32px',
|
||||
background: 'rgba(16, 185, 129, 0.3)',
|
||||
margin: '0 8px'
|
||||
}}></div>
|
||||
<button
|
||||
className="btn btn-green"
|
||||
onClick={() => { setLimit(10); setFilters(prev => ({ ...prev, limit: 10 })) }}
|
||||
>
|
||||
10
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-green"
|
||||
onClick={() => { setLimit(25); setFilters(prev => ({ ...prev, limit: 25 })) }}
|
||||
>
|
||||
25
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Slider + Action Buttons - BOTTOM */}
|
||||
<div style={{
|
||||
padding: '20px',
|
||||
display: 'flex',
|
||||
gap: '16px',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap'
|
||||
}}>
|
||||
<div style={{ flex: '1', minWidth: '200px' }}>
|
||||
<input
|
||||
type="range"
|
||||
min="5"
|
||||
max="50"
|
||||
value={limit}
|
||||
className="slider"
|
||||
onChange={handleLimitChange}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '8px', flexWrap: 'nowrap' }}>
|
||||
<button
|
||||
className="btn btn-dark"
|
||||
onClick={() => setShowCreateModal(true)}
|
||||
style={{ whiteSpace: 'nowrap' }}
|
||||
>
|
||||
CREATE NEW TICKET
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-dark"
|
||||
onClick={() => setShowOverviewModal(true)}
|
||||
style={{ whiteSpace: 'nowrap' }}
|
||||
>
|
||||
QUICK OVERVIEW
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
@@ -184,26 +311,47 @@ export default function TicketsPage() {
|
||||
</table>
|
||||
|
||||
{workorders.length > 0 && workorders.length >= limit && (
|
||||
<div className="text-center mt-2">
|
||||
<div style={{ textAlign: 'center', marginTop: '24px' }}>
|
||||
<button
|
||||
className="btn"
|
||||
style={{ background: 'none', border: 'none' }}
|
||||
className="btn btn-green"
|
||||
style={{
|
||||
padding: '16px 32px',
|
||||
fontSize: '16px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
margin: '0 auto'
|
||||
}}
|
||||
onClick={handleLoadMore}
|
||||
>
|
||||
<FaAngleDown size={48} />
|
||||
Load More <FaAngleDown size={20} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="text-center text-grey mt-2">
|
||||
Summary: Listed a total of {workorders.length} Workorders.
|
||||
<br />EOL =)
|
||||
</p>
|
||||
<div style={{
|
||||
background: 'rgba(45, 55, 72, 0.95)',
|
||||
borderRadius: '12px',
|
||||
border: '1px solid rgba(16, 185, 129, 0.2)',
|
||||
padding: '16px',
|
||||
marginTop: '24px',
|
||||
textAlign: 'center',
|
||||
color: '#a0aec0'
|
||||
}}>
|
||||
<p style={{ margin: 0 }}>
|
||||
Summary: Listed a total of <span style={{
|
||||
color: 'var(--green-primary)',
|
||||
fontWeight: 'bold'
|
||||
}}>{workorders.length}</span> Workorders.
|
||||
<br />EOL =)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<CreateTicketModal
|
||||
isOpen={showCreateModal}
|
||||
onClose={() => setShowCreateModal(false)}
|
||||
onCreate={handleCreate}
|
||||
customers={customers}
|
||||
/>
|
||||
|
||||
<QuickOverviewModal
|
||||
|
||||
Reference in new issue
Block a user