refactor: migrate room photos from rooms.photos to images table

- Photos now stored exclusively in images table (no duplication)
- rooms.photos array cleared, freed 17 MB storage
- Updated room APIs to fetch images by room_id
- Added PUT endpoint for room updates
- Updated RoomEditor to load/delete images from images table
- Fixed images API to return 'data' key consistently
This commit is contained in:
2026-07-01 21:54:39 -05:00
parent ee68dc2897
commit 09bbe0a441
6 changed files with 222 additions and 21 deletions
+1 -1
View File
@@ -71,7 +71,7 @@ export async function GET(request: NextRequest) {
}
const { rows } = await db.query(query, params);
return NextResponse.json({ images: rows, count: rows.length });
return NextResponse.json({ data: rows, count: rows.length });
} catch (error) {
if ((error as Error).message === 'Unauthorized') {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
+12 -4
View File
@@ -29,15 +29,16 @@ export async function PUT(request: NextRequest, { params }: { params: { id: stri
const id = parseInt(params.id);
if (isNaN(id)) return NextResponse.json({ error: 'Invalid id' }, { status: 400 });
const body = await request.json();
const { name, description, price, photos, sort_order, featured_photo, active, amenity_ids } = body;
const { name, description, price, sort_order, featured_photo, active, amenity_ids } = body;
if (!name || !description || price === undefined) {
return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
}
// Photos are stored in images table, not rooms.photos
const { rows } = await db.query(
`UPDATE rooms SET name = $1, description = $2, price = $3, photos = $4, sort_order = $5, featured_photo = $6, active = $7 WHERE id = $8 RETURNING *`,
[name, description, price, photos || [], sort_order || 0, featured_photo || null, active !== false, id]
`UPDATE rooms SET name = $1, description = $2, price = $3, sort_order = $4, featured_photo = $5, active = $6 WHERE id = $7 RETURNING *`,
[name, description, price, sort_order || 0, featured_photo || null, active !== false, id]
);
// Update amenities
@@ -47,7 +48,14 @@ export async function PUT(request: NextRequest, { params }: { params: { id: stri
await db.query(`INSERT INTO room_amenity_links (room_id, amenity_id) VALUES ${values}`);
}
return NextResponse.json({ data: rows[0] });
// Get photos from images table
const { rows: imageRows } = await db.query(
'SELECT id, data, thumbnail FROM images WHERE room_id = $1 ORDER BY id',
[id]
);
const photos = imageRows.map((r: any) => r.thumbnail || r.data);
return NextResponse.json({ data: { ...rows[0], photos, amenity_ids: amenity_ids || [] } });
} catch (error: any) {
if ((error as Error).message === 'Unauthorized') return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
console.error('PUT /api/rooms/[id]:', error);
+51 -3
View File
@@ -20,8 +20,24 @@ export async function GET() {
amenityMap.get(row.room_id)!.push({ id: row.id, name: row.name, icon_svg: row.icon_svg });
}
// Get photos from images table for all rooms
const { rows: imageRows } = await db.query(`
SELECT room_id, data, thumbnail, medium
FROM images
WHERE room_id IS NOT NULL
ORDER BY room_id, id
`);
const imageMap = new Map<number, string[]>();
for (const row of imageRows) {
if (!imageMap.has(row.room_id)) imageMap.set(row.room_id, []);
// Prefer thumbnail for display, fallback to data
imageMap.get(row.room_id)!.push(row.thumbnail || row.data);
}
const roomsWithAmenities = rows.map(r => ({
...r,
photos: imageMap.get(r.id) || [],
amenities: amenityMap.get(r.id) || []
}));
@@ -36,17 +52,18 @@ export async function POST(request: NextRequest) {
try {
await requireRole('admin');
const body = await request.json();
const { name, description, price, photos, sort_order, featured_photo, amenity_ids } = body;
const { name, description, price, sort_order, featured_photo, amenity_ids } = body;
if (!name || !description || price === undefined) {
return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
}
// Photos are now stored in images table, not rooms.photos
const { rows } = await db.query(
`INSERT INTO rooms (name, description, price, photos, sort_order, featured_photo)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *`,
[name, description, price, photos || [], sort_order || 0, featured_photo || null]
[name, description, price, [], sort_order || 0, featured_photo || null]
);
const roomId = rows[0].id;
@@ -57,9 +74,40 @@ export async function POST(request: NextRequest) {
await db.query(`INSERT INTO room_amenity_links (room_id, amenity_id) VALUES ${values}`);
}
return NextResponse.json({ data: { ...rows[0], amenity_ids: amenity_ids || [] } });
return NextResponse.json({ data: { ...rows[0], photos: [], amenity_ids: amenity_ids || [] } });
} catch (error: any) {
console.error('POST /api/rooms error:', error);
return NextResponse.json({ error: error.message || 'Failed to create room' }, { status: 500 });
}
}
export async function PUT(request: NextRequest) {
try {
await requireRole('admin');
const body = await request.json();
const { id, name, description, price, active, sort_order, featured_photo, amenity_ids } = body;
if (!id) {
return NextResponse.json({ error: 'Room ID required' }, { status: 400 });
}
// Photos are stored in images table, not rooms.photos
const { rows } = await db.query(
`UPDATE rooms SET name = $1, description = $2, price = $3, active = $4, sort_order = $5, featured_photo = $6
WHERE id = $7 RETURNING *`,
[name, description, price, active ?? true, sort_order ?? 0, featured_photo || null, id]
);
// Update amenity links
await db.query('DELETE FROM room_amenity_links WHERE room_id = $1', [id]);
if (amenity_ids && amenity_ids.length > 0) {
const values = amenity_ids.map((aid: number) => `(${id}, ${aid})`).join(',');
await db.query(`INSERT INTO room_amenity_links (room_id, amenity_id) VALUES ${values}`);
}
return NextResponse.json({ data: { ...rows[0], photos: [], amenity_ids: amenity_ids || [] } });
} catch (error: any) {
console.error('PUT /api/rooms error:', error);
return NextResponse.json({ error: error.message || 'Failed to update room' }, { status: 500 });
}
}