fork download
  1. import React, { useState, useEffect, useRef } from "react";
  2. import jsPDF from "jspdf";
  3. import autoTable from "jspdf-autotable";
  4.  
  5. export default function ReceiptApp() {
  6. const sidebarRef = useRef(null);
  7. const [sidebarOpen, setSidebarOpen] = useState(false);
  8. const [formData, setFormData] = useState({
  9. name: "",
  10. amount: "",
  11. date: "",
  12. idCard: null,
  13. email: "",
  14. phone: "",
  15. });
  16. const [receipts, setReceipts] = useState([]);
  17. const [visits, setVisits] = useState(0);
  18. const [step, setStep] = useState("กรอกข้อมูล");
  19.  
  20. useEffect(() => {
  21. const stored = localStorage.getItem("visits") || 0;
  22. const next = parseInt(stored) + 1;
  23. setVisits(next);
  24. localStorage.setItem("visits", next);
  25.  
  26. const handleMouseMove = (e) => {
  27. if (sidebarRef.current && e.clientX < 50) {
  28. setSidebarOpen(true);
  29. } else if (sidebarRef.current && e.clientX > 300) {
  30. setSidebarOpen(false);
  31. }
  32. };
  33. window.addEventListener("mousemove", handleMouseMove);
  34. return () => window.removeEventListener("mousemove", handleMouseMove);
  35. }, []);
  36.  
  37. const handleChange = (e) => {
  38. const { name, value, files } = e.target;
  39. if (name === "idCard") {
  40. setFormData({ ...formData, idCard: files[0] });
  41. } else {
  42. setFormData({ ...formData, [name]: value });
  43. }
  44. };
  45.  
  46. const generateReferenceCode = () => {
  47. return "REF-" + Math.random().toString(36).substring(2, 8).toUpperCase();
  48. };
  49.  
  50. const handleSubmit = (e) => {
  51. e.preventDefault();
  52. const { name, amount, date, idCard, email, phone } = formData;
  53. if (!name || !amount || !date || !idCard || !email || !phone) {
  54. alert("⚠️ กรุณากรอกข้อมูลให้ครบทุกช่องก่อนส่ง");
  55. return;
  56. }
  57. if (!confirm("คุณต้องการยืนยันการบันทึกข้อมูลใช่หรือไม่?")) return;
  58. const newReceipt = {
  59. ...formData,
  60. id: Date.now(),
  61. approved: false,
  62. reference: generateReferenceCode(),
  63. statusDetail: "ข้อมูลถูกส่งเรียบร้อย รอการอนุมัติภายใน 3 วัน โดยหัวหน้าฝ่ายการเงิน",
  64. approver: "นางสาวสมฤดี ใจดี",
  65. approveDate: new Date(Date.now() + 3 * 86400000).toLocaleDateString(),
  66. };
  67. setReceipts([...receipts, newReceipt]);
  68. setFormData({ name: "", amount: "", date: "", idCard: null, email: "", phone: "" });
  69. setStep("รอการอนุมัติ");
  70. };
  71.  
  72. const handleApprove = (id) => {
  73. setReceipts(receipts.map((r) => (r.id === id ? { ...r, approved: true } : r)));
  74. };
  75.  
  76. const exportPDF = () => {
  77. const doc = new jsPDF();
  78. doc.setFontSize(18);
  79. doc.text("ใบสำคัญรับเงิน - รายงานทั้งหมด", 105, 15, { align: "center" });
  80. autoTable(doc, {
  81. startY: 30,
  82. head: [["รหัสอ้างอิง", "ชื่อ", "จำนวน", "วันที่", "สถานะ"]],
  83. body: receipts.map((r) => [r.reference, r.name, r.amount, r.date, r.approved ? "✔ อนุมัติ" : "⏳ รอดำเนินการ"]),
  84. });
  85. doc.save("receipts.pdf");
  86. };
  87.  
  88. return (
  89. <div className="flex flex-col min-h-screen bg-gradient-to-br from-slate-50 via-purple-100 to-indigo-100 font-sans">
  90. <div className="flex flex-1 overflow-hidden">
  91. <div
  92. ref={sidebarRef}
  93. className={`bg-gradient-to-b from-indigo-900 to-purple-700 text-white transition-all duration-300 ${sidebarOpen ? "w-72" : "w-16"} flex flex-col shadow-2xl rounded-tr-3xl rounded-br-3xl`}
  94. >
  95. <div className="flex items-center justify-between p-4">
  96. <span className="text-xl font-extrabold tracking-wider">
  97. {sidebarOpen ? "✨ ReceiptPro Ultra" : "✨"}
  98. </span>
  99. </div>
  100. <nav className="flex flex-col space-y-3 px-3 mt-4 text-sm">
  101. <button className="hover:bg-purple-600 rounded-xl p-3 text-left font-medium">
  102. {sidebarOpen ? "📝 กรอกข้อมูล" : "📝"}
  103. </button>
  104. <button className="hover:bg-purple-600 rounded-xl p-3 text-left font-medium">
  105. {sidebarOpen ? "📂 ประวัติรับเงิน" : "📂"}
  106. </button>
  107. <button
  108. onClick={exportPDF}
  109. className="hover:bg-purple-600 rounded-xl p-3 text-left font-medium"
  110. >
  111. {sidebarOpen ? "📄 ดาวน์โหลด PDF" : "📄"}
  112. </button>
  113. </nav>
  114. </div>
  115.  
  116. <div className="flex-1 overflow-y-auto p-8">
  117. <div className="bg-white p-10 rounded-3xl shadow-2xl border border-purple-200">
  118. <h1 className="text-4xl font-extrabold text-purple-700 mb-2 text-center tracking-tight">
  119. 🚀 ระบบใบสำคัญรับเงิน
  120. </h1>
  121. <p className="text-center text-indigo-600 font-medium mb-6">🔄 ขั้นตอนล่าสุด: {step}</p>
  122.  
  123. <form onSubmit={handleSubmit} className="grid gap-5 grid-cols-1 md:grid-cols-3">
  124. <input type="text" name="name" value={formData.name} onChange={handleChange} placeholder="ชื่อผู้รับเงิน" className="p-3 border rounded-xl shadow-inner focus:outline-none focus:ring-2 focus:ring-purple-400" required />
  125. <input type="email" name="email" value={formData.email} onChange={handleChange} placeholder="อีเมล" className="p-3 border rounded-xl shadow-inner focus:outline-none focus:ring-2 focus:ring-purple-400" required />
  126. <input type="tel" name="phone" value={formData.phone} onChange={handleChange} placeholder="เบอร์โทรศัพท์" className="p-3 border rounded-xl shadow-inner focus:outline-none focus:ring-2 focus:ring-purple-400" required />
  127. <input type="number" name="amount" value={formData.amount} onChange={handleChange} placeholder="จำนวนเงิน (บาท)" className="p-3 border rounded-xl shadow-inner focus:outline-none focus:ring-2 focus:ring-purple-400" required />
  128. <input type="date" name="date" value={formData.date} onChange={handleChange} className="p-3 border rounded-xl shadow-inner focus:outline-none focus:ring-2 focus:ring-purple-400" required />
  129. <input type="file" name="idCard" accept="image/*,.pdf" onChange={handleChange} className="p-3 border rounded-xl" required />
  130. <button type="submit" className="col-span-full bg-purple-600 hover:bg-purple-700 text-white font-semibold py-3 rounded-xl shadow-lg transition">💾 บันทึกข้อมูล</button>
  131. </form>
  132.  
  133. <div className="mt-12">
  134. <h2 className="text-2xl font-bold text-indigo-700 mb-5">📜 ประวัติการรับเงิน</h2>
  135. <ul className="space-y-4">
  136. {receipts.map((item) => (
  137. <li key={item.id} className="bg-white border border-purple-200 rounded-xl p-4 shadow-md">
  138. <div className="grid grid-cols-1 md:grid-cols-5 gap-x-4 gap-y-2 text-sm">
  139. <p><strong>รหัส:</strong> {item.reference}</p>
  140. <p><strong>ชื่อ:</strong> {item.name}</p>
  141. <p><strong>จำนวน:</strong> {item.amount} บาท</p>
  142. <p><strong>วันที่:</strong> {item.date}</p>
  143. <p><strong>สถานะ:</strong> {item.approved ? "✔ อนุมัติแล้ว" : "⏳ รออนุมัติ"}</p>
  144. </div>
  145. <div className="text-xs text-gray-600 mt-2">
  146. <p>{item.statusDetail}</p>
  147. <p>กำหนดอนุมัติ: {item.approveDate} โดย {item.approver}</p>
  148. </div>
  149. {!item.approved && (
  150. <button onClick={() => handleApprove(item.id)} className="mt-3 bg-emerald-500 hover:bg-emerald-600 text-white px-4 py-2 rounded-xl shadow">
  151. ✅ ร้องขออนุมัติ
  152. </button>
  153. )}
  154. </li>
  155. ))}
  156. {receipts.length === 0 && <p className="text-gray-500 text-center">ไม่มีข้อมูลในระบบ</p>}
  157. </ul>
  158. </div>
  159. </div>
  160. </div>
  161. </div>
  162.  
  163. <footer className="bg-white text-center py-5 text-sm text-gray-600 border-t mt-8">
  164. <div>© {new Date().getFullYear()} พัฒนาโดย ReceiptPro Ultra Team</div>
  165. <div>จำนวนผู้เข้าใช้งาน: {visits}</div>
  166. <div>วันที่: {new Date().toLocaleDateString()} เวลา: {new Date().toLocaleTimeString()}</div>
  167. </footer>
  168. </div>
  169. );
  170. }
  171.  
