fix
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.6d8e0960.css",
|
||||
"main.js": "/static/js/main.34bdd0b7.js",
|
||||
"main.js": "/static/js/main.dd937a88.js",
|
||||
"static/js/787.c4e7f8f9.chunk.js": "/static/js/787.c4e7f8f9.chunk.js",
|
||||
"static/media/landing.png": "/static/media/landing.d4c6072db7a67dff6a78.png",
|
||||
"index.html": "/index.html",
|
||||
"main.6d8e0960.css.map": "/static/css/main.6d8e0960.css.map",
|
||||
"main.34bdd0b7.js.map": "/static/js/main.34bdd0b7.js.map",
|
||||
"main.dd937a88.js.map": "/static/js/main.dd937a88.js.map",
|
||||
"787.c4e7f8f9.chunk.js.map": "/static/js/787.c4e7f8f9.chunk.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.6d8e0960.css",
|
||||
"static/js/main.34bdd0b7.js"
|
||||
"static/js/main.dd937a88.js"
|
||||
]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.34bdd0b7.js"></script><link href="/static/css/main.6d8e0960.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.dd937a88.js"></script><link href="/static/css/main.6d8e0960.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
client/.DS_Store
vendored
BIN
client/.DS_Store
vendored
Binary file not shown.
@@ -90,6 +90,25 @@ const EventsCalendar = () => {
|
||||
const [newReminderTitleCategory, setNewReminderTitleCategory] = useState('');
|
||||
const [newReminderAssociatedEntity, setNewReminderAssociatedEntity] = useState(null);
|
||||
|
||||
// Edit modal state (for non-medical tabs)
|
||||
const [showEditModal, setShowEditModal] = useState(false);
|
||||
const [editingEventId, setEditingEventId] = useState(null);
|
||||
const [editEventTitle, setEditEventTitle] = useState('');
|
||||
const [editEventStartDateTime, setEditEventStartDateTime] = useState(null);
|
||||
const [editEventLocation, setEditEventLocation] = useState('');
|
||||
const [editEventRecurring, setEditEventRecurring] = useState('');
|
||||
// Activity edit fields
|
||||
const [editActivityCategory, setEditActivityCategory] = useState('');
|
||||
// Attendance edit fields
|
||||
const [editAttendanceCustomer, setEditAttendanceCustomer] = useState(null);
|
||||
const [editAttendanceReason, setEditAttendanceReason] = useState('');
|
||||
// Meal Plan edit fields
|
||||
const [editMealType, setEditMealType] = useState('');
|
||||
const [editMealIngredients, setEditMealIngredients] = useState('');
|
||||
// Important Dates edit fields
|
||||
const [editReminderTitleCategory, setEditReminderTitleCategory] = useState('');
|
||||
const [editReminderAssociatedEntity, setEditReminderAssociatedEntity] = useState(null);
|
||||
|
||||
// Helper function to format name from "lastname, firstname" to "firstname lastname"
|
||||
const formatFullName = (name) => {
|
||||
if (!name) return '';
|
||||
@@ -458,6 +477,115 @@ const EventsCalendar = () => {
|
||||
});
|
||||
}
|
||||
|
||||
// Edit modal functions (for non-medical tabs)
|
||||
const openEditModal = (calendarEvent) => {
|
||||
const eventData = allEvents.find(e => e.id === calendarEvent.id) || calendarEvent;
|
||||
setEditingEventId(eventData.id);
|
||||
setEditEventStartDateTime(eventData.start_time ? new Date(eventData.start_time) : new Date());
|
||||
|
||||
if (currentTab === 'activitiesCalendar') {
|
||||
setEditEventTitle(eventData.title || '');
|
||||
setEditActivityCategory(eventData.activity_category || eventData.color || '');
|
||||
setEditEventLocation(eventData.event_location || '');
|
||||
setEditEventRecurring(eventData.rrule || '');
|
||||
} else if (currentTab === 'incidentsCalendar') {
|
||||
setEditAttendanceCustomer(eventData.target_uuid ? { value: eventData.target_uuid, label: eventData.target_name } : null);
|
||||
setEditAttendanceReason(eventData.description || '');
|
||||
} else if (currentTab === 'mealPlanCalendar') {
|
||||
setEditEventTitle(eventData.title || '');
|
||||
setEditMealType(eventData.meal_type || '');
|
||||
setEditMealIngredients(eventData.ingredients || '');
|
||||
setEditEventRecurring(eventData.rrule || '');
|
||||
} else if (currentTab === 'reminderDatesCalendar') {
|
||||
setEditReminderTitleCategory(eventData.event_reminder_type || '');
|
||||
setEditReminderAssociatedEntity(eventData.target_uuid ? { value: eventData.target_uuid, label: eventData.target_name } : null);
|
||||
setEditEventRecurring(eventData.rrule || '');
|
||||
}
|
||||
|
||||
setShowEditModal(true);
|
||||
};
|
||||
|
||||
const closeEditModal = () => {
|
||||
setShowEditModal(false);
|
||||
setEditingEventId(null);
|
||||
setEditEventTitle('');
|
||||
setEditEventStartDateTime(null);
|
||||
setEditEventLocation('');
|
||||
setEditEventRecurring('');
|
||||
setEditActivityCategory('');
|
||||
setEditAttendanceCustomer(null);
|
||||
setEditAttendanceReason('');
|
||||
setEditMealType('');
|
||||
setEditMealIngredients('');
|
||||
setEditReminderTitleCategory('');
|
||||
setEditReminderAssociatedEntity(null);
|
||||
};
|
||||
|
||||
const handleEditSave = () => {
|
||||
const userName = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name;
|
||||
|
||||
let data = {
|
||||
edit_by: userName,
|
||||
edit_date: new Date(),
|
||||
};
|
||||
|
||||
if (currentTab === 'activitiesCalendar') {
|
||||
data = {
|
||||
...data,
|
||||
title: editEventTitle,
|
||||
start_time: editEventStartDateTime,
|
||||
stop_time: editEventStartDateTime,
|
||||
activity_category: editActivityCategory,
|
||||
color: editActivityCategory,
|
||||
event_location: editEventLocation,
|
||||
rrule: editEventRecurring || undefined,
|
||||
};
|
||||
} else if (currentTab === 'incidentsCalendar') {
|
||||
data = {
|
||||
...data,
|
||||
title: editAttendanceCustomer?.label ? `Attendance Note - ${editAttendanceCustomer.label}` : 'Attendance Note',
|
||||
start_time: editEventStartDateTime,
|
||||
stop_time: editEventStartDateTime,
|
||||
target_type: 'customer',
|
||||
target_uuid: editAttendanceCustomer?.value,
|
||||
target_name: editAttendanceCustomer?.label,
|
||||
description: editAttendanceReason,
|
||||
};
|
||||
} else if (currentTab === 'mealPlanCalendar') {
|
||||
data = {
|
||||
...data,
|
||||
title: editEventTitle,
|
||||
start_time: editEventStartDateTime,
|
||||
stop_time: editEventStartDateTime,
|
||||
meal_type: editMealType,
|
||||
color: editMealType === 'breakfast' ? 'brown' : (editMealType === 'lunch' ? 'green' : 'red'),
|
||||
ingredients: editMealIngredients,
|
||||
rrule: editEventRecurring || undefined,
|
||||
};
|
||||
} else if (currentTab === 'reminderDatesCalendar') {
|
||||
const isMemberCategory = ['birthday', 'adcaps_completion', 'center_qualification_expiration'].includes(editReminderTitleCategory);
|
||||
data = {
|
||||
...data,
|
||||
title: getReminderTitleLabel(editReminderTitleCategory),
|
||||
start_time: editEventStartDateTime,
|
||||
stop_time: editEventStartDateTime,
|
||||
event_reminder_type: editReminderTitleCategory,
|
||||
target_type: isMemberCategory ? 'customer' : 'vehicle',
|
||||
target_uuid: editReminderAssociatedEntity?.value,
|
||||
target_name: editReminderAssociatedEntity?.label,
|
||||
rrule: editEventRecurring || undefined,
|
||||
color: isMemberCategory ? 'member_related' : 'vehicle_maintenance',
|
||||
};
|
||||
}
|
||||
|
||||
EventsService.updateEvent(editingEventId, data).then(() => {
|
||||
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then((data) => {
|
||||
setAllEvents(data.data);
|
||||
closeEditModal();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const toggleColorFilter = (colorValue) => {
|
||||
setSelectedColorFilters(prev =>
|
||||
prev.includes(colorValue)
|
||||
@@ -480,6 +608,13 @@ const EventsCalendar = () => {
|
||||
setTargetedEventType(eventTypeMap[value]);
|
||||
setCurrentTab(value);
|
||||
setSelectedColorFilters([]);
|
||||
// Dismiss any open calendar event tile popup via plugin API
|
||||
try {
|
||||
calendar?.config?.plugins?.eventModal?.close();
|
||||
} catch (e) {
|
||||
// Fallback: remove the modal element from DOM
|
||||
document.querySelectorAll('.sx__event-modal').forEach(el => el.remove());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -490,12 +625,12 @@ const EventsCalendar = () => {
|
||||
{calendarEvent?.doctor && <div className="sx__event-modal__time">{`${calendarEvent?.doctor}`}</div>}
|
||||
<div className="sx__event-modal__time">{`${calendarEvent?.start}`}</div>
|
||||
<div className="sx__event-modal__time" style={{ display: 'flex', gap: '12px', marginTop: '8px' }}>
|
||||
<PencilSquare
|
||||
size={16}
|
||||
onClick={() => currentTab === 'medicalCalendar' ? goToEdit(calendarEvent?.id) : goToEdit(calendarEvent?.id)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
title="Edit"
|
||||
/>
|
||||
<PencilSquare
|
||||
size={16}
|
||||
onClick={() => currentTab === 'medicalCalendar' ? goToEdit(calendarEvent?.id) : openEditModal(calendarEvent)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
title="Edit"
|
||||
/>
|
||||
<Archive
|
||||
size={16}
|
||||
onClick={() => disableEvent(calendarEvent?.id)}
|
||||
@@ -1245,6 +1380,223 @@ const getReminderTitleLabel = (value) => {
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
|
||||
{/* Edit Event Modal (non-medical tabs) */}
|
||||
<Modal show={showEditModal} onHide={closeEditModal} size="sm" dialogClassName="calendar-event-modal">
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>
|
||||
{currentTab === 'activitiesCalendar' && 'Edit Activity'}
|
||||
{currentTab === 'incidentsCalendar' && 'Edit Attendance Note'}
|
||||
{currentTab === 'mealPlanCalendar' && 'Edit Meal Item'}
|
||||
{currentTab === 'reminderDatesCalendar' && 'Edit Important Date'}
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
{/* Activity Edit Fields */}
|
||||
{currentTab === 'activitiesCalendar' && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Activity Name<span className="required">*</span></div>
|
||||
<input type="text" className="form-control" placeholder="Enter activity name" value={editEventTitle || ''} onChange={e => setEditEventTitle(e.target.value)}/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Date & Time</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={editEventStartDateTime}
|
||||
onChange={setEditEventStartDateTime}
|
||||
showTimeSelect
|
||||
timeFormat="HH:mm"
|
||||
timeIntervals={15}
|
||||
dateFormat="MM/dd/yyyy, HH:mm"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Category<span className="required">*</span></div>
|
||||
<select className="form-control" value={editActivityCategory} onChange={(e) => setEditActivityCategory(e.target.value)}>
|
||||
<option value="">Select Category</option>
|
||||
<option value="red">Classes</option>
|
||||
<option value="pink">Games</option>
|
||||
<option value="green">Events</option>
|
||||
<option value="blue">Outings</option>
|
||||
<option value="purple">Personal Care</option>
|
||||
<option value="brown">Care Activities</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Location</div>
|
||||
<input type="text" className="form-control" placeholder="Enter location" value={editEventLocation || ''} onChange={e => setEditEventLocation(e.target.value)}/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Repeat</div>
|
||||
<select className="form-control" value={editEventRecurring} onChange={(e) => setEditEventRecurring(e.target.value)}>
|
||||
<option value="">No (One-time)</option>
|
||||
<option value="FREQ=DAILY">Daily</option>
|
||||
<option value="FREQ=WEEKLY">Weekly</option>
|
||||
<option value="FREQ=MONTHLY">Monthly</option>
|
||||
<option value="FREQ=YEARLY">Yearly</option>
|
||||
</select>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Attendance Note Edit Fields */}
|
||||
{currentTab === 'incidentsCalendar' && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Customer Name<span className="required">*</span></div>
|
||||
<Select
|
||||
value={editAttendanceCustomer}
|
||||
onChange={setEditAttendanceCustomer}
|
||||
options={customers.filter(item => item?.status === 'active' && item?.type !== 'discharged').map(c => ({ value: c.id, label: c.name }))}
|
||||
placeholder="Select Customer"
|
||||
isClearable
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Date<span className="required">*</span></div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={editEventStartDateTime}
|
||||
onChange={setEditEventStartDateTime}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Reason</div>
|
||||
<input type="text" className="form-control" placeholder="Enter reason" value={editAttendanceReason || ''} onChange={e => setEditAttendanceReason(e.target.value)}/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Meal Item Edit Fields */}
|
||||
{currentTab === 'mealPlanCalendar' && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Dish Name<span className="required">*</span></div>
|
||||
<input type="text" className="form-control" placeholder="Enter dish name" value={editEventTitle || ''} onChange={e => setEditEventTitle(e.target.value)}/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Meal Type<span className="required">*</span></div>
|
||||
<select className="form-control" value={editMealType} onChange={(e) => setEditMealType(e.target.value)}>
|
||||
<option value="">Select Meal Type</option>
|
||||
<option value="breakfast">Breakfast</option>
|
||||
<option value="lunch">Lunch</option>
|
||||
<option value="snack">Snack</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={editEventStartDateTime}
|
||||
onChange={setEditEventStartDateTime}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Repeat</div>
|
||||
<select className="form-control" value={editEventRecurring} onChange={(e) => setEditEventRecurring(e.target.value)}>
|
||||
<option value="">No (One-time)</option>
|
||||
<option value="FREQ=DAILY">Daily</option>
|
||||
<option value="FREQ=WEEKLY">Weekly</option>
|
||||
<option value="FREQ=MONTHLY">Monthly</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Ingredients</div>
|
||||
<input type="text" className="form-control" placeholder="Enter ingredients" value={editMealIngredients || ''} onChange={e => setEditMealIngredients(e.target.value)}/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Important Dates Edit Fields */}
|
||||
{currentTab === 'reminderDatesCalendar' && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Title<span className="required">*</span></div>
|
||||
<select className="form-control" value={editReminderTitleCategory} onChange={(e) => {
|
||||
setEditReminderTitleCategory(e.target.value);
|
||||
setEditReminderAssociatedEntity(null);
|
||||
}}>
|
||||
<option value="">Select Title</option>
|
||||
<optgroup label="Member">
|
||||
<option value="adcaps_completion">ADCAPS Completion</option>
|
||||
<option value="center_qualification_expiration">Center Qualification Expiration</option>
|
||||
</optgroup>
|
||||
<optgroup label="Vehicle">
|
||||
<option value="oil_change">Oil Change</option>
|
||||
<option value="monthly_vehicle_inspection">Monthly Vehicle Inspection</option>
|
||||
<option value="emissions_inspection">Emissions Inspection</option>
|
||||
<option value="insurance_expiration">Insurance Expiration</option>
|
||||
<option value="license_plate_expiration">License Plate Expiration</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
{editReminderTitleCategory && (
|
||||
<div className="mb-3">
|
||||
<div className="field-label">
|
||||
{['birthday', 'adcaps_completion', 'center_qualification_expiration'].includes(editReminderTitleCategory)
|
||||
? 'Associated Person'
|
||||
: 'Associated Vehicle'}<span className="required">*</span>
|
||||
</div>
|
||||
<Select
|
||||
value={editReminderAssociatedEntity}
|
||||
onChange={setEditReminderAssociatedEntity}
|
||||
options={
|
||||
['birthday', 'adcaps_completion', 'center_qualification_expiration'].includes(editReminderTitleCategory)
|
||||
? customers.filter(item => item?.status === 'active' && item?.type !== 'discharged').map(c => ({ value: c.id, label: c.name }))
|
||||
: vehicles.map(v => ({ value: v.id, label: v.vehicle_number }))
|
||||
}
|
||||
placeholder={['birthday', 'adcaps_completion', 'center_qualification_expiration'].includes(editReminderTitleCategory) ? 'Select Person' : 'Select Vehicle'}
|
||||
isClearable
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={editEventStartDateTime}
|
||||
onChange={setEditEventStartDateTime}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Category</div>
|
||||
<div className="field-value" style={{ padding: '8px 0', color: '#666' }}>
|
||||
{editReminderTitleCategory
|
||||
? (['birthday', 'adcaps_completion', 'center_qualification_expiration'].includes(editReminderTitleCategory) ? 'Member Related' : 'Vehicle Related')
|
||||
: '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Repeat Cycle</div>
|
||||
<select className="form-control" value={editEventRecurring} onChange={(e) => setEditEventRecurring(e.target.value)}>
|
||||
<option value="">No (One-time)</option>
|
||||
<option value="FREQ=DAILY">Daily</option>
|
||||
<option value="FREQ=WEEKLY">Weekly</option>
|
||||
<option value="FREQ=MONTHLY">Monthly</option>
|
||||
<option value="FREQ=YEARLY">Yearly</option>
|
||||
</select>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" size="sm" onClick={closeEditModal}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="primary" size="sm" onClick={handleEditSave} disabled={
|
||||
(currentTab === 'activitiesCalendar' && (!editEventTitle || !editActivityCategory)) ||
|
||||
(currentTab === 'incidentsCalendar' && (!editAttendanceCustomer || !editEventStartDateTime)) ||
|
||||
(currentTab === 'mealPlanCalendar' && (!editEventTitle || !editMealType)) ||
|
||||
(currentTab === 'reminderDatesCalendar' && (!editReminderTitleCategory || !editReminderAssociatedEntity))
|
||||
}>
|
||||
Save
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
YES_NO, YES_NO_TEXT,
|
||||
PREFERRED_TEXT_LANGUAGE, PREFERRED_TEXT_LANGUAGE_TEXT
|
||||
} from "../../shared";
|
||||
import { Upload } from "react-bootstrap-icons";
|
||||
import { Upload, BoxArrowRight } from "react-bootstrap-icons";
|
||||
|
||||
const UpdateCustomer = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -87,6 +87,13 @@ const UpdateCustomer = () => {
|
||||
const [dischargeReason, setDischargeReason] = useState('');
|
||||
const [dischargeReasonOther, setDischargeReasonOther] = useState('');
|
||||
|
||||
// Discharge confirmation modal
|
||||
const [showDischargeConfirmModal, setShowDischargeConfirmModal] = useState(false);
|
||||
const [dischargeConfirmDate, setDischargeConfirmDate] = useState(new Date());
|
||||
const [dischargeConfirmReason, setDischargeConfirmReason] = useState('');
|
||||
const [dischargeConfirmReasonOther, setDischargeConfirmReasonOther] = useState('');
|
||||
const [isDischarging, setIsDischarging] = useState(false);
|
||||
|
||||
// Care & Services
|
||||
const [dietaryRestrictions, setDietaryRestrictions] = useState([]);
|
||||
const [dietaryRestrictionsOther, setDietaryRestrictionsOther] = useState('');
|
||||
@@ -209,6 +216,55 @@ const UpdateCustomer = () => {
|
||||
currentCustomer?.type !== 'deceased';
|
||||
};
|
||||
|
||||
// Discharge confirmation modal handlers
|
||||
const openDischargeConfirmModal = () => {
|
||||
setDischargeConfirmDate(new Date());
|
||||
setDischargeConfirmReason('');
|
||||
setDischargeConfirmReasonOther('');
|
||||
setShowDischargeConfirmModal(true);
|
||||
};
|
||||
|
||||
const closeDischargeConfirmModal = () => {
|
||||
setShowDischargeConfirmModal(false);
|
||||
setDischargeConfirmDate(new Date());
|
||||
setDischargeConfirmReason('');
|
||||
setDischargeConfirmReasonOther('');
|
||||
};
|
||||
|
||||
const handleDischarge = async () => {
|
||||
if (!dischargeConfirmDate || !dischargeConfirmReason) {
|
||||
alert('Please fill in Discharge Date and Discharge Reason.');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsDischarging(true);
|
||||
|
||||
const currentUserName = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user'))?.name : '';
|
||||
|
||||
const dischargeData = {
|
||||
status: 'inactive',
|
||||
type: 'discharged',
|
||||
discharge_date: formatDateForBackend(dischargeConfirmDate),
|
||||
discharge_reason: dischargeConfirmReason,
|
||||
discharge_reason_other: dischargeConfirmReasonOther,
|
||||
discharge_by: currentUserName,
|
||||
edit_by: currentUserName,
|
||||
edit_date: new Date()
|
||||
};
|
||||
|
||||
try {
|
||||
await CustomerService.updateCustomer(currentCustomer.id, dischargeData);
|
||||
closeDischargeConfirmModal();
|
||||
alert('Customer has been discharged successfully.');
|
||||
navigate(`/customers/${currentCustomer.id}`);
|
||||
} catch (error) {
|
||||
console.error('Error discharging customer:', error);
|
||||
alert('Error discharging customer. Please try again.');
|
||||
} finally {
|
||||
setIsDischarging(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onPharmacyChange = (selectedPharmacy) => {
|
||||
setPharmacy(selectedPharmacy);
|
||||
setPharmacyId(selectedPharmacy?.value);
|
||||
@@ -1177,6 +1233,13 @@ const UpdateCustomer = () => {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{isCustomerActive() && (
|
||||
<div style={{ marginTop: '16px' }}>
|
||||
<button className="btn btn-warning btn-sm" onClick={openDischargeConfirmModal}>
|
||||
<BoxArrowRight className="me-2" size={16}></BoxArrowRight>Discharge Customer
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="list row mb-5">
|
||||
<div className="col-md-12 col-sm-12 col-xs-12">
|
||||
@@ -1639,6 +1702,64 @@ const UpdateCustomer = () => {
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
|
||||
{/* Discharge Confirmation Modal */}
|
||||
<Modal show={showDischargeConfirmModal} onHide={closeDischargeConfirmModal}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>Discharge Customer</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<p className="text-muted mb-3">
|
||||
Are you sure you want to discharge <strong>{currentCustomer?.firstname} {currentCustomer?.lastname}</strong>?
|
||||
This will set the customer's status to inactive.
|
||||
</p>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Discharge Date<span className="text-danger">*</span></label>
|
||||
<DatePicker
|
||||
selected={dischargeConfirmDate}
|
||||
onChange={(date) => setDischargeConfirmDate(date)}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
placeholderText="MM/DD/YYYY"
|
||||
className="form-control"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Discharge Reason<span className="text-danger">*</span></label>
|
||||
<select className="form-control" value={dischargeConfirmReason} onChange={e => setDischargeConfirmReason(e.target.value)}>
|
||||
<option value="">Select...</option>
|
||||
{Object.keys(CUSTOMER_DISCHARGE_REASON).map(key => (
|
||||
<option key={key} value={CUSTOMER_DISCHARGE_REASON[key]}>
|
||||
{CUSTOMER_DISCHARGE_REASON_TEXT[CUSTOMER_DISCHARGE_REASON[key]]}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
{dischargeConfirmReason === 'other' && (
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Discharge Reason (Other)</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Please specify..."
|
||||
value={dischargeConfirmReasonOther}
|
||||
onChange={e => setDischargeConfirmReasonOther(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={closeDischargeConfirmModal}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="warning"
|
||||
onClick={handleDischarge}
|
||||
disabled={isDischarging || !dischargeConfirmDate || !dischargeConfirmReason}
|
||||
>
|
||||
{isDischarging ? 'Discharging...' : 'Confirm Discharge'}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React, {useState, useEffect} from "react";
|
||||
import { PencilSquare, BoxArrowRight } from "react-bootstrap-icons";
|
||||
import { PencilSquare } from "react-bootstrap-icons";
|
||||
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
|
||||
import { AuthService, CustomerService } from "../../services";
|
||||
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Modal, Button } from "react-bootstrap";
|
||||
import DatePicker from "react-datepicker";
|
||||
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab } from "react-bootstrap";
|
||||
import {
|
||||
CUSTOMER_TYPE_TEXT,
|
||||
PROGRAM_TYPE_TEXT,
|
||||
@@ -17,7 +16,6 @@ import {
|
||||
EMERGENCY_CONTACT_ROLE_OPTIONS,
|
||||
DAYS_OF_WEEK_OPTIONS,
|
||||
REFERRAL_SOURCE_TEXT,
|
||||
CUSTOMER_DISCHARGE_REASON,
|
||||
CUSTOMER_DISCHARGE_REASON_TEXT,
|
||||
DIETARY_RESTRICTIONS_OPTIONS,
|
||||
DIET_TEXTURE_TEXT,
|
||||
@@ -35,12 +33,6 @@ const ViewCustomer = () => {
|
||||
const [currentAvatar, setCurrentAvatar] = useState(undefined);
|
||||
const [activeTab, setActiveTab] = useState(searchParams.get('tab') || 'personalInfo');
|
||||
|
||||
// Discharge modal state
|
||||
const [showDischargeModal, setShowDischargeModal] = useState(false);
|
||||
const [dischargeDate, setDischargeDate] = useState(new Date());
|
||||
const [dischargeReason, setDischargeReason] = useState('');
|
||||
const [dischargeReasonOther, setDischargeReasonOther] = useState('');
|
||||
const [isDischarging, setIsDischarging] = useState(false);
|
||||
|
||||
const redirectTo = () => {
|
||||
navigate(`/customers/list`)
|
||||
@@ -58,68 +50,6 @@ const ViewCustomer = () => {
|
||||
currentCustomer?.type !== 'deceased';
|
||||
};
|
||||
|
||||
// Handle discharge modal
|
||||
const openDischargeModal = () => {
|
||||
setDischargeDate(new Date());
|
||||
setDischargeReason('');
|
||||
setDischargeReasonOther('');
|
||||
setShowDischargeModal(true);
|
||||
};
|
||||
|
||||
const closeDischargeModal = () => {
|
||||
setShowDischargeModal(false);
|
||||
setDischargeDate(new Date());
|
||||
setDischargeReason('');
|
||||
setDischargeReasonOther('');
|
||||
};
|
||||
|
||||
// Format date for backend
|
||||
const formatDateForBackend = (date) => {
|
||||
if (!date) return '';
|
||||
const d = new Date(date);
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(d.getDate()).padStart(2, '0');
|
||||
const year = d.getFullYear();
|
||||
return `${month}/${day}/${year}`;
|
||||
};
|
||||
|
||||
// Handle discharge save
|
||||
const handleDischarge = async () => {
|
||||
if (!dischargeDate || !dischargeReason) {
|
||||
alert('Please fill in Discharge Date and Discharge Reason.');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsDischarging(true);
|
||||
|
||||
const currentUserName = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user'))?.name : '';
|
||||
|
||||
const dischargeData = {
|
||||
status: 'inactive',
|
||||
type: 'discharged',
|
||||
discharge_date: formatDateForBackend(dischargeDate),
|
||||
discharge_reason: dischargeReason,
|
||||
discharge_reason_other: dischargeReasonOther,
|
||||
discharge_by: currentUserName,
|
||||
edit_by: currentUserName,
|
||||
edit_date: new Date()
|
||||
};
|
||||
|
||||
try {
|
||||
await CustomerService.updateCustomer(currentCustomer.id, dischargeData);
|
||||
// Reload customer data
|
||||
const updatedData = await CustomerService.getCustomer(currentCustomer.id);
|
||||
setCurrentCustomer(updatedData.data);
|
||||
closeDischargeModal();
|
||||
alert('Customer has been discharged successfully.');
|
||||
} catch (error) {
|
||||
console.error('Error discharging customer:', error);
|
||||
alert('Error discharging customer. Please try again.');
|
||||
} finally {
|
||||
setIsDischarging(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!AuthService.canViewCustomers()) {
|
||||
window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.')
|
||||
@@ -795,72 +725,9 @@ const ViewCustomer = () => {
|
||||
</Tabs>
|
||||
<div className="list-func-panel">
|
||||
<button className="btn btn-primary me-2" onClick={() => goToEdit(currentCustomer?.id)}><PencilSquare className="me-2" size={16}></PencilSquare>Edit</button>
|
||||
{isCustomerActive() && (
|
||||
<button className="btn btn-warning" onClick={openDischargeModal}>
|
||||
<BoxArrowRight className="me-2" size={16}></BoxArrowRight>Discharge Customer
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Discharge Modal */}
|
||||
<Modal show={showDischargeModal} onHide={closeDischargeModal}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>Discharge Customer</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<p className="text-muted mb-3">
|
||||
Are you sure you want to discharge <strong>{currentCustomer?.firstname} {currentCustomer?.lastname}</strong>?
|
||||
This will set the customer's status to inactive.
|
||||
</p>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Discharge Date<span className="text-danger">*</span></label>
|
||||
<DatePicker
|
||||
selected={dischargeDate}
|
||||
onChange={(date) => setDischargeDate(date)}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
placeholderText="MM/DD/YYYY"
|
||||
className="form-control"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Discharge Reason<span className="text-danger">*</span></label>
|
||||
<select className="form-control" value={dischargeReason} onChange={e => setDischargeReason(e.target.value)}>
|
||||
<option value="">Select...</option>
|
||||
{Object.keys(CUSTOMER_DISCHARGE_REASON).map(key => (
|
||||
<option key={key} value={CUSTOMER_DISCHARGE_REASON[key]}>
|
||||
{CUSTOMER_DISCHARGE_REASON_TEXT[CUSTOMER_DISCHARGE_REASON[key]]}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
{dischargeReason === 'other' && (
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Discharge Reason (Other)</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Please specify..."
|
||||
value={dischargeReasonOther}
|
||||
onChange={e => setDischargeReasonOther(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={closeDischargeModal}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="warning"
|
||||
onClick={handleDischarge}
|
||||
disabled={isDischarging || !dischargeDate || !dischargeReason}
|
||||
>
|
||||
{isDischarging ? 'Discharging...' : 'Confirm Discharge'}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useNavigate, useParams } from "react-router-dom";
|
||||
import { customerSlice } from "./../../store";
|
||||
import { AuthService, CustomerService, EventsService, LabelService } from "../../services";
|
||||
import { CUSTOMER_TYPE, ManageTable } from "../../shared";
|
||||
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Dropdown } from "react-bootstrap";
|
||||
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Dropdown, Modal } from "react-bootstrap";
|
||||
import { Columns, Download, Filter, PencilSquare, PersonSquare, Plus } from "react-bootstrap-icons";
|
||||
|
||||
const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, title = null }) => {
|
||||
@@ -27,6 +27,10 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
const [serviceRequirementFilter, setServiceRequirementFilter] = useState('');
|
||||
const [tagsFilter, setTagsFilter] = useState([]);
|
||||
const [availableLabels, setAvailableLabels] = useState([]);
|
||||
const [showAvatarModal, setShowAvatarModal] = useState(false);
|
||||
const [avatarData, setAvatarData] = useState(null);
|
||||
const [avatarCustomerName, setAvatarCustomerName] = useState('');
|
||||
const [avatarLoading, setAvatarLoading] = useState(false);
|
||||
const [columns, setColumns] = useState([
|
||||
{
|
||||
key: 'name',
|
||||
@@ -271,6 +275,25 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
navigate(`/customers/${id}`);
|
||||
}
|
||||
|
||||
const showProfilePicture = (customer) => {
|
||||
setAvatarCustomerName(customer?.name || '');
|
||||
setAvatarData(null);
|
||||
setAvatarLoading(true);
|
||||
setShowAvatarModal(true);
|
||||
CustomerService.getAvatar(customer?.id).then((data) => {
|
||||
setAvatarData(data.data);
|
||||
setAvatarLoading(false);
|
||||
}).catch(() => {
|
||||
setAvatarLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
const closeAvatarModal = () => {
|
||||
setShowAvatarModal(false);
|
||||
setAvatarData(null);
|
||||
setAvatarCustomerName('');
|
||||
}
|
||||
|
||||
const table = <div className="list row mb-4">
|
||||
<div className="col-md-12" style={{ overflow: 'auto'}}>
|
||||
<table className="personnel-info-table">
|
||||
@@ -290,7 +313,7 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
filteredCustomers.map((customer, index) => <tr key={customer.id}>
|
||||
<td className="td-checkbox"><input type="checkbox" checked={selectedItems.includes(customer.id)} onClick={()=>toggleItem(customer?.id)}/></td>
|
||||
<td className="td-index">{index + 1}</td>
|
||||
{columns.find(col => col.key === 'name')?.show && <td> {AuthService.canAddOrEditCustomers() && <PencilSquare size={16} className="clickable me-2" onClick={() => goToEdit(customer?.id)}></PencilSquare>} {AuthService.canViewCustomers() && <PersonSquare onClick={() => goToView(customer?.id)} size={16} className="clickable me-2" />} {customer?.name}</td>}
|
||||
{columns.find(col => col.key === 'name')?.show && <td> {AuthService.canAddOrEditCustomers() && <PencilSquare size={16} className="clickable me-2" onClick={() => goToView(customer?.id)}></PencilSquare>} {AuthService.canViewCustomers() && <PersonSquare onClick={() => showProfilePicture(customer)} size={16} className="clickable me-2" />} {customer?.name}</td>}
|
||||
{columns.find(col => col.key === 'chinese_name')?.show && <td>{customer?.name_cn}</td>}
|
||||
{columns.find(col => col.key === 'email')?.show && <td>{customer?.email}</td>}
|
||||
{columns.find(col => col.key === 'type')?.show && <td>{customer?.type}</td>}
|
||||
@@ -467,6 +490,24 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Modal show={showAvatarModal} onHide={closeAvatarModal} centered>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title style={{ fontSize: '16px' }}>{avatarCustomerName}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '200px' }}>
|
||||
{avatarLoading && <Spinner animation="border" variant="primary" />}
|
||||
{!avatarLoading && avatarData && (
|
||||
<img
|
||||
src={`data:image/png;base64, ${avatarData}`}
|
||||
alt={avatarCustomerName}
|
||||
style={{ maxWidth: '100%', maxHeight: '400px', objectFit: 'contain' }}
|
||||
/>
|
||||
)}
|
||||
{!avatarLoading && !avatarData && (
|
||||
<div style={{ textAlign: 'center', color: '#999' }}>No profile picture available</div>
|
||||
)}
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
};
|
||||
|
||||
@@ -2,15 +2,11 @@ import React, {useState, useEffect, useRef} from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { AuthService, EventsService, CustomerService, ResourceService } from "../../services";
|
||||
import moment from 'moment';
|
||||
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Button, Modal, Dropdown } from "react-bootstrap";
|
||||
import { Breadcrumb, Tabs, Tab, Button, Modal, Dropdown } from "react-bootstrap";
|
||||
import { useCalendarApp, ScheduleXCalendar } from '@schedule-x/react';
|
||||
import {
|
||||
viewWeek,
|
||||
viewDay,
|
||||
viewMonthGrid,
|
||||
viewMonthAgenda,
|
||||
createViewDay,
|
||||
createViewMonthAgenda,
|
||||
createViewWeek,
|
||||
createViewMonthGrid
|
||||
} from '@schedule-x/calendar';
|
||||
@@ -18,7 +14,7 @@ import { createEventsServicePlugin } from '@schedule-x/events-service';
|
||||
import { createEventModalPlugin} from '@schedule-x/event-modal';
|
||||
import '@schedule-x/theme-default/dist/calendar.css';
|
||||
import { Archive, PencilSquare, Filter } from "react-bootstrap-icons";
|
||||
// import { Scheduler } from "@aldabil/react-scheduler";
|
||||
import DatePicker from "react-datepicker";
|
||||
|
||||
|
||||
const EventsCalendar = () => {
|
||||
@@ -26,6 +22,7 @@ const EventsCalendar = () => {
|
||||
const [events, setEvents] = useState([]);
|
||||
const calendarColumnRef = useRef(null);
|
||||
const [listHeight, setListHeight] = useState(null);
|
||||
const [allEvents, setAllEvents] = useState([]);
|
||||
const [customers, setCustomers] = useState([]);
|
||||
const [resources, setResources] = useState([]);
|
||||
const [fromDate, setFromDate] = useState(new Date(new Date().getFullYear(), new Date().getMonth(), 1));
|
||||
@@ -34,6 +31,7 @@ const EventsCalendar = () => {
|
||||
const [currentTotalTranslate2, setCurrentTotalTranslate2] = useState(0);
|
||||
const [currentTotalResource, setCurrentTotalResource] = useState(0);
|
||||
const [showDeletedItems, setShowDeletedItems] = useState(false);
|
||||
const [selectedColorFilters, setSelectedColorFilters] = useState([]);
|
||||
const [timeData, setTimeData] = useState([]);
|
||||
const [showFilterDropdown, setShowFilterDropdown] = useState(false);
|
||||
const eventsServicePlugin = createEventsServicePlugin();
|
||||
@@ -41,6 +39,20 @@ const EventsCalendar = () => {
|
||||
const [groupedEvents, setGroupedEvents] = useState(new Map());
|
||||
const [currentRangeStart, setCurrentRangeStart] = useState(null);
|
||||
const [currentRangeEnd, setCurrentRangeEnd] = useState(null);
|
||||
const [showCreationModal, setShowCreationModal] = useState(false);
|
||||
const [newEventStartDateTime, setNewEventStartDateTime] = useState(new Date());
|
||||
const [newEventEndDateTime, setNewEventEndDateTime] = useState(new Date());
|
||||
const [newEventColor, setNewEventColor] = useState('');
|
||||
|
||||
// Medical appointment specific fields
|
||||
const [newEventCustomer, setNewEventCustomer] = useState('');
|
||||
const [newEventResource, setNewEventResource] = useState('');
|
||||
const [newEventInterpreter, setNewEventInterpreter] = useState('');
|
||||
const [newEventFasting, setNewEventFasting] = useState('');
|
||||
const [newEventNeedId, setNewEventNeedId] = useState('');
|
||||
const [newEventNewPatient, setNewEventNewPatient] = useState('');
|
||||
const [newEventDisability, setNewEventDisability] = useState('');
|
||||
const [newEventTransMethod, setNewEventTransMethod] = useState('');
|
||||
|
||||
// Helper function to format name from "lastname, firstname" to "firstname lastname"
|
||||
const formatFullName = (name) => {
|
||||
@@ -52,36 +64,21 @@ const EventsCalendar = () => {
|
||||
return name;
|
||||
};
|
||||
|
||||
// Helper function to get shortened name
|
||||
const getShortenedName = (name) => {
|
||||
if (!name) return '';
|
||||
const fullName = formatFullName(name);
|
||||
const parts = fullName.split(' ');
|
||||
if (parts.length >= 2) {
|
||||
return `${parts[0]} ${parts[parts.length - 1].charAt(0)}`; // FirstName L
|
||||
}
|
||||
return fullName;
|
||||
};
|
||||
|
||||
// Helper function to format event title - now uses full name, CSS handles overflow
|
||||
const formatEventTitle = (customerName, startTime) => {
|
||||
const fullName = formatFullName(customerName);
|
||||
// Return full name - CSS will handle truncation with text-overflow: ellipsis
|
||||
return fullName;
|
||||
};
|
||||
|
||||
// Get full name for description/tooltip
|
||||
const getEventDescription = (customerName, doctorName) => {
|
||||
const fullName = formatFullName(customerName);
|
||||
return `${fullName} - ${doctorName}`;
|
||||
return doctorName ? `${fullName} - ${doctorName}` : fullName;
|
||||
};
|
||||
|
||||
const calendar = useCalendarApp({
|
||||
views: [createViewMonthGrid(), createViewDay(), createViewMonthAgenda(), createViewWeek()],
|
||||
views: [createViewMonthGrid(), createViewDay(), createViewWeek()],
|
||||
monthGridOptions: {
|
||||
/**
|
||||
* Number of events to display in a day cell before the "+ N events" button is shown
|
||||
* */
|
||||
nEventsPerDay: 50,
|
||||
},
|
||||
defaultView: viewMonthGrid.name,
|
||||
@@ -91,25 +88,34 @@ const EventsCalendar = () => {
|
||||
plugins: [eventModalService, eventsServicePlugin],
|
||||
callbacks: {
|
||||
onRangeUpdate(range) {
|
||||
console.log('new calendar range start date', range.start);
|
||||
console.log('new calendar range end date', range.end);
|
||||
setCurrentRangeStart(range.start);
|
||||
setCurrentRangeEnd(range.end);
|
||||
// Update fromDate/toDate for API fetching based on the range
|
||||
const startDate = new Date(range.start);
|
||||
const endDate = new Date(range.end);
|
||||
setFromDate(new Date(startDate.getFullYear(), startDate.getMonth(), 1));
|
||||
setToDate(new Date(endDate.getFullYear(), endDate.getMonth() + 1, 0));
|
||||
},
|
||||
onClickDate(date) {
|
||||
// Parse as local date to avoid UTC timezone offset shifting the day
|
||||
const [y, m, d] = date.split('-').map(Number);
|
||||
const localDate = new Date(y, m - 1, d);
|
||||
// Default to 10:00 AM
|
||||
localDate.setHours(10, 0, 0, 0);
|
||||
setNewEventStartDateTime(localDate);
|
||||
setNewEventEndDateTime(localDate);
|
||||
setShowCreationModal(true);
|
||||
},
|
||||
onClickDateTime(dateTime) {
|
||||
setNewEventStartDateTime(new Date(dateTime.replace(' ', 'T')));
|
||||
setNewEventEndDateTime(new Date(dateTime.replace(' ', 'T')));
|
||||
setShowCreationModal(true);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Filter events based on current calendar range
|
||||
const getFilteredEvents = () => {
|
||||
console.log("EventsCalendar - range:", currentRangeStart, "to", currentRangeEnd, "events:", events.length);
|
||||
|
||||
if (!currentRangeStart || !currentRangeEnd) {
|
||||
// If no range set yet, show all events for current month
|
||||
const now = moment();
|
||||
return events.filter(event => {
|
||||
const eventDate = moment(event.start_time);
|
||||
@@ -117,7 +123,6 @@ const EventsCalendar = () => {
|
||||
});
|
||||
}
|
||||
|
||||
// Filter events within the calendar's visible range
|
||||
const rangeStart = moment(currentRangeStart);
|
||||
const rangeEnd = moment(currentRangeEnd);
|
||||
|
||||
@@ -141,7 +146,7 @@ const EventsCalendar = () => {
|
||||
}
|
||||
}
|
||||
return eventsDateMap;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
@@ -161,54 +166,64 @@ const EventsCalendar = () => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (customers?.length > 0 && resources.length > 0) {
|
||||
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then((data) => {
|
||||
setEvents(data.data.filter((item) => {
|
||||
const customerField = item?.data?.customer ? (customers?.find(c => c.id === item?.data?.customer)?.name || item?.data?.client_name || '') : (item?.data?.client_name || '');
|
||||
const doctorField = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.name || item?.data?.resource_name || '') : (item?.data?.resource_name || '');
|
||||
item.event_id = item.id;
|
||||
item.customer = customerField;
|
||||
item.doctor = doctorField;
|
||||
item.phone = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.phone || item?.data?.resource_phone || '') : (item?.data?.resource_phone || '');
|
||||
item.contact = item?.data?.resource? ((resources?.find(r => r.id === item?.data?.resource))?.contact || item?.data?.resource_contact || '') : (item?.data?.resource_contact || '')
|
||||
item.address = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.address || item?.data?.resource_address || '') : (item?.data?.resource_address || '');
|
||||
item.translation = item?.data?.interpreter || '';
|
||||
item.newPatient = item?.data?.new_patient || '';
|
||||
item.needId = item?.data?.need_id || '';
|
||||
item.disability = item?.data?.disability || '';
|
||||
item.startTime = item?.start_time? `${moment(new Date(item?.start_time)).format('hh:mm A')}` : '' ;
|
||||
item.endTime = item?.start_time? `${moment(new Date(item?.end_time)).format('hh:mm A')}` : '' ;
|
||||
item.fasting = item?.data?.fasting || '';
|
||||
item.transportation = item?.link_event_name || '';
|
||||
item.title = formatEventTitle(customerField, item?.start_time);
|
||||
item.description = getEventDescription(customerField, doctorField); // Full info for tooltip
|
||||
item.start = item?.start_time? `${moment(new Date(item?.start_time)).format('YYYY-MM-DD HH:mm')}` : '';
|
||||
item.end = item.stop_time? `${moment(new Date(item?.stop_time)).format('YYYY-MM-DD HH:mm')}` : '';
|
||||
const transportationInfo = EventsService.getTransportationInfo(data.data, item, timeData);
|
||||
const { isFutureEvent, maxTranslate1, maxTranslate2, maxResource, totalTranslate1, totalTranslate2, totalResource} = transportationInfo;
|
||||
item.color = item?.color;
|
||||
item._options = { additionalClasses: [`event-${item?.color || 'primary'}`]};
|
||||
item.showWarnings = isFutureEvent;
|
||||
item.maxTranslate1 = maxTranslate1;
|
||||
item.maxTranslate2 = maxTranslate2;
|
||||
item.maxResource = maxResource;
|
||||
item.totalTranslate1 = totalTranslate1;
|
||||
setCurrentTotalTranslate1(item.totalTranslate1);
|
||||
item.totalTranslate2 = totalTranslate2;
|
||||
setCurrentTotalTranslate2(item.totalTranslate2);
|
||||
item.totalResource = totalResource;
|
||||
setCurrentTotalResource(item.totalResource);
|
||||
return item;
|
||||
}).filter(item => item.type === 'medical')?.filter(item => (!showDeletedItems && item.status === 'active') || showDeletedItems));
|
||||
})}
|
||||
}, [fromDate, toDate, customers, resources, timeData, showDeletedItems]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then(data => setAllEvents(data?.data));
|
||||
}, [fromDate, toDate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (customers?.length > 0 && resources.length > 0) {
|
||||
const orignialEvents = [...allEvents];
|
||||
setEvents(orignialEvents?.filter(item => item.type === 'medical')?.map((item) => {
|
||||
const customerField = item?.data?.customer ? (customers?.find(c => c.id === item?.data?.customer)?.name || item?.data?.client_name || '') : (item?.data?.client_name || '');
|
||||
const doctorField = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.name || item?.data?.resource_name || '') : (item?.data?.resource_name || '');
|
||||
item.event_id = item.id;
|
||||
item.customer = customerField;
|
||||
item.doctor = doctorField;
|
||||
item.phone = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.phone || item?.data?.resource_phone || '') : (item?.data?.resource_phone || '');
|
||||
item.contact = item?.data?.resource? ((resources?.find(r => r.id === item?.data?.resource))?.contact || item?.data?.resource_contact || '') : (item?.data?.resource_contact || '')
|
||||
item.address = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.address || item?.data?.resource_address || '') : (item?.data?.resource_address || '');
|
||||
item.translation = item?.data?.interpreter || '';
|
||||
item.newPatient = item?.data?.new_patient || '';
|
||||
item.needId = item?.data?.need_id || '';
|
||||
item.disability = item?.data?.disability || '';
|
||||
item.startTime = item?.start_time? `${moment(new Date(item?.start_time)).format('YYYY-MM-DD HH:mm')}` : '' ;
|
||||
item.endTime = item?.start_time? `${moment(new Date(item?.end_time)).format('YYYY-MM-DD HH:mm')}` : '' ;
|
||||
item.fasting = item?.data?.fasting || '';
|
||||
item.transportation = item?.link_event_name || '';
|
||||
item.title = formatEventTitle(customerField, item?.start_time);
|
||||
item.description = getEventDescription(customerField, doctorField);
|
||||
item.start = item?.start_time? `${moment(new Date(item?.start_time)).format('YYYY-MM-DD HH:mm')}` : `${moment().format('YYYY-MM-DD HH:mm')}`;
|
||||
item.end = item?.stop_time? `${moment(new Date(item?.stop_time)).format('YYYY-MM-DD HH:mm')}` : (item?.start_time? `${moment(item?.start_time).format('YYYY-MM-DD HH:mm')}` : `${moment().format('YYYY-MM-DD HH:mm')}`);
|
||||
const transportationInfo = EventsService.getTransportationInfo(allEvents, item, timeData);
|
||||
const { isFutureEvent, maxTranslate1, maxTranslate2, maxResource, totalTranslate1, totalTranslate2, totalResource} = transportationInfo;
|
||||
item.color = item?.color;
|
||||
item._options = { additionalClasses: [`event-${item?.color || 'primary'}`]};
|
||||
item.showWarnings = isFutureEvent;
|
||||
item.maxTranslate1 = maxTranslate1;
|
||||
item.maxTranslate2 = maxTranslate2;
|
||||
item.maxResource = maxResource;
|
||||
item.totalTranslate1 = totalTranslate1;
|
||||
setCurrentTotalTranslate1(item.totalTranslate1);
|
||||
item.totalTranslate2 = totalTranslate2;
|
||||
setCurrentTotalTranslate2(item.totalTranslate2);
|
||||
item.totalResource = totalResource;
|
||||
setCurrentTotalResource(item.totalResource);
|
||||
return item;
|
||||
})?.filter(item => (!showDeletedItems && item.status === 'active') || showDeletedItems)
|
||||
?.filter(item => {
|
||||
if (selectedColorFilters.length === 0) return true;
|
||||
if (selectedColorFilters.includes(item.color)) return true;
|
||||
// When "Drop-Off Only" (purple) is selected, also show events with no label
|
||||
if (selectedColorFilters.includes('purple') && !item.color) return true;
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
}, [customers, resources, timeData, allEvents, showDeletedItems, selectedColorFilters])
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (events && calendar) {
|
||||
console.log("EventsCalendar useEffect - events:", events.length, "range:", currentRangeStart, "to", currentRangeEnd);
|
||||
calendar?.eventsService?.set(events);
|
||||
setGroupedEvents(getGroupedEvents());
|
||||
}
|
||||
@@ -223,10 +238,7 @@ const EventsCalendar = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Initial measurement after render
|
||||
const timer = setTimeout(updateListHeight, 100);
|
||||
|
||||
// Update on window resize
|
||||
window.addEventListener('resize', updateListHeight);
|
||||
|
||||
return () => {
|
||||
@@ -236,7 +248,6 @@ const EventsCalendar = () => {
|
||||
}, [events]);
|
||||
|
||||
|
||||
|
||||
const redirectToAdmin = () => {
|
||||
navigate(`/medical`)
|
||||
}
|
||||
@@ -250,66 +261,36 @@ const EventsCalendar = () => {
|
||||
}
|
||||
|
||||
const goToList = () => {
|
||||
navigate(`/medical/events/list`)
|
||||
navigate(`/medical/events/list`)
|
||||
}
|
||||
|
||||
const goToMultipleList = () => {
|
||||
navigate(`/medical/events/multiple-list`)
|
||||
navigate(`/medical/events/multiple-list`)
|
||||
}
|
||||
|
||||
|
||||
const goToView = (id) => {
|
||||
navigate(`/medical/events/${id}`)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const disableEvent = (id) => {
|
||||
const currentEvent = events.find(item => item.id === id);
|
||||
EventsService.disableEvent(id, { status: 'inactive', edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name,
|
||||
edit_date: new Date(),
|
||||
edit_history: currentEvent?.edit_history? [...currentEvent.edit_history, { employee: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, date: new Date() }] : [{ employee: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, date: new Date() }]}).then(() => {
|
||||
edit_date: new Date(),
|
||||
edit_history: currentEvent?.edit_history? [...currentEvent.edit_history, { employee: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, date: new Date() }] : [{ employee: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, date: new Date() }]}).then(() => {
|
||||
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then((data) => {
|
||||
setEvents(data.data.filter((item) => {
|
||||
const customerField = item?.data?.customer ? (customers?.find(c => c.id === item?.data?.customer)?.name || item?.data?.client_name || '') : (item?.data?.client_name || '');
|
||||
const doctorField = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.name || item?.data?.resource_name || '') : (item?.data?.resource_name || '');
|
||||
item.event_id = item.id;
|
||||
item.customer = customerField;
|
||||
item.doctor = doctorField;
|
||||
item.phone = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.phone || item?.data?.resource_phone || '') : (item?.data?.resource_phone || '');
|
||||
item.contact = item?.data?.resource? ((resources?.find(r => r.id === item?.data?.resource))?.contact || item?.data?.resource_contact || '') : (item?.data?.resource_contact || '')
|
||||
item.address = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.address || item?.data?.resource_address || '') : (item?.data?.resource_address || '');
|
||||
item.translation = item?.data?.interpreter || '';
|
||||
item.newPatient = item?.data?.new_patient || '';
|
||||
item.needId = item?.data?.need_id || '';
|
||||
item.disability = item?.data?.disability || '';
|
||||
item.startTime = item?.start_time? `${moment(new Date(item?.start_time)).format('hh:mm A')}` : '' ;
|
||||
item.endTime = item?.start_time? `${moment(new Date(item?.end_time)).format('hh:mm A')}` : '' ;
|
||||
item.fasting = item?.data?.fasting || '';
|
||||
item.transportation = item?.link_event_name || '';
|
||||
item.title = formatEventTitle(customerField, item?.start_time);
|
||||
item.start = item?.start_time? `${moment(new Date(item?.start_time)).format('YYYY-MM-DD HH:mm')}` : '';
|
||||
item.end = item.stop_time? `${moment(new Date(item?.stop_time)).format('YYYY-MM-DD HH:mm')}` : '';
|
||||
item._options = { additionalClasses: [`event-${item?.color || 'primary'}`]};
|
||||
const transportationInfo = EventsService.getTransportationInfo(data.data, item, timeData);
|
||||
const { isFutureEvent, maxTranslate1, maxTranslate2, maxResource, totalTranslate1, totalTranslate2, totalResource} = transportationInfo;
|
||||
item.color = item?.color;
|
||||
item.showWarnings = isFutureEvent;
|
||||
item.maxTranslate1 = maxTranslate1;
|
||||
item.maxTranslate2 = maxTranslate2;
|
||||
item.maxResource = maxResource;
|
||||
item.totalTranslate1 = totalTranslate1;
|
||||
setCurrentTotalTranslate1(item.totalTranslate1);
|
||||
item.totalTranslate2 = totalTranslate2;
|
||||
setCurrentTotalTranslate2(item.totalTranslate2);
|
||||
item.totalResource = totalResource;
|
||||
setCurrentTotalResource(item.totalResource);
|
||||
return item;
|
||||
}).filter(item => item.type === 'medical' && ((!showDeletedItems && item.status === 'active') || showDeletedItems)));
|
||||
setAllEvents(data?.data);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const toggleColorFilter = (colorValue) => {
|
||||
setSelectedColorFilters(prev =>
|
||||
prev.includes(colorValue)
|
||||
? prev.filter(c => c !== colorValue)
|
||||
: [...prev, colorValue]
|
||||
);
|
||||
};
|
||||
|
||||
const FilterAndClose = () => {
|
||||
setShowFilterDropdown(false);
|
||||
}
|
||||
@@ -317,18 +298,29 @@ const EventsCalendar = () => {
|
||||
const cleanFilterAndClose = () => {
|
||||
setShowFilterDropdown(false);
|
||||
setShowDeletedItems(false);
|
||||
setSelectedColorFilters([]);
|
||||
}
|
||||
|
||||
|
||||
const customComponents = {
|
||||
eventModal: ({calendarEvent}) => {
|
||||
return <>
|
||||
<div className="sx__event-modal__title">{calendarEvent?.customer}</div>
|
||||
<div className="sx__event-modal__time">{`${calendarEvent?.doctor}`}</div>
|
||||
<div className="sx__event-modal__time">{`${calendarEvent?.startTime}`}</div>
|
||||
<div className="sx__event-modal__time">
|
||||
<PencilSquare size={16} onClick={() => goToEdit(calendarEvent?.id)} className="me-4"></PencilSquare>
|
||||
<Archive size={16} onClick={() =>{disableEvent(calendarEvent?.id)}}></Archive> </div>
|
||||
{calendarEvent?.doctor && <div className="sx__event-modal__time">{`${calendarEvent?.doctor}`}</div>}
|
||||
<div className="sx__event-modal__time">{`${calendarEvent?.start}`}</div>
|
||||
<div className="sx__event-modal__time" style={{ display: 'flex', gap: '12px', marginTop: '8px' }}>
|
||||
<PencilSquare
|
||||
size={16}
|
||||
onClick={() => goToEdit(calendarEvent?.id)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
title="Edit"
|
||||
/>
|
||||
<Archive
|
||||
size={16}
|
||||
onClick={() => disableEvent(calendarEvent?.id)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
title="Delete"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
};
|
||||
@@ -349,7 +341,44 @@ const EventsCalendar = () => {
|
||||
<input type="checkbox" value={showDeletedItems} checked={showDeletedItems === true} onClick={() => setShowDeletedItems(!showDeletedItems)} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="list row">
|
||||
<hr style={{ margin: '8px 0' }} />
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<div className="field-label" style={{ marginBottom: '8px' }}>Filter by Type</div>
|
||||
{EventsService.labelOptions.map((item) => (
|
||||
<div key={item.value} className="d-flex align-items-center mb-1" style={{ cursor: 'pointer' }} onClick={() => toggleColorFilter(item.value)}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedColorFilters.includes(item.value)}
|
||||
onChange={() => toggleColorFilter(item.value)}
|
||||
style={{ marginRight: '8px', marginLeft: '0' }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
<span
|
||||
className={`event-${item.value}`}
|
||||
style={{
|
||||
width: '14px',
|
||||
height: '14px',
|
||||
borderRadius: '3px',
|
||||
display: 'inline-block',
|
||||
marginRight: '6px',
|
||||
flexShrink: 0
|
||||
}}
|
||||
></span>
|
||||
<span style={{ fontSize: '12px' }}>{item.label}</span>
|
||||
</div>
|
||||
))}
|
||||
{selectedColorFilters.length > 0 && (
|
||||
<div style={{ marginTop: '4px' }}>
|
||||
<span
|
||||
style={{ fontSize: '11px', color: '#0066B1', cursor: 'pointer', textDecoration: 'underline' }}
|
||||
onClick={() => setSelectedColorFilters([])}
|
||||
>
|
||||
Clear all
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="list row" style={{ marginTop: '8px' }}>
|
||||
<div className="col-md-12">
|
||||
<button className="btn btn-default btn-sm float-right" onClick={() => cleanFilterAndClose()}> Cancel </button>
|
||||
<button className="btn btn-primary btn-sm float-right" onClick={() => FilterAndClose()}> Filter </button>
|
||||
@@ -359,6 +388,124 @@ const EventsCalendar = () => {
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
const calendarView = <div className="multi-columns-container" style={{ display: 'flex', alignItems: 'flex-start', width: '100%' }}>
|
||||
<div className="column-container" ref={calendarColumnRef} style={{ minWidth: '1000px', flexShrink: 0, display: 'flex', flexDirection: 'column' }}>
|
||||
{calendar && <ScheduleXCalendar customComponents={customComponents} calendarApp={calendar} />}
|
||||
{/* Legend */}
|
||||
<div className="calendar-legend mt-3">
|
||||
<h6 className="text-muted mb-2" style={{ fontSize: '12px' }}>Legend:</h6>
|
||||
<div className="d-flex flex-wrap gap-3">
|
||||
{EventsService.labelOptions?.map((item) => (
|
||||
<div key={item.value} className="d-flex align-items-center">
|
||||
<span
|
||||
className={`event-${item.value}`}
|
||||
style={{
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
borderRadius: '4px',
|
||||
display: 'inline-block',
|
||||
marginRight: '6px'
|
||||
}}
|
||||
></span>
|
||||
<span style={{ fontSize: '12px' }}>{item.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="column-container calendar-list-column" style={{ display: 'flex', flexDirection: 'column', flex: 1, minWidth: 0 }}>
|
||||
<div className="column-card" style={{ height: listHeight ? `${listHeight}px` : 'auto', overflowY: 'auto', overflowX: 'hidden', padding: '16px', display: 'flex', flexDirection: 'column', width: '100%', boxSizing: 'border-box' }}>
|
||||
<h6 className="text-primary mb-3">List</h6>
|
||||
<div style={{ flex: 1, overflowY: 'auto', width: '100%' }}>
|
||||
{(!groupedEvents || groupedEvents.size === 0) && (
|
||||
<div style={{ padding: '24px', textAlign: 'center', color: '#999' }}>
|
||||
No events for this period
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
Array.from(groupedEvents?.keys())?.map((key) => {
|
||||
return <div key={key}>
|
||||
<h6 className="text-primary me-2">{key}</h6>
|
||||
{
|
||||
groupedEvents.get(key).map(eventItem => <div
|
||||
key={eventItem.id}
|
||||
className={`event-${eventItem.color || 'primary'} mb-3 event-list-item-container`}
|
||||
onClick={() => goToView(eventItem.id)}
|
||||
style={{ cursor: 'pointer', padding: '8px 12px', borderRadius: '4px' }}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<span className="sx__month-agenda-event__title">{formatFullName(eventItem.customer)}</span>
|
||||
<span style={{ fontSize: '12px' }}>{moment(eventItem?.start_time).format('HH:mm')} - {moment(eventItem?.stop_time || eventItem?.start_time).format('HH:mm')}</span>
|
||||
</div>
|
||||
<div className="sx__event-modal__time" style={{ fontSize: '12px', marginTop: '4px' }}>Provider: {eventItem?.doctor || '-'}</div>
|
||||
</div>)
|
||||
}
|
||||
</div>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
const handleClose = () => {
|
||||
setShowCreationModal(false);
|
||||
setNewEventStartDateTime(undefined);
|
||||
setNewEventEndDateTime(undefined);
|
||||
setNewEventColor('');
|
||||
setNewEventCustomer('');
|
||||
setNewEventResource('');
|
||||
setNewEventInterpreter('');
|
||||
setNewEventFasting('');
|
||||
setNewEventNeedId('');
|
||||
setNewEventNewPatient('');
|
||||
setNewEventDisability('');
|
||||
setNewEventTransMethod('');
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
const userName = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name;
|
||||
const selectedCustomer = customers.find(c => c.id === newEventCustomer);
|
||||
const selectedResource = resources.find(r => r.id === newEventResource);
|
||||
|
||||
const data = {
|
||||
type: 'medical',
|
||||
status: 'active',
|
||||
create_by: userName,
|
||||
edit_by: userName,
|
||||
edit_date: new Date(),
|
||||
create_date: new Date(),
|
||||
edit_history: [{ employee: userName, date: new Date() }],
|
||||
title: selectedCustomer ? `${selectedCustomer.name} - Medical Appointment` : 'Medical Appointment',
|
||||
start_time: newEventStartDateTime,
|
||||
stop_time: newEventStartDateTime,
|
||||
color: newEventColor,
|
||||
confirmed: true,
|
||||
data: {
|
||||
customer: newEventCustomer,
|
||||
client_name: selectedCustomer?.name || '',
|
||||
resource: newEventResource,
|
||||
resource_name: selectedResource?.name || '',
|
||||
resource_phone: selectedResource?.phone || '',
|
||||
resource_address: selectedResource?.address || '',
|
||||
interpreter: newEventInterpreter,
|
||||
fasting: newEventFasting,
|
||||
need_id: newEventNeedId,
|
||||
new_patient: newEventNewPatient,
|
||||
disability: newEventDisability,
|
||||
trans_method: newEventTransMethod,
|
||||
}
|
||||
};
|
||||
|
||||
EventsService.createNewEvent(data).then(() => {
|
||||
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then((data) => {
|
||||
setAllEvents(data.data);
|
||||
handleClose();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -378,70 +525,12 @@ const EventsCalendar = () => {
|
||||
|
||||
<div className="app-main-content-list-container" style={{"min-width": "1500px"}}>
|
||||
<div className="app-main-content-list-func-container">
|
||||
<Tabs defaultActiveKey="eventsCalendar" id="events-calendar-tab">
|
||||
<Tab eventKey="eventsCalendar" title="Medical Appointments">
|
||||
<Tabs defaultActiveKey="medicalCalendar" id="events-calendar-tab">
|
||||
<Tab eventKey="medicalCalendar" title="Medical Appointments">
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
<div className="multi-columns-container" style={{ display: 'flex', alignItems: 'flex-start', width: '100%' }}>
|
||||
<div className="column-container" ref={calendarColumnRef} style={{ minWidth: '1000px', flexShrink: 0, display: 'flex', flexDirection: 'column' }}>
|
||||
{calendar && <ScheduleXCalendar customComponents={customComponents} calendarApp={calendar} />}
|
||||
{/* Legend */}
|
||||
<div className="calendar-legend mt-3">
|
||||
<h6 className="text-muted mb-2" style={{ fontSize: '12px' }}>Legend:</h6>
|
||||
<div className="d-flex flex-wrap gap-3">
|
||||
{EventsService.labelOptions?.map((item) => (
|
||||
<div key={item.value} className="d-flex align-items-center">
|
||||
<span
|
||||
className={`event-${item.value}`}
|
||||
style={{
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
borderRadius: '4px',
|
||||
display: 'inline-block',
|
||||
marginRight: '6px'
|
||||
}}
|
||||
></span>
|
||||
<span style={{ fontSize: '12px' }}>{item.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="column-container calendar-list-column" style={{ display: 'flex', flexDirection: 'column', flex: 1, minWidth: 0 }}>
|
||||
<div className="column-card" style={{ height: listHeight ? `${listHeight}px` : 'auto', overflowY: 'auto', overflowX: 'hidden', padding: '16px', display: 'flex', flexDirection: 'column', width: '100%', boxSizing: 'border-box' }}>
|
||||
<h6 className="text-primary mb-3">List</h6>
|
||||
<div style={{ flex: 1, overflowY: 'auto', width: '100%' }}>
|
||||
{(!groupedEvents || groupedEvents.size === 0) && (
|
||||
<div style={{ padding: '24px', textAlign: 'center', color: '#999' }}>
|
||||
No events for this period
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
Array.from(groupedEvents?.keys())?.map((key) => {
|
||||
return <div key={key}>
|
||||
<h6 className="text-primary me-2">{key}</h6>
|
||||
{
|
||||
groupedEvents.get(key).map(eventItem => <div
|
||||
key={eventItem.id}
|
||||
className={`event-${eventItem.color || 'primary'} mb-3 event-list-item-container`}
|
||||
onClick={() => goToView(eventItem.id)}
|
||||
style={{ cursor: 'pointer', padding: '8px 12px', borderRadius: '4px' }}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<span className="sx__month-agenda-event__title">{formatFullName(eventItem.customer)}</span>
|
||||
<span style={{ fontSize: '12px' }}>{moment(eventItem?.start_time).format('HH:mm')} - {moment(eventItem?.stop_time || eventItem?.start_time).format('HH:mm')}</span>
|
||||
</div>
|
||||
<div className="sx__event-modal__time" style={{ fontSize: '12px', marginTop: '4px' }}>Provider: {eventItem?.doctor || '-'}</div>
|
||||
</div>)
|
||||
}
|
||||
</div>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{ calendarView }
|
||||
<div className="list-func-panel">
|
||||
<Dropdown
|
||||
key={'event-calendar-filter'}
|
||||
@@ -456,10 +545,83 @@ const EventsCalendar = () => {
|
||||
<Dropdown.Menu as={customMenu}/>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<Modal show={showCreationModal} onHide={handleClose} size="sm" dialogClassName="calendar-event-modal">
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>New Medical Appointment</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Customer<span className="required">*</span></div>
|
||||
<select className="form-control" value={newEventCustomer} onChange={(e) => setNewEventCustomer(e.target.value)}>
|
||||
<option value="">Select Customer</option>
|
||||
{customers.filter(item => item?.status === 'active' && item?.type !== 'discharged').map((item) => <option key={item.id} value={item.id}>{item.name}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Provider<span className="required">*</span></div>
|
||||
<select className="form-control" value={newEventResource} onChange={(e) => setNewEventResource(e.target.value)}>
|
||||
<option value="">Select Provider</option>
|
||||
{resources.filter(item => item?.status === 'active').map((item) => <option key={item.id} value={item.id}>{item.name}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Appointment Time<span className="required">*</span></div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={newEventStartDateTime}
|
||||
onChange={setNewEventStartDateTime}
|
||||
showTimeSelect
|
||||
timeFormat="HH:mm"
|
||||
timeIntervals={15}
|
||||
dateFormat="MM/dd/yyyy, HH:mm"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Language Support</div>
|
||||
<select className="form-control" value={newEventInterpreter} onChange={(e) => setNewEventInterpreter(e.target.value)}>
|
||||
<option value="">Select</option>
|
||||
<option value="Checkin">Checkin</option>
|
||||
<option value="Center">Center</option>
|
||||
<option value="Family">Family</option>
|
||||
<option value="Office in person">Office in person</option>
|
||||
<option value="Office by phone">Office by phone</option>
|
||||
<option value="Nurse">Nurse</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Transportation Support</div>
|
||||
<select className="form-control" value={newEventTransMethod} onChange={(e) => setNewEventTransMethod(e.target.value)}>
|
||||
<option value="">Select</option>
|
||||
<option value="ByOwn">Self-Transport</option>
|
||||
<option value="By Center Transportation">By Center Transportation</option>
|
||||
<option value="DropOff Only">Drop-Off Only</option>
|
||||
<option value="Pickup Only">Pick-Up Only</option>
|
||||
<option value="Client Does Not need to Go">Medication Pickup Only</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Label</div>
|
||||
<select className="form-control" value={newEventColor} onChange={(e) => setNewEventColor(e.target.value)}>
|
||||
<option value="">Select</option>
|
||||
{EventsService.labelOptions?.map((item) => <option key={item.value} value={item.value}>{item.label}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" size="sm" onClick={handleClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="primary" size="sm" onClick={handleSave} disabled={
|
||||
!newEventCustomer || !newEventResource || !newEventStartDateTime
|
||||
}>
|
||||
Save
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
};
|
||||
|
||||
export default EventsCalendar;
|
||||
export default EventsCalendar;
|
||||
|
||||
Reference in New Issue
Block a user