Bewertung

This commit is contained in:
2026-08-16 09:25:32 +00:00
parent 48ba6ae282
commit 9f5f4911f2
4 changed files with 1078 additions and 519 deletions

View File

@@ -0,0 +1,27 @@
import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
export const dynamic = 'force-dynamic';
// POST: Bewertung speichern oder aktualisieren
export async function POST(req: NextRequest, { params }: { params: { id: string } }) {
try {
const { member_id, grade, comment } = await req.json();
if (!member_id || !grade) {
return NextResponse.json({ error: 'Mitreisender und Note erforderlich' }, { status: 400 });
}
const res = await query(
`INSERT INTO hotel_ratings (hotel_id, member_id, grade, comment, updated_at)
VALUES ($1, $2, $3, $4, NOW())
ON CONFLICT (hotel_id, member_id)
DO UPDATE SET grade = EXCLUDED.grade, comment = EXCLUDED.comment, updated_at = NOW()
RETURNING *`,
[params.id, member_id, grade, comment ? comment.trim() : null]
);
return NextResponse.json({ success: true, rating: res.rows[0] });
} catch (e: any) {
return NextResponse.json({ success: false, error: e.message }, { status: 500 });
}
}

View File

@@ -6,33 +6,53 @@ export const dynamic = 'force-dynamic';
export async function GET() {
try {
const res = await query(`
SELECT
h.*,
t.name AS trip_name,
ph.price_numeric::float AS latest_price,
ph.price_per_night::float AS latest_price_per_night,
ph.price_per_person::float AS latest_price_per_person,
ph.nights AS latest_nights,
ph.persons AS latest_persons,
bp.best_price,
hr.avg_grade,
hr.total_votes,
hr.ratings_json
FROM hotels h
LEFT JOIN trips t ON h.trip_id = t.id
LEFT JOIN LATERAL (
SELECT price_numeric, price_per_night, price_per_person, nights, persons
FROM hotel_price_history
WHERE hotel_id::text = h.id::text
ORDER BY tracked_at DESC NULLS LAST, id DESC
LIMIT 1
) ph ON true
LEFT JOIN LATERAL (
SELECT MIN(price_numeric)::float AS best_price
FROM hotel_price_history
WHERE hotel_id::text = h.id::text AND price_numeric > 0
) bp ON true
LEFT JOIN LATERAL (
SELECT
h.*,
t.name AS trip_name,
ph.price_numeric::float AS latest_price,
ph.price_per_night::float AS latest_price_per_night,
ph.price_per_person::float AS latest_price_per_person,
ph.nights AS latest_nights,
ph.persons AS latest_persons,
bp.best_price,
bp.best_price_per_night
FROM hotels h
LEFT JOIN trips t ON h.trip_id = t.id
LEFT JOIN LATERAL (
SELECT price_numeric, price_per_night, price_per_person, nights, persons
FROM hotel_price_history
WHERE hotel_id::text = h.id::text
ORDER BY tracked_at DESC NULLS LAST, id DESC
LIMIT 1
) ph ON true
LEFT JOIN LATERAL (
SELECT
MIN(price_numeric)::float AS best_price,
MIN(price_per_night)::float AS best_price_per_night
FROM hotel_price_history
WHERE hotel_id::text = h.id::text AND price_numeric > 0
) bp ON true
ORDER BY h.checkin_date ASC NULLS LAST, h.id ASC
ROUND(AVG(hr_sub.grade), 1)::float AS avg_grade,
COUNT(hr_sub.id)::int AS total_votes,
COALESCE(
json_agg(
json_build_object(
'member_id', hr_sub.member_id,
'name', tm.name,
'color', tm.color,
'grade', hr_sub.grade,
'comment', hr_sub.comment
)
ORDER BY hr_sub.updated_at DESC
),
'[]'
) AS ratings_json
FROM hotel_ratings hr_sub
JOIN trip_members tm ON hr_sub.member_id = tm.id
WHERE hr_sub.hotel_id = h.id
) hr ON true
`);
return NextResponse.json({ success: true, hotels: res.rows });
} catch (error: any) {

View File

@@ -0,0 +1,55 @@
import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
export const dynamic = 'force-dynamic';
// GET: Alle Mitreisenden eines Trips laden
export async function GET(req: NextRequest, { params }: { params: { id: string } }) {
try {
const res = await query(
`SELECT * FROM trip_members WHERE trip_id = $1 ORDER BY created_at ASC`,
[params.id]
);
return NextResponse.json({ success: true, members: res.rows });
} catch (e: any) {
return NextResponse.json({ success: false, error: e.message }, { status: 500 });
}
}
// POST: Neuen Mitreisenden hinzufügen
export async function POST(req: NextRequest, { params }: { params: { id: string } }) {
try {
const { name, color } = await req.json();
if (!name) {
return NextResponse.json({ error: 'Name ist erforderlich' }, { status: 400 });
}
const defaultColors = ['#2563eb', '#db2777', '#059669', '#d97706', '#7c3aed', '#0891b2'];
const randomColor = color || defaultColors[Math.floor(Math.random() * defaultColors.length)];
const res = await query(
`INSERT INTO trip_members (trip_id, name, color)
VALUES ($1, $2, $3)
RETURNING *`,
[params.id, name.trim(), randomColor]
);
return NextResponse.json({ success: true, member: res.rows[0] });
} catch (e: any) {
return NextResponse.json({ success: false, error: e.message }, { status: 500 });
}
}
// DELETE: Mitreisenden entfernen
export async function DELETE(req: NextRequest) {
try {
const { searchParams } = new URL(req.url);
const memberId = searchParams.get('memberId');
if (!memberId) return NextResponse.json({ error: 'memberId fehlt' }, { status: 400 });
await query(`DELETE FROM trip_members WHERE id = $1`, [memberId]);
return NextResponse.json({ success: true });
} catch (e: any) {
return NextResponse.json({ success: false, error: e.message }, { status: 500 });
}
}

File diff suppressed because it is too large Load Diff