110 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			110 lines
		
	
	
		
			4.8 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 RfaNew() {
 | |
| +   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({
 | |
| +     title: "", code: "", discipline: "", due_date: "", description: ""
 | |
| +   });
 | |
| +   const [errs, setErrs] = React.useState({});
 | |
| +
 | |
| +   // simple validate (client)
 | |
| +   const validate = (f) => {
 | |
| +     const e = {};
 | |
| +     if (!f.title?.trim()) e.title = "กรุณากรอกชื่อเรื่อง";
 | |
| +     if (!f.due_date) e.due_date = "กรุณากำหนดวันที่ครบกำหนด";
 | |
| +     return e;
 | |
| +   };
 | |
| +
 | |
| +   // debounce autosave
 | |
| +   const tRef = React.useRef(0);
 | |
| +   React.useEffect(() => {
 | |
| +     clearTimeout(tRef.current);
 | |
| +     tRef.current = window.setTimeout(async () => {
 | |
| +       const e = validate(form);
 | |
| +       setErrs(e); // แสดง error ทันที (soft)
 | |
| +       try {
 | |
| +         setSaving(true);
 | |
| +         if (!draftId) {
 | |
| +           // create draft
 | |
| +           const res = await api("/rfas", { method: "POST", body: { ...form, status: "draft" } });
 | |
| +           setDraftId(res.id);
 | |
| +         } else {
 | |
| +           // update draft
 | |
| +           await api(`/rfas/${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(`/rfas/${draftId}`, { method: "PATCH", body: { ...form, status: "submitted" } })).id || draftId
 | |
| +         : (await api("/rfas", { method: "POST", body: { ...form, status: "submitted" } })).id;
 | |
| +       router.replace(`/rfas`); // หรือไปหน้า detail `/rfas/${id}`
 | |
| +     } catch (err) {
 | |
| +       setError(err.message || "ส่งคำขอไม่สำเร็จ");
 | |
| +     } finally {
 | |
| +       setSaving(false);
 | |
| +     }
 | |
| +   };
 | |
| +
 | |
| +   return (
 | |
| +     <form onSubmit={onSubmit} className="space-y-4 rounded-2xl p-5 bg-white">
 | |
| +       <div className="text-lg font-semibold">สร้าง RFA</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">ชื่อเรื่อง *</label>
 | |
| +           <Input value={form.title} onChange={(e)=>setForm(f=>({...f, title:e.target.value}))}/>
 | |
| +           {errs.title && <div className="text-xs text-red-600 mt-1">{errs.title}</div>}
 | |
| +         </div>
 | |
| +         <div>
 | |
| +           <label className="text-sm">รหัส (ถ้ามี)</label>
 | |
| +           <Input value={form.code} onChange={(e)=>setForm(f=>({...f, code:e.target.value}))}/>
 | |
| +         </div>
 | |
| +         <div>
 | |
| +           <label className="text-sm">สาขา/หมวด (Discipline)</label>
 | |
| +           <Input value={form.discipline} onChange={(e)=>setForm(f=>({...f, discipline:e.target.value}))}/>
 | |
| +         </div>
 | |
| +         <div>
 | |
| +           <label className="text-sm">กำหนดส่ง *</label>
 | |
| +           <input type="date" className="border rounded-xl p-2 w-full" value={form.due_date}
 | |
| +                  onChange={(e)=>setForm(f=>({...f, due_date:e.target.value}))}/>
 | |
| +           {errs.due_date && <div className="text-xs text-red-600 mt-1">{errs.due_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}>ส่งเพื่อพิจารณา</Button>
 | |
| +         <span className="text-sm opacity-70">
 | |
| +           {saving ? "กำลังบันทึก…" : savedAt ? `บันทึกล่าสุด ${savedAt.toLocaleTimeString()}` : "ยังไม่เคยบันทึก"}
 | |
| +         </span>
 | |
| +       </div>
 | |
| +     </form>
 | |
| +   );
 | |
| + } |