diff --git a/crawler/frontend/package-lock.json b/crawler/frontend/package-lock.json index 68a156c..c3cbbde 100644 --- a/crawler/frontend/package-lock.json +++ b/crawler/frontend/package-lock.json @@ -13,6 +13,7 @@ "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-hover-card": "^1.1.14", "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-popover": "^1.1.14", "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-scroll-area": "^1.2.9", "@radix-ui/react-select": "^2.2.5", @@ -25,14 +26,17 @@ "@types/d3": "^7.4.3", "@types/mapbox-gl": "^3.4.1", "@types/turf": "^3.5.32", + "chrono-node": "^2.8.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "crossfilter2": "^1.5.4", "d3": "^7.9.0", + "date-fns": "^4.1.0", "lucide-react": "^0.515.0", "mapbox-gl": "^3.12.0", "oidc-client-ts": "^3.2.1", "react": "^19.1.0", + "react-day-picker": "^9.7.0", "react-dom": "^19.1.0", "react-hook-form": "^7.58.1", "react-oidc-context": "^3.3.0", @@ -71,6 +75,12 @@ "node": ">=6.0.0" } }, + "node_modules/@date-fns/tz": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.2.0.tgz", + "integrity": "sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==", + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", @@ -1211,6 +1221,43 @@ } } }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.14.tgz", + "integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popper": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz", @@ -3260,6 +3307,18 @@ "node": ">=18" } }, + "node_modules/chrono-node": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/chrono-node/-/chrono-node-2.8.3.tgz", + "integrity": "sha512-YukiXak31pshonVWaeJ9cZ4xxWIlbsyn5qYUkG5pQ+usZ6l22ASXDIk0kHUQkIBNOCLRevFkHJjnGKXwZNtyZw==", + "license": "MIT", + "dependencies": { + "dayjs": "^1.10.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", @@ -3755,6 +3814,28 @@ "node": ">=12" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/date-fns-jalali": { + "version": "4.1.0-0", + "resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz", + "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -5136,6 +5217,27 @@ "node": ">=0.10.0" } }, + "node_modules/react-day-picker": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.7.0.tgz", + "integrity": "sha512-urlK4C9XJZVpQ81tmVgd2O7lZ0VQldZeHzNejbwLWZSkzHH498KnArT0EHNfKBOWwKc935iMLGZdxXPRISzUxQ==", + "license": "MIT", + "dependencies": { + "@date-fns/tz": "1.2.0", + "date-fns": "4.1.0", + "date-fns-jalali": "4.1.0-0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/react-dom": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", diff --git a/crawler/frontend/package.json b/crawler/frontend/package.json index bf0764c..466d325 100644 --- a/crawler/frontend/package.json +++ b/crawler/frontend/package.json @@ -15,6 +15,7 @@ "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-hover-card": "^1.1.14", "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-popover": "^1.1.14", "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-scroll-area": "^1.2.9", "@radix-ui/react-select": "^2.2.5", @@ -27,14 +28,17 @@ "@types/d3": "^7.4.3", "@types/mapbox-gl": "^3.4.1", "@types/turf": "^3.5.32", + "chrono-node": "^2.8.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "crossfilter2": "^1.5.4", "d3": "^7.9.0", + "date-fns": "^4.1.0", "lucide-react": "^0.515.0", "mapbox-gl": "^3.12.0", "oidc-client-ts": "^3.2.1", "react": "^19.1.0", + "react-day-picker": "^9.7.0", "react-dom": "^19.1.0", "react-hook-form": "^7.58.1", "react-oidc-context": "^3.3.0", diff --git a/crawler/frontend/src/App.tsx b/crawler/frontend/src/App.tsx index 68c116f..9261836 100644 --- a/crawler/frontend/src/App.tsx +++ b/crawler/frontend/src/App.tsx @@ -35,6 +35,9 @@ const fetchData = async (user: User, baseQueyrUri: string, parameters: Parameter if (parameters.last_seen_days) { queryString.append("last_seen_days", parameters.last_seen_days.toString()); } + if (parameters.available_from) { + queryString.append("let_date_available_from", parameters.available_from.toISOString()); + } const response = await fetch(baseQueyrUri + '?' + queryString, { diff --git a/crawler/frontend/src/components/Parameters.tsx b/crawler/frontend/src/components/Parameters.tsx index 70b9bed..b30439f 100644 --- a/crawler/frontend/src/components/Parameters.tsx +++ b/crawler/frontend/src/components/Parameters.tsx @@ -4,9 +4,11 @@ import { useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { Button } from "./ui/button"; +import { Calendar } from "./ui/calendar"; import { Dialog, DialogContent, DialogTrigger } from "./ui/dialog"; -import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "./ui/form"; +import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "./ui/form"; import { Input } from "./ui/input"; +import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"; @@ -31,6 +33,7 @@ export interface ParameterValues { max_price?: number min_sqm?: number last_seen_days?: number + available_from?: Date } export function Parameters( @@ -54,6 +57,7 @@ export function Parameters( min_price: z.number().min(0).optional(), min_sqm: z.number().optional(), last_seen_days: z.number().min(0).optional(), + available_from: z.date(), }) const form = useForm>({ resolver: zodResolver(formSchema), @@ -65,6 +69,7 @@ export function Parameters( min_price: 2000, min_sqm: 0, last_seen_days: 7, + available_from: new Date(), }, }) // 2. Define a submit handler. @@ -76,6 +81,7 @@ export function Parameters( props.onSubmit(action, values) } } + const now = new Date(); @@ -216,6 +222,44 @@ export function Parameters( )} /> + ( + + Available from + + + + + + + + + date < new Date() || date > new Date(now.setMonth(now.getMonth() + 3)) + } + captionLayout="dropdown" + /> + + + + Applicable for renting listings only + + + + )} + />