import React, { useEffect, useState, useMemo } from 'react';
import toast from 'react-hot-toast';
import { useFlutter } from '../../contexts/FlutterContext';
import { useAuth } from '../../hooks/useAuth';
import WardSelectDialog from '../common/WardSelectDialog';
import Modal from '../ui/Modal';
/**
* MemoForm component for creating a new memo.
*
* @param {Object} props - The component props.
* @param {function} props.onSuccess - Callback function to be executed on successful memo creation.
* @param {function} props.onCancel - Callback function to be executed when the form creation is canceled.
*/
const MemoForm = ({ onSuccess, onCancel }) => {
const { apiService, hospital, user, updateAuth } = useAuth();
const { updateUserInFlutter } = useFlutter();
const hospitalId = hospital?.id || localStorage.getItem('hospitalId');
const [roles, setRoles] = useState([]);
const [loading, setLoading] = useState(false);
const [createdMemoUuid, setCreatedMemoUuid] = useState(null);
const [showWardConfirm, setShowWardConfirm] = useState(true);
const [showWardSelect, setShowWardSelect] = useState(false);
const [formData, setFormData] = useState({
complaint: '',
department: '',
tagged_role_id: '',
ward_id: user?.current_ward_id || '',
});
/**
* Generates a unique UUID for the memo using `crypto.randomUUID`.
* This UUID is generated only once when the component mounts.
*
* @type {string}
*/
const memoUuid = useMemo(() => crypto.randomUUID(), []);
useEffect(() => {
const fetchRoles = async () => {
try {
const data = await apiService.getRoles(hospitalId);
const filtered = (data || []).filter(
(r) => r?.name?.toLowerCase() !== 'admin' && r?.is_responder
);
setRoles(filtered);
} catch (err) {
console.error(err);
toast.error('Failed to fetch roles');
}
};
fetchRoles();
}, [apiService, hospitalId]);
/**
* Handles changes to the form input fields.
* Updates the `formData` state with the new value.
*
* @param {object} e - The change event object.
*/
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
};
/**
* Handles the form submission.
* Validates the form, creates the memo via the API, and handles success/failure.
*/
const handleSubmit = async (e) => {
e.preventDefault();
const { complaint, department, tagged_role_id } = formData;
if (!complaint || !department || !tagged_role_id) {
toast.error('Please fill all fields');
return;
}
setLoading(true);
try {
const taggedRole = roles.find((r) => r.id === parseInt(tagged_role_id));
await apiService.createMemo(hospitalId, {
uuid: memoUuid,
payload: {
...formData,
tagged_role_name: taggedRole?.name || '',
},
metadata: JSON.parse(localStorage.getItem('user')) || {},
});
toast.success('Memo created');
setCreatedMemoUuid(memoUuid); // Show the created memo title
onSuccess(memoUuid);
} catch (err) {
console.error(err);
toast.error('Failed to create memo');
} finally {
setLoading(false);
}
};
/**
* Handles the ward change from the WardSelectDialog.
* Switches the user's current ward via the API and updates authentication state.
*
* @param {object} ward - The selected ward object.
*/
const handleWardChange = async (ward) => {
try {
setLoading(true);
await apiService.switchWard(ward.id);
const data = await apiService.refreshUser();
updateAuth(data.user, data.hospital, data.token);
updateUserInFlutter();
toast.success('Ward switched');
setShowWardConfirm(false);
setShowWardSelect(false);
} catch (err) {
console.error(err);
toast.error('Failed to switch ward');
} finally {
setLoading(false);
}
};
return (
<>
{showWardConfirm && (
<Modal
isOpen={true}
onClose={() => {
setShowWardConfirm(false);
onCancel();
}}
title="Confirm Ward"
size="sm"
className="z-50"
>
{/* Replace blur with darkened overlay */}
<div className="fixed inset-0 bg-black/60 z-40" />
<div className="fixed inset-0 z-50 flex items-center justify-center">
<div className="bg-white rounded-xl border border-gray-300 shadow-2xl p-6 max-w-sm w-full mx-4 brightness-110">
<div className="space-y-4 text-sm text-gray-700">
{user?.current_ward_id ? (
<>
<p>You are currently in:</p>
<div className="space-y-1 pl-2">
<p><strong>Ward:</strong> {user?.current_ward_name || 'N/A'}</p>
<p><strong>Floor:</strong> {user?.floor || 'N/A'}</p>
<p><strong>Block:</strong> {user?.current_block_name || 'N/A'}</p>
</div>
<p className="pt-2">
The memo will be created for the above ward. Do you want to proceed?
</p>
</>
) : (
<p>You are not currently in a ward. Please select a ward for this memo.</p>
)}
<div className="flex justify-end gap-2 pt-2">
{user?.current_ward_id && (
<button
onClick={() => setShowWardConfirm(false)}
className="px-3 py-1 rounded bg-blue-600 text-white text-sm"
>
Yes
</button>
)}
<button
onClick={() => {
setShowWardConfirm(false);
setShowWardSelect(true);
}}
className="px-3 py-1 rounded bg-gray-200 text-gray-700 text-sm"
>
Change Ward
</button>
</div>
</div>
</div>
</div>
</Modal>
)}
{showWardSelect && (
<WardSelectDialog
isOpen={true}
onClose={() => {
setShowWardSelect(false);
onCancel();
}}
apiService={apiService}
hospitalId={hospitalId}
onSelect={handleWardChange}
/>
)}
{!showWardConfirm && !showWardSelect && (
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Complaint</label>
<textarea
name="complaint"
value={formData.complaint}
onChange={handleChange}
className="w-full border rounded p-2"
rows={3}
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Department</label>
<input
type="text"
name="department"
value={formData.department}
onChange={handleChange}
className="w-full border rounded p-2"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Role to Tag</label>
<select
name="tagged_role_id"
value={formData.tagged_role_id}
onChange={handleChange}
className="w-full border rounded p-2"
required
>
<option value="" disabled>Select role</option>
{roles.map((r) => (
<option key={r.id} value={r.id}>
{r.name}
</option>
))}
</select>
</div>
<div className="flex justify-end space-x-2 pt-2">
{!loading && (
<button
type="button"
className="px-4 py-2 rounded bg-gray-200 text-gray-700"
onClick={onCancel}
>
Cancel
</button>
)}
<button
type="submit"
disabled={loading}
className="px-4 py-2 rounded bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-60"
>
{loading ? 'Creating...' : 'Create'}
</button>
</div>
{createdMemoUuid && (
<div className="mt-4 p-3 bg-green-50 border border-green-200 rounded text-sm text-green-800">
✅ Memo Created Successfully with ID: <strong>{createdMemoUuid}</strong>
</div>
)}
</form>
)}
</>
);
};
export default MemoForm;
Source