diff --git a/bun.lockb b/bun.lockb index a279dd6..422f823 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 95207ee..0367af6 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-switch": "^1.1.0", + "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-toast": "^1.2.1", "@radix-ui/react-tooltip": "^1.1.2", "@tanstack/react-table": "^8.20.1", diff --git a/src/components/assets/missing-location/TagMissingLocationDialog.tsx b/src/components/assets/missing-location/TagMissingLocationDialog.tsx deleted file mode 100644 index 27aa5f4..0000000 --- a/src/components/assets/missing-location/TagMissingLocationDialog.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import ErrorBlock from "@/components/shared/ErrorBlock"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import Loader from "@/components/ui/loader"; -import { useToast } from "@/components/ui/use-toast"; -import { searchPlaces } from "@/handlers/api/common.handler"; -import { cn } from "@/lib/utils"; -import { IPlace } from "@/types/common"; -import clsx from "clsx"; -import { Check } from "lucide-react"; -import React, { useRef, useState } from "react"; - -interface ITagMissingLocationDialogProps { - onSubmit: (place: IPlace) => Promise; -} -export default function TagMissingLocationDialog({ - onSubmit, -}: ITagMissingLocationDialogProps) { - const [searchedPlaces, setSearchedPlaces] = useState(null); - const searchTimer = useRef(); - const [loading, setLoading] = useState(false); - const [submitting, setSubmitting] = useState(false); - const [selectedPlace, setSelectedPlace] = useState(null); - const [open, setOpen] = useState(false); - const { toast } = useToast(); - - const handleSearch = (value: string) => { - if (searchTimer.current) { - clearTimeout(searchTimer.current); - } - - searchTimer.current = setTimeout(() => { - if (!value || !value.trim().length) { - setSearchedPlaces(null); - return; - } - setSelectedPlace(null); - setSearchedPlaces([]); - setLoading(true); - if (!value) { - setSearchedPlaces([]); - setLoading(false); - return; - } - - return searchPlaces(value) - .then(setSearchedPlaces) - .finally(() => { - setLoading(false); - }); - }, 500); - }; - - const handleSelect = (place: IPlace) => { - if (selectedPlace && selectedPlace.name === place.name) { - setSelectedPlace(null); - } else { - setSelectedPlace(place); - } - }; - - const handleSubmit = () => { - if (!selectedPlace) { - return; - } - - setSubmitting(true); - onSubmit(selectedPlace) - .then(() => { - toast({ - title: "Location updated", - description: "Location updated successfully", - }); - }) - .then(() => { - setOpen(false); - setSelectedPlace(null); - }) - .catch(() => { - toast({ - title: "Error", - description: "Error updating location", - }); - }) - .finally(() => { - setSubmitting(false); - }); - }; - - return ( - - - - - - - Tag Missing Location - - Tagging a location will add the location to the selected assets. - - -
-
- - { - handleSearch(e.target.value); - }} - /> -
-
- {loading && } - {!loading && searchedPlaces && searchedPlaces.length === 0 && ( - - )} - {!loading && searchedPlaces && searchedPlaces.length > 0 && ( -
- {searchedPlaces.map((place) => ( -
handleSelect(place)} - className={cn( - "hover:bg-zinc-900 flex justify-between items-center px-2 py-1 rounded-lg cursor-pointer", - { - "bg-zinc-900": - selectedPlace && selectedPlace.name === place.name, - } - )} - > -
-

{place.name}

- - {place.latitude}, {place.longitude} - -
- {selectedPlace && selectedPlace.name === place.name && ( - - )} -
- ))} -
- )} -
-
- -
-
-
-
- ); -} diff --git a/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationDialog.tsx b/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationDialog.tsx new file mode 100644 index 0000000..3590b01 --- /dev/null +++ b/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationDialog.tsx @@ -0,0 +1,65 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { IPlace } from "@/types/common"; +import React, { useState } from "react"; +import TagMissingLocationSearchAndAdd from "./TagMissingLocationSearchAndAdd"; +import TagMissingLocationSearchLatLong from "./TagMissingLocationSearchLatLong"; +import { MapPinCheck } from "lucide-react"; + +interface ITagMissingLocationDialogProps { + onSubmit: (place: IPlace) => Promise; +} +export default function TagMissingLocationDialog({ + onSubmit, +}: ITagMissingLocationDialogProps) { + + const [open, setOpen] = useState(false); + + + return ( + + + + + + + Tag Missing Location + + Tagging a location will add the location to the selected assets. + + + + + Search and Pick + + Latitude and Longitude + + + Google Maps + + + + + + + + +
+ +

Google Maps Coming Soon...

+
+
+
+ +
+
+ ); +} diff --git a/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationSearchAndAdd.tsx b/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationSearchAndAdd.tsx new file mode 100644 index 0000000..949bf79 --- /dev/null +++ b/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationSearchAndAdd.tsx @@ -0,0 +1,151 @@ +import ErrorBlock from "@/components/shared/ErrorBlock"; +import { Button } from "@/components/ui/button"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import Loader from "@/components/ui/loader"; +import { useToast } from "@/components/ui/use-toast"; +import { searchPlaces } from "@/handlers/api/common.handler"; +import { cn } from "@/lib/utils"; +import { IPlace } from "@/types/common"; +import clsx from "clsx"; +import { Check } from "lucide-react"; +import React, { useRef, useState } from "react"; + +interface ITagMissingLocationSearchAndAddProps { + onSubmit: (place: IPlace) => Promise; + onOpenChange: (open: boolean) => void; +} +export default function TagMissingLocationSearchAndAdd( + { + onSubmit, + onOpenChange + }: ITagMissingLocationSearchAndAddProps +) { + const { toast } = useToast(); + const [searchedPlaces, setSearchedPlaces] = useState(null); + const searchTimer = useRef(); + const [loading, setLoading] = useState(false); + const [submitting, setSubmitting] = useState(false); + const [selectedPlace, setSelectedPlace] = useState(null); + + const handleSearch = (value: string) => { + if (searchTimer.current) { + clearTimeout(searchTimer.current); + } + + searchTimer.current = setTimeout(() => { + if (!value || !value.trim().length) { + setSearchedPlaces(null); + return; + } + setSelectedPlace(null); + setSearchedPlaces([]); + setLoading(true); + if (!value) { + setSearchedPlaces([]); + setLoading(false); + return; + } + + return searchPlaces(value) + .then(setSearchedPlaces) + .finally(() => { + setLoading(false); + }); + }, 500); + }; + + const handleSelect = (place: IPlace) => { + if (selectedPlace && selectedPlace.name === place.name) { + setSelectedPlace(null); + } else { + setSelectedPlace(place); + } + }; + + const handleSubmit = () => { + if (!selectedPlace) { + return; + } + + setSubmitting(true); + onSubmit(selectedPlace) + .then(() => { + toast({ + title: "Location updated", + description: "Location updated successfully", + }); + }) + .then(() => { + onOpenChange(false); + setSelectedPlace(null); + }) + .catch(() => { + toast({ + title: "Error", + description: "Error updating location", + }); + }) + .finally(() => { + setSubmitting(false); + }); + }; + + + return ( +
+
+ + { + handleSearch(e.target.value); + }} + /> +
+
+ {loading && } + {!loading && searchedPlaces && searchedPlaces.length === 0 && ( + + )} + {!loading && searchedPlaces && searchedPlaces.length > 0 && ( +
+ {searchedPlaces.map((place) => ( +
handleSelect(place)} + className={cn( + "hover:bg-zinc-900 flex justify-between items-center px-2 py-1 rounded-lg cursor-pointer", + { + "bg-zinc-900": + selectedPlace && selectedPlace.name === place.name, + } + )} + > +
+

{place.name}

+ + {place.latitude}, {place.longitude} + +
+ {selectedPlace && selectedPlace.name === place.name && ( + + )} +
+ ))} +
+ )} +
+
+ +
+
+ ) +} diff --git a/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationSearchLatLong.tsx b/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationSearchLatLong.tsx new file mode 100644 index 0000000..d0098dc --- /dev/null +++ b/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationSearchLatLong.tsx @@ -0,0 +1,133 @@ +import ErrorBlock from "@/components/shared/ErrorBlock"; +import { Button } from "@/components/ui/button"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import Loader from "@/components/ui/loader"; +import { useToast } from "@/components/ui/use-toast"; +import { searchPlaces } from "@/handlers/api/common.handler"; +import { cn } from "@/lib/utils"; +import { IPlace } from "@/types/common"; +import clsx from "clsx"; +import { Check } from "lucide-react"; +import React, { useMemo, useRef, useState } from "react"; + +interface TagMissingLocationSearchLatLongProps { + onSubmit: (place: IPlace) => Promise; + onOpenChange: (open: boolean) => void; +} +export default function TagMissingLocationSearchLatLong( + { + onSubmit, + onOpenChange + }: TagMissingLocationSearchLatLongProps +) { + const { toast } = useToast(); + const [formData, setFormData] = useState({ + latitude: 0, + longitude: 0, + name: "", + }); + const [submitting, setSubmitting] = useState(false); + + const isValid = useMemo(() => { + if (formData.latitude === 0 || formData.longitude === 0) { + return false; + } + if (isNaN(formData.latitude) || isNaN(formData.longitude)) { + return false; + } + return true; + }, [formData]); + + const handleChange = (key: string, value: string) => { + if (value.includes(",")) { + const latLong = value.split(","); + if (latLong.length === 2) { + setFormData({ + ...formData, + latitude: parseFloat(latLong[0]), + longitude: parseFloat(latLong[1]), + }); + } + } else { + + setFormData({ + ...formData, + [key]: value, + }); + } + } + + const handleSubmit = () => { + if (!isValid) { + return; + } + + setSubmitting(true); + onSubmit(formData) + .then(() => { + toast({ + title: "Location updated", + description: "Location updated successfully", + }); + }) + .then(() => { + onOpenChange(false); + setFormData({ + latitude: 0, + longitude: 0, + name: "", + }); + }) + .catch(() => { + toast({ + title: "Error", + description: "Error updating location", + }); + }) + .finally(() => { + setSubmitting(false); + }); + }; + + + return ( +
+
+
+ + { + handleChange("latitude", e.target.value); + }} + /> +
+
+ + { + handleChange("longitude", e.target.value); + }} + /> +
+
+ +
+ +
+
+ ) +} diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx new file mode 100644 index 0000000..85d83be --- /dev/null +++ b/src/components/ui/tabs.tsx @@ -0,0 +1,53 @@ +import * as React from "react" +import * as TabsPrimitive from "@radix-ui/react-tabs" + +import { cn } from "@/lib/utils" + +const Tabs = TabsPrimitive.Root + +const TabsList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsList.displayName = TabsPrimitive.List.displayName + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsContent.displayName = TabsPrimitive.Content.displayName + +export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/src/pages/assets/missing-locations.tsx b/src/pages/assets/missing-locations.tsx index fcda8ef..d8192bf 100644 --- a/src/pages/assets/missing-locations.tsx +++ b/src/pages/assets/missing-locations.tsx @@ -3,7 +3,7 @@ import PotentialAlbumsAssets from "@/components/albums/potential-albums/Potentia import PotentialAlbumsDates from "@/components/albums/potential-albums/PotentialAlbumsDates"; import MissingLocationAssets from "@/components/assets/missing-location/MissingLocationAssets"; import MissingLocationDates from "@/components/assets/missing-location/MissingLocationDates"; -import TagMissingLocationDialog from "@/components/assets/missing-location/TagMissingLocationDialog"; +import TagMissingLocationDialog from "@/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationDialog"; import PageLayout from "@/components/layouts/PageLayout"; import Header from "@/components/shared/Header"; import { Badge } from "@/components/ui/badge";