Pain Point ที่เจอจริง
ปัญหาเล็กๆ ที่เกิดขึ้นทุกวัน แต่สร้างความรำคาญซ้ำแล้วซ้ำเล่า
ลืมชั้นจอดรถ — ทั้งที่คอนโดและที่ทำงาน
- คอนโด — ไม่มีช่องจอดประจำ จอดไม่ตรงชั้นทุกวัน ลงลิฟต์มาแล้วจำไม่ได้ว่าจอดชั้นไหน
- ออฟฟิศ — ที่ทำงานอยู่ในห้างสรรพสินค้า ลานจอดรถหลายชั้น ทุกชั้นหน้าตาเหมือนกันหมด
- Google Maps "Save Parking" — เซฟตำแหน่งได้ แต่ไม่บอกว่าจอด ชั้นไหน ของตึก
- คนที่บ้านเป็นห่วง — ต้องพิมพ์บอกใน LINE ทุกครั้งว่าถึงแล้ว จอดชั้นไหน ซึ่งบางทีก็ลืมบอก
Design Thinking Process
จาก pain point สู่ solution ที่ใช้งานจริงทุกวัน
ที่ทำงานก็เหมือนกัน ลานจอดห้างหลายชั้น ออกจากออฟฟิศทีก็ลืมทุกที
นอกจากนี้ คนที่บ้านก็ถามทุกวันว่า "ถึงยัง?" ต้องพิมพ์ LINE บอกทุกครั้ง
"ผู้ใช้ต้องการ (1) จำที่จอดรถได้ง่ายโดยไม่ต้องพึ่งความจำ (2) ได้รับแจ้งเตือนก่อนเวลาออก (3) แจ้งคนที่บ้านอัตโนมัติว่าถึงที่หมายแล้วจอดชั้นไหน — ทั้งหมดนี้ทำครั้งเดียวจบ"
ไอเดีย: สร้าง Web App ฟอร์มง่ายๆ → กรอกสถานที่ + ชั้น + เวลาที่อยากให้เตือน → กดบันทึก → ระบบทำ 3 อย่างพร้อมกัน:
1) บันทึกข้อมูลลง Google Sheets
2) สร้าง Google Calendar event พร้อม popup reminder
3) ส่ง LINE แจ้งคนที่บ้าน "ถึงแล้ว จอดชั้น X"
ปัญหาที่พบตอน test:
— Calendar event สร้างซ้ำเมื่อ LINE API ล้มเหลว (trigger retry ทำให้สร้าง event ซ้ำ)
— เวลาคลาดเคลื่อนเพราะ timezone ไม่ตรง
→ แก้: แยก try-catch Calendar กับ LINE, ใช้ status tracking (CAL_OK / LINE_FAIL / SENT), ใช้ RFC3339 + timezone
User Journey — กดครั้งเดียว ได้ 3 อย่าง
จากมุมมองผู้ใช้ ขั้นตอนทั้งหมดเสร็จใน 15 วินาที
Technical Solution
Architecture
Framer (Frontend) → Google Sheets (DB) → Google Apps Script (Logic) → Calendar + LINE (Output)
- Web App (Framer) — ฟอร์มกรอกข้อมูลง่ายๆ บนมือถือ ไม่ต้องลงแอป
- Google Sheets เป็น database — ดู history ย้อนหลังได้ทุกเมื่อ
- GAS Trigger ทุก 1 นาที — ตรวจ row ใหม่แล้วประมวลผลทันที
- Google Calendar event + popup reminder 30 นาทีก่อนเวลาที่ตั้ง
- LINE Flex Card ส่งแจ้งคนที่บ้านอัตโนมัติ
- Status tracking (SENT / CAL_OK / LINE_FAIL) ป้องกัน Calendar สร้างซ้ำ
- Retry mechanism — ถ้า LINE ล้มเหลว ระบบ retry อัตโนมัติทุกชั่วโมง
UI Preview
Web App ฟอร์มที่ใช้กรอก + LINE Flex Card ที่ส่งไปถึงคนที่บ้าน
Creative Problem Solving
ปัญหาที่เจอระหว่างทำ และวิธีแก้ที่คิดขึ้นมา
สิ่งที่คิดเพิ่มจาก pain point เดิม
Technical Challenges & Solutions
- Calendar สร้างซ้ำ: เมื่อ LINE API ล้มเหลว trigger จะ retry → สร้าง event ซ้ำ → แก้โดยแยก try-catch + mark status "CAL_OK" ทันทีหลัง Calendar สำเร็จ
- Timezone คลาดเคลื่อน: GAS ใช้ UTC แต่ผู้ใช้กรอกเวลาไทย → แก้ด้วย RFC3339 format พร้อม timeZone: "Asia/Bangkok"
- LINE Retry: สร้าง retry mechanism แยก — มี trigger ทุกชั่วโมงหา row ที่ status = LINE_FAIL แล้วลองส่งใหม่
- Sheet เป็น Date object: timeReminder จาก Sheet มาเป็น Date object ไม่ใช่ string → เขียน normalizeTimeHM_() รองรับทั้ง Date, "HH:mm", "HH:mm:ss"
// Main: ตรวจ row ใหม่ → สร้าง Calendar + ส่ง LINE function syncParkingRows() { const sheet = SpreadsheetApp.getActiveSpreadsheet() .getSheetByName("Sheet1"); const rows = sheet.getRange(2, 1, lastRow - 1, 8).getValues(); for (let i = 0; i < rows.length; i++) { const p = mapSheetRowToPayload_(rows[i]); // กันซ้ำ — ข้ามถ้าสร้าง Calendar แล้ว if (p.status === "SENT" || p.status === "CAL_OK") continue; // ===== 1. สร้าง Calendar ===== try { createCalendarEventWithPopupReminder_( calendarId, title, start, end, location, description ); calendarOk = true; } catch (e) { continue; } // ===== 2. ส่ง LINE (แยก try-catch) ===== try { const flex = buildParkingFlexCard_(p); linePush_(to, [flex]); // to = CENSORED lineOk = true; } catch (e) { lineErrorMsg = e.message; } // ===== 3. Mark Status ทันที ===== sheet.getRange(rowNumber, 8) .setValue(lineOk ? "SENT" : `LINE_FAIL:${err}`); } }
// RFC3339 + timezone — กัน Calendar เวลาคลาดเคลื่อน function createCalendarEventWithPopupReminder_( calendarId, title, start, end, location, description ) { const resource = { summary: title, location: location, start: { dateTime: toRfc3339_(start), // "2026-02-07T17:30:00+07:00" timeZone: "Asia/Bangkok" }, end: { dateTime: toRfc3339_(end), timeZone: "Asia/Bangkok" }, reminders: { useDefault: false, overrides: [ { method: "popup", minutes: 30 } ] } }; Calendar.Events.insert(resource, calendarId); // CALENDAR_ID CENSORED } // รองรับ Date / "HH:mm" / "HH:mm:ss" จาก Sheet function normalizeTimeHM_(v) { if (v instanceof Date) { return { hh: v.getHours(), mm: v.getMinutes() }; } const m = String(v).match(/^(\d{1,2}):(\d{2})/); if (m) return { hh: Number(m[1]), mm: Number(m[2]) }; return null; }
Data Model — Google Sheets
แต่ละ row คือการจอดรถ 1 ครั้ง ระบบอ่านแล้วประมวลผลอัตโนมัติ
| Column | Field | Description | Example |
|---|---|---|---|
| A | Date |
Timestamp ที่กรอกฟอร์ม | 2026-02-07 08:45 |
| B | timeReminder |
เวลาที่ต้องการให้เตือน | 17:30 |
| C | parkingMap |
URL แผนที่ (optional) | — |
| D | parkingFloor |
ชั้นที่จอด | B2 |
| E | note |
โน้ตเพิ่มเติม | จอดใกล้ลิฟต์ฝั่งซ้าย |
| F | parkingLocation |
สถานที่จอด | คอนโด |
| G | exitDateReminder |
วันที่ให้เตือน | 2026-02-07 |
| H | NoteType |
โหมดการบันทึก | SENT / กรอกย้อนหลัง |
| I | TimeUseTracking |
เวลาจริงที่ใช้ tracking (แยกจากเวลาเตือน) | 22:32 / 09:12 |
Error Recovery Design
ออกแบบ status tracking เพื่อป้องกัน Calendar ซ้ำ + retry LINE อัตโนมัติ
ผลลัพธ์
(จากเดิมต้องพิมพ์ LINE)
(Sheets + Calendar + LINE)
(หลังใช้ระบบ)
ทุกครั้งที่บันทึก
🔀 Iterate: Dual Recording Mode
จาก "บันทึกได้แค่ตอนจอด" สู่ "กรอกย้อนหลังได้ + แยก Tracking Time ออกจาก Reminder Time"
🔍 ปัญหาที่เจอ
บางครั้งลืมกรอกตอนจอดรถ ต้องกรอกย้อนหลัง แต่ระบบเดิมต้องตั้งเวลาเตือนทุกครั้ง → เวลาที่เอาไป track ปนกับเวลาเตือน ข้อมูลไม่ตรง
Solution: เพิ่ม Dropdown "บันทึกแบบไหน?" แยก 2 โหมด + เพิ่ม column
TimeUseTracking เก็บเวลาจริงที่ใช้ track แยกจากเวลาเตือน
TimeUseTracking บันทึกเวลาจริงทั้ง 2 โหมด → Dashboard ดึงจาก column นี้เท่านั้น💡 ผลลัพธ์ที่ได้
- ข้อมูลไม่ปนกัน — เวลาที่ใช้เตือน (timeReminder) แยกจากเวลาจริง (TimeUseTracking)
- กรอกย้อนหลังได้ — ไม่ต้อง skip การบันทึกเพราะลืมกรอกตอนจอด
- Dashboard แม่นยำขึ้น — ใช้ TimeUseTracking เป็น source of truth ทุก chart/metric
- ไม่ยิง LINE ผิดโหมด — โหมดกรอกย้อนหลังไม่สร้าง Calendar + ไม่ส่ง LINE
📊 Iterate: Analytics Dashboard
จาก "แค่บันทึกข้อมูล" สู่ "เข้าใจพฤติกรรมตัวเอง" ผ่าน data visualization
🔍 ทำไมต้องมี Dashboard?
หลังใช้งานไปสักพัก เริ่มสงสัยว่า "เราถึงที่ทำงานกี่โมงโดยเฉลี่ย?" "ถึงคอนโดกี่โมง?" — ข้อมูลมีอยู่ใน Google Sheets แล้ว แต่ไม่สามารถมองเห็น pattern ได้
Insight: "ถ้าเห็นเป็นกราฟ จะรู้ว่าเราถึงที่ทำงานเฉลี่ยกี่โมง ถึงคอนโดกี่โมง ช่วยวางแผนเวลาได้ดีขึ้น"
Solution: สร้าง Live Dashboard อ่านข้อมูลจาก Google Sheets แบบ real-time แสดงเป็นกราฟและ metrics
- ⏰ เวลาที่บันทึกเฉลี่ย — แสดงว่าถึงคอนโดโดยเฉลี่ยกี่โมง ถึงที่ทำงานกี่โมง แยกตามสถานที่ พร้อม filter สัปดาห์นี้ / เดือนนี้ / ทั้งหมด
- 📈 Trend เวลาที่บันทึก — กราฟแสดง 2 เส้น (คอนโด vs ที่ทำงาน) เฉพาะวันจันทร์-ศุกร์ ใช้
TimeUseTrackingเป็น source of truth - 📍 สัดส่วนสถานที่จอด — pie chart แสดงว่าจอดที่ไหนบ่อยที่สุด
- 🅿️ ชั้นที่จอดบ่อยสุด — bar chart แสดงว่าที่คอนโดจอดชั้นไหนบ่อยที่สุด
- 📊 จำนวนบันทึกรายวัน — timeline การใช้งานระบบ วันที่ครบ 2 ครั้ง/วันแสดง ✓ เป็น visual indicator
- 📱 Mobile Responsive — ซ่อนคอลัมน์ที่ไม่จำเป็นบนมือถือ location badge ไม่ตัดบรรทัด
- 🔴 Live Data — ดึงข้อมูลจาก Google Sheets แบบ real-time ทุก 5 นาที
💡 Insights ที่ได้จาก Dashboard
- รู้ว่าเราถึงที่ทำงานเฉลี่ยกี่โมง — ช่วยวางแผนว่าควรออกจากบ้านกี่โมงถึงจะไม่สาย สามารถดูเป็นรายสัปดาห์/เดือนได้
- เห็น pattern การจอดรถ — เช่น "ชั้น 3 จอดบ่อยสุดเพราะใกล้ลิฟต์" หรือ "ช่วงนี้จอดที่ทำงานบ่อยกว่าคอนโด"
- ตรวจสอบความ consistent — Trend chart แสดงเฉพาะวันทำงาน (จ.-ศ.) เห็นชัดว่าเวลาถึงผันผวนแค่ไหน
- Track ความสม่ำเสมอ — วันที่บันทึกครบ 2 ครั้ง (ไป-กลับ) แสดง ✓ เป็น visual goal
- Data-driven decision — ตัดสินใจจากข้อมูลจริง ไม่ใช่จากความรู้สึก
🎨 Technical Implementation
สิ่งที่ได้เรียนรู้
- Design Thinking เริ่มจากตัวเองได้ — ปัญหาเล็กๆ ที่เจอทุกวัน (ลืมชั้นจอดรถ) พอคิดต่อกลายเป็น solution ที่ช่วยได้ทั้งตัวเองและคนที่บ้าน
- "1 action = multiple outputs" — กดครั้งเดียว ได้ทั้งบันทึก + เตือน + แจ้งคนที่บ้าน ช่วยลดขั้นตอนที่ต้องทำซ้ำทุกวัน
- Status Tracking สำคัญ — ถ้าไม่มี status tracking ระบบจะสร้าง Calendar ซ้ำเมื่อ LINE API ล้มเหลว ต้องคิดเรื่อง idempotency ตั้งแต่แรก
- Error Recovery ต้องออกแบบ — แยก try-catch + retry mechanism ทำให้ระบบ resilient โดยไม่ต้องมานั่งดูแลทุกวัน
- Timezone เป็นปัญหาคลาสสิก — GAS ใช้ UTC ภายใน แต่ผู้ใช้คิดเป็นเวลาไทย ต้อง explicit ทุก datetime operation
- No-code + code ผสมกันได้ดี — ใช้ Framer ทำ frontend (เร็ว สวย) + GAS ทำ backend logic (ฟรี มี trigger) เหมาะกับ side project
Tech Stack
ผสม no-code (Framer) กับ code (GAS) เพื่อ ship ให้ไวที่สุด