From ed3ed37ed8c0ccc4f5c74a87804b6c9adbac8a0d Mon Sep 17 00:00:00 2001 From: gorlemZ Date: Mon, 2 Oct 2023 12:14:47 +0200 Subject: [PATCH 01/11] price filter --- src/components/PriceFilter.tsx | 33 +++++++++++++++++++++++++++++++++ src/locales/en.json | 3 ++- src/pages/Home.tsx | 12 +++++++----- 3 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 src/components/PriceFilter.tsx diff --git a/src/components/PriceFilter.tsx b/src/components/PriceFilter.tsx new file mode 100644 index 0000000..8677c36 --- /dev/null +++ b/src/components/PriceFilter.tsx @@ -0,0 +1,33 @@ +import { CheckboxField, Stack, Title } from "@buildo/bento-design-system"; +import React from "react"; +import { useTranslation } from "react-i18next"; + +function PriceFilter() { + const [priceRange, setpriceRange] = React.useState( + new Array(4).fill(false) + ); + const { t } = useTranslation(); + + return ( + + {t("priceRangefilter")} + {[...Array(4).keys()].map((_, position) => { + return ( + { + setpriceRange( + priceRange.map((item, index) => + index === position ? !item : item + ) + ); + }} + /> + ); + })} + + ); +} + +export default PriceFilter; diff --git a/src/locales/en.json b/src/locales/en.json index 2270ae8..e2b4143 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -2,5 +2,6 @@ "title": "YelpLike", "Card": { "Rating": "Rating: {{rating}} ★" - } + }, + "priceRangefilter":"Filter by average price : " } \ No newline at end of file diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index fb40299..8542392 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -8,8 +8,7 @@ import { Inset, } from "@buildo/bento-design-system"; import RestaurantPreview from "../components/RestaurantPreview"; - -import useGetRestaurantList from "../hooks"; +import rest_detail from "../mock-data/rest_detail.json"; function Home() { const { isLoading, isError, data } = useGetRestaurantList(10); @@ -41,10 +40,13 @@ function Home() { - filters + + + Date: Thu, 19 Oct 2023 12:37:02 +0200 Subject: [PATCH 02/11] rebase ok --- src/components/LocationFilter.tsx | 23 +++++++++ src/components/PriceFilter.tsx | 20 ++++---- src/components/RangeDistanceFilter.tsx | 22 ++++++++ src/locales/en.json | 11 +++- src/pages/Home.tsx | 70 ++++++++++++++++++++++---- 5 files changed, 125 insertions(+), 21 deletions(-) create mode 100644 src/components/LocationFilter.tsx create mode 100644 src/components/RangeDistanceFilter.tsx diff --git a/src/components/LocationFilter.tsx b/src/components/LocationFilter.tsx new file mode 100644 index 0000000..3072d22 --- /dev/null +++ b/src/components/LocationFilter.tsx @@ -0,0 +1,23 @@ +import { TextField } from "@buildo/bento-design-system"; +import { useTranslation } from "react-i18next"; + +type LocationProps = { + location: string; + setLocation: (arg: string) => void; +}; + +function LocationFilter({ location, setLocation }: LocationProps) { + const { t } = useTranslation(); + + return ( + + ); +} + +export default LocationFilter; diff --git a/src/components/PriceFilter.tsx b/src/components/PriceFilter.tsx index 8677c36..d853ad8 100644 --- a/src/components/PriceFilter.tsx +++ b/src/components/PriceFilter.tsx @@ -1,11 +1,12 @@ import { CheckboxField, Stack, Title } from "@buildo/bento-design-system"; -import React from "react"; import { useTranslation } from "react-i18next"; -function PriceFilter() { - const [priceRange, setpriceRange] = React.useState( - new Array(4).fill(false) - ); +type PriceFilterProps = { + price: boolean[]; + setPrice: (arg: boolean[]) => void; +}; + +function PriceFilter({ price, setPrice }: PriceFilterProps) { const { t } = useTranslation(); return ( @@ -14,13 +15,12 @@ function PriceFilter() { {[...Array(4).keys()].map((_, position) => { return ( { - setpriceRange( - priceRange.map((item, index) => - index === position ? !item : item - ) + setPrice( + price.map((item, index) => (index === position ? !item : item)) ); }} /> diff --git a/src/components/RangeDistanceFilter.tsx b/src/components/RangeDistanceFilter.tsx new file mode 100644 index 0000000..c8d684a --- /dev/null +++ b/src/components/RangeDistanceFilter.tsx @@ -0,0 +1,22 @@ +import { SliderField } from "@buildo/bento-design-system"; +import { useTranslation } from "react-i18next"; +type RangeFilterProps = { + distance: number; + setDistance: (arg: number) => void; +}; + +function RangeDistanceFilter({ distance, setDistance }: RangeFilterProps) { + const { t } = useTranslation(); + return ( + + ); +} +export default RangeDistanceFilter; diff --git a/src/locales/en.json b/src/locales/en.json index e2b4143..354d690 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -3,5 +3,14 @@ "Card": { "Rating": "Rating: {{rating}} ★" }, - "priceRangefilter":"Filter by average price : " + "priceRangefilter":"Filter by average price : ", + "Location":{ + "Placeholder":"Type here your location ... ", + "Label": "Location" + }, + "RangeDistance":{ + "Label":"Distance :" + }, + "SearchButton": "Search" + } \ No newline at end of file diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 8542392..46df717 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,16 +1,24 @@ import { Box, ContentWithSidebar, - Body, - Tiles, - AreaLoader, + Stack, + Divider, + Button, Feedback, + AreaLoader, Inset, + Tiles, } from "@buildo/bento-design-system"; import RestaurantPreview from "../components/RestaurantPreview"; -import rest_detail from "../mock-data/rest_detail.json"; +import PriceFilter from "../components/PriceFilter"; +import LocationFilter from "../components/LocationFilter"; +import RangeDistanceFilter from "../components/RangeDistanceFilter"; +import { useTranslation } from "react-i18next"; +import React from "react"; +import useGetRestaurantList from "../hooks"; function Home() { + const { t } = useTranslation(); const { isLoading, isError, data } = useGetRestaurantList(10); if (isLoading) { @@ -28,6 +36,30 @@ function Home() { ); } + const [filters, setFilters] = React.useState({ + prices: [false, false, false, false], + location: "", + range: 0, + }); + + const setPricesFilter = (prices: boolean[]) => + setFilters({ + ...filters, + prices, + }); + + const setRangeFilter = (range: number) => + setFilters({ + ...filters, + range, + }); + + const setLocation = (location: string) => + setFilters({ + ...filters, + location, + }); + const cards = data?.businesses.map((element) => { return ; }); @@ -39,14 +71,32 @@ function Home() { > - - - + + + + + + + + + + ); diff --git a/src/locales/en.json b/src/locales/en.json index 354d690..f4117f9 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1,7 +1,8 @@ { "title": "YelpLike", "Card": { - "Rating": "Rating: {{rating}} ★" + "Rating": "Rating: {{rating}} ★", + "ButtonLabel":"See details" }, "priceRangefilter":"Filter by average price : ", "Location":{ From 25b64b41f5e428e1959efcd0f8f30c662f3f1179 Mon Sep 17 00:00:00 2001 From: gorlemZ Date: Thu, 26 Oct 2023 15:32:51 +0200 Subject: [PATCH 05/11] fix fetching on submit --- .npmrc | 1 + package.json | 1 + pnpm-lock.yaml | 11 ++++ src/api.tsx | 20 ++++++- src/components/RestaurantPreview.tsx | 5 +- src/hooks.tsx | 18 ++++-- src/models.tsx | 4 +- src/pages/Home.tsx | 85 +++++++++++++++------------- 8 files changed, 97 insertions(+), 48 deletions(-) create mode 100644 .npmrc diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..f87a044 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +auto-install-peers=true \ No newline at end of file diff --git a/package.json b/package.json index b0172ec..4e863f5 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@buildo/bento-design-system": "^0.20.4", + "@buildo/formo": "^2.0.2", "@tanstack/react-query": "^4.36.1", "i18next": "^23.5.1", "react": "^18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc7a54f..9ef3e98 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ dependencies: '@buildo/bento-design-system': specifier: ^0.20.4 version: 0.20.4(@types/react@18.2.25)(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0) + '@buildo/formo': + specifier: ^2.0.2 + version: 2.0.2(react@18.2.0) '@tanstack/react-query': specifier: ^4.36.1 version: 4.36.1(react-dom@18.2.0)(react@18.2.0) @@ -373,6 +376,14 @@ packages: - prop-types dev: false + /@buildo/formo@2.0.2(react@18.2.0): + resolution: {integrity: sha512-BcmPPcmC66cJBHmQNlqwkO6qM+5PqxKQYhGbXjdX4GISRwKSn3k+ciZslqZZjLS+s2+vyMcFZq63IpTdxcO1/Q==} + peerDependencies: + react: ^17.0.1 + dependencies: + react: 18.2.0 + dev: false + /@datepicker-react/hooks@2.8.4(react@18.2.0): resolution: {integrity: sha512-qaYJKK5sOSdqcL/OnCtyv3/Q6fRRljfeAyl5ISTPgEO0CM5xZzkGmTx40+6wvqjH5lEZH4ysS95nPyLwZS2tlw==} peerDependencies: diff --git a/src/api.tsx b/src/api.tsx index 339c6b4..e6eaca2 100644 --- a/src/api.tsx +++ b/src/api.tsx @@ -2,8 +2,24 @@ import { apiSecret } from "./models"; const apiKey = import.meta.env.VITE_API_KEY; -export const getRestaurantList = async (range: number) => { - const uri = `/api/search?sort_by=best_match&limit=${range}&location=Milano`; +export const getRestaurantList = async ({ + prices, + location, + radius, +}: { + prices: boolean[]; + location: string; + radius: number; +}) => { + const priceParamsString: string = prices + .map((price, index) => { + if (price) { + return `price=${index + 1}`; + } + }) + .filter((price) => price !== "") + .join("&"); + const uri = `/api/search?sort_by=best_match&location=${location}&radius=${radius}000&${priceParamsString}`; const apik = apiSecret.safeParse(apiKey); if (apik.success) { diff --git a/src/components/RestaurantPreview.tsx b/src/components/RestaurantPreview.tsx index c6fa5b9..29eedab 100644 --- a/src/components/RestaurantPreview.tsx +++ b/src/components/RestaurantPreview.tsx @@ -22,7 +22,10 @@ function RestaurantPreview(props: PreviewProp) { return ( - + {props.name} {props.address} diff --git a/src/hooks.tsx b/src/hooks.tsx index b2f9a13..08d6b5f 100644 --- a/src/hooks.tsx +++ b/src/hooks.tsx @@ -1,15 +1,21 @@ import { useQuery } from "@tanstack/react-query"; import { getRestaurantList } from "./api"; import { fromJsonToProp } from "./utils"; +import { PreviewList } from "./models"; -function useGetRestaurantList(range: number) { - return useQuery({ - queryKey: ["retrieve-list", range], - queryFn: async () => { - const prom: JSON = await getRestaurantList(range); +function useGetRestaurantList(filtersParams: { + prices: boolean[]; + location: string; + radius: number; +}) { + return useQuery( + ["restaurantList"], + async (): Promise => { + const prom: JSON = await getRestaurantList(filtersParams); return fromJsonToProp(prom); }, - }); + { enabled: false } + ); } export default useGetRestaurantList; diff --git a/src/models.tsx b/src/models.tsx index db20013..4730ca7 100644 --- a/src/models.tsx +++ b/src/models.tsx @@ -29,6 +29,8 @@ export const previewList = z.object({ export type PreviewList = z.infer; export const filterParams = z.object({ - range: z.number(), + prices: z.array(z.boolean()), + location: z.string(), + radius: z.number(), }); export type FiltersParams = z.infer; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 471ccfc..bd9889a 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -8,18 +8,43 @@ import { AreaLoader, Inset, Tiles, + TextField, + SliderField, } from "@buildo/bento-design-system"; +import useGetRestaurantList from "../hooks"; import RestaurantPreview from "../components/RestaurantPreview"; -import PriceFilter from "../components/PriceFilter"; -import LocationFilter from "../components/LocationFilter"; -import RangeDistanceFilter from "../components/RangeDistanceFilter"; import { useTranslation } from "react-i18next"; -import React from "react"; -import useGetRestaurantList from "../hooks"; +import { validators, useFormo, success } from "@buildo/formo"; +import { useEffect } from "react"; function Home() { const { t } = useTranslation(); - const { isLoading, isError, data } = useGetRestaurantList(10); + const initialValues = { + prices: [true, true, true, true], + location: "Milan", + radius: 0, + }; + + const { fieldProps, handleSubmit, values } = useFormo( + { + initialValues, + fieldValidators: () => ({ + location: validators.minLength(2, "City name is too short"), + }), + }, + { + onSubmit: async (values) => { + refetch(); + return success(values); + }, + } + ); + + useEffect(() => { + refetch(); + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + const { isLoading, isError, data, refetch } = useGetRestaurantList(values); if (isLoading) { return ; @@ -36,30 +61,6 @@ function Home() { ); } - const [filters, setFilters] = React.useState({ - prices: [false, false, false, false], - location: "", - range: 0, - }); - - const setPricesFilter = (prices: boolean[]) => - setFilters({ - ...filters, - prices, - }); - - const setRangeFilter = (range: number) => - setFilters({ - ...filters, - range, - }); - - const setLocationFilter = (location: string) => - setFilters({ - ...filters, - location, - }); - const cards = data?.businesses.map((element) => { return ; }); @@ -71,17 +72,25 @@ function Home() { > - + {/* price filters */} + - + {/* range filters */} - @@ -89,7 +98,7 @@ function Home() { kind="solid" hierarchy="primary" label={t("SearchButton")} - onPress={() => window.alert("" + filters.location)} + onPress={handleSubmit} /> From 64682ca850419e5ceef6d65a8357df5b4fcd5a23 Mon Sep 17 00:00:00 2001 From: gorlemZ Date: Fri, 27 Oct 2023 15:01:06 +0200 Subject: [PATCH 06/11] apis integrated in detail view --- src/App.tsx | 11 +++++--- src/api.tsx | 20 ++++++++++++++ src/components/RestaurantPreview.tsx | 19 ++++++++------ src/hooks.tsx | 15 ++++++++--- src/models.tsx | 13 +++++++--- src/pages/Home.tsx | 20 +++++++++++--- src/pages/RestaurantDetail.tsx | 39 ++++++++++++++++++++++++++++ 7 files changed, 117 insertions(+), 20 deletions(-) create mode 100644 src/pages/RestaurantDetail.tsx diff --git a/src/App.tsx b/src/App.tsx index f9b7343..01515be 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,10 +6,12 @@ import { BentoProvider, Headline, Inset } from "@buildo/bento-design-system"; import { BrowserRouter, Routes, Route } from "react-router-dom"; import { useTranslation } from "react-i18next"; import Home from "./pages/Home"; -// import RestaurantDetail from "./pages/RestaurantDetail"; +import RestaurantDetail from "./pages/RestaurantDetail"; +import React from "react"; function App() { const { t } = useTranslation(); + const [restId, setRestId] = React.useState("test"); return ( @@ -20,8 +22,11 @@ function App() { - } /> - {/* } /> */} + } /> + } + /> diff --git a/src/api.tsx b/src/api.tsx index e6eaca2..3237c60 100644 --- a/src/api.tsx +++ b/src/api.tsx @@ -37,3 +37,23 @@ export const getRestaurantList = async ({ throw apik.error; } }; + +export const getRestaurantDetails = async (id: string) => { + const uri = `/business_id_or_alias/${id}`; + const apik = apiSecret.safeParse(apiKey); + + if (apik.success) { + return ( + await fetch(uri, { + method: "get", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + Authorization: "Bearer " + apiKey, + }, + }) + ).json(); + } else { + throw apik.error; + } +}; diff --git a/src/components/RestaurantPreview.tsx b/src/components/RestaurantPreview.tsx index 29eedab..8f3cdf5 100644 --- a/src/components/RestaurantPreview.tsx +++ b/src/components/RestaurantPreview.tsx @@ -7,16 +7,16 @@ import { Button, } from "@buildo/bento-design-system"; import { useTranslation } from "react-i18next"; -import { PreviewProp } from "../models"; import { useNavigate } from "react-router-dom"; +import { PreviewPropComponent } from "../models"; -function RestaurantPreview(props: PreviewProp) { +function RestaurantPreview(props: PreviewPropComponent) { const { t } = useTranslation(); - const rating = props.rating; + const rating = props.vars.rating; const imagePrev = - props.imageUrl === "" + props.vars.imageUrl === "" ? "https://upload.wikimedia.org/wikipedia/commons/thumb/6/62/Barbieri_-_ViaSophia25668.jpg/1200px-Barbieri_-_ViaSophia25668.jpg" - : props.imageUrl; + : props.vars.imageUrl; const navigate = useNavigate(); return ( @@ -26,16 +26,19 @@ function RestaurantPreview(props: PreviewProp) { src={imagePrev} style={{ height: "200px", width: "100%", objectFit: "scale-down" }} /> - {props.name} + {props.vars.name} - {props.address} + {props.vars.address}