From bcf9d7cd2bc3fd1613d5f37e253936cca54a3998 Mon Sep 17 00:00:00 2001 From: Samandar Turgunboyev Date: Sat, 29 Nov 2025 11:49:26 +0500 Subject: [PATCH] doctor list --- src/features/auth/ui/AuthLogin.tsx | 1 - src/features/districts/ui/DeleteDiscrit.tsx | 1 - src/features/doctors/lib/api.ts | 20 ++ src/features/doctors/lib/data.ts | 42 +++ src/features/doctors/ui/AddedDoctor.tsx | 33 +- .../doctors/ui/DoctorDetailDialog.tsx | 149 ++++++-- src/features/doctors/ui/DoctorsList.tsx | 325 ++++-------------- src/features/doctors/ui/FilterDoctor.tsx | 116 +++++++ src/features/doctors/ui/PaginationDoctor.tsx | 57 +++ src/features/doctors/ui/TableDoctor.tsx | 134 ++++++++ src/features/region/lib/api.ts | 15 + src/features/region/ui/AddedRegion.tsx | 75 ++-- src/features/region/ui/DeleteRegion.tsx | 88 +++++ src/features/region/ui/RegionList.tsx | 138 +++----- src/features/region/ui/RegionTable.tsx | 89 +++++ src/features/users/ui/AddUsers.tsx | 18 +- src/features/users/ui/Filter.tsx | 2 +- src/providers/routing/AppRoutes.tsx | 2 +- src/shared/config/api/URLs.ts | 3 +- src/widgets/sidebar-layout/index.tsx | 2 +- 20 files changed, 872 insertions(+), 438 deletions(-) create mode 100644 src/features/doctors/lib/api.ts create mode 100644 src/features/doctors/ui/FilterDoctor.tsx create mode 100644 src/features/doctors/ui/PaginationDoctor.tsx create mode 100644 src/features/doctors/ui/TableDoctor.tsx create mode 100644 src/features/region/ui/DeleteRegion.tsx create mode 100644 src/features/region/ui/RegionTable.tsx diff --git a/src/features/auth/ui/AuthLogin.tsx b/src/features/auth/ui/AuthLogin.tsx index 7eadd3f..bf34727 100644 --- a/src/features/auth/ui/AuthLogin.tsx +++ b/src/features/auth/ui/AuthLogin.tsx @@ -44,7 +44,6 @@ const AuthLogin = () => { navigate("dashboard"); }, onError: (err: AxiosError) => { - console.log(err); const errMessage = err.response?.data as { message: string }; const messageText = errMessage.message; diff --git a/src/features/districts/ui/DeleteDiscrit.tsx b/src/features/districts/ui/DeleteDiscrit.tsx index 40ab633..351fa77 100644 --- a/src/features/districts/ui/DeleteDiscrit.tsx +++ b/src/features/districts/ui/DeleteDiscrit.tsx @@ -40,7 +40,6 @@ const DeleteDiscrit = ({ setDiscritDelete(null); }, onError: (err: AxiosError) => { - console.log(err); const errMessage = err.response?.data as { message: string }; const messageText = errMessage.message; toast.error(messageText || "Xatolik yuz berdi", { diff --git a/src/features/doctors/lib/api.ts b/src/features/doctors/lib/api.ts new file mode 100644 index 0000000..719c931 --- /dev/null +++ b/src/features/doctors/lib/api.ts @@ -0,0 +1,20 @@ +import type { DoctorListRes } from "@/features/doctors/lib/data"; +import httpClient from "@/shared/config/api/httpClient"; +import { DOCTOR } from "@/shared/config/api/URLs"; +import type { AxiosResponse } from "axios"; + +export const doctor_api = { + async list(params: { + limit?: number; + offset?: number; + full_name?: string; + district_name?: string; + place_name?: string; + work_place?: string; + sphere?: string; + user?: string; + }): Promise> { + const res = await httpClient.get(`${DOCTOR}list/`, { params }); + return res; + }, +}; diff --git a/src/features/doctors/lib/data.ts b/src/features/doctors/lib/data.ts index e311f7d..ea797ee 100644 --- a/src/features/doctors/lib/data.ts +++ b/src/features/doctors/lib/data.ts @@ -50,3 +50,45 @@ export const doctorListData: DoctorListType[] = [ long: ObjectListData[1].long, }, ]; + +export interface DoctorListRes { + status_code: number; + status: string; + message: string; + data: { + count: number; + next: string; + previous: string; + results: DoctorListResData[]; + }; +} + +export interface DoctorListResData { + id: number; + first_name: string; + last_name: string; + phone_number: string; + work_place: string; + sphere: string; + description: string; + district: { + id: number; + name: string; + }; + place: { + id: number; + name: string; + }; + user: { + id: number; + first_name: string; + last_name: string; + }; + longitude: number; + latitude: number; + extra_location: { + latitude: number; + longitude: number; + }; + created_at: string; +} diff --git a/src/features/doctors/ui/AddedDoctor.tsx b/src/features/doctors/ui/AddedDoctor.tsx index 4469429..9cff9a7 100644 --- a/src/features/doctors/ui/AddedDoctor.tsx +++ b/src/features/doctors/ui/AddedDoctor.tsx @@ -32,10 +32,9 @@ import type z from "zod"; interface Props { initialValues: DoctorListType | null; setDialogOpen: Dispatch>; - setData: Dispatch>; } -const AddedDoctor = ({ initialValues, setData, setDialogOpen }: Props) => { +const AddedDoctor = ({ initialValues, setDialogOpen }: Props) => { const [load, setLoad] = useState(false); const form = useForm>({ resolver: zodResolver(DoctorForm), @@ -65,34 +64,8 @@ const AddedDoctor = ({ initialValues, setData, setDialogOpen }: Props) => { function onSubmit(values: z.infer) { setLoad(true); - const newObject: DoctorListType = { - id: initialValues ? initialValues.id : Date.now(), - user: FakeUserList.find((u) => u.id === Number(values.user))!, - district: fakeDistrict.find((d) => d.id === Number(values.district))!, - desc: values.desc, - first_name: values.first_name, - last_name: values.last_name, - lat: values.lat, - long: values.long, - object: ObjectListData.find((d) => d.id === Number(values.object))!, - phone_number: values.phone_number, - spec: values.spec, - work: values.work, - }; - - setTimeout(() => { - setData((prev) => { - if (initialValues) { - return prev.map((item) => - item.id === initialValues.id ? newObject : item, - ); - } else { - return [...prev, newObject]; - } - }); - setLoad(false); - setDialogOpen(false); - }, 2000); + console.log(values); + setDialogOpen(false); } return ( diff --git a/src/features/doctors/ui/DoctorDetailDialog.tsx b/src/features/doctors/ui/DoctorDetailDialog.tsx index d21250c..e49367f 100644 --- a/src/features/doctors/ui/DoctorDetailDialog.tsx +++ b/src/features/doctors/ui/DoctorDetailDialog.tsx @@ -1,4 +1,4 @@ -import type { DoctorListType } from "@/features/doctors/lib/data"; +import type { DoctorListResData } from "@/features/doctors/lib/data"; import formatPhone from "@/shared/lib/formatPhone"; import { Button } from "@/shared/ui/button"; import { @@ -8,15 +8,88 @@ import { DialogHeader, DialogTitle, } from "@/shared/ui/dialog"; -import { Circle, Map, Placemark, YMaps } from "@pbe/react-yandex-maps"; +import { + Circle, + Map, + Placemark, + Polygon, + YMaps, + ZoomControl, +} from "@pbe/react-yandex-maps"; +import { useEffect, useState } from "react"; + +interface CoordsData { + lat: number; + lon: number; + polygon: [number, number][][]; +} interface Props { detail: boolean; setDetail: (open: boolean) => void; - object: DoctorListType | null; + object: DoctorListResData | null; } const DoctorDetailDialog = ({ detail, setDetail, object }: Props) => { + const [coords, setCoords] = useState<[number, number]>([ + 41.311081, 69.240562, + ]); + + const [polygonCoords, setPolygonCoords] = useState<[number, number][][]>([]); + + const [circleCoords] = useState<[number, number] | null>(null); + + const getCoords = async (name: string): Promise => { + try { + const res = await fetch( + `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent( + name, + )}&format=json&polygon_geojson=1&limit=1`, + ); + const data = await res.json(); + + if (!data.length || !data[0].geojson) return null; + + const lat = parseFloat(data[0].lat); + const lon = parseFloat(data[0].lon); + + let polygon: [number, number][][] = []; + + if (data[0].geojson.type === "Polygon") { + polygon = data[0].geojson.coordinates.map((ring: []) => + ring.map((c) => [c[1], c[0]]), + ); + } + + if (data[0].geojson.type === "MultiPolygon") { + polygon = data[0].geojson.coordinates[0].map((ring: []) => + ring.map((c) => [c[1], c[0]]), + ); + } + + return { lat, lon, polygon }; + } catch { + return null; + } + }; + + useEffect(() => { + if (!object) return; + + const load = async () => { + const district = await getCoords(object.district.name); + + if (district) { + setPolygonCoords(district.polygon); + } + + setCoords([object.latitude, object.longitude]); + // setCircleCoords([object.latitude, object.longitude]); + }; + + load(); + }, [object]); + if (!object) return null; return ( @@ -28,61 +101,77 @@ const DoctorDetailDialog = ({ detail, setDetail, object }: Props) => {

- Ism Familiya:{" "} - {object.first_name} {object.last_name} + Ism: {object.first_name}{" "} + {object.last_name}

Telefon:{" "} {formatPhone(object.phone_number)}

- Ish joyi: {object.work} + Ish joyi: {object.work_place}

- Mutaxassislik: {object.spec} -

-

- Tavsif: {object.desc} + Mutaxassislik:{" "} + {object.sphere}

Tuman: {object.district.name}

- Foydalanuvchi:{" "} - {object.user.firstName} {object.user.lastName} + Manzili:

-

- Obyekt: {object.object.name} -

- Manzili: + + {/* 🗺 MAP */}
- + - - + + {/* Ish joyining markazi */} + + + {/* Tuman polygon */} + {polygonCoords.length > 0 && ( + + )} + + {/* Radius circle (ish joyi atrofida) */} + {circleCoords && ( + + )}
- diff --git a/src/features/doctors/ui/DoctorsList.tsx b/src/features/doctors/ui/DoctorsList.tsx index 9f7ae28..7b8e0bf 100644 --- a/src/features/doctors/ui/DoctorsList.tsx +++ b/src/features/doctors/ui/DoctorsList.tsx @@ -1,49 +1,22 @@ +import { doctor_api } from "@/features/doctors/lib/api"; import { - doctorListData, + type DoctorListResData, type DoctorListType, } from "@/features/doctors/lib/data"; -import AddedDoctor from "@/features/doctors/ui/AddedDoctor"; import DoctorDetailDialog from "@/features/doctors/ui/DoctorDetailDialog"; -import formatPhone from "@/shared/lib/formatPhone"; -import { Badge } from "@/shared/ui/badge"; -import { Button } from "@/shared/ui/button"; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/shared/ui/dialog"; -import { Input } from "@/shared/ui/input"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/shared/ui/table"; -import clsx from "clsx"; -import { - ChevronLeft, - ChevronRight, - Eye, - Pencil, - Plus, - Trash2, -} from "lucide-react"; -import { useMemo, useState } from "react"; +import FilterDoctor from "@/features/doctors/ui/FilterDoctor"; +import PaginationDoctor from "@/features/doctors/ui/PaginationDoctor"; +import TableDoctor from "@/features/doctors/ui/TableDoctor"; +import { useQuery } from "@tanstack/react-query"; +import { useState } from "react"; const DoctorsList = () => { - const [data, setData] = useState(doctorListData); - const [detail, setDetail] = useState(null); + const [detail, setDetail] = useState(null); const [detailDialog, setDetailDialog] = useState(false); const [editingPlan, setEditingPlan] = useState(null); const [dialogOpen, setDialogOpen] = useState(false); const [currentPage, setCurrentPage] = useState(1); - const totalPages = 5; - // Filter states const [searchName, setSearchName] = useState(""); const [searchDistrict, setSearchDistrict] = useState(""); const [searchObject, setSearchObject] = useState(""); @@ -51,50 +24,44 @@ const DoctorsList = () => { const [searchSpec, setSearchSpec] = useState(""); const [searchUser, setSearchUser] = useState(""); - const handleDelete = (id: number) => { - setData((prev) => prev.filter((e) => e.id !== id)); - }; + const limit = 20; - // Filtered data - const filteredData = useMemo(() => { - return data.filter((item) => { - const nameMatch = `${item.first_name} ${item.last_name}` - .toLowerCase() - .includes(searchName.toLowerCase()); - const districtMatch = item.district.name - .toLowerCase() - .includes(searchDistrict.toLowerCase()); - const objectMatch = item.object.name - .toLowerCase() - .includes(searchObject.toLowerCase()); - const workMatch = item.work - .toLowerCase() - .includes(searchWork.toLowerCase()); - const specMatch = item.spec - .toLowerCase() - .includes(searchSpec.toLowerCase()); - const userMatch = `${item.user.firstName} ${item.user.lastName}` - .toLowerCase() - .includes(searchUser.toLowerCase()); + const { + data: doctor, + isError, + isLoading, + isFetching, + } = useQuery({ + queryKey: [ + "doctor_list", + currentPage, + searchDistrict, + searchName, + searchObject, + searchWork, + searchSpec, + searchUser, + ], + queryFn: () => + doctor_api.list({ + limit, + offset: (currentPage - 1) * limit, + district_name: searchDistrict, + full_name: searchName, + place_name: searchObject, + work_place: searchWork, + sphere: searchSpec, + user: searchUser, + }), + select(data) { + return data.data.data; + }, + }); - return ( - nameMatch && - districtMatch && - objectMatch && - workMatch && - specMatch && - userMatch - ); - }); - }, [ - data, - searchName, - searchDistrict, - searchObject, - searchWork, - searchSpec, - searchUser, - ]); + // const handleDelete = (id: number) => { + // }; + + const totalPages = doctor ? Math.ceil(doctor.count / limit) : 1; return (
@@ -103,69 +70,25 @@ const DoctorsList = () => {

Shifokorlarni boshqarish

-
- setSearchName(e.target.value)} - className="w-full md:w-48" - /> - setSearchDistrict(e.target.value)} - className="w-full md:w-48" - /> - setSearchObject(e.target.value)} - className="w-full md:w-48" - /> - setSearchWork(e.target.value)} - className="w-full md:w-48" - /> - setSearchSpec(e.target.value)} - className="w-full md:w-48" - /> - setSearchUser(e.target.value)} - className="w-full md:w-48" - /> - - - - - - - - {editingPlan - ? "Shifokor tahrirlash" - : "Yangi shifokor qo'shish"} - - - - - -
+
{ />
-
- - - - # - Shifokor Ism Familiyasi - Telefon raqami - Tuman - Obyekt - Ish joyi - Sohasi - Kim qo'shgan - Amallar - - - - {filteredData.map((item, index) => ( - - {index + 1} - - {item.first_name} {item.last_name} - - - {formatPhone(item.phone_number)} - - - {item.district.name} - - {item.object.name} - {item.work} - {item.spec} - - {item.user.firstName} {item.user.lastName} - - - - - - - - ))} - -
-
+ -
- - {Array.from({ length: totalPages }, (_, i) => ( - - ))} - -
+ ); }; diff --git a/src/features/doctors/ui/FilterDoctor.tsx b/src/features/doctors/ui/FilterDoctor.tsx new file mode 100644 index 0000000..4f720f8 --- /dev/null +++ b/src/features/doctors/ui/FilterDoctor.tsx @@ -0,0 +1,116 @@ +import type { DoctorListType } from "@/features/doctors/lib/data"; +import AddedDoctor from "@/features/doctors/ui/AddedDoctor"; +import { Button } from "@/shared/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/shared/ui/dialog"; +import { Input } from "@/shared/ui/input"; +import { Plus } from "lucide-react"; +import type { Dispatch, SetStateAction } from "react"; + +interface Props { + searchName: string; + setSearchName: Dispatch>; + searchDistrict: string; + setSearchDistrict: Dispatch>; + searchWork: string; + setSearchWork: Dispatch>; + searchSpec: string; + setSearchSpec: Dispatch>; + searchUser: string; + setSearchUser: Dispatch>; + dialogOpen: boolean; + setDialogOpen: Dispatch>; + searchObject: string; + setSearchObject: Dispatch>; + setEditingPlan: Dispatch>; + editingPlan: DoctorListType | null; +} + +const FilterDoctor = ({ + searchName, + setSearchName, + searchDistrict, + setSearchDistrict, + searchObject, + setSearchObject, + searchWork, + setSearchWork, + searchSpec, + setSearchSpec, + searchUser, + setSearchUser, + dialogOpen, + setDialogOpen, + setEditingPlan, + editingPlan, +}: Props) => { + return ( +
+ setSearchName(e.target.value)} + className="w-full md:w-48" + /> + setSearchDistrict(e.target.value)} + className="w-full md:w-48" + /> + setSearchObject(e.target.value)} + className="w-full md:w-48" + /> + setSearchWork(e.target.value)} + className="w-full md:w-48" + /> + setSearchSpec(e.target.value)} + className="w-full md:w-48" + /> + setSearchUser(e.target.value)} + className="w-full md:w-48" + /> + + + + + + + + {editingPlan ? "Shifokor tahrirlash" : "Yangi shifokor qo'shish"} + + + + + +
+ ); +}; + +export default FilterDoctor; diff --git a/src/features/doctors/ui/PaginationDoctor.tsx b/src/features/doctors/ui/PaginationDoctor.tsx new file mode 100644 index 0000000..e0f0a9e --- /dev/null +++ b/src/features/doctors/ui/PaginationDoctor.tsx @@ -0,0 +1,57 @@ +import { Button } from "@/shared/ui/button"; +import clsx from "clsx"; +import { ChevronLeft, ChevronRight } from "lucide-react"; +import type { Dispatch, SetStateAction } from "react"; + +interface Props { + currentPage: number; + setCurrentPage: Dispatch>; + totalPages: number; +} + +const PaginationDoctor = ({ + currentPage, + setCurrentPage, + totalPages, +}: Props) => { + return ( +
+ + {Array.from({ length: totalPages }, (_, i) => ( + + ))} + +
+ ); +}; + +export default PaginationDoctor; diff --git a/src/features/doctors/ui/TableDoctor.tsx b/src/features/doctors/ui/TableDoctor.tsx new file mode 100644 index 0000000..3f57935 --- /dev/null +++ b/src/features/doctors/ui/TableDoctor.tsx @@ -0,0 +1,134 @@ +import type { DoctorListResData } from "@/features/doctors/lib/data"; +import formatPhone from "@/shared/lib/formatPhone"; +import { Badge } from "@/shared/ui/badge"; +import { Button } from "@/shared/ui/button"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/shared/ui/table"; +import { Eye, Loader2, Pencil, Trash2 } from "lucide-react"; +import type { Dispatch, SetStateAction } from "react"; + +interface Props { + setDetail: Dispatch>; + setDetailDialog: Dispatch>; + doctor: DoctorListResData[] | []; + isLoading: boolean; + isError: boolean; + isFetching: boolean; +} + +const TableDoctor = ({ + doctor, + setDetail, + setDetailDialog, + isError, + isLoading, + isFetching, +}: Props) => { + return ( +
+ {(isLoading || isFetching) && ( +
+ + + +
+ )} + + {isError && ( +
+ + Ma'lumotlarni olishda xatolik yuz berdi. + +
+ )} + + {!isLoading && !isError && ( + + + + # + Shifokor Ism Familiyasi + Telefon raqami + Tuman + Obyekt + Ish joyi + Sohasi + Kim qo'shgan + Amallar + + + + {doctor.length > 0 ? ( + doctor.map((item, index) => ( + + {index + 1} + + {item.first_name} {item.last_name} + + + {formatPhone(item.phone_number)} + + + {item.district.name} + + {item.place.name} + {item.work_place} + {item.sphere} + + {item.user.first_name} {item.user.last_name} + + + + + + + + )) + ) : ( + + + Shifokor topilmadi. + + + )} + +
+ )} +
+ ); +}; + +export default TableDoctor; diff --git a/src/features/region/lib/api.ts b/src/features/region/lib/api.ts index 66b5063..a8e3da5 100644 --- a/src/features/region/lib/api.ts +++ b/src/features/region/lib/api.ts @@ -11,4 +11,19 @@ export const region_api = { }): Promise> { return await httpClient.get(`${REGIONS}list/`, { params }); }, + + async create(body: { name: string }) { + const res = await httpClient.post(`${REGIONS}create/`, body); + return res; + }, + + async update({ body, id }: { id: number; body: { name: string } }) { + const res = await httpClient.patch(`${REGIONS}${id}/update/`, body); + return res; + }, + + async delete(id: number) { + const res = await httpClient.delete(`${REGIONS}${id}/delete/`); + return res; + }, }; diff --git a/src/features/region/ui/AddedRegion.tsx b/src/features/region/ui/AddedRegion.tsx index 60dba65..7408c77 100644 --- a/src/features/region/ui/AddedRegion.tsx +++ b/src/features/region/ui/AddedRegion.tsx @@ -1,3 +1,4 @@ +import { region_api } from "@/features/region/lib/api"; import type { RegionType } from "@/features/region/lib/data"; import { regionForm } from "@/features/region/lib/form"; import { Button } from "@/shared/ui/button"; @@ -11,42 +12,69 @@ import { import { Input } from "@/shared/ui/input"; import { Label } from "@/shared/ui/label"; import { zodResolver } from "@hookform/resolvers/zod"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import type { AxiosError } from "axios"; import { Loader2 } from "lucide-react"; -import { useState, type Dispatch, type SetStateAction } from "react"; +import { type Dispatch, type SetStateAction } from "react"; import { useForm } from "react-hook-form"; +import { toast } from "sonner"; import type z from "zod"; interface Props { initialValues: RegionType | null; setDialogOpen: Dispatch>; - setPlans: Dispatch>; } -const AddedRegion = ({ initialValues, setDialogOpen, setPlans }: Props) => { - const [load, setLoad] = useState(false); +const AddedRegion = ({ initialValues, setDialogOpen }: Props) => { const form = useForm>({ resolver: zodResolver(regionForm), defaultValues: { name: initialValues?.name || "" }, }); + const queryClient = useQueryClient(); + + const { mutate, isPending } = useMutation({ + mutationFn: (body: { name: string }) => region_api.create(body), + onSuccess: () => { + queryClient.refetchQueries({ queryKey: ["region_list"] }); + toast.success(`Yangi hudud qo'shildi`); + setDialogOpen(false); + }, + onError: (err: AxiosError) => { + const errMessage = err.response?.data as { message: string }; + const messageText = errMessage.message; + toast.error(messageText || "Xatolik yuz berdi", { + richColors: true, + position: "top-center", + }); + }, + }); + + const { mutate: edit, isPending: editPending } = useMutation({ + mutationFn: ({ body, id }: { id: number; body: { name: string } }) => + region_api.update({ body, id }), + onSuccess: () => { + queryClient.refetchQueries({ queryKey: ["region_list"] }); + toast.success(`Yangi hudud qo'shildi`); + setDialogOpen(false); + }, + onError: (err: AxiosError) => { + const errMessage = err.response?.data as { message: string }; + const messageText = errMessage.message; + toast.error(messageText || "Xatolik yuz berdi", { + richColors: true, + position: "top-center", + }); + }, + }); function onSubmit(value: z.infer) { - setLoad(true); - - setTimeout(() => { - setPlans((prev) => { - if (initialValues) { - return prev.map((item) => - item.id === initialValues.id ? { ...item, ...value } : item, - ); - } - return [ - ...prev, - { id: prev.length ? prev[prev.length - 1].id + 1 : 1, ...value }, - ]; + if (initialValues) { + edit({ id: initialValues.id, body: { name: value.name } }); + } else { + mutate({ + name: value.name, }); - setLoad(false); - setDialogOpen(false); - }, 2000); + } } return ( @@ -66,8 +94,11 @@ const AddedRegion = ({ initialValues, setDialogOpen, setPlans }: Props) => { )} /> - + + + + + ); +}; + +export default DeleteRegion; diff --git a/src/features/region/ui/RegionList.tsx b/src/features/region/ui/RegionList.tsx index c7d6479..b8e0ef1 100644 --- a/src/features/region/ui/RegionList.tsx +++ b/src/features/region/ui/RegionList.tsx @@ -1,5 +1,8 @@ -import { fakeRegionList, type RegionType } from "@/features/region/lib/data"; +import { region_api } from "@/features/region/lib/api"; +import { type RegionListResData } from "@/features/region/lib/data"; import AddedRegion from "@/features/region/ui/AddedRegion"; +import DeleteRegion from "@/features/region/ui/DeleteRegion"; +import RegionTable from "@/features/region/ui/RegionTable"; import { Button } from "@/shared/ui/button"; import { Dialog, @@ -8,28 +11,33 @@ import { DialogTitle, DialogTrigger, } from "@/shared/ui/dialog"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/shared/ui/table"; -import clsx from "clsx"; -import { ChevronLeft, ChevronRight, Edit, Plus, Trash } from "lucide-react"; +import { useQuery } from "@tanstack/react-query"; + +import { Plus } from "lucide-react"; import { useState } from "react"; const RegionList = () => { - const [currentPage, setCurrentPage] = useState(1); - const totalPages = 5; - const [plans, setPlans] = useState(fakeRegionList); + const { data, isLoading, isError } = useQuery({ + queryKey: ["region_list"], + queryFn: () => region_api.list({}), + select(data) { + return data.data.data; + }, + }); - const [editingPlan, setEditingPlan] = useState(null); + const [regionDelete, setRegionDelete] = useState( + null, + ); + const [opneDelete, setOpenDelete] = useState(false); + + const [editingPlan, setEditingPlan] = useState( + null, + ); const [dialogOpen, setDialogOpen] = useState(false); - const handleDelete = (id: number) => { - setPlans(plans.filter((p) => p.id !== id)); + const handleDelete = (user: RegionListResData) => { + setRegionDelete(user); + setOpenDelete(true); }; return ( @@ -58,91 +66,27 @@ const RegionList = () => { - -
- - - - ID - Nomi - - - - {plans.map((plan) => ( - - {plan.id} - {plan.name} - - - - - - ))} - -
-
- -
- - {Array.from({ length: totalPages }, (_, i) => ( - - ))} - -
+ {data && ( + + )} + ); }; diff --git a/src/features/region/ui/RegionTable.tsx b/src/features/region/ui/RegionTable.tsx new file mode 100644 index 0000000..af4539c --- /dev/null +++ b/src/features/region/ui/RegionTable.tsx @@ -0,0 +1,89 @@ +import type { RegionListResData } from "@/features/region/lib/data"; +import { Button } from "@/shared/ui/button"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/shared/ui/table"; +import { Edit, Loader2, Trash } from "lucide-react"; +import type { Dispatch, SetStateAction } from "react"; + +const RegionTable = ({ + region, + handleDelete, + isLoading, + isError, + setEditingRegion, + setDialogOpen, +}: { + region: RegionListResData[]; + isLoading: boolean; + isError: boolean; + setEditingRegion: Dispatch>; + setDialogOpen: Dispatch>; + handleDelete: (user: RegionListResData) => void; +}) => { + return ( +
+ {isLoading && ( +
+ + + +
+ )} + + {isError && ( +
+ + Ma'lumotlarni olishda xatolik yuz berdi. + +
+ )} + {!isLoading && !isError && ( + + + + ID + Nomi + + + + {region.map((plan, index) => ( + + {index + 1} + {plan.name} + + + + + + ))} + +
+ )} +
+ ); +}; + +export default RegionTable; diff --git a/src/features/users/ui/AddUsers.tsx b/src/features/users/ui/AddUsers.tsx index f26c689..dd9f942 100644 --- a/src/features/users/ui/AddUsers.tsx +++ b/src/features/users/ui/AddUsers.tsx @@ -1,3 +1,4 @@ +import { region_api } from "@/features/region/lib/api"; import { user_api } from "@/features/users/lib/api"; import type { UserCreateReq, @@ -23,7 +24,7 @@ import { SelectValue, } from "@/shared/ui/select"; import { zodResolver } from "@hookform/resolvers/zod"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import type { AxiosError } from "axios"; import { Loader2 } from "lucide-react"; import { useForm } from "react-hook-form"; @@ -103,6 +104,12 @@ const AddUsers = ({ initialData, setDialogOpen }: UserFormProps) => { } } + const { data: regions } = useQuery({ + queryKey: ["region_list"], + queryFn: () => region_api.list({}), + select: (res) => res.data.data, + }); + return (
@@ -154,9 +161,12 @@ const AddUsers = ({ initialData, setDialogOpen }: UserFormProps) => { - Toshkent - Samarqand - Bekobod + {regions && + regions.map((item) => ( + + {item.name} + + ))} diff --git a/src/features/users/ui/Filter.tsx b/src/features/users/ui/Filter.tsx index 08c8d78..0a600b5 100644 --- a/src/features/users/ui/Filter.tsx +++ b/src/features/users/ui/Filter.tsx @@ -101,7 +101,7 @@ const Filter = ({ role="combobox" aria-expanded={openRegion} className={cn( - "w-64 h-12 justify-between", + "w-64 justify-between", !regionValue && "text-muted-foreground", )} > diff --git a/src/providers/routing/AppRoutes.tsx b/src/providers/routing/AppRoutes.tsx index ba60694..71a28e8 100644 --- a/src/providers/routing/AppRoutes.tsx +++ b/src/providers/routing/AppRoutes.tsx @@ -67,7 +67,7 @@ const AppRouter = () => { element: , }, { - path: "/dashboard/pharm", + path: "/dashboard/pharmaceuticals", element: , }, { diff --git a/src/shared/config/api/URLs.ts b/src/shared/config/api/URLs.ts index 65a9cf6..d2ac152 100644 --- a/src/shared/config/api/URLs.ts +++ b/src/shared/config/api/URLs.ts @@ -6,5 +6,6 @@ const USER = "/api/v1/admin/user/"; const REGION = "/api/v1/admin/district/"; const REGIONS = "/api/v1/admin/region/"; const DISTRICT = "/api/v1/admin/district/"; +const DOCTOR = "/api/v1/admin/doctor/"; -export { BASE_URL, DISTRICT, LOGIN, REGION, REGIONS, USER }; +export { BASE_URL, DISTRICT, DOCTOR, LOGIN, REGION, REGIONS, USER }; diff --git a/src/widgets/sidebar-layout/index.tsx b/src/widgets/sidebar-layout/index.tsx index 32eba19..9941050 100644 --- a/src/widgets/sidebar-layout/index.tsx +++ b/src/widgets/sidebar-layout/index.tsx @@ -93,7 +93,7 @@ const items = [ }, { title: "Farmasevtikalar", - url: "/dashboard/pharm", + url: "/dashboard/pharmaceuticals", icon: Microscope, }, ];