Iman_AMS/asm_app/src/pages/MaintenanceCalendarPage.tsx

258 lines
11 KiB
TypeScript

import React, { useState, useEffect, useMemo } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { FaFilter, FaChevronDown, FaChevronUp, FaTimes, FaCalendarAlt, FaMap } from 'react-icons/fa';
import MaintenanceCalendar from '../components/MaintenanceCalendar';
import LinkField from '../components/LinkField';
import useUserHospitalFilter from '../hooks/useUserHospitalFilter';
const MaintenanceCalendarPage: React.FC = () => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const { filterHospital, filtersLocked, handleHospitalChange } = useUserHospitalFilter();
// Filter states
const [filterDepartment, setFilterDepartment] = useState('');
const [filterStatus, setFilterStatus] = useState('');
const [filterAssignTo, setFilterAssignTo] = useState('');
// Load filters from URL on mount (only when hospital is not locked to user profile)
useEffect(() => {
if (filtersLocked) return;
const hospital = searchParams.get('hospital');
const status = searchParams.get('status');
if (hospital) handleHospitalChange(hospital);
if (status) setFilterStatus(status);
}, [searchParams, filtersLocked, handleHospitalChange]);
// View type states
const [viewType, setViewType] = useState<'maintenance-log' | 'ppm-planner'>('maintenance-log');
// UI states
const [isFilterExpanded, setIsFilterExpanded] = useState(false);
const [activeFilterCount, setActiveFilterCount] = useState(0);
// Update active filter count
useEffect(() => {
const count = [filterHospital, filterDepartment, filterStatus, filterAssignTo].filter(Boolean).length;
setActiveFilterCount(count);
}, [filterHospital, filterDepartment, filterStatus, filterAssignTo]);
const handleClearFilters = () => {
if (!filtersLocked) {
handleHospitalChange('');
}
setFilterDepartment('');
setFilterStatus('');
setFilterAssignTo('');
};
const hasActiveFilters = filterHospital || filterDepartment || filterStatus || filterAssignTo;
// Build filters for calendar - memoized to prevent object reference changes
const calendarFilters: Record<string, any> = useMemo(() => {
const filters: Record<string, any> = {};
if (filterHospital) filters['company'] = filterHospital;
if (filterDepartment) filters['department'] = filterDepartment;
if (filterStatus) filters['maintenance_status'] = filterStatus;
if (filterAssignTo) filters['assign_to_name'] = filterAssignTo;
return filters;
}, [filterHospital, filterDepartment, filterStatus, filterAssignTo]);
return (
<div className="flex flex-col h-screen bg-gray-50 dark:bg-gray-900 overflow-hidden">
{/* Header - Single Row on Desktop/Laptop */}
<div className="flex-shrink-0 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 px-4 py-2.5 lg:px-6">
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-3 md:gap-4">
{/* Left Side - Title */}
<div className="flex items-center gap-2.5 min-w-0">
<FaCalendarAlt className="text-blue-600 dark:text-blue-400 flex-shrink-0" size={22} />
<div className="min-w-0">
<h1 className="text-lg md:text-xl font-bold text-gray-800 dark:text-white whitespace-nowrap">
Maintenance Calendar
</h1>
</div>
</div>
{/* Right Side - All Controls in Single Row */}
<div className="flex items-end gap-2 md:gap-3 flex-wrap md:flex-nowrap">
{/* View Type Dropdown */}
<div className="relative">
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">
View Type
</label>
<select
value={viewType}
onChange={(e) => setViewType(e.target.value as 'maintenance-log' | 'ppm-planner')}
className="px-2.5 md:px-3 py-1.5 text-xs md:text-sm border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
>
<option value="maintenance-log">Maintenance Log</option>
<option value="ppm-planner">PPM Planner</option>
</select>
</div>
{/* Filters Button */}
<div className="relative">
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1 invisible">
Filters
</label>
<button
onClick={() => setIsFilterExpanded(!isFilterExpanded)}
className={`px-3 md:px-4 py-1.5 md:py-2 border rounded-lg transition-colors flex items-center gap-1.5 md:gap-2 text-xs md:text-sm ${
hasActiveFilters
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400'
: 'border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300'
}`}
>
<FaFilter size={14} />
<span className="hidden sm:inline">Filters</span>
{activeFilterCount > 0 && (
<span className="bg-blue-600 text-white rounded-full w-4 h-4 md:w-5 md:h-5 flex items-center justify-center text-[10px] md:text-xs">
{activeFilterCount}
</span>
)}
{isFilterExpanded ? <FaChevronUp size={12} /> : <FaChevronDown size={12} />}
</button>
</div>
{/* Yearly PPM Planner Button */}
{viewType === 'ppm-planner' && (
<div className="relative">
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1 invisible">
Yearly Map
</label>
<button
onClick={() => navigate('/yearly-ppm-planner')}
className="px-3 md:px-4 py-1.5 md:py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transition-colors flex items-center gap-1.5 md:gap-2 text-xs md:text-sm font-medium whitespace-nowrap"
title="View Yearly PPM Planner Map"
>
<FaMap size={14} />
<span className="hidden sm:inline">Yearly Map</span>
<span className="sm:hidden">Map</span>
</button>
</div>
)}
</div>
</div>
{/* Filter Panel */}
{isFilterExpanded && (
<div className="mt-2.5 md:mt-3 p-3 md:p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg border border-gray-200 dark:border-gray-600">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3">
{/* Hospital */}
<div className="relative z-[60]">
<LinkField
label="Hospital"
doctype="Company"
value={filterHospital}
onChange={handleHospitalChange}
placeholder="Select Hospital"
compact={true}
disabled={filtersLocked}
/>
{filterHospital && !filtersLocked && (
<button
onClick={() => handleHospitalChange('')}
className="absolute right-2 top-6 text-gray-400 hover:text-red-500 transition-colors z-10"
>
<FaTimes size={10} />
</button>
)}
</div>
{/* Department */}
<div className="relative z-[55]">
<LinkField
label="Department"
doctype="Department"
value={filterDepartment}
onChange={(val) => setFilterDepartment(val)}
placeholder="All Departments"
compact={true}
/>
{filterDepartment && (
<button
onClick={() => setFilterDepartment('')}
className="absolute right-2 top-6 text-gray-400 hover:text-red-500 transition-colors z-10"
>
<FaTimes size={10} />
</button>
)}
</div>
{/* Status */}
<div className="relative">
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">
Status
</label>
<select
value={filterStatus}
onChange={(e) => setFilterStatus(e.target.value)}
className="w-full px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
>
<option value="">All Statuses</option>
<option value="Planned">Planned</option>
<option value="Completed">Completed</option>
<option value="Overdue">Overdue</option>
<option value="Cancelled">Cancelled</option>
</select>
{filterStatus && (
<button
onClick={() => setFilterStatus('')}
className="absolute right-8 top-7 text-gray-400 hover:text-red-500 transition-colors"
>
<FaTimes size={10} />
</button>
)}
</div>
{/* Assigned To */}
<div className="relative z-[50]">
<LinkField
label="Assigned To"
doctype="User"
value={filterAssignTo}
onChange={(val) => setFilterAssignTo(val)}
placeholder="All Technicians"
compact={true}
/>
{filterAssignTo && (
<button
onClick={() => setFilterAssignTo('')}
className="absolute right-2 top-6 text-gray-400 hover:text-red-500 transition-colors z-10"
>
<FaTimes size={10} />
</button>
)}
</div>
</div>
{hasActiveFilters && (
<div className="mt-3 flex justify-end">
<button
onClick={handleClearFilters}
className="px-3 py-1.5 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 flex items-center gap-2"
>
<FaTimes />
Clear Filters
</button>
</div>
)}
</div>
)}
</div>
{/* Calendar */}
<div className="flex-1 overflow-hidden px-3 pb-3 lg:px-4 lg:pb-4">
<MaintenanceCalendar
filters={calendarFilters}
viewType={viewType}
timeView="day-month"
/>
</div>
</div>
);
};
export default MaintenanceCalendarPage;