Success #stdin #stdout 0.03s 26096KB
stdin
Standard input is empty
stdout
import React, { useState, useEffect, useRef } from "react";
import jsPDF from "jspdf";
import autoTable from "jspdf-autotable";

export default function ReceiptApp() {
  const sidebarRef = useRef(null);
  const [sidebarOpen, setSidebarOpen] = useState(false);
  const [formData, setFormData] = useState({
    name: "",
    amount: "",
    date: "",
    idCard: null,
    email: "",
    phone: "",
  });
  const [receipts, setReceipts] = useState([]);
  const [visits, setVisits] = useState(0);
  const [step, setStep] = useState("กรอกข้อมูล");

  useEffect(() => {
    const stored = localStorage.getItem("visits") || 0;
    const next = parseInt(stored) + 1;
    setVisits(next);
    localStorage.setItem("visits", next);

    const handleMouseMove = (e) => {
      if (sidebarRef.current && e.clientX < 50) {
        setSidebarOpen(true);
      } else if (sidebarRef.current && e.clientX > 300) {
        setSidebarOpen(false);
      }
    };
    window.addEventListener("mousemove", handleMouseMove);
    return () => window.removeEventListener("mousemove", handleMouseMove);
  }, []);

  const handleChange = (e) => {
    const { name, value, files } = e.target;
    if (name === "idCard") {
      setFormData({ ...formData, idCard: files[0] });
    } else {
      setFormData({ ...formData, [name]: value });
    }
  };

  const generateReferenceCode = () => {
    return "REF-" + Math.random().toString(36).substring(2, 8).toUpperCase();
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    const { name, amount, date, idCard, email, phone } = formData;
    if (!name || !amount || !date || !idCard || !email || !phone) {
      alert("⚠️ กรุณากรอกข้อมูลให้ครบทุกช่องก่อนส่ง");
      return;
    }
    if (!confirm("คุณต้องการยืนยันการบันทึกข้อมูลใช่หรือไม่?")) return;
    const newReceipt = {
      ...formData,
      id: Date.now(),
      approved: false,
      reference: generateReferenceCode(),
      statusDetail: "ข้อมูลถูกส่งเรียบร้อย รอการอนุมัติภายใน 3 วัน โดยหัวหน้าฝ่ายการเงิน",
      approver: "นางสาวสมฤดี ใจดี",
      approveDate: new Date(Date.now() + 3 * 86400000).toLocaleDateString(),
    };
    setReceipts([...receipts, newReceipt]);
    setFormData({ name: "", amount: "", date: "", idCard: null, email: "", phone: "" });
    setStep("รอการอนุมัติ");
  };

  const handleApprove = (id) => {
    setReceipts(receipts.map((r) => (r.id === id ? { ...r, approved: true } : r)));
  };

  const exportPDF = () => {
    const doc = new jsPDF();
    doc.setFontSize(18);
    doc.text("ใบสำคัญรับเงิน - รายงานทั้งหมด", 105, 15, { align: "center" });
    autoTable(doc, {
      startY: 30,
      head: [["รหัสอ้างอิง", "ชื่อ", "จำนวน", "วันที่", "สถานะ"]],
      body: receipts.map((r) => [r.reference, r.name, r.amount, r.date, r.approved ? "✔ อนุมัติ" : "⏳ รอดำเนินการ"]),
    });
    doc.save("receipts.pdf");
  };

  return (
    <div className="flex flex-col min-h-screen bg-gradient-to-br from-slate-50 via-purple-100 to-indigo-100 font-sans">
      <div className="flex flex-1 overflow-hidden">
        <div
          ref={sidebarRef}
          className={`bg-gradient-to-b from-indigo-900 to-purple-700 text-white transition-all duration-300 ${sidebarOpen ? "w-72" : "w-16"} flex flex-col shadow-2xl rounded-tr-3xl rounded-br-3xl`}
        >
          <div className="flex items-center justify-between p-4">
            <span className="text-xl font-extrabold tracking-wider">
              {sidebarOpen ? "✨ ReceiptPro Ultra" : "✨"}
            </span>
          </div>
          <nav className="flex flex-col space-y-3 px-3 mt-4 text-sm">
            <button className="hover:bg-purple-600 rounded-xl p-3 text-left font-medium">
              {sidebarOpen ? "📝 กรอกข้อมูล" : "📝"}
            </button>
            <button className="hover:bg-purple-600 rounded-xl p-3 text-left font-medium">
              {sidebarOpen ? "📂 ประวัติรับเงิน" : "📂"}
            </button>
            <button
              onClick={exportPDF}
              className="hover:bg-purple-600 rounded-xl p-3 text-left font-medium"
            >
              {sidebarOpen ? "📄 ดาวน์โหลด PDF" : "📄"}
            </button>
          </nav>
        </div>

        <div className="flex-1 overflow-y-auto p-8">
          <div className="bg-white p-10 rounded-3xl shadow-2xl border border-purple-200">
            <h1 className="text-4xl font-extrabold text-purple-700 mb-2 text-center tracking-tight">
              🚀 ระบบใบสำคัญรับเงิน
            </h1>
            <p className="text-center text-indigo-600 font-medium mb-6">🔄 ขั้นตอนล่าสุด: {step}</p>

            <form onSubmit={handleSubmit} className="grid gap-5 grid-cols-1 md:grid-cols-3">
              <input type="text" name="name" value={formData.name} onChange={handleChange} placeholder="ชื่อผู้รับเงิน" className="p-3 border rounded-xl shadow-inner focus:outline-none focus:ring-2 focus:ring-purple-400" required />
              <input type="email" name="email" value={formData.email} onChange={handleChange} placeholder="อีเมล" className="p-3 border rounded-xl shadow-inner focus:outline-none focus:ring-2 focus:ring-purple-400" required />
              <input type="tel" name="phone" value={formData.phone} onChange={handleChange} placeholder="เบอร์โทรศัพท์" className="p-3 border rounded-xl shadow-inner focus:outline-none focus:ring-2 focus:ring-purple-400" required />
              <input type="number" name="amount" value={formData.amount} onChange={handleChange} placeholder="จำนวนเงิน (บาท)" className="p-3 border rounded-xl shadow-inner focus:outline-none focus:ring-2 focus:ring-purple-400" required />
              <input type="date" name="date" value={formData.date} onChange={handleChange} className="p-3 border rounded-xl shadow-inner focus:outline-none focus:ring-2 focus:ring-purple-400" required />
              <input type="file" name="idCard" accept="image/*,.pdf" onChange={handleChange} className="p-3 border rounded-xl" required />
              <button type="submit" className="col-span-full bg-purple-600 hover:bg-purple-700 text-white font-semibold py-3 rounded-xl shadow-lg transition">💾 บันทึกข้อมูล</button>
            </form>

            <div className="mt-12">
              <h2 className="text-2xl font-bold text-indigo-700 mb-5">📜 ประวัติการรับเงิน</h2>
              <ul className="space-y-4">
                {receipts.map((item) => (
                  <li key={item.id} className="bg-white border border-purple-200 rounded-xl p-4 shadow-md">
                    <div className="grid grid-cols-1 md:grid-cols-5 gap-x-4 gap-y-2 text-sm">
                      <p><strong>รหัส:</strong> {item.reference}</p>
                      <p><strong>ชื่อ:</strong> {item.name}</p>
                      <p><strong>จำนวน:</strong> {item.amount} บาท</p>
                      <p><strong>วันที่:</strong> {item.date}</p>
                      <p><strong>สถานะ:</strong> {item.approved ? "✔ อนุมัติแล้ว" : "⏳ รออนุมัติ"}</p>
                    </div>
                    <div className="text-xs text-gray-600 mt-2">
                      <p>{item.statusDetail}</p>
                      <p>กำหนดอนุมัติ: {item.approveDate} โดย {item.approver}</p>
                    </div>
                    {!item.approved && (
                      <button onClick={() => handleApprove(item.id)} className="mt-3 bg-emerald-500 hover:bg-emerald-600 text-white px-4 py-2 rounded-xl shadow">
                        ✅ ร้องขออนุมัติ
                      </button>
                    )}
                  </li>
                ))}
                {receipts.length === 0 && <p className="text-gray-500 text-center">ไม่มีข้อมูลในระบบ</p>}
              </ul>
            </div>
          </div>
        </div>
      </div>

      <footer className="bg-white text-center py-5 text-sm text-gray-600 border-t mt-8">
        <div>© {new Date().getFullYear()} พัฒนาโดย ReceiptPro Ultra Team</div>
        <div>จำนวนผู้เข้าใช้งาน: {visits}</div>
        <div>วันที่: {new Date().toLocaleDateString()} เวลา: {new Date().toLocaleTimeString()}</div>
      </footer>
    </div>
  );
}