2025-06-17 20:42:41 +00:00
|
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
|
|
|
import { DialogTitle } from "@radix-ui/react-dialog";
|
2026-02-06 20:55:10 +00:00
|
|
|
import { useState } from "react";
|
2025-06-17 20:42:41 +00:00
|
|
|
import { useForm } from "react-hook-form";
|
|
|
|
|
import { z } from "zod";
|
2025-06-15 21:06:10 +00:00
|
|
|
import { Button } from "./ui/button";
|
2025-06-21 23:43:35 +00:00
|
|
|
import { Calendar29 } from "./ui/DatePicker";
|
2025-06-17 20:42:41 +00:00
|
|
|
import { Dialog, DialogContent, DialogTrigger } from "./ui/dialog";
|
2025-06-21 23:22:11 +00:00
|
|
|
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "./ui/form";
|
2025-06-18 20:38:50 +00:00
|
|
|
import { Input } from "./ui/input";
|
2025-06-15 21:06:10 +00:00
|
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
|
2025-06-14 15:36:38 +00:00
|
|
|
|
2025-06-17 20:42:41 +00:00
|
|
|
|
2025-06-18 18:56:02 +00:00
|
|
|
export enum Metric {
|
|
|
|
|
qmprice = 'qmprice',
|
|
|
|
|
rooms = 'rooms',
|
|
|
|
|
qm = 'qm',
|
|
|
|
|
price = 'total_price',
|
2025-06-17 20:42:41 +00:00
|
|
|
}
|
2025-06-18 20:38:50 +00:00
|
|
|
export enum ListingType {
|
|
|
|
|
RENT = 'RENT',
|
|
|
|
|
BUY = 'BUY'
|
|
|
|
|
}
|
2025-06-17 20:42:41 +00:00
|
|
|
|
2026-02-06 20:55:10 +00:00
|
|
|
export enum FurnishType {
|
|
|
|
|
FURNISHED = 'furnished',
|
|
|
|
|
PART_FURNISHED = 'partFurnished',
|
|
|
|
|
UNFURNISHED = 'unfurnished',
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-17 20:42:41 +00:00
|
|
|
|
2025-06-18 18:56:02 +00:00
|
|
|
export interface ParameterValues {
|
2025-06-17 20:42:41 +00:00
|
|
|
metric: Metric
|
2025-06-18 20:38:50 +00:00
|
|
|
listing_type: ListingType
|
2025-06-18 20:54:47 +00:00
|
|
|
min_bedrooms?: number
|
|
|
|
|
max_bedrooms?: number
|
2025-06-18 20:57:20 +00:00
|
|
|
min_price?: number
|
2025-06-18 20:38:50 +00:00
|
|
|
max_price?: number
|
2025-06-18 21:05:38 +00:00
|
|
|
min_sqm?: number
|
2026-02-06 20:55:10 +00:00
|
|
|
max_sqm?: number
|
|
|
|
|
min_price_per_sqm?: number
|
|
|
|
|
max_price_per_sqm?: number
|
2025-06-21 17:39:10 +00:00
|
|
|
last_seen_days?: number
|
2025-06-21 23:22:11 +00:00
|
|
|
available_from?: Date
|
2025-06-23 21:16:07 +00:00
|
|
|
district: string
|
2026-02-06 20:55:10 +00:00
|
|
|
furnish_types?: FurnishType[]
|
2025-06-17 20:42:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-15 12:48:52 +00:00
|
|
|
export function Parameters(
|
|
|
|
|
props: {
|
2025-06-15 21:06:10 +00:00
|
|
|
isOpen: boolean,
|
2025-06-21 12:49:04 +00:00
|
|
|
onSubmit: (action: 'fetch-data' | 'visualize', fromValues: ParameterValues) => void,
|
2025-06-21 17:26:45 +00:00
|
|
|
setIsOpen: (isOpen: boolean) => void
|
2025-06-15 12:48:52 +00:00
|
|
|
}
|
|
|
|
|
) {
|
2025-06-17 20:42:41 +00:00
|
|
|
const {
|
|
|
|
|
register,
|
|
|
|
|
} = useForm<ParameterValues>()
|
2025-06-21 12:49:04 +00:00
|
|
|
const [action, setAction] = useState<'fetch-data' | 'visualize' | null>(null)
|
2025-06-21 23:43:35 +00:00
|
|
|
const [availableFromRawInput, setAvailableFromRawInput] = useState("now");
|
2025-06-17 20:42:41 +00:00
|
|
|
|
|
|
|
|
const formSchema = z.object({
|
|
|
|
|
metric: z.nativeEnum(Metric, { required_error: "Metric is required" }),
|
2025-06-18 20:38:50 +00:00
|
|
|
listing_type: z.nativeEnum(ListingType, { required_error: "Listing Type is required" }),
|
2025-06-18 20:54:47 +00:00
|
|
|
min_bedrooms: z.number().min(1).max(10).optional(),
|
|
|
|
|
max_bedrooms: z.number().min(1).max(10).optional(),
|
2025-06-18 20:38:50 +00:00
|
|
|
max_price: z.number().optional(),
|
2025-06-21 12:49:04 +00:00
|
|
|
min_price: z.number().min(0).optional(),
|
2025-06-18 21:05:38 +00:00
|
|
|
min_sqm: z.number().optional(),
|
2025-06-21 17:39:10 +00:00
|
|
|
last_seen_days: z.number().min(0).optional(),
|
2025-06-21 23:22:11 +00:00
|
|
|
available_from: z.date(),
|
2025-06-23 21:16:07 +00:00
|
|
|
district: z.string()
|
2025-06-17 20:42:41 +00:00
|
|
|
})
|
|
|
|
|
const form = useForm<z.infer<typeof formSchema>>({
|
|
|
|
|
resolver: zodResolver(formSchema),
|
|
|
|
|
defaultValues: {
|
|
|
|
|
metric: Metric.qmprice,
|
2025-06-21 23:43:35 +00:00
|
|
|
listing_type: ListingType.RENT,
|
2025-06-18 20:54:47 +00:00
|
|
|
min_bedrooms: 1,
|
|
|
|
|
max_bedrooms: 3,
|
2025-06-18 20:38:50 +00:00
|
|
|
max_price: 3000,
|
2025-06-21 12:49:04 +00:00
|
|
|
min_price: 2000,
|
2025-06-21 23:43:35 +00:00
|
|
|
min_sqm: 50,
|
2025-07-15 18:52:25 +00:00
|
|
|
last_seen_days: 28,
|
2025-06-21 23:22:11 +00:00
|
|
|
available_from: new Date(),
|
2025-06-23 21:16:07 +00:00
|
|
|
district: ''
|
2025-06-17 20:42:41 +00:00
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
// 2. Define a submit handler.
|
|
|
|
|
function onSubmit(values: z.infer<typeof formSchema>) {
|
|
|
|
|
// Do something with the form values.
|
|
|
|
|
// ✅ This will be type-safe and validated.
|
2025-06-18 20:38:50 +00:00
|
|
|
console.log(values)
|
2025-06-21 12:49:04 +00:00
|
|
|
if (action) {
|
|
|
|
|
props.onSubmit(action, values)
|
|
|
|
|
}
|
2025-06-17 20:42:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-06-21 12:49:04 +00:00
|
|
|
|
2025-06-14 15:36:38 +00:00
|
|
|
return <>
|
2025-06-21 17:26:45 +00:00
|
|
|
<Dialog open={props.isOpen} onOpenChange={props.setIsOpen} >
|
|
|
|
|
{/* <Dialog > */}
|
2025-06-17 20:42:41 +00:00
|
|
|
<DialogTrigger asChild>
|
2025-06-21 17:26:45 +00:00
|
|
|
<Button variant="outline" onClick={() => props.setIsOpen(true)}>Open Parameters</Button>
|
2025-06-17 20:42:41 +00:00
|
|
|
</DialogTrigger>
|
|
|
|
|
<DialogContent className="sm:max-w-[425px]">
|
|
|
|
|
<DialogTitle>
|
|
|
|
|
Visualization Parameters
|
|
|
|
|
</DialogTitle>
|
|
|
|
|
|
|
|
|
|
<Form {...form}>
|
2025-06-23 21:16:07 +00:00
|
|
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
2025-06-17 20:42:41 +00:00
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="metric"
|
|
|
|
|
render={({ field }) => (
|
2025-06-23 21:16:07 +00:00
|
|
|
<FormItem className="flex flex-row items-center gap-4">
|
2025-06-17 20:42:41 +00:00
|
|
|
<FormLabel>Metric to visualize</FormLabel>
|
|
|
|
|
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<SelectTrigger className="w-[180px]">
|
|
|
|
|
<SelectValue placeholder="Metric to Visualize" />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
</FormControl>
|
|
|
|
|
<SelectContent {...register('metric')} >
|
|
|
|
|
<SelectItem value={Metric.qmprice}>Price per squaremeter</SelectItem>
|
|
|
|
|
<SelectItem value={Metric.rooms}>Number of rooms</SelectItem>
|
|
|
|
|
<SelectItem value={Metric.qm}>Area</SelectItem>
|
|
|
|
|
<SelectItem value={Metric.price}>Price</SelectItem>
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
2025-06-18 20:38:50 +00:00
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="listing_type"
|
|
|
|
|
render={({ field }) => (
|
2025-06-23 21:16:07 +00:00
|
|
|
<FormItem className="flex flex-row items-center gap-4">
|
2025-06-18 20:38:50 +00:00
|
|
|
<FormLabel>Listing Type</FormLabel>
|
|
|
|
|
<FormControl >
|
|
|
|
|
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<SelectTrigger className="w-[180px]">
|
|
|
|
|
<SelectValue placeholder="Metric to Visualize" />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
</FormControl>
|
|
|
|
|
<SelectContent >
|
|
|
|
|
<SelectItem value={ListingType.BUY}>To buy</SelectItem>
|
|
|
|
|
<SelectItem value={ListingType.RENT}>To rent</SelectItem>
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
</FormControl>
|
|
|
|
|
<FormMessage />
|
2025-06-18 20:54:47 +00:00
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
2025-06-21 23:43:35 +00:00
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="min_sqm"
|
|
|
|
|
render={({ field }) => (
|
2025-06-23 21:16:07 +00:00
|
|
|
<FormItem className="flex flex-row items-center gap-4">
|
2025-06-21 23:43:35 +00:00
|
|
|
<FormLabel>Min square meters</FormLabel>
|
|
|
|
|
<FormControl >
|
|
|
|
|
<Input type="number" placeholder={"m²"} {...field} onChange={(e) => field.onChange(Number(e.target.value))} />
|
|
|
|
|
</FormControl>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
2025-06-18 20:54:47 +00:00
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="min_bedrooms"
|
|
|
|
|
render={({ field }) => (
|
2025-06-23 21:16:07 +00:00
|
|
|
<FormItem className="flex flex-row items-center gap-4">
|
2025-06-18 20:54:47 +00:00
|
|
|
<FormLabel>Minimum number of bedrooms</FormLabel>
|
|
|
|
|
<FormControl >
|
|
|
|
|
<Input type="number" placeholder={"# bedrooms"} {...field} onChange={(e) => field.onChange(Number(e.target.value))} />
|
|
|
|
|
</FormControl>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="max_bedrooms"
|
|
|
|
|
render={({ field }) => (
|
2025-06-23 21:16:07 +00:00
|
|
|
<FormItem className="flex flex-row items-center gap-4">
|
2025-06-18 20:54:47 +00:00
|
|
|
<FormLabel>Maximum number of bedrooms</FormLabel>
|
|
|
|
|
<FormControl >
|
|
|
|
|
<Input type="number" placeholder={"# bedrooms"} {...field} onChange={(e) => field.onChange(Number(e.target.value))} />
|
|
|
|
|
</FormControl>
|
|
|
|
|
<FormMessage />
|
2025-06-18 20:38:50 +00:00
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
2025-06-18 21:05:38 +00:00
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="min_price"
|
|
|
|
|
render={({ field }) => (
|
2025-06-23 21:16:07 +00:00
|
|
|
<FormItem className="flex flex-row items-center gap-4">
|
2025-06-18 21:05:38 +00:00
|
|
|
<FormLabel>Min price</FormLabel>
|
|
|
|
|
<FormControl >
|
|
|
|
|
<Input type="number" placeholder={"£"} {...field} onChange={(e) => field.onChange(Number(e.target.value))} />
|
|
|
|
|
</FormControl>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
2025-06-18 20:38:50 +00:00
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="max_price"
|
|
|
|
|
render={({ field }) => (
|
2025-06-23 21:16:07 +00:00
|
|
|
<FormItem className="flex flex-row items-center gap-4">
|
2025-06-18 20:38:50 +00:00
|
|
|
<FormLabel>Max price</FormLabel>
|
|
|
|
|
<FormControl >
|
|
|
|
|
<Input type="number" placeholder={"£"} {...field} onChange={(e) => field.onChange(Number(e.target.value))} />
|
|
|
|
|
</FormControl>
|
|
|
|
|
<FormMessage />
|
2025-06-18 20:57:20 +00:00
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
2025-06-21 23:43:35 +00:00
|
|
|
name="available_from"
|
2025-06-18 20:57:20 +00:00
|
|
|
render={({ field }) => (
|
2025-06-21 23:43:35 +00:00
|
|
|
<FormItem className="flex flex-col">
|
|
|
|
|
<FormLabel>Available from</FormLabel>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Calendar29 onSelect={field.onChange} selected={field.value} rawInputValue={availableFromRawInput} onChangeRawInputValue={setAvailableFromRawInput} />
|
2025-06-18 20:57:20 +00:00
|
|
|
</FormControl>
|
2025-06-21 23:43:35 +00:00
|
|
|
<FormDescription>
|
|
|
|
|
Applicable for renting listings only
|
|
|
|
|
</FormDescription>
|
2025-06-18 20:57:20 +00:00
|
|
|
<FormMessage />
|
2025-06-18 20:38:50 +00:00
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
2025-06-21 17:39:10 +00:00
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="last_seen_days"
|
|
|
|
|
render={({ field }) => (
|
2025-06-23 21:16:07 +00:00
|
|
|
<FormItem className="flex flex-row items-center gap-4">
|
2025-06-21 17:39:10 +00:00
|
|
|
<FormLabel>Last seen days</FormLabel>
|
|
|
|
|
<FormControl >
|
|
|
|
|
<Input type="number" placeholder={"days"} {...field} onChange={(e) => field.onChange(Number(e.target.value))} />
|
|
|
|
|
</FormControl>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
2025-06-18 20:38:50 +00:00
|
|
|
|
2025-07-04 23:10:43 +00:00
|
|
|
<Button type="submit" value={"visualize"} onClick={() => setAction("visualize")}>Visualize existing data</Button>
|
|
|
|
|
<Button type="submit" value={"fetch-data"} onClick={() => setAction("fetch-data")}>Submit data refresh job</Button>
|
2025-06-17 20:42:41 +00:00
|
|
|
</form>
|
|
|
|
|
</Form>
|
|
|
|
|
</DialogContent>
|
2025-06-15 21:06:10 +00:00
|
|
|
</Dialog>
|
2025-06-14 15:36:38 +00:00
|
|
|
</>
|
|
|
|
|
}
|
|
|
|
|
|