feat: guest reservations and messages
- Remove Reserve button from rooms page - Calendar modal now supports direct reservation for guests - Message modal works for guests (asks for name, contact method, contact) - Add guest_name, guest_contact_method, guest_contact to reservations and messages tables - LEFT JOIN users in reservations GET to include guest reservations - Auto-fill form for logged-in users, show input fields for guests
This commit is contained in:
@@ -5,21 +5,34 @@ import { getSession } from '@/lib/auth';
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const user = await getSession();
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const { room_id, message } = await request.json();
|
||||
const body = await request.json();
|
||||
|
||||
// Support both logged-in users and guests
|
||||
const { room_id, message, guest_name, guest_contact_method, guest_contact } = body;
|
||||
|
||||
if (!message || !message.trim()) {
|
||||
return NextResponse.json({ error: 'Message is required' }, { status: 400 });
|
||||
}
|
||||
|
||||
const { rows } = await db.query(`
|
||||
// For guests, require name and contact info
|
||||
if (!user && (!guest_name || !guest_contact)) {
|
||||
return NextResponse.json({ error: 'Name and contact info are required' }, { status: 400 });
|
||||
}
|
||||
|
||||
let finalMessage = message.trim();
|
||||
let finalUserId = user?.id || null;
|
||||
|
||||
// For guests, prepend their contact info to the message
|
||||
if (!user && guest_name && guest_contact) {
|
||||
const methodLabel = guest_contact_method ? ` (${guest_contact_method})` : '';
|
||||
finalMessage = `[Guest: ${guest_name}${methodLabel} - ${guest_contact}]\n\n${message.trim()}`;
|
||||
}
|
||||
|
||||
const result = await db.query(`
|
||||
INSERT INTO messages (room_id, user_id, message)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING *
|
||||
`, [room_id || null, user.id, message.trim()]);
|
||||
`, [room_id || null, finalUserId, finalMessage]);
|
||||
|
||||
// Get admin emails for notification
|
||||
const { rows: admins } = await db.query(`SELECT email, phone FROM users WHERE role = 'admin' AND (email IS NOT NULL OR phone IS NOT NULL)`);
|
||||
|
||||
@@ -5,11 +5,13 @@ import { getSession } from '@/lib/auth';
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const user = await getSession();
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
const body = await request.json();
|
||||
const { room_id, check_in, check_out, guest_name, guest_contact_method, guest_contact } = body;
|
||||
|
||||
const { room_id, check_in, check_out } = await request.json();
|
||||
// Require either logged-in user or guest info
|
||||
if (!user && (!guest_name || !guest_contact)) {
|
||||
return NextResponse.json({ error: 'Please provide your name and contact information' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (!room_id || !check_in || !check_out) {
|
||||
return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
|
||||
@@ -57,12 +59,21 @@ export async function POST(request: Request) {
|
||||
const pricePerNight = parseFloat(room.price) || 0;
|
||||
const total_price = nights * pricePerNight;
|
||||
|
||||
// Create reservation
|
||||
// Create reservation - for guests, user_id is NULL
|
||||
const { rows } = await db.query(`
|
||||
INSERT INTO reservations (room_id, user_id, check_in, check_out, status, total_price)
|
||||
VALUES ($1, $2, $3, $4, 'pending', $5)
|
||||
INSERT INTO reservations (room_id, user_id, check_in, check_out, status, total_price, guest_name, guest_contact_method, guest_contact)
|
||||
VALUES ($1, $2, $3, $4, 'pending', $5, $6, $7, $8)
|
||||
RETURNING *
|
||||
`, [room_id, user.id, check_in, check_out, total_price]);
|
||||
`, [
|
||||
room_id,
|
||||
user?.id || null,
|
||||
check_in,
|
||||
check_out,
|
||||
total_price,
|
||||
guest_name || null,
|
||||
guest_contact_method || null,
|
||||
guest_contact || null,
|
||||
]);
|
||||
|
||||
// Block the dates
|
||||
const insertPromises = [];
|
||||
@@ -107,10 +118,11 @@ export async function GET(request: Request) {
|
||||
const status = searchParams.get('status') || 'all';
|
||||
|
||||
let query = `
|
||||
SELECT r.*, rm.name as room_name, u.username, u.first_name, u.last_name, u.email, u.phone
|
||||
SELECT r.*, rm.name as room_name,
|
||||
u.username, u.first_name, u.last_name, u.email, u.phone
|
||||
FROM reservations r
|
||||
JOIN rooms rm ON r.room_id = rm.id
|
||||
JOIN users u ON r.user_id = u.id
|
||||
LEFT JOIN users u ON r.user_id = u.id
|
||||
`;
|
||||
|
||||
const params: any[] = [];
|
||||
|
||||
+162
-44
@@ -33,6 +33,7 @@ interface User {
|
||||
role: 'admin' | 'user';
|
||||
first_name: string | null;
|
||||
last_name: string | null;
|
||||
email?: string;
|
||||
comments_disabled: boolean;
|
||||
}
|
||||
|
||||
@@ -68,6 +69,7 @@ export default function RoomsPage() {
|
||||
const [messageText, setMessageText] = useState('');
|
||||
const [reviewText, setReviewText] = useState('');
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [guestForm, setGuestForm] = useState({ name: '', contactMethod: 'email', contact: '' });
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/api/rooms')
|
||||
@@ -186,10 +188,17 @@ export default function RoomsPage() {
|
||||
};
|
||||
|
||||
const handleReserve = async () => {
|
||||
if (!selectedRoom || !reservationDates.checkIn || !reservationDates.checkOut || !user) {
|
||||
alert('Please log in and select dates');
|
||||
if (!selectedRoom || !reservationDates.checkIn || !reservationDates.checkOut) {
|
||||
alert('Please select dates');
|
||||
return;
|
||||
}
|
||||
|
||||
// For guests, require name and contact
|
||||
if (!user && (!guestForm.name.trim() || !guestForm.contact.trim())) {
|
||||
alert('Please provide your name and contact info');
|
||||
return;
|
||||
}
|
||||
|
||||
setSubmitting(true);
|
||||
try {
|
||||
const res = await fetch('/api/reservations', {
|
||||
@@ -200,11 +209,18 @@ export default function RoomsPage() {
|
||||
room_id: selectedRoom.id,
|
||||
check_in: reservationDates.checkIn,
|
||||
check_out: reservationDates.checkOut,
|
||||
...(user ? {} : {
|
||||
guest_name: guestForm.name.trim(),
|
||||
guest_contact_method: guestForm.contactMethod,
|
||||
guest_contact: guestForm.contact.trim(),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
if (res.ok) {
|
||||
alert('Reservation request sent! You will receive a confirmation email shortly.');
|
||||
alert('Reservation request sent! You will receive a confirmation shortly.');
|
||||
closeModal();
|
||||
setReservationDates({ checkIn: '', checkOut: '' });
|
||||
setGuestForm({ name: '', contactMethod: 'email', contact: '' });
|
||||
} else {
|
||||
const err = await res.json();
|
||||
alert(err.error || 'Failed to create reservation');
|
||||
@@ -216,10 +232,17 @@ export default function RoomsPage() {
|
||||
};
|
||||
|
||||
const handleSendMessage = async () => {
|
||||
if (!selectedRoom || !messageText.trim() || !user) {
|
||||
alert('Please log in and enter a message');
|
||||
if (!selectedRoom || !messageText.trim()) {
|
||||
alert('Please enter a message');
|
||||
return;
|
||||
}
|
||||
|
||||
// For guests, require name and contact
|
||||
if (!user && (!guestForm.name.trim() || !guestForm.contact.trim())) {
|
||||
alert('Please provide your name and contact info');
|
||||
return;
|
||||
}
|
||||
|
||||
setSubmitting(true);
|
||||
try {
|
||||
const res = await fetch('/api/messages', {
|
||||
@@ -229,11 +252,18 @@ export default function RoomsPage() {
|
||||
body: JSON.stringify({
|
||||
room_id: selectedRoom.id,
|
||||
message: messageText.trim(),
|
||||
...(user ? {} : {
|
||||
guest_name: guestForm.name.trim(),
|
||||
guest_contact_method: guestForm.contactMethod,
|
||||
guest_contact: guestForm.contact.trim(),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
if (res.ok) {
|
||||
alert('Message sent to our team!');
|
||||
closeModal();
|
||||
setMessageText('');
|
||||
setGuestForm({ name: '', contactMethod: 'email', contact: '' });
|
||||
} else {
|
||||
const err = await res.json();
|
||||
alert(err.error || 'Failed to send message');
|
||||
@@ -484,23 +514,7 @@ export default function RoomsPage() {
|
||||
</div>
|
||||
|
||||
{/* ─── Action Buttons ─── */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '0.5rem', marginTop: '1rem' }}>
|
||||
<button
|
||||
onClick={() => openModal('reserve', room)}
|
||||
style={{
|
||||
padding: '0.6rem 0.5rem',
|
||||
borderRadius: '10px',
|
||||
border: 'none',
|
||||
background: gold,
|
||||
color: navy,
|
||||
fontWeight: 600,
|
||||
fontSize: '12px',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s',
|
||||
}}
|
||||
>
|
||||
Reserve
|
||||
</button>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '0.5rem', marginTop: '1rem' }}>
|
||||
<button
|
||||
onClick={() => openModal('message', room)}
|
||||
style={{
|
||||
@@ -531,7 +545,7 @@ export default function RoomsPage() {
|
||||
transition: 'all 0.2s',
|
||||
}}
|
||||
>
|
||||
Calendar
|
||||
Reserve
|
||||
</button>
|
||||
<button
|
||||
onClick={() => openModal('review', room)}
|
||||
@@ -725,34 +739,138 @@ export default function RoomsPage() {
|
||||
<>
|
||||
<h3 style={{ margin: '0 0 1rem', color: navy, fontSize: '24px' }}>Send a Message</h3>
|
||||
<p style={{ color: '#555', marginBottom: '1rem' }}>Send a message about {selectedRoom.name} to our team.</p>
|
||||
{!user ? (
|
||||
<p style={{ color: '#777' }}>Please <a href="/login" style={{ color: gold }}>log in</a> to send a message.</p>
|
||||
) : (
|
||||
<>
|
||||
<textarea
|
||||
value={messageText}
|
||||
onChange={(e) => setMessageText(e.target.value)}
|
||||
placeholder="Your message..."
|
||||
rows={5}
|
||||
style={{ width: '100%', padding: '0.75rem', borderRadius: '10px', border: '1px solid rgba(1,13,30,0.2)', fontSize: '14px', resize: 'vertical' }}
|
||||
/>
|
||||
<div style={{ display: 'flex', gap: '1rem', marginTop: '1.5rem' }}>
|
||||
<button onClick={closeModal} style={{ flex: 1, padding: '0.75rem', borderRadius: '10px', border: `2px solid ${navy}`, background: 'transparent', color: navy, fontWeight: 600, cursor: 'pointer' }}>Cancel</button>
|
||||
<button onClick={handleSendMessage} disabled={submitting || !messageText.trim()} style={{ flex: 1, padding: '0.75rem', borderRadius: '10px', border: 'none', background: gold, color: navy, fontWeight: 600, cursor: 'pointer', opacity: submitting ? 0.6 : 1 }}>{submitting ? 'Sending...' : 'Send Message'}</button>
|
||||
{!user && (
|
||||
<div style={{ marginBottom: '1rem', padding: '1rem', background: '#f8f8f8', borderRadius: '10px' }}>
|
||||
<div style={{ marginBottom: '0.75rem' }}>
|
||||
<label style={{ display: 'block', marginBottom: '0.25rem', fontWeight: 600, color: navy, fontSize: '13px' }}>Your Name *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={guestForm.name}
|
||||
onChange={(e) => setGuestForm({ ...guestForm, name: e.target.value })}
|
||||
placeholder="Enter your name"
|
||||
style={{ width: '100%', padding: '0.5rem', borderRadius: '8px', border: '1px solid rgba(1,13,30,0.2)', fontSize: '14px' }}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
<div style={{ marginBottom: '0.75rem' }}>
|
||||
<label style={{ display: 'block', marginBottom: '0.25rem', fontWeight: 600, color: navy, fontSize: '13px' }}>Preferred Contact Method</label>
|
||||
<select
|
||||
value={guestForm.contactMethod}
|
||||
onChange={(e) => setGuestForm({ ...guestForm, contactMethod: e.target.value })}
|
||||
style={{ width: '100%', padding: '0.5rem', borderRadius: '8px', border: '1px solid rgba(1,13,30,0.2)', fontSize: '14px' }}
|
||||
>
|
||||
<option value="email">Email</option>
|
||||
<option value="whatsapp">WhatsApp</option>
|
||||
<option value="phone">Phone</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ display: 'block', marginBottom: '0.25rem', fontWeight: 600, color: navy, fontSize: '13px' }}>
|
||||
{guestForm.contactMethod === 'email' ? 'Email Address' : guestForm.contactMethod === 'whatsapp' ? 'WhatsApp Number' : 'Phone Number'} *
|
||||
</label>
|
||||
<input
|
||||
type={guestForm.contactMethod === 'email' ? 'email' : 'tel'}
|
||||
value={guestForm.contact}
|
||||
onChange={(e) => setGuestForm({ ...guestForm, contact: e.target.value })}
|
||||
placeholder={guestForm.contactMethod === 'email' ? 'your@email.com' : '+593 99 999 9999'}
|
||||
style={{ width: '100%', padding: '0.5rem', borderRadius: '8px', border: '1px solid rgba(1,13,30,0.2)', fontSize: '14px' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{user && (
|
||||
<div style={{ marginBottom: '1rem', padding: '0.75rem', background: '#f8f8f8', borderRadius: '10px', fontSize: '13px', color: '#555' }}>
|
||||
<strong>From:</strong> {user.first_name} {user.last_name} ({user.email})
|
||||
</div>
|
||||
)}
|
||||
<textarea
|
||||
value={messageText}
|
||||
onChange={(e) => setMessageText(e.target.value)}
|
||||
placeholder="Your message..."
|
||||
rows={5}
|
||||
style={{ width: '100%', padding: '0.75rem', borderRadius: '10px', border: '1px solid rgba(1,13,30,0.2)', fontSize: '14px', resize: 'vertical' }}
|
||||
/>
|
||||
<div style={{ display: 'flex', gap: '1rem', marginTop: '1.5rem' }}>
|
||||
<button onClick={closeModal} style={{ flex: 1, padding: '0.75rem', borderRadius: '10px', border: `2px solid ${navy}`, background: 'transparent', color: navy, fontWeight: 600, cursor: 'pointer' }}>Cancel</button>
|
||||
<button onClick={handleSendMessage} disabled={submitting || !messageText.trim() || (!user && (!guestForm.name.trim() || !guestForm.contact.trim()))} style={{ flex: 1, padding: '0.75rem', borderRadius: '10px', border: 'none', background: gold, color: navy, fontWeight: 600, cursor: 'pointer', opacity: submitting ? 0.6 : 1 }}>{submitting ? 'Sending...' : 'Send Message'}</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Calendar Modal */}
|
||||
{/* Reserve/Calendar Modal */}
|
||||
{activeModal === 'calendar' && (
|
||||
<>
|
||||
<h3 style={{ margin: '0 0 1rem', color: navy, fontSize: '24px' }}>Availability Calendar</h3>
|
||||
<p style={{ color: '#555', marginBottom: '1rem' }}>Check availability for {selectedRoom.name}.</p>
|
||||
<RoomCalendar roomId={selectedRoom.id} />
|
||||
<div style={{ marginTop: '1.5rem' }}>
|
||||
<button onClick={closeModal} style={{ width: '100%', padding: '0.75rem', borderRadius: '10px', border: `2px solid ${navy}`, background: 'transparent', color: navy, fontWeight: 600, cursor: 'pointer' }}>Close</button>
|
||||
<h3 style={{ margin: '0 0 1rem', color: navy, fontSize: '24px' }}>Reserve {selectedRoom.name}</h3>
|
||||
<p style={{ color: '#555', marginBottom: '1rem' }}>Select your dates and we'll contact you to confirm.</p>
|
||||
{!user && (
|
||||
<div style={{ marginBottom: '1rem', padding: '1rem', background: '#f8f8f8', borderRadius: '10px' }}>
|
||||
<div style={{ marginBottom: '0.75rem' }}>
|
||||
<label style={{ display: 'block', marginBottom: '0.25rem', fontWeight: 600, color: navy, fontSize: '13px' }}>Your Name *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={guestForm.name}
|
||||
onChange={(e) => setGuestForm({ ...guestForm, name: e.target.value })}
|
||||
placeholder="Enter your name"
|
||||
style={{ width: '100%', padding: '0.5rem', borderRadius: '8px', border: '1px solid rgba(1,13,30,0.2)', fontSize: '14px' }}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ marginBottom: '0.75rem' }}>
|
||||
<label style={{ display: 'block', marginBottom: '0.25rem', fontWeight: 600, color: navy, fontSize: '13px' }}>Preferred Contact Method</label>
|
||||
<select
|
||||
value={guestForm.contactMethod}
|
||||
onChange={(e) => setGuestForm({ ...guestForm, contactMethod: e.target.value })}
|
||||
style={{ width: '100%', padding: '0.5rem', borderRadius: '8px', border: '1px solid rgba(1,13,30,0.2)', fontSize: '14px' }}
|
||||
>
|
||||
<option value="email">Email</option>
|
||||
<option value="whatsapp">WhatsApp</option>
|
||||
<option value="phone">Phone</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ display: 'block', marginBottom: '0.25rem', fontWeight: 600, color: navy, fontSize: '13px' }}>
|
||||
{guestForm.contactMethod === 'email' ? 'Email Address' : guestForm.contactMethod === 'whatsapp' ? 'WhatsApp Number' : 'Phone Number'} *
|
||||
</label>
|
||||
<input
|
||||
type={guestForm.contactMethod === 'email' ? 'email' : 'tel'}
|
||||
value={guestForm.contact}
|
||||
onChange={(e) => setGuestForm({ ...guestForm, contact: e.target.value })}
|
||||
placeholder={guestForm.contactMethod === 'email' ? 'your@email.com' : '+593 99 999 9999'}
|
||||
style={{ width: '100%', padding: '0.5rem', borderRadius: '8px', border: '1px solid rgba(1,13,30,0.2)', fontSize: '14px' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{user && (
|
||||
<div style={{ marginBottom: '1rem', padding: '0.75rem', background: '#f8f8f8', borderRadius: '10px', fontSize: '13px', color: '#555' }}>
|
||||
<strong>Reserving as:</strong> {user.first_name} {user.last_name} ({user.email})
|
||||
</div>
|
||||
)}
|
||||
<div style={{ marginBottom: '1rem' }}>
|
||||
<label style={{ display: 'block', marginBottom: '0.5rem', fontWeight: 600, color: navy }}>Check-in Date *</label>
|
||||
<input
|
||||
type="date"
|
||||
value={reservationDates.checkIn}
|
||||
onChange={(e) => setReservationDates({ ...reservationDates, checkIn: e.target.value })}
|
||||
min={new Date().toISOString().split('T')[0]}
|
||||
style={{ width: '100%', padding: '0.75rem', borderRadius: '10px', border: '1px solid rgba(1,13,30,0.2)', fontSize: '16px' }}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ marginBottom: '1rem' }}>
|
||||
<label style={{ display: 'block', marginBottom: '0.5rem', fontWeight: 600, color: navy }}>Check-out Date *</label>
|
||||
<input
|
||||
type="date"
|
||||
value={reservationDates.checkOut}
|
||||
onChange={(e) => setReservationDates({ ...reservationDates, checkOut: e.target.value })}
|
||||
min={reservationDates.checkIn || new Date().toISOString().split('T')[0]}
|
||||
style={{ width: '100%', padding: '0.75rem', borderRadius: '10px', border: '1px solid rgba(1,13,30,0.2)', fontSize: '16px' }}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ marginBottom: '1rem' }}>
|
||||
<label style={{ display: 'block', marginBottom: '0.5rem', fontWeight: 600, color: navy }}>Availability Calendar</label>
|
||||
<RoomCalendar roomId={selectedRoom.id} />
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '1rem', marginTop: '1.5rem' }}>
|
||||
<button onClick={closeModal} style={{ flex: 1, padding: '0.75rem', borderRadius: '10px', border: `2px solid ${navy}`, background: 'transparent', color: navy, fontWeight: 600, cursor: 'pointer' }}>Cancel</button>
|
||||
<button onClick={handleReserve} disabled={submitting || !reservationDates.checkIn || !reservationDates.checkOut || (!user && (!guestForm.name.trim() || !guestForm.contact.trim()))} style={{ flex: 1, padding: '0.75rem', borderRadius: '10px', border: 'none', background: gold, color: navy, fontWeight: 600, cursor: 'pointer', opacity: submitting ? 0.6 : 1 }}>{submitting ? 'Processing...' : 'Request Reservation'}</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
+20
-2
@@ -319,11 +319,14 @@ export async function initSchema() {
|
||||
CREATE TABLE IF NOT EXISTS reservations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
room_id INTEGER NOT NULL REFERENCES rooms(id),
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
user_id INTEGER REFERENCES users(id),
|
||||
check_in DATE NOT NULL,
|
||||
check_out DATE NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
total_price DECIMAL(10,2),
|
||||
guest_name VARCHAR(100),
|
||||
guest_contact_method VARCHAR(20),
|
||||
guest_contact VARCHAR(255),
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
@@ -345,8 +348,11 @@ export async function initSchema() {
|
||||
CREATE TABLE IF NOT EXISTS messages (
|
||||
id SERIAL PRIMARY KEY,
|
||||
room_id INTEGER REFERENCES rooms(id),
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
user_id INTEGER REFERENCES users(id),
|
||||
message TEXT NOT NULL,
|
||||
guest_name VARCHAR(100),
|
||||
guest_contact_method VARCHAR(20),
|
||||
guest_contact VARCHAR(255),
|
||||
read_by_admins BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
@@ -354,6 +360,18 @@ export async function initSchema() {
|
||||
|
||||
await db.query(`ALTER TABLE room_comments ADD COLUMN IF NOT EXISTS status VARCHAR(20) DEFAULT 'approved'`);
|
||||
|
||||
// Add guest columns to reservations
|
||||
await db.query(`ALTER TABLE reservations ADD COLUMN IF NOT EXISTS guest_name VARCHAR(100)`);
|
||||
await db.query(`ALTER TABLE reservations ADD COLUMN IF NOT EXISTS guest_contact_method VARCHAR(20)`);
|
||||
await db.query(`ALTER TABLE reservations ADD COLUMN IF NOT EXISTS guest_contact VARCHAR(255)`);
|
||||
await db.query(`ALTER TABLE reservations ALTER COLUMN user_id DROP NOT NULL`);
|
||||
|
||||
// Add guest columns to messages
|
||||
await db.query(`ALTER TABLE messages ADD COLUMN IF NOT EXISTS guest_name VARCHAR(100)`);
|
||||
await db.query(`ALTER TABLE messages ADD COLUMN IF NOT EXISTS guest_contact_method VARCHAR(20)`);
|
||||
await db.query(`ALTER TABLE messages ADD COLUMN IF NOT EXISTS guest_contact VARCHAR(255)`);
|
||||
await db.query(`ALTER TABLE messages ALTER COLUMN user_id DROP NOT NULL`);
|
||||
|
||||
await db.query(`CREATE INDEX IF NOT EXISTS idx_reservations_room_dates ON reservations(room_id, check_in, check_out)`);
|
||||
await db.query(`CREATE INDEX IF NOT EXISTS idx_room_availability_room_date ON room_availability(room_id, date)`);
|
||||
await db.query(`CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at DESC)`);
|
||||
|
||||
Reference in New Issue
Block a user