Skip to content

Commit d14e1da

Browse files
authored
Merge branch 'varun-raj:main' into feature/unselect-all-different
2 parents f96b5c3 + c5ffba6 commit d14e1da

File tree

4 files changed

+206
-16
lines changed

4 files changed

+206
-16
lines changed

src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationDialog.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { IPlace } from "@/types/common";
1212
import React, { useState } from "react";
1313
import TagMissingLocationSearchAndAdd from "./TagMissingLocationSearchAndAdd";
1414
import TagMissingLocationSearchLatLong from "./TagMissingLocationSearchLatLong";
15+
import TagMissingLocationOSMSearchAndAdd from "./TagMissingLocationOSMSearchAndAdd";
1516
import dynamic from "next/dynamic";
1617
import { useMissingLocationContext } from "@/contexts/MissingLocationContext";
1718

@@ -25,15 +26,14 @@ interface ITagMissingLocationDialogProps {
2526
export default function TagMissingLocationDialog({
2627
onSubmit,
2728
}: ITagMissingLocationDialogProps) {
28-
const {selectedIds} = useMissingLocationContext();
29+
const { selectedIds } = useMissingLocationContext();
2930
const [open, setOpen] = useState(false);
30-
const [mapPosition,setMapPosition] = useState<IPlace>({
31-
latitude: 48.0,
32-
longitude: 16.0,
33-
name: "home"
31+
const [mapPosition, setMapPosition] = useState<IPlace>({
32+
latitude: 48.210033,
33+
longitude: 16.363449,
34+
name: "Vienna, Austria"
3435
});
35-
36-
36+
3737
return (
3838
<Dialog open={open} onOpenChange={setOpen}>
3939
<DialogTrigger asChild >
@@ -46,20 +46,21 @@ export default function TagMissingLocationDialog({
4646
Tagging a location will add the location to the selected assets.
4747
</DialogDescription>
4848
</DialogHeader>
49-
<Tabs defaultValue="search">
50-
<TabsList className="flex justify-center">
51-
<TabsTrigger value="search">Search and Pick</TabsTrigger>
52-
<TabsTrigger value="latlong">
53-
Lat & Long
54-
</TabsTrigger>
49+
<Tabs defaultValue="search" className="border rounded-lg">
50+
<TabsList className="flex justify-between">
51+
<TabsTrigger value="search">Immich Search</TabsTrigger>
52+
<TabsTrigger value="searchOsm">OSM Search</TabsTrigger>
53+
<TabsTrigger value="latlong">Lat &amp; Long</TabsTrigger>
5554
<TabsTrigger value="maps">Map</TabsTrigger>
5655
</TabsList>
5756
<TabsContent value="search">
5857
<TagMissingLocationSearchAndAdd onSubmit={onSubmit} onOpenChange={setOpen} />
5958
</TabsContent>
59+
<TabsContent value="searchOsm">
60+
<TagMissingLocationOSMSearchAndAdd onSubmit={onSubmit} onOpenChange={setOpen} location={mapPosition} onLocationChange={setMapPosition} />
61+
</TabsContent>
6062
<TabsContent value="latlong">
61-
<TagMissingLocationSearchLatLong onSubmit={onSubmit}
62-
onOpenChange={setOpen} location={mapPosition} onLocationChange={setMapPosition} />
63+
<TagMissingLocationSearchLatLong onSubmit={onSubmit} onOpenChange={setOpen} location={mapPosition} onLocationChange={setMapPosition} />
6364
</TabsContent>
6465
<TabsContent value="maps">
6566
<div className="flex flex-col gap-6 items-center ">
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import ErrorBlock from "@/components/shared/ErrorBlock";
2+
import { Button } from "@/components/ui/button";
3+
4+
import { Input } from "@/components/ui/input";
5+
import { Label } from "@/components/ui/label";
6+
import Loader from "@/components/ui/loader";
7+
import { useToast } from "@/components/ui/use-toast";
8+
import { cn } from "@/lib/utils";
9+
import { IPlace } from "@/types/common";
10+
import axios from "axios";
11+
import { Check } from "lucide-react";
12+
import React, { useRef, useState } from "react";
13+
14+
interface ITagMissingLocationSearchAndAddProps {
15+
onSubmit: (place: IPlace) => Promise<any>;
16+
onOpenChange: (open: boolean) => void;
17+
location: IPlace;
18+
onLocationChange: (place: IPlace) => void;
19+
}
20+
21+
export default function TagMissingLocationOSMSearchAndAdd(
22+
{
23+
onSubmit,
24+
onOpenChange,
25+
location,
26+
onLocationChange
27+
}: ITagMissingLocationSearchAndAddProps
28+
) {
29+
const { toast } = useToast();
30+
const [searchedPlaces, setSearchedPlaces] = useState<IPlace[] | null>(null);
31+
const searchTimer = useRef<NodeJS.Timeout>();
32+
const [loading, setLoading] = useState(false);
33+
const [submitting, setSubmitting] = useState(false);
34+
const [selectedPlace, setSelectedPlace] = useState<IPlace | null>(null);
35+
36+
const searchPlaces = async (value: string) => {
37+
try {
38+
const response = await axios.get("https://nominatim.openstreetmap.org/search", {
39+
params: {
40+
q: value,
41+
format: "json", // Response in JSON format
42+
addressdetails: 1, // Include address details
43+
limit: 5, // Limit the results to 1
44+
},
45+
});
46+
47+
if (response.data.length > 0) {
48+
const data = (response.data) as Array<any>;
49+
50+
const places = data.map((result): IPlace => ({
51+
name: result.display_name as string, // Full address or place name
52+
latitude: parseFloat(result.lat),
53+
longitude: parseFloat(result.lon),
54+
}));
55+
56+
return places;
57+
} else {
58+
console.log("No results found for the location.");
59+
return null;
60+
}
61+
} catch (error) {
62+
console.error("Error fetching coordinates:", error);
63+
return null;
64+
}
65+
}
66+
67+
const handleSearch = (value: string) => {
68+
if (searchTimer.current) {
69+
clearTimeout(searchTimer.current);
70+
}
71+
72+
searchTimer.current = setTimeout(() => {
73+
if (!value || !value.trim().length) {
74+
setSearchedPlaces(null);
75+
return;
76+
}
77+
setSelectedPlace(null);
78+
setSearchedPlaces([]);
79+
setLoading(true);
80+
if (!value) {
81+
setSearchedPlaces([]);
82+
setLoading(false);
83+
return;
84+
}
85+
86+
return searchPlaces(value)
87+
.then(setSearchedPlaces)
88+
.finally(() => {
89+
setLoading(false);
90+
});
91+
}, 500);
92+
};
93+
94+
const handleSelect = (place: IPlace) => {
95+
if (selectedPlace && selectedPlace.name === place.name) {
96+
setSelectedPlace(null);
97+
} else {
98+
setSelectedPlace(place);
99+
debugger;
100+
onLocationChange(place);
101+
}
102+
};
103+
104+
const handleSubmit = () => {
105+
if (!selectedPlace) {
106+
return;
107+
}
108+
109+
setSubmitting(true);
110+
onSubmit(selectedPlace)
111+
.then(() => {
112+
toast({
113+
title: "Location updated",
114+
description: "Location updated successfully",
115+
});
116+
})
117+
.then(() => {
118+
onOpenChange(false);
119+
setSelectedPlace(null);
120+
})
121+
.catch(() => {
122+
toast({
123+
title: "Error",
124+
description: "Error updating location",
125+
});
126+
})
127+
.finally(() => {
128+
setSubmitting(false);
129+
});
130+
};
131+
132+
133+
return (
134+
<div className="flex flex-col gap-4 py-4 px-2">
135+
<div className="flex flex-col gap-2">
136+
<Label>Search Location</Label>
137+
<Input
138+
placeholder="Search location"
139+
onChange={(e) => {
140+
handleSearch(e.target.value);
141+
}}
142+
/>
143+
</div>
144+
<div>
145+
{loading && <Loader />}
146+
{!loading && searchedPlaces && searchedPlaces.length === 0 && (
147+
<ErrorBlock description="No results found" />
148+
)}
149+
{!loading && searchedPlaces && searchedPlaces.length > 0 && (
150+
<div className="flex flex-col gap-2 max-h-[400px] overflow-y-auto">
151+
{searchedPlaces.map((place) => (
152+
<div
153+
key={place.name}
154+
onClick={() => handleSelect(place)}
155+
className={cn(
156+
"hover:bg-gray-300 flex justify-between items-center px-2 py-1 rounded-lg cursor-pointer",
157+
{
158+
"bg-gray-300":
159+
selectedPlace && selectedPlace.name === place.name,
160+
}
161+
)}
162+
>
163+
<div>
164+
<p>{place.name}</p>
165+
<span className="text-xs text-gray-600">
166+
{place.latitude}, {place.longitude}
167+
</span>
168+
</div>
169+
{selectedPlace && selectedPlace.name === place.name && (
170+
<Check className="text-green-500" />
171+
)}
172+
</div>
173+
))}
174+
</div>
175+
)}
176+
</div>
177+
<div className="self-end">
178+
<Button
179+
variant="outline"
180+
onClick={handleSubmit}
181+
disabled={!selectedPlace || submitting}
182+
>
183+
Add New Location
184+
</Button>
185+
</div>
186+
</div>
187+
)
188+
}

src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationSearchLatLong.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
import ErrorBlock from "@/components/shared/ErrorBlock";
23
import { Button } from "@/components/ui/button";
34

src/components/ui/dialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const DialogContent = React.forwardRef<
4141
<DialogPrimitive.Content
4242
ref={ref}
4343
className={cn(
44-
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
44+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-6xl translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
4545
className
4646
)}
4747
{...props}

0 commit comments

Comments
 (0)