react
This commit is contained in:
359
src/features/location/ui/MyLocation.tsx
Normal file
359
src/features/location/ui/MyLocation.tsx
Normal file
@@ -0,0 +1,359 @@
|
||||
"use client";
|
||||
import { district_api } from "@/features/district/lib/api";
|
||||
import { doctor_api } from "@/features/doctor/lib/api";
|
||||
import { location_api, type CreateLocation } from "@/features/home/lib/api";
|
||||
import { object_api } from "@/features/object/lib/api";
|
||||
import { pharmacy_api } from "@/features/phamarcy/lib/api";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import { Card, CardDescription, CardHeader, CardTitle } from "@/shared/ui/card";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/shared/ui/dialog";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/shared/ui/table";
|
||||
import { DashboardLayout } from "@/widgets/dashboard-layout/ui";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import { AxiosError } from "axios";
|
||||
import { Loader2, MapPin, Send } from "lucide-react";
|
||||
import React, { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { columns } from "../lib/column";
|
||||
|
||||
const MyLocation: React.FC = () => {
|
||||
const [showMainModal, setShowMainModal] = useState<boolean>(false);
|
||||
const queryClinent = useQueryClient();
|
||||
const [showSelectModal, setShowSelectModal] = useState<boolean>(false);
|
||||
const [selectType, setSelectType] = useState<
|
||||
"district" | "object" | "doctor" | "pharm" | null
|
||||
>(null);
|
||||
const [loadingButtonId, setLoadingButtonId] = useState<number | null>(null);
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: (body: CreateLocation) => location_api.create_location(body),
|
||||
onSuccess: () => {
|
||||
toast.success("Lokatsiya jo'natildi");
|
||||
setShowSelectModal(false);
|
||||
setLoadingButtonId(null);
|
||||
queryClinent.refetchQueries({ queryKey: ["location_list"] });
|
||||
},
|
||||
onError: (error: AxiosError) => {
|
||||
const data = error.response?.data as { message?: string };
|
||||
const errorData = error.response?.data as {
|
||||
messages?: {
|
||||
token_class: string;
|
||||
token_type: string;
|
||||
message: string;
|
||||
}[];
|
||||
};
|
||||
const errorName = error.response?.data as {
|
||||
data?: {
|
||||
name: string[];
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
errorName.data?.name?.[0] ||
|
||||
data.message ||
|
||||
errorData?.messages?.[0]?.message ||
|
||||
"Xatolik yuz berdi",
|
||||
);
|
||||
setLoadingButtonId(null);
|
||||
},
|
||||
});
|
||||
|
||||
const { data: location } = useQuery({
|
||||
queryKey: ["location_list"],
|
||||
queryFn: () => location_api.list_location(),
|
||||
select(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
});
|
||||
|
||||
const { data: districts, isLoading: isDistrictsLoading } = useQuery({
|
||||
queryKey: ["my_disctrict"],
|
||||
queryFn: () => district_api.getDiscrict(),
|
||||
select: (data) => data.data.data,
|
||||
});
|
||||
|
||||
const { data: objects, isLoading: isObjectsLoading } = useQuery({
|
||||
queryKey: ["object_list"],
|
||||
queryFn: () => object_api.getAll({}),
|
||||
select: (data) => data.data.data,
|
||||
});
|
||||
|
||||
const { data: doctors, isLoading: isDoctorsLoading } = useQuery({
|
||||
queryKey: ["doctor_list"],
|
||||
queryFn: () => doctor_api.list(),
|
||||
select: (data) => data.data.data,
|
||||
});
|
||||
|
||||
const { data: pharm, isLoading: isPharmLoading } = useQuery({
|
||||
queryKey: ["pharmacy_list"],
|
||||
queryFn: () => pharmacy_api.list(),
|
||||
select: (data) => data.data.data,
|
||||
});
|
||||
|
||||
const getOptions = () => {
|
||||
if (selectType === "district") return districts;
|
||||
if (selectType === "object") return objects;
|
||||
if (selectType === "doctor") return doctors;
|
||||
if (selectType === "pharm") return pharm;
|
||||
return [];
|
||||
};
|
||||
|
||||
const isLoading = () => {
|
||||
if (selectType === "district") return isDistrictsLoading;
|
||||
if (selectType === "object") return isObjectsLoading;
|
||||
if (selectType === "doctor") return isDoctorsLoading;
|
||||
if (selectType === "pharm") return isPharmLoading;
|
||||
return false;
|
||||
};
|
||||
|
||||
const typeLabel = (type: "district" | "object" | "doctor" | "pharm") => {
|
||||
if (type === "district") return "Tuman";
|
||||
if (type === "object") return "Obyekt";
|
||||
if (type === "doctor") return "Shifokor";
|
||||
if (type === "pharm") return "Dorixona";
|
||||
};
|
||||
|
||||
const handleAddLocation = (id: number) => {
|
||||
if (!selectType) return;
|
||||
|
||||
setLoadingButtonId(id); // faqat shu button loading
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
const latitude = position.coords.latitude;
|
||||
const longitude = position.coords.longitude;
|
||||
|
||||
const body: CreateLocation = {
|
||||
latitude,
|
||||
longitude,
|
||||
};
|
||||
|
||||
switch (selectType) {
|
||||
case "district":
|
||||
body.district_id = id;
|
||||
break;
|
||||
case "object":
|
||||
body.place_id = id;
|
||||
break;
|
||||
case "doctor":
|
||||
body.doctor_id = id;
|
||||
break;
|
||||
case "pharm":
|
||||
body.pharmacy_id = id;
|
||||
break;
|
||||
}
|
||||
|
||||
mutation.mutate(body);
|
||||
},
|
||||
() => {
|
||||
toast.error("Geolokatsiya olinmadi");
|
||||
setLoadingButtonId(null);
|
||||
},
|
||||
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 0 },
|
||||
);
|
||||
};
|
||||
|
||||
const table = useReactTable({
|
||||
data: location || [],
|
||||
columns: columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
});
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<div className="space-y-4">
|
||||
<Card>
|
||||
<CardHeader className="flex flex-col">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-12 h-12 bg-blue-500 rounded-full flex items-center justify-center">
|
||||
<MapPin className="text-white" size={24} />
|
||||
</div>
|
||||
<div>
|
||||
<CardTitle className="text-2xl">Lokatsiya Tizimi</CardTitle>
|
||||
<CardDescription>{"Manzil jo'natish va tarix"}</CardDescription>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
|
||||
{/* Main Modal */}
|
||||
<Dialog open={showMainModal} onOpenChange={setShowMainModal}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-2xl">
|
||||
{"Nimani jo'natasiz?"}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-3 mt-4">
|
||||
{(["district", "object", "doctor", "pharm"] as const).map(
|
||||
(type) => (
|
||||
<Button
|
||||
key={type}
|
||||
onClick={() => {
|
||||
setSelectType(type);
|
||||
setShowSelectModal(true);
|
||||
setShowMainModal(false);
|
||||
}}
|
||||
className="w-full h-12 text-lg"
|
||||
variant="outline"
|
||||
>
|
||||
{typeLabel(type)}
|
||||
</Button>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Select Modal */}
|
||||
<Dialog open={showSelectModal} onOpenChange={setShowSelectModal}>
|
||||
<DialogContent className="sm:max-w-md h-[60%] flex flex-col justify-start overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-2xl">
|
||||
Mavjud {selectType && typeLabel(selectType)}lar
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-2 flex flex-col flex-1">
|
||||
{isLoading() ? (
|
||||
<div className="flex flex-col items-center justify-center py-12 gap-3">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-blue-500" />
|
||||
<p className="text-muted-foreground">Yuklanmoqda...</p>
|
||||
</div>
|
||||
) : getOptions()?.length === 0 ? (
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<p className="text-muted-foreground">Ma'lumot topilmadi</p>
|
||||
</div>
|
||||
) : (
|
||||
getOptions()?.map((item) => {
|
||||
const id = item.id;
|
||||
const label =
|
||||
"name" in item
|
||||
? item.name
|
||||
: `${item.first_name} ${item.last_name}`;
|
||||
const isButtonLoading = loadingButtonId === id;
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={id}
|
||||
onClick={() => handleAddLocation(id)}
|
||||
variant="outline"
|
||||
className="w-full justify-start h-auto py-3 flex items-center gap-2"
|
||||
disabled={isButtonLoading}
|
||||
>
|
||||
<MapPin className="h-4 w-4" />
|
||||
{isButtonLoading ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
Yuklanmoqda...
|
||||
</>
|
||||
) : (
|
||||
label
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => setShowSelectModal(false)}
|
||||
variant="outline"
|
||||
className="w-full mt-2"
|
||||
>
|
||||
Bekor qilish
|
||||
</Button>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={() => setShowMainModal(true)} className="gap-2">
|
||||
<Send size={20} />
|
||||
<span>{"Lokatsiya Jo'natish"}</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="w-full">
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead
|
||||
key={header.id}
|
||||
className="border-r text-center"
|
||||
>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
className="border-r text-center"
|
||||
>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DashboardLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default MyLocation;
|
||||
Reference in New Issue
Block a user