Add kitchen/laundry/barbecue amenities

This commit is contained in:
Tero Halla-aho 2025-11-26 23:19:09 +02:00
parent 460efb9616
commit 35f2551c6b
9 changed files with 106 additions and 0 deletions

View file

@ -100,6 +100,10 @@ export async function GET(req: Request) {
petsAllowed: listing.petsAllowed,
byTheLake: listing.byTheLake,
hasAirConditioning: listing.hasAirConditioning,
hasKitchen: listing.hasKitchen,
hasDishwasher: listing.hasDishwasher,
hasWashingMachine: listing.hasWashingMachine,
hasBarbecue: listing.hasBarbecue,
evCharging: listing.evCharging,
maxGuests: listing.maxGuests,
bedrooms: listing.bedrooms,
@ -230,6 +234,10 @@ export async function POST(req: Request) {
petsAllowed: Boolean(body.petsAllowed),
byTheLake: Boolean(body.byTheLake),
hasAirConditioning: Boolean(body.hasAirConditioning),
hasKitchen: Boolean(body.hasKitchen),
hasDishwasher: Boolean(body.hasDishwasher),
hasWashingMachine: Boolean(body.hasWashingMachine),
hasBarbecue: Boolean(body.hasBarbecue),
evCharging: normalizeEvCharging(body.evCharging),
priceHintPerNightEuros,
contactName,

View file

@ -18,6 +18,10 @@ const amenityIcons: Record<string, string> = {
lake: '🌊',
ac: '❄️',
ev: '⚡',
kitchen: '🍽️',
dishwasher: '🧼',
washer: '🧺',
barbecue: '🍖',
};
export async function generateMetadata({ params }: ListingPageProps): Promise<Metadata> {
@ -52,6 +56,10 @@ export default async function ListingPage({ params }: ListingPageProps) {
listing.hasAirConditioning ? { icon: amenityIcons.ac, label: t('amenityAirConditioning') } : null,
listing.evCharging === 'FREE' ? { icon: amenityIcons.ev, label: t('amenityEvFree') } : null,
listing.evCharging === 'PAID' ? { icon: amenityIcons.ev, label: t('amenityEvPaid') } : null,
listing.hasKitchen ? { icon: amenityIcons.kitchen, label: t('amenityKitchen') } : null,
listing.hasDishwasher ? { icon: amenityIcons.dishwasher, label: t('amenityDishwasher') } : null,
listing.hasWashingMachine ? { icon: amenityIcons.washer, label: t('amenityWashingMachine') } : null,
listing.hasBarbecue ? { icon: amenityIcons.barbecue, label: t('amenityBarbecue') } : null,
].filter(Boolean) as { icon: string; label: string }[];
const addressLine = `${listing.streetAddress ? `${listing.streetAddress}, ` : ''}${listing.city}, ${listing.region}, ${listing.country}`;
const capacityLine = `${t('capacityGuests', { count: listing.maxGuests })} · ${t('capacityBedrooms', { count: listing.bedrooms })} · ${t('capacityBeds', { count: listing.beds })} · ${t('capacityBathrooms', { count: listing.bathrooms })}`;

View file

@ -42,6 +42,10 @@ export default function NewListingPage() {
const [petsAllowed, setPetsAllowed] = useState(false);
const [byTheLake, setByTheLake] = useState(false);
const [hasAirConditioning, setHasAirConditioning] = useState(false);
const [hasKitchen, setHasKitchen] = useState(true);
const [hasDishwasher, setHasDishwasher] = useState(false);
const [hasWashingMachine, setHasWashingMachine] = useState(false);
const [hasBarbecue, setHasBarbecue] = useState(false);
const [evCharging, setEvCharging] = useState<'NONE' | 'FREE' | 'PAID'>('NONE');
const [selectedImages, setSelectedImages] = useState<SelectedImage[]>([]);
const [coverImageIndex, setCoverImageIndex] = useState(1);
@ -111,6 +115,10 @@ export default function NewListingPage() {
{ key: 'pets', label: t('amenityPets'), icon: '🐾', checked: petsAllowed, toggle: setPetsAllowed },
{ key: 'lake', label: t('amenityLake'), icon: '🌊', checked: byTheLake, toggle: setByTheLake },
{ key: 'ac', label: t('amenityAirConditioning'), icon: '❄️', checked: hasAirConditioning, toggle: setHasAirConditioning },
{ key: 'kitchen', label: t('amenityKitchen'), icon: '🍽️', checked: hasKitchen, toggle: setHasKitchen },
{ key: 'dishwasher', label: t('amenityDishwasher'), icon: '🧼', checked: hasDishwasher, toggle: setHasDishwasher },
{ key: 'washer', label: t('amenityWashingMachine'), icon: '🧺', checked: hasWashingMachine, toggle: setHasWashingMachine },
{ key: 'barbecue', label: t('amenityBarbecue'), icon: '🍖', checked: hasBarbecue, toggle: setHasBarbecue },
];
function parseImages(): ImageInput[] {
@ -162,6 +170,10 @@ export default function NewListingPage() {
petsAllowed,
byTheLake,
hasAirConditioning,
hasKitchen,
hasDishwasher,
hasWashingMachine,
hasBarbecue,
evCharging,
coverImageIndex,
images: parseImages(),

View file

@ -24,6 +24,10 @@ type ListingResult = {
petsAllowed: boolean;
byTheLake: boolean;
hasAirConditioning: boolean;
hasKitchen: boolean;
hasDishwasher: boolean;
hasWashingMachine: boolean;
hasBarbecue: boolean;
evCharging: 'NONE' | 'FREE' | 'PAID';
maxGuests: number;
bedrooms: number;
@ -394,6 +398,10 @@ export default function ListingsIndexPage() {
{l.evCharging === 'FREE' ? <span className="badge">{t('amenityEvFree')}</span> : null}
{l.evCharging === 'PAID' ? <span className="badge">{t('amenityEvPaid')}</span> : null}
{l.hasAirConditioning ? <span className="badge">{t('amenityAirConditioning')}</span> : null}
{l.hasKitchen ? <span className="badge">{t('amenityKitchen')}</span> : null}
{l.hasDishwasher ? <span className="badge">{t('amenityDishwasher')}</span> : null}
{l.hasWashingMachine ? <span className="badge">{t('amenityWashingMachine')}</span> : null}
{l.hasBarbecue ? <span className="badge">{t('amenityBarbecue')}</span> : null}
{l.hasSauna ? <span className="badge">{t('amenitySauna')}</span> : null}
{l.hasWifi ? <span className="badge">{t('amenityWifi')}</span> : null}
</div>

View file

@ -157,6 +157,10 @@ const allMessages = {
amenityPets: 'Pets allowed',
amenityLake: 'By the lake',
amenityAirConditioning: 'Air conditioning',
amenityKitchen: 'Kitchen',
amenityDishwasher: 'Dishwasher',
amenityWashingMachine: 'Washing machine',
amenityBarbecue: 'Barbecue grill',
amenityEvFree: 'EV charging (free)',
amenityEvPaid: 'EV charging (paid)',
evChargingLabel: 'EV charging',
@ -370,6 +374,10 @@ const allMessages = {
amenityPets: 'Lemmikit sallittu',
amenityLake: 'Järven rannalla',
amenityAirConditioning: 'Ilmastointi',
amenityKitchen: 'Keittiö',
amenityDishwasher: 'Astianpesukone',
amenityWashingMachine: 'Pyykinpesukone',
amenityBarbecue: 'Grilli',
amenityEvFree: 'Sähköauton lataus (ilmainen)',
amenityEvPaid: 'Sähköauton lataus (maksullinen)',
evChargingLabel: 'Sähköauton lataus',

View file

@ -0,0 +1,5 @@
ALTER TABLE "Listing"
ADD COLUMN "hasKitchen" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "hasDishwasher" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "hasWashingMachine" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "hasBarbecue" BOOLEAN NOT NULL DEFAULT false;

View file

@ -30,6 +30,11 @@ CREATE TABLE "Listing" (
"hasSauna" BOOLEAN NOT NULL DEFAULT false,
"hasFireplace" BOOLEAN NOT NULL DEFAULT false,
"hasWifi" BOOLEAN NOT NULL DEFAULT false,
"hasAirConditioning" BOOLEAN NOT NULL DEFAULT false,
"hasKitchen" BOOLEAN NOT NULL DEFAULT false,
"hasDishwasher" BOOLEAN NOT NULL DEFAULT false,
"hasWashingMachine" BOOLEAN NOT NULL DEFAULT false,
"hasBarbecue" BOOLEAN NOT NULL DEFAULT false,
"petsAllowed" BOOLEAN NOT NULL DEFAULT false,
"byTheLake" BOOLEAN NOT NULL DEFAULT false,
"priceHintPerNightEuros" INTEGER,

View file

@ -88,6 +88,10 @@ model Listing {
petsAllowed Boolean @default(false)
byTheLake Boolean @default(false)
hasAirConditioning Boolean @default(false)
hasKitchen Boolean @default(false)
hasDishwasher Boolean @default(false)
hasWashingMachine Boolean @default(false)
hasBarbecue Boolean @default(false)
evCharging EvCharging @default(NONE)
priceHintPerNightEuros Int?
contactName String

View file

@ -153,6 +153,10 @@ async function main() {
hasFireplace: true,
hasWifi: true,
hasAirConditioning: false,
hasKitchen: true,
hasDishwasher: false,
hasWashingMachine: false,
hasBarbecue: false,
petsAllowed: false,
byTheLake: true,
evCharging: 'FREE',
@ -193,6 +197,10 @@ async function main() {
hasFireplace: false,
hasWifi: true,
hasAirConditioning: true,
hasKitchen: true,
hasDishwasher: false,
hasWashingMachine: false,
hasBarbecue: false,
petsAllowed: false,
byTheLake: false,
evCharging: 'PAID',
@ -231,6 +239,10 @@ async function main() {
hasFireplace: false,
hasWifi: true,
hasAirConditioning: false,
hasKitchen: true,
hasDishwasher: false,
hasWashingMachine: false,
hasBarbecue: false,
petsAllowed: true,
byTheLake: false,
evCharging: 'NONE',
@ -268,6 +280,10 @@ async function main() {
hasFireplace: true,
hasWifi: true,
hasAirConditioning: false,
hasKitchen: true,
hasDishwasher: false,
hasWashingMachine: false,
hasBarbecue: false,
petsAllowed: false,
byTheLake: true,
evCharging: 'FREE',
@ -305,6 +321,10 @@ async function main() {
hasFireplace: false,
hasWifi: true,
hasAirConditioning: true,
hasKitchen: true,
hasDishwasher: false,
hasWashingMachine: false,
hasBarbecue: false,
petsAllowed: false,
byTheLake: false,
evCharging: 'NONE',
@ -340,6 +360,10 @@ async function main() {
hasFireplace: true,
hasWifi: true,
hasAirConditioning: false,
hasKitchen: true,
hasDishwasher: false,
hasWashingMachine: false,
hasBarbecue: false,
petsAllowed: true,
byTheLake: true,
evCharging: 'PAID',
@ -375,6 +399,10 @@ async function main() {
hasFireplace: false,
hasWifi: true,
hasAirConditioning: false,
hasKitchen: true,
hasDishwasher: false,
hasWashingMachine: false,
hasBarbecue: false,
petsAllowed: false,
byTheLake: true,
evCharging: 'FREE',
@ -410,6 +438,10 @@ async function main() {
hasFireplace: true,
hasWifi: true,
hasAirConditioning: false,
hasKitchen: true,
hasDishwasher: false,
hasWashingMachine: false,
hasBarbecue: false,
petsAllowed: false,
byTheLake: false,
evCharging: 'NONE',
@ -445,6 +477,10 @@ async function main() {
hasFireplace: false,
hasWifi: true,
hasAirConditioning: true,
hasKitchen: true,
hasDishwasher: false,
hasWashingMachine: false,
hasBarbecue: false,
petsAllowed: false,
byTheLake: false,
evCharging: 'FREE',
@ -480,6 +516,10 @@ async function main() {
hasFireplace: false,
hasWifi: true,
hasAirConditioning: false,
hasKitchen: true,
hasDishwasher: false,
hasWashingMachine: false,
hasBarbecue: false,
petsAllowed: false,
byTheLake: true,
evCharging: 'PAID',
@ -525,6 +565,10 @@ async function main() {
hasFireplace: item.hasFireplace,
hasWifi: item.hasWifi,
hasAirConditioning: item.hasAirConditioning,
hasKitchen: item.hasKitchen ?? false,
hasDishwasher: item.hasDishwasher ?? false,
hasWashingMachine: item.hasWashingMachine ?? false,
hasBarbecue: item.hasBarbecue ?? false,
petsAllowed: item.petsAllowed,
byTheLake: item.byTheLake,
evCharging: item.evCharging,
@ -568,6 +612,10 @@ async function main() {
hasFireplace: item.hasFireplace,
hasWifi: item.hasWifi,
hasAirConditioning: item.hasAirConditioning,
hasKitchen: item.hasKitchen ?? false,
hasDishwasher: item.hasDishwasher ?? false,
hasWashingMachine: item.hasWashingMachine ?? false,
hasBarbecue: item.hasBarbecue ?? false,
petsAllowed: item.petsAllowed,
byTheLake: item.byTheLake,
evCharging: item.evCharging,