แอปบันทึกรายรับ-รายจ่ายส่วนตัวและแบบ สมุดบัญชีร่วมกับคนอื่น พัฒนาด้วย React + TypeScript และ Firebase ข้อมูลอัปเดตแบบ Realtime, ล็อกอินด้วย Google, รองรับ Light/Dark mode และใช้งานได้ทั้งคอมพิวเตอร์และมือถือ
🌐 ใช้งานจริง: https://money-7d89c.web.app
ภาพทั้งหมดถ่ายจากข้อมูลตัวอย่าง (ผู้ใช้สมมติ Alice / Bob / Carol) ไม่ใช่ข้อมูลจริงของใคร
| ภาพรวม (Light) | ภาพรวม (Dark) |
|---|---|
![]() |
![]() |
| บันทึกรายการ | สรุปรายสัปดาห์/เดือน |
![]() |
![]() |
| รายวัน | สมุดร่วม — เจ้าของอนุมัติสมาชิก |
![]() |
![]() |
| ภาพรวม | บันทึกรายการ | ภาพรวม (Dark) | ลบบัญชี |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
| หมวด | รายละเอียด |
|---|---|
| 🔐 บัญชี | ล็อกอินด้วย Google, แก้ชื่อ/รูปโปรไฟล์, ลบบัญชีและข้อมูลทั้งหมดได้เองในแอป |
| 💸 บันทึก | รายรับ/รายจ่าย 13 หมวดหมู่ พร้อมหมายเหตุ, ปุ่มจำนวนเงินลัด |
| 📊 วิเคราะห์ | Dashboard (ยอดคงเหลือ, % เก็บออม, กราฟ 7 วัน / 6 เดือน, สัดส่วนรายจ่าย), มุมมองรายวัน, สรุปรายสัปดาห์/เดือน, เปรียบเทียบหมวดหมู่ที่เลือก |
| 👥 สมุดร่วม | สร้างสมุดแชร์กับครอบครัว/เพื่อน, เจ้าของอนุมัติคำขอเข้าร่วม, สิทธิ์ Owner / Editor / Viewer |
| ⚡ Realtime | ข้อมูลอัปเดตทันทีทุกอุปกรณ์ผ่าน Firestore |
| 🎨 ดีไซน์ | Modern Fintech, Light/Dark (ตามระบบหรือเลือกเอง), Responsive พร้อมแถบเมนูล่างบนมือถือ |
| ♿ การเข้าถึง | focus ring, label ของปุ่มไอคอน, ปิด dialog ด้วย Esc, เคารพ prefers-reduced-motion |
แอปเป็น Single-Page App ที่คุยกับ Firebase โดยตรงจากเบราว์เซอร์ ไม่มี backend ของตัวเอง ความปลอดภัยทั้งหมดจึงบังคับด้วย Firestore Security Rules (ทดสอบอัตโนมัติ 35 กรณี)
flowchart LR
subgraph Browser["เบราว์เซอร์ / มือถือ"]
UI["React + TypeScript<br/>(Vite)"]
HK["hooks: useBooks · useTransactions · useTheme"]
UI --> HK
end
HK -- "onSnapshot / writes" --> FS[("Cloud Firestore")]
UI -- "Google Sign-in" --> AU["Firebase Auth"]
FS -. "ตรวจสิทธิ์ทุกคำสั่ง" .-> RU{{"firestore.rules"}}
HOST["Firebase Hosting"] --> UI
Tech stack: React 18, TypeScript 5 (strict), Vite 5, Recharts, lucide-react, Firebase (Auth, Firestore, Hosting, Analytics), Vitest + Firestore Emulator
users/{uid}/transactions/{txId} รายการส่วนตัว (เจ้าของคนเดียวเข้าถึงได้)
books/{bookId} สมุดร่วม — อ่านได้เฉพาะสมาชิก
name, ownerUid, ownerName, createdAt, updatedAt, deleted?
memberIds: string[] ใช้กรองด้วย array-contains
members: { [uid]: { name, photoURL, role, joinedAt } }
books/{bookId}/transactions/{txId} รายการในสมุดร่วม
books/{bookId}/joinRequests/{uid} คำขอเข้าร่วม (doc id = uid ผู้ขอ)
users/{uid}/joinRequestRefs/{bookId} รายการสมุดที่ฉันขอเข้าร่วม (ใช้ได้ทุกอุปกรณ์ + ใช้ถอนคำขอตอนลบบัญชี)
| การกระทำ | Owner | Editor | Viewer | คนนอก |
|---|---|---|---|---|
| ดูสมุดและรายการ | ✅ | ✅ | ✅ | ❌ |
| เพิ่ม/แก้/ลบรายการ | ✅ | ✅ | ❌ | ❌ |
| อนุมัติ/ปฏิเสธคำขอ, เปลี่ยนสิทธิ์, ลบสมาชิก, เปลี่ยนชื่อ, ลบสมุด | ✅ | ❌ | ❌ | ❌ |
| ออกจากสมุด | ❌ (ต้องลบสมุด) | ✅ | ✅ | – |
| ส่งคำขอเข้าร่วม | – | – | – | ✅ (ต้องรู้ ID สมุด) |
การเข้าร่วมสมุด: เจ้าของกด "คัดลอก ID" ส่งให้เพื่อน → เพื่อนกด เข้าร่วม ใส่ ID → เจ้าของเห็นคำขอในหน้าโปรไฟล์ แล้ว อนุมัติ (เลือก Viewer/Editor) หรือ ปฏิเสธ ไม่มีรหัสผ่านเก็บในฐานข้อมูล — คนนอกจึงอ่านสมุดไม่ได้เลยแม้รู้ ID
- ข้อมูลส่วนตัวแยกตาม
uid; สมุดร่วมอ่านได้เฉพาะสมาชิก; เจ้าของเป็นผู้เดียวที่อนุมัติสมาชิก - ตรวจรูปแบบข้อมูลฝั่ง server (ประเภท จำนวนเงิน ความยาวข้อความ URL รูป) และห้ามเปลี่ยนผู้สร้างรายการ
- ชุดทดสอบ
tests/rules/firestore.rules.test.tsครอบคลุม 35 กรณี เช่น คนนอกอ่านสมุด/ใส่ตัวเองเป็นสมาชิกไม่ได้, Viewer เขียนไม่ได้, สมาชิกลบคนอื่นหรือเลื่อนสิทธิ์ตัวเองไม่ได้ - รายละเอียดเชิงลึก: docs/SECURITY.md · นโยบายความเป็นส่วนตัว:
/privacy.html
แอปใช้ Firebase Analytics เก็บสถิติการใช้งานแบบไม่ระบุตัวตน (ระบุไว้ในนโยบายความเป็นส่วนตัวแล้ว)
ผู้ใช้ลบบัญชีเองได้ที่ โปรไฟล์ → ลบบัญชี ตามข้อกำหนดของ Apple App Store และ Google Play
sequenceDiagram
actor U as ผู้ใช้
participant A as แอป
participant G as Google (popup)
participant F as Firestore
participant AU as Firebase Auth
U->>A: กด "ลบบัญชี" และพิมพ์คำยืนยัน
A->>G: reauthenticateWithPopup (ยืนยันตัวตนใหม่)
A->>F: ลบ users/{uid}/transactions ทั้งหมด
loop ทุกสมุดที่เป็นสมาชิก
alt เป็นเจ้าของ
A->>F: ลบรายการ + คำขอเข้าร่วม แล้วลบสมุด
else เป็นสมาชิก
A->>F: เปลี่ยนชื่อผู้บันทึกเป็น "ผู้ใช้ที่ลบบัญชี" แล้วออกจากสมุด
end
end
A->>AU: deleteUser
A-->>U: กลับหน้า Login + ข้อความยืนยัน
| ข้อมูล | สิ่งที่เกิดขึ้น |
|---|---|
| รายการรับ-จ่ายส่วนตัว | ลบถาวร |
| สมุดร่วมที่เป็นเจ้าของ | ลบถาวร (รวมรายการและคำขอเข้าร่วม) — สมาชิกคนอื่นจะเข้าไม่ได้อีก (แสดงชัดในหน้ายืนยัน) |
| สมุดร่วมที่เป็นสมาชิก | ถูกนำออกจากสมุด (ทุกบทบาท รวมถึงอดีต Editor); รายการที่เคยบันทึกคงอยู่ในสมุดแต่ไม่แสดงตัวตน |
| คำขอเข้าร่วมที่ค้างอยู่ | ถอนทั้งหมด (รายการเก็บใน Firestore จึงครบแม้ใช้หลายอุปกรณ์) |
| บัญชีเข้าสู่ระบบ | ลบออกจาก Firebase Auth |
ทุกขั้นตอนทำซ้ำได้อย่างปลอดภัย (idempotent) หากขัดข้องกลางทางสามารถกดลองใหม่ได้ รายละเอียด: docs/ACCOUNT-DELETION.md
- Node.js 20+ และ npm
- โปรเจค Firebase (เปิด Authentication แบบ Google และ Firestore)
- (เฉพาะรันชุดทดสอบ rules) JDK 21+
npm install
cp .env.example .env.local # กรอกค่า VITE_FIREBASE_* จาก Project settings > Your apps > Web
npm run dev # http://localhost:5173ค่า config ดึงได้ด้วย firebase apps:sdkconfig WEB <appId> แล้วแก้ .firebaserc ให้เป็น project id ของคุณ
อย่าลืมเพิ่มโดเมนที่ deploy ใน Authentication → Settings → Authorized domains
| คำสั่ง | คำอธิบาย |
|---|---|
npm run dev |
รัน dev server |
npm run build |
ตรวจ type แล้ว build production |
npm run lint |
ESLint |
npm test |
Unit test (โปรไฟล์, ฟอร์ม, CSV) 47 เทสต์ |
npm run test:rules |
ทดสอบ Firestore Security Rules กับ Emulator 35 เทสต์ (ต้องมี JDK 21+) |
npm run build
firebase deploy --only firestore:rules # ขึ้น rules ก่อนเสมอ
firebase deploy --only hostingยังไม่มี CI/CD ใน repo นี้ หากต้องการ auto-deploy ให้เพิ่ม workflow ด้วย
FirebaseExtended/action-hosting-deployและรันnpm testกับnpm run test:rulesเป็นขั้นตอนตรวจก่อน deploy
src/
├─ main.tsx · App.tsx · firebase.ts · theme.ts · types.ts · styles.css
├─ contexts/AuthContext.tsx ล็อกอิน, ลบบัญชี
├─ lib/ ตรรกะที่ไม่พึ่ง UI: accountDeletion (ลบข้อมูล), profile / transactionForm (ตรวจค่าให้ตรงกับ rules), csv (ส่งออกอย่างปลอดภัย)
├─ hooks/
│ ├─ useTransactions.ts รายการ (realtime)
│ ├─ useBooks.ts สมุดร่วม, คำขอเข้าร่วม, จัดการสมาชิก
│ └─ useAction.ts · useTheme.ts · useFocusTrap.ts
└─ components/
├─ Login · Layout · Dashboard · AddForm · DailyView · SummaryView
└─ ProfileView · DeleteAccount · BookSwitcher · BookMembers · shared
tests/rules/ ทดสอบ Security Rules กับ Firestore Emulator
tests/unit/ Unit test ของตรรกะที่ไม่พึ่ง Firebase
firestore.rules · firebase.json กฎความปลอดภัยและการตั้งค่า Firebase
public/privacy.html นโยบายความเป็นส่วนตัว (ใช้กับ App Store / Play Store)
docs/ เอกสารเชิงลึกและภาพหน้าจอ
- แอปมือถือ (Android/iOS) ด้วย Capacitor — ต้องเปลี่ยน Google Sign-in เป็นแบบ native และเพิ่ม Sign in with Apple
- ติดตั้ง CI (lint + build +
npm test+test:rules) - แก้ไขรายการที่บันทึกไว้, งบประมาณรายหมวด, ส่งออกข้อมูลทั้งหมด
Fork แล้วเปิด Pull Request ได้เลย (กรุณารัน npm run lint, npm test และ npm run test:rules ก่อน)
โปรเจคนี้ยังไม่ได้ระบุ License — โปรดติดต่อเจ้าของ repository หากต้องการนำไปใช้ต่อ














