Source

components/memos/MemoForm.jsx

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;