chore: make seed idempotent and fix reset suspense

This commit is contained in:
Tero Halla-aho 2025-11-24 20:22:52 +02:00
parent fb1489e8f0
commit 91abec0295
2 changed files with 121 additions and 113 deletions

View file

@ -1,10 +1,10 @@
'use client'; 'use client';
import { useEffect, useState } from 'react'; import { Suspense, useEffect, useState } from 'react';
import { useSearchParams } from 'next/navigation'; import { useSearchParams } from 'next/navigation';
import { useI18n } from '../../components/I18nProvider'; import { useI18n } from '../../components/I18nProvider';
export default function ResetPasswordPage() { function ResetForm() {
const { t } = useI18n(); const { t } = useI18n();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
@ -66,3 +66,11 @@ export default function ResetPasswordPage() {
</main> </main>
); );
} }
export default function ResetPasswordPage() {
return (
<Suspense fallback={<main className="panel" style={{ maxWidth: 480, margin: '40px auto' }}><p>Loading</p></main>}>
<ResetForm />
</Suspense>
);
}

View file

@ -55,12 +55,6 @@ async function main() {
} }
const sampleHostHash = await bcrypt.hash('changeme-sample', 12); const sampleHostHash = await bcrypt.hash('changeme-sample', 12);
const existing = await prisma.listingTranslation.findFirst({ where: { slug: SAMPLE_SLUG } });
if (existing) {
console.log('Sample listing already exists, skipping seed.');
return;
}
const owner = await prisma.user.upsert({ const owner = await prisma.user.upsert({
where: { email: SAMPLE_EMAIL }, where: { email: SAMPLE_EMAIL },
update: { update: {
@ -84,15 +78,12 @@ async function main() {
}, },
}); });
const listing = await prisma.listing.create({ const listings = [
data: { {
ownerId: owner.id, slug: SAMPLE_SLUG,
status: ListingStatus.PUBLISHED,
approvedAt: new Date(),
approvedById: adminUser ? adminUser.id : owner.id,
country: 'Finland',
region: 'South Karelia',
city: 'Punkaharju', city: 'Punkaharju',
region: 'South Karelia',
country: 'Finland',
streetAddress: 'Saimaan rantatie 12', streetAddress: 'Saimaan rantatie 12',
addressNote: 'Lakeside trail, 5 min from main road', addressNote: 'Lakeside trail, 5 min from main road',
latitude: 61.756, latitude: 61.756,
@ -104,65 +95,28 @@ async function main() {
hasSauna: true, hasSauna: true,
hasFireplace: true, hasFireplace: true,
hasWifi: true, hasWifi: true,
hasAirConditioning: false,
petsAllowed: false, petsAllowed: false,
byTheLake: true, byTheLake: true,
hasAirConditioning: false,
evCharging: 'FREE', evCharging: 'FREE',
priceHintPerNightCents: 14500, priceHintPerNightCents: 14500,
contactName: 'Sample Host', cover: {
contactEmail: SAMPLE_EMAIL,
contactPhone: '+358401234567',
externalUrl: 'https://example.com/saimaa-cabin',
published: true,
images: {
createMany: {
data: [
{
url: 'https://images.unsplash.com/photo-1505691938895-1758d7feb511?auto=format&fit=crop&w=1600&q=80', url: 'https://images.unsplash.com/photo-1505691938895-1758d7feb511?auto=format&fit=crop&w=1600&q=80',
altText: 'Lakeside cabin with sauna', altText: 'Lakeside cabin with sauna',
isCover: true,
order: 1,
},
{
url: 'https://images.unsplash.com/photo-1505693416388-ac5ce068fe85?auto=format&fit=crop&w=1600&q=80',
altText: 'Wood-fired sauna by the lake',
order: 2,
},
{
url: 'https://images.unsplash.com/photo-1470246973918-29a93221c455?auto=format&fit=crop&w=1600&q=80',
altText: 'Living area with fireplace',
order: 3,
}, },
images: [
{ url: 'https://images.unsplash.com/photo-1505693416388-ac5ce068fe85?auto=format&fit=crop&w=1600&q=80', altText: 'Wood-fired sauna by the lake' },
{ url: 'https://images.unsplash.com/photo-1470246973918-29a93221c455?auto=format&fit=crop&w=1600&q=80', altText: 'Living area with fireplace' },
], ],
}, titleEn: 'Saimaa lakeside cabin with sauna',
}, teaserEn: 'Sauna, lake view, private dock, and cozy fireplace.',
}, descEn:
});
await prisma.listingTranslation.createMany({
data: [
{
listingId: listing.id,
locale: DEFAULT_LOCALE,
slug: SAMPLE_SLUG,
title: 'Saimaa lakeside cabin with sauna',
description:
'Classic timber cabin right on Lake Saimaa. Wood-fired sauna, private dock, and a short forest walk to the nearest village. Perfect for slow weekends and midsummer gatherings.', 'Classic timber cabin right on Lake Saimaa. Wood-fired sauna, private dock, and a short forest walk to the nearest village. Perfect for slow weekends and midsummer gatherings.',
teaser: 'Sauna, lake view, private dock, and cozy fireplace.', titleFi: 'Saimaan rantamökki saunalla',
}, teaserFi: 'Puusauna, järvinäköala, oma laituri ja takka.',
{ descFi:
listingId: listing.id,
locale: 'fi',
slug: SAMPLE_SLUG,
title: 'Saimaan rantamökki saunalla',
description:
'Perinteinen hirsimökki Saimaan rannalla. Puusauna, oma laituri ja lyhyt metsäreitti kylään. Sopii täydellisesti viikonloppuihin ja juhannukseen.', 'Perinteinen hirsimökki Saimaan rannalla. Puusauna, oma laituri ja lyhyt metsäreitti kylään. Sopii täydellisesti viikonloppuihin ja juhannukseen.',
teaser: 'Puusauna, järvinäköala, oma laituri ja takka.',
}, },
],
});
const extraListings = [
{ {
slug: 'helsinki-design-loft', slug: 'helsinki-design-loft',
city: 'Helsinki', city: 'Helsinki',
@ -469,7 +423,9 @@ async function main() {
}, },
]; ];
for (const item of extraListings) { for (const item of listings) {
const existing = await prisma.listingTranslation.findFirst({ where: { slug: item.slug }, select: { listingId: true } });
if (!existing) {
const created = await prisma.listing.create({ const created = await prisma.listing.create({
data: { data: {
ownerId: owner.id, ownerId: owner.id,
@ -502,31 +458,14 @@ async function main() {
translations: { translations: {
createMany: { createMany: {
data: [ data: [
{ { locale: 'en', slug: item.slug, title: item.titleEn, teaser: item.teaserEn, description: item.descEn },
locale: 'en', { locale: 'fi', slug: item.slug, title: item.titleFi, teaser: item.teaserFi, description: item.descFi },
slug: item.slug,
title: item.titleEn,
teaser: item.teaserEn,
description: item.descEn,
},
{
locale: 'fi',
slug: item.slug,
title: item.titleFi,
teaser: item.teaserFi,
description: item.descFi,
},
], ],
}, },
}, },
images: { images: {
create: [ create: [
{ { url: item.cover.url, altText: item.cover.altText, order: 1, isCover: true },
url: item.cover.url,
altText: item.cover.altText,
order: 1,
isCover: true,
},
...item.images.map((img, idx) => ({ ...item.images.map((img, idx) => ({
url: img.url, url: img.url,
altText: img.altText ?? null, altText: img.altText ?? null,
@ -538,9 +477,70 @@ async function main() {
}, },
}); });
console.log('Seeded listing:', created.id, item.slug); console.log('Seeded listing:', created.id, item.slug);
continue;
} }
console.log('Seeded sample listing at slug:', SAMPLE_SLUG); const listingId = existing.listingId;
await prisma.listing.update({
where: { id: listingId },
data: {
country: item.country,
region: item.region,
city: item.city,
streetAddress: item.streetAddress,
addressNote: item.addressNote,
latitude: item.latitude,
longitude: item.longitude,
maxGuests: item.maxGuests,
bedrooms: item.bedrooms,
beds: item.beds,
bathrooms: item.bathrooms,
hasSauna: item.hasSauna,
hasFireplace: item.hasFireplace,
hasWifi: item.hasWifi,
hasAirConditioning: item.hasAirConditioning,
petsAllowed: item.petsAllowed,
byTheLake: item.byTheLake,
evCharging: item.evCharging,
priceHintPerNightCents: item.priceHintPerNightCents,
contactName: 'Sample Host',
contactEmail: SAMPLE_EMAIL,
contactPhone: owner.phone,
published: true,
status: ListingStatus.PUBLISHED,
approvedAt: new Date(),
},
});
await prisma.listingTranslation.upsert({
where: { slug_locale: { slug: item.slug, locale: 'en' } },
create: { listingId, locale: 'en', slug: item.slug, title: item.titleEn, teaser: item.teaserEn, description: item.descEn },
update: { title: item.titleEn, teaser: item.teaserEn, description: item.descEn },
});
await prisma.listingTranslation.upsert({
where: { slug_locale: { slug: item.slug, locale: 'fi' } },
create: { listingId, locale: 'fi', slug: item.slug, title: item.titleFi, teaser: item.teaserFi, description: item.descFi },
update: { title: item.titleFi, teaser: item.teaserFi, description: item.descFi },
});
await prisma.listingImage.deleteMany({ where: { listingId } });
await prisma.listingImage.createMany({
data: [
{ listingId, url: item.cover.url, altText: item.cover.altText, order: 1, isCover: true },
...item.images.map((img, idx) => ({
listingId,
url: img.url,
altText: img.altText ?? null,
order: idx + 2,
isCover: false,
})),
],
});
console.log('Updated listing:', item.slug);
}
console.log('Seed completed for sample listings.');
} }
main() main()