108 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			108 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
|  "use client";
 | |
|  import React from "react";
 | |
|  import { useRouter } from "next/navigation";
 | |
|  import { api } from "@/lib/api";
 | |
|  import { Input } from "@/components/ui/input";
 | |
|  import { Button } from "@/components/ui/button";
 | |
| 
 | |
|  export default function TransmittalNew() {
 | |
|    const router = useRouter();
 | |
|    const [draftId, setDraftId] = React.useState(null);
 | |
|    const [saving, setSaving] = React.useState(false);
 | |
|    const [savedAt, setSavedAt] = React.useState(null);
 | |
|    const [error, setError] = React.useState("");
 | |
|    const [form, setForm] = React.useState({
 | |
|      subject: "", number: "", to_party: "", sent_date: "", description: ""
 | |
|    });
 | |
|    const [errs, setErrs] = React.useState({});
 | |
| 
 | |
|    const validate = (f) => {
 | |
|      const e = {};
 | |
|      if (!f.subject?.trim()) e.subject = "กรุณากรอกเรื่อง (Subject)";
 | |
|      if (!f.to_party?.trim()) e.to_party = "กรุณาระบุผู้รับ (To)";
 | |
|      if (!f.sent_date) e.sent_date = "กรุณาระบุวันที่ส่ง";
 | |
|      return e;
 | |
|    };
 | |
| 
 | |
|    const tRef = React.useRef(0);
 | |
|    React.useEffect(() => {
 | |
|      clearTimeout(tRef.current);
 | |
|      tRef.current = window.setTimeout(async () => {
 | |
|        const e = validate(form);
 | |
|        setErrs(e);
 | |
|        try {
 | |
|          setSaving(true);
 | |
|          if (!draftId) {
 | |
|            const res = await api("/transmittals", { method: "POST", body: { ...form, status: "draft" } });
 | |
|            setDraftId(res.id);
 | |
|          } else {
 | |
|            await api(`/transmittals/${draftId}`, { method: "PATCH", body: { ...form, status: "draft" } });
 | |
|          }
 | |
|          setSavedAt(new Date());
 | |
|        } catch (err) {
 | |
|          setError(err.message || "บันทึกฉบับร่างไม่สำเร็จ");
 | |
|        } finally {
 | |
|          setSaving(false);
 | |
|        }
 | |
|      }, 800);
 | |
|      return () => clearTimeout(tRef.current);
 | |
|    }, [form, draftId]);
 | |
| 
 | |
|    const onSubmit = async (e) => {
 | |
|      e.preventDefault();
 | |
|      const eobj = validate(form);
 | |
|      setErrs(eobj);
 | |
|      if (Object.keys(eobj).length) return;
 | |
|      try {
 | |
|        setSaving(true);
 | |
|        const id = draftId
 | |
|          ? (await api(`/transmittals/${draftId}`, { method: "PATCH", body: { ...form, status: "submitted" } })).id || draftId
 | |
|          : (await api("/transmittals", { method: "POST", body: { ...form, status: "submitted" } })).id;
 | |
|        router.replace(`/transmittals`);
 | |
|      } catch (err) {
 | |
|        setError(err.message || "ส่ง Transmittal ไม่สำเร็จ");
 | |
|      } finally {
 | |
|        setSaving(false);
 | |
|      }
 | |
|    };
 | |
| 
 | |
|    return (
 | |
|      <form onSubmit={onSubmit} className="space-y-4 rounded-2xl p-5 bg-white">
 | |
|        <div className="text-lg font-semibold">สร้าง Transmittal</div>
 | |
|        {error && <div className="text-sm text-red-600">{error}</div>}
 | |
|        <div className="grid md:grid-cols-2 gap-3">
 | |
|          <div>
 | |
|            <label className="text-sm">เรื่อง (Subject) *</label>
 | |
|            <Input value={form.subject} onChange={(e)=>setForm(f=>({...f, subject:e.target.value}))}/>
 | |
|            {errs.subject && <div className="text-xs text-red-600 mt-1">{errs.subject}</div>}
 | |
|          </div>
 | |
|          <div>
 | |
|            <label className="text-sm">เลขที่ (ถ้ามี)</label>
 | |
|            <Input value={form.number} onChange={(e)=>setForm(f=>({...f, number:e.target.value}))}/>
 | |
|          </div>
 | |
|          <div>
 | |
|            <label className="text-sm">ถึง (To) *</label>
 | |
|            <Input value={form.to_party} onChange={(e)=>setForm(f=>({...f, to_party:e.target.value}))}/>
 | |
|            {errs.to_party && <div className="text-xs text-red-600 mt-1">{errs.to_party}</div>}
 | |
|          </div>
 | |
|          <div>
 | |
|            <label className="text-sm">วันที่ส่ง *</label>
 | |
|            <input type="date" className="border rounded-xl p-2 w-full" value={form.sent_date}
 | |
|                   onChange={(e)=>setForm(f=>({...f, sent_date:e.target.value}))}/>
 | |
|            {errs.sent_date && <div className="text-xs text-red-600 mt-1">{errs.sent_date}</div>}
 | |
|          </div>
 | |
|        </div>
 | |
|        <div>
 | |
|          <label className="text-sm">รายละเอียด</label>
 | |
|          <textarea rows={5} className="border rounded-xl p-2 w-full"
 | |
|                    value={form.description} onChange={(e)=>setForm(f=>({...f, description:e.target.value}))}/>
 | |
|        </div>
 | |
|        <div className="flex items-center gap-3">
 | |
|          <Button type="submit" disabled={saving}>ส่ง Transmittal</Button>
 | |
|          <span className="text-sm opacity-70">
 | |
|            {saving ? "กำลังบันทึก…" : savedAt ? `บันทึกล่าสุด ${savedAt.toLocaleTimeString()}` : "ยังไม่เคยบันทึก"}
 | |
|          </span>
 | |
|        </div>
 | |
|      </form>
 | |
|    );
 | |
|  } |