Skip to content

Commit 7854db2

Browse files
authored
Merge pull request #34 from sjdonado/chore-release-prep
Chore release prep
2 parents fdc6eb1 + de010de commit 7854db2

File tree

14 files changed

+135
-235
lines changed

14 files changed

+135
-235
lines changed
Lines changed: 64 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,88 @@
1-
import { useRef, useState, useEffect } from 'react';
2-
import { useControlField } from 'remix-validated-form';
1+
import { useRef, useState, useEffect, useCallback } from 'react';
2+
3+
import { Loader as GoogleMapsApiLoader } from '@googlemaps/js-api-loader';
34

4-
import {
5-
initializeAutocomplete,
6-
cleanUpAutocomplete,
7-
} from '~/utils/autocomplete.client';
85
import { type InputProps, default as Input } from './Input';
6+
import type { Search } from '~/schemas/search';
7+
import { useField } from 'remix-validated-form';
98

109
interface AutocompletePlacesInput extends InputProps {
11-
placesApiKey: string;
12-
onCoordinatesChange: (coordinates: {
13-
latitude: number;
14-
longitude: number;
15-
}) => void;
10+
autocompleteApiKey: string;
1611
}
1712

18-
export default function AutocompletePlacesInput ({
13+
export default function AutocompletePlacesInput({
1914
name,
2015
label,
2116
placeholder,
22-
placesApiKey,
17+
autocompleteApiKey,
2318
defaultValue,
2419
icon,
2520
disabled,
26-
onCoordinatesChange,
27-
...rest
2821
}: AutocompletePlacesInput) {
29-
const [zipCode, setZipCode] = useState(defaultValue);
30-
const [
31-
isAutocompleteInitialized,
32-
setIsAutocompleteInitialized,
33-
] = useState<boolean>(false);
34-
35-
const [latitude, setLatitude] =
36-
useControlField<number | undefined>('latitude', 'searchForm');
37-
const [longitude, setLongitude] =
38-
useControlField<number | undefined>('longitude', 'searchForm');
39-
40-
const zipCodeInputRef = useRef(null);
41-
42-
const onPlaceChangeHandler = (
43-
autocomplete: google.maps.places.Autocomplete,
44-
) => {
45-
return () => {
46-
const place = autocomplete.getPlace();
47-
const parsedZipCode = place
48-
?.address_components
49-
?.find((component) => component.types.includes('postal_code'))
50-
?.long_name;
51-
const onlyNumbersRegExp = /^\d+$/;
52-
if (parsedZipCode && onlyNumbersRegExp.test(parsedZipCode)) {
53-
const latitude = place?.geometry?.location?.lat();
54-
const longitude = place?.geometry?.location?.lng();
55-
if (latitude && longitude) {
56-
setLatitude(latitude);
57-
setLongitude(longitude);
58-
onCoordinatesChange({
59-
latitude,
60-
longitude,
61-
});
62-
cleanUpAutocomplete(autocomplete);
63-
setIsAutocompleteInitialized(false);
64-
}
65-
}
22+
const AddressInputRef = useRef(null);
23+
24+
const [isAutocompleteInitialized, setIsAutocompleteInitialized] = useState(false);
25+
26+
const [coordinates, setCoordinates] = useState<Search['coordinates']>();
27+
const { error, getInputProps } = useField('coordinates');
28+
29+
const initializeAutocomplete = useCallback(async () => {
30+
if (!AddressInputRef.current || isAutocompleteInitialized) {
31+
return;
32+
}
33+
34+
const googleMapsApiLoader = new GoogleMapsApiLoader({
35+
apiKey: autocompleteApiKey,
36+
version: 'weekly',
37+
});
38+
39+
const { Autocomplete } = await googleMapsApiLoader.importLibrary('places');
40+
41+
const options = {
42+
fields: ['formatted_address', 'geometry', 'name', 'address_components'],
43+
strictBounds: false,
6644
};
67-
}
6845

69-
useEffect(() => {
70-
const loadAutocomplete = async() => {
71-
if (zipCodeInputRef.current) {
72-
if (!isAutocompleteInitialized) {
73-
await initializeAutocomplete({
74-
input: zipCodeInputRef.current,
75-
onPlaceChangeHandler,
76-
apiKey: placesApiKey,
77-
});
78-
setIsAutocompleteInitialized(true);
79-
}
46+
const autocomplete = new Autocomplete(AddressInputRef.current, options);
47+
48+
autocomplete.addListener('place_changed', () => {
49+
const place = autocomplete.getPlace();
50+
51+
const latitude = place?.geometry?.location?.lat();
52+
const longitude = place?.geometry?.location?.lng();
53+
54+
if (latitude && longitude) {
55+
setCoordinates({ latitude, longitude });
56+
google.maps?.event.clearInstanceListeners(autocomplete);
8057
}
81-
}
58+
});
8259

83-
loadAutocomplete();
60+
setIsAutocompleteInitialized(true);
61+
}, [AddressInputRef, isAutocompleteInitialized, autocompleteApiKey]);
62+
63+
useEffect(() => {
64+
initializeAutocomplete();
8465
});
8566

8667
return (
87-
<div>
68+
<div className="flex flex-col">
8869
<Input
89-
name={name}
90-
label={label}
91-
type="number"
92-
placeholder="080001"
93-
icon={icon}
94-
defaultValue={defaultValue}
95-
disabled={disabled}
96-
onChange={(e) => setZipCode(e.target.value)}
97-
value={zipCode}
98-
forwardedRef={zipCodeInputRef}
99-
/>
100-
<input
101-
type="hidden"
102-
name="latitude"
103-
value={latitude}
104-
onChange={(e) => setLatitude(parseFloat(e.target.value))}
105-
/>
106-
<input
107-
type="hidden"
108-
name="longitude"
109-
value={longitude}
110-
onChange={(e) => setLongitude(parseFloat(e.target.value))}
111-
/>
70+
className="!mb-0"
71+
forwardedRef={AddressInputRef}
72+
name={name}
73+
label={label}
74+
type="text"
75+
placeholder={placeholder}
76+
icon={icon}
77+
defaultValue={defaultValue}
78+
disabled={disabled}
79+
/>
80+
<input
81+
type="hidden"
82+
name="coordinates"
83+
{...getInputProps({ value: JSON.stringify(coordinates) })}
84+
/>
85+
{error && <span className="mt-1 text-xs text-red-500">{error}</span>}
11286
</div>
11387
);
11488
}

app/components/Header.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ export default function Header() {
77
<h1 className="text-3xl font-bold">Cule filo</h1>
88
</Link>
99
<p className="text-justify text-gray-500">
10-
Search for your favorite meal near you, quickly, enhanced by AI and for free.
10+
Craving your favorite meal? Our AI-powered search makes it easy to find the top 3
11+
restaurants serving it near you. Just enter the dish you're looking for and your
12+
location, and let our intelligent algorithm do the rest. Whether it's a local
13+
specialty or a global delicacy, our free app will guide you to the perfect spot.
1114
</p>
1215
</div>
1316
);

app/components/Input.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ export default function Input({
3131
<div className="relative mt-2 rounded-md">
3232
<div className="relative">
3333
<input
34+
ref={forwardedRef}
3435
className={clsx(
3536
'peer input input-sm input-bordered !h-10 w-full rounded-md !pl-9',
3637
error && 'input-error'
3738
)}
38-
ref={forwardedRef}
3939
{...rest}
4040
{...getInputProps({
4141
id: name,

app/components/Logs.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import { useState, useRef, useEffect } from "react";
1+
import { useState, useRef, useEffect } from 'react';
22
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/20/solid';
33

44
interface LogsProps {
55
logs: string[];
66
}
77

8-
export default function Logs({
9-
logs,
10-
}: LogsProps) {
8+
export default function Logs({ logs }: LogsProps) {
119
const [isExpanded, setIsExpanded] = useState<boolean>(false);
1210
const logsRef = useRef<null | HTMLDivElement>(null);
1311

@@ -19,29 +17,29 @@ export default function Logs({
1917

2018
return (
2119
<div className="mb-4 flex flex-col justify-center gap-4">
22-
<hr className="divider" />
23-
<div className="flex flex-row place-content-start items-center gap-4">
24-
<h3 className="text-lg">Search logs</h3>
20+
<hr />
21+
<div className="flex place-content-start items-center gap-2">
22+
<h3 className="cursor-pointer text-lg">Search logs</h3>
2523
{isExpanded && (
2624
<ChevronUpIcon
2725
className="cursor-pointer"
28-
onClick={(_e) => setIsExpanded(false)}
26+
onClick={_e => setIsExpanded(false)}
2927
width="20"
3028
height="20"
3129
/>
3230
)}
3331
{!isExpanded && (
3432
<ChevronDownIcon
3533
className="cursor-pointer"
36-
onClick={(_e) => setIsExpanded(true)}
34+
onClick={_e => setIsExpanded(true)}
3735
width="20"
3836
height="20"
3937
/>
4038
)}
4139
</div>
4240
{isExpanded && (
4341
<div className="flex w-full flex-col items-start gap-2" ref={logsRef}>
44-
{(logs).map(log => (
42+
{logs.map(log => (
4543
<p key={log} className="text-sm text-gray-500">
4644
{log}
4745
</p>

app/components/PlaceCard.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,6 @@ export default function PlaceCard({ place }: { place: Place }) {
2121
<span className="text-xs font-bold">{place.rating}</span>
2222
</div>
2323
)}
24-
{place.isOpen !== null && (
25-
<div className="badge badge-ghost">
26-
<span className="text-xs font-bold">
27-
{place.isOpen ? 'Open now' : 'Closed now'}
28-
</span>
29-
</div>
30-
)}
3124
{place.price !== null && (
3225
<div className="badge badge-ghost">
3326
<span className="text-xs font-bold">{place.price}</span>

app/components/SearchCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export default function SearchCard({ search }: { search: SearchJobSerialized })
66
<div className="flex flex-col gap-2 rounded-lg border p-4">
77
<div className="mb-1 flex flex-wrap justify-between gap-4">
88
<Link to={`/search?id=${search.id}`} className="link">
9-
{search.input.favoriteMealName} - {search.input.zipCode}
9+
{search.input.favoriteMealName} - {search.input.address}
1010
</Link>
1111
<div className="badge badge-ghost">
1212
<span className="text-xs font-bold">{search.state}</span>

app/jobs/search.server.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { DONE_JOB_MESSAGE, SearchJobState } from '~/constants/job';
33

44
import type { SearchJob } from '~/schemas/job';
55
import { SearchJobParsedSchema } from '~/schemas/job';
6-
import type { PlaceGeoData } from '~/schemas/place';
6+
import type { PlaceLocation } from '~/schemas/place';
77

88
import {
99
getKVRecord,
@@ -22,19 +22,18 @@ import {
2222
export async function createSearchJob(
2323
context: AppLoadContext,
2424
favoriteMealName: string,
25-
geoData: PlaceGeoData
25+
address: string,
26+
location: PlaceLocation
2627
) {
2728
const key = crypto.randomUUID();
2829

2930
const initState = SearchJobParsedSchema.parse({
3031
input: {
3132
favoriteMealName,
32-
zipCode: geoData.zipCode,
33-
latitude: geoData.coordinates.latitude,
34-
longitude:geoData.coordinates.longitude,
33+
address,
3534
},
35+
location,
3636
state: SearchJobState.Created,
37-
geoData,
3837
createdAt: Date.now(),
3938
});
4039

@@ -90,7 +89,7 @@ export async function startOrCheckSearchJob(context: AppLoadContext, key: string
9089
const allPlaces = new Map<string, PlaceAPIResponse['places'][0]>();
9190

9291
const originalQuery = job.input.favoriteMealName;
93-
const coordinates = job.geoData.coordinates;
92+
const coordinates = job.location.coordinates;
9493

9594
async function searchAndAppendToAllPlaces(query: string, baseProgress = 0.01) {
9695
console.log(
@@ -265,7 +264,6 @@ export async function startOrCheckSearchJob(context: AppLoadContext, key: string
265264
const name = place.displayName.text;
266265
const address = place.formattedAddress;
267266
const url = place.googleMapsUri;
268-
const isOpen = place.currentOpeningHours?.openNow;
269267

270268
const description =
271269
placesDescriptions.find(p => p?.id === id)?.description ?? null;
@@ -281,7 +279,6 @@ export async function startOrCheckSearchJob(context: AppLoadContext, key: string
281279
thumbnail,
282280
rating: { number: place.rating, count: place.userRatingCount },
283281
price: place.priceLevel,
284-
isOpen,
285282
};
286283
});
287284

app/root.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
1414
/>
1515
<meta
1616
name="description"
17-
content="Search for your favorite meal near you, quickly, enhanced by AI and for free."
17+
content="Discover the top 3 restaurants serving your favorite meal near you. Just enter your craving and location in our free AI-powered app, and start your culinary adventure today!"
1818
/>
1919

2020
<meta property="og:title" content="Cule filo" />
@@ -23,10 +23,10 @@ export function Layout({ children }: { children: React.ReactNode }) {
2323
<meta property="og:site_name" content="Cule filo" />
2424
<meta
2525
property="og:description"
26-
content="Search for your favorite meal near you, quickly, enhanced by AI and for free."
26+
content="Discover the top 3 restaurants serving your favorite meal near you. Just enter your craving and location in our free AI-powered app, and start your culinary adventure today!"
2727
/>
2828

29-
<title>Cule filo</title>
29+
<title>Cule filo - AI-powered meal search</title>
3030

3131
<Meta />
3232
<Links />

0 commit comments

Comments
 (0)