// server.js — Express backend // Randevu sistemi: müşteri kaydı + çakışma kontrolü + günlük liste const express = require('express'); const path = require('path'); const storage = require('./storage'); const app = express(); const PORT = process.env.PORT || 3000; // Çalışma saatleri (09:00 - 18:00, her saat başı slot) const OPEN_HOUR = 9; const CLOSE_HOUR = 18; const SLOT_MINUTES = 60; app.use(express.json()); app.use(express.static(path.join(__dirname, 'public'))); // --- Yardımcılar --- // Tarihi "YYYY-MM-DD" biçimine getirir function normalizeDate(date) { return date; // beklenen format zaten "YYYY-MM-DD" } // Saati "HH:MM" dakikaya çevirir (çakışma için) function toMinutes(hhmm) { const [h, m] = hhmm.split(':').map(Number); return h * 60 + m; } // Belirtilen günde belirtilen zaman aralığına çakışan randevu var mı? function isConflict(list, date, startMin, durationMin) { const end = startMin + durationMin; return list.some(a => { if (a.date !== date) return false; const aStart = toMinutes(a.time); const aEnd = aStart + (a.duration || 0); // Çakışma: iki aralık birbirine giriyorsa return startMin < aEnd && end > aStart; }); } // Beklenen çalışma saati içinde mi? function withinHours(startMin) { return startMin >= OPEN_HOUR * 60 && (startMin + SLOT_MINUTES) <= CLOSE_HOUR * 60; } // --- API --- // Sağlık kontrolü app.get('/api/health', (req, res) => { res.json({ ok: true, service: 'randevu-sistemi' }); }); // Boş slotları döndürür (bugünün veya istenen günün) app.get('/api/slots', (req, res) => { const date = req.query.date || null; const list = storage.getAll(); const slots = []; for (let m = OPEN_HOUR * 60; m < CLOSE_HOUR * 60; m += SLOT_MINUTES) { const hh = String(Math.floor(m / 60)).padStart(2, '0'); const mm = String(m % 60).padStart(2, '0'); const time = `${hh}:${mm}`; // Belirli gün verildiyse o günkü çakışmaları, yoksa genel geçmiş/tekrarı kontrol etme const busy = date ? isConflict(list, date, m, SLOT_MINUTES) : false; if (!busy) { slots.push(time); } } // date verilmediyse tüm saat dilimlerini döndür if (!date) return res.json(slots); res.json(slots); }); // Yeni randevu oluştur (çakışma kontrolü ile) app.post('/api/appointments', (req, res) => { const { name, phone, date, time, service, notes } = req.body; // Doğrulama if (!name || !phone || !date || !time) { return res.status(400).json({ error: 'Ad, telefon, tarih ve saat zorunludur.' }); } // Tarih formatı "YYYY-MM-DD", saat "HH:MM" if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) { return res.status(400).json({ error: 'Geçersiz tarih formatı. YYYY-MM-DD kullanın.' }); } if (!/^\d{2}:\d{2}$/.test(time)) { return res.status(400).json({ error: 'Geçersiz saat formatı. HH:MM kullanın.' }); } const startMin = toMinutes(time); if (!withinHours(startMin)) { return res.status(400).json({ error: `Randevular yalnızca ${String(OPEN_HOUR).padStart(2,'0')}:00 - ${String(CLOSE_HOUR).padStart(2,'0')}:00 arası alınabilir.` }); } // Çakışma kontrolü const list = storage.getAll(); if (isConflict(list, date, startMin, SLOT_MINUTES)) { return res.status(409).json({ error: 'Bu saat dilimi dolu. Lütfen başka bir saat seçin.' }); } const record = storage.add({ name, phone, date, time, service: service || 'Genel', notes: notes || '', duration: SLOT_MINUTES, createdAt: new Date().toISOString() }); res.status(201).json({ ok: true, appointment: record }); }); // Belirli bir günün randevularını döndürür (admin) app.get('/api/appointments', (req, res) => { const date = req.query.date; let list = storage.getAll(); if (date) { list = list.filter(a => a.date === date); } // Tarihe ve saate göre sırala list.sort((a, b) => (a.date + a.time).localeCompare(b.date + b.time)); res.json(list); }); // Belirli bir randevuyu sil (admin) app.delete('/api/appointments/:id', (req, res) => { const ok = storage.remove(req.params.id); if (!ok) { return res.status(404).json({ error: 'Randevu bulunamadı.' }); } res.json({ ok: true }); }); // --- Sunucuyu başlat --- storage.init(); app.listen(PORT, () => { console.log(`Randevu sistemi çalışıyor: http://localhost:${PORT}`); });