diff --git a/package-lock.json b/package-lock.json index b36b4c5..8ce54fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,8 @@ "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.13", - "zod": "^4.1.13" + "zod": "^4.1.13", + "zustand": "^5.0.9" }, "devDependencies": { "@eslint/js": "^9.25.0", @@ -6156,6 +6157,35 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zustand": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.9.tgz", + "integrity": "sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index cd2237d..b8038be 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,8 @@ "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.13", - "zod": "^4.1.13" + "zod": "^4.1.13", + "zustand": "^5.0.9" }, "devDependencies": { "@eslint/js": "^9.25.0", diff --git a/src/SidebarLayout.tsx b/src/SidebarLayout.tsx index 874882f..47ef7f1 100644 --- a/src/SidebarLayout.tsx +++ b/src/SidebarLayout.tsx @@ -1,8 +1,26 @@ +import { user_api } from "@/shared/config/api/user/api"; +import { userStore } from "@/shared/hooks/user"; import { SidebarProvider, SidebarTrigger } from "@/shared/ui/sidebar"; import { AppSidebar } from "@/widgets/sidebar-layout"; -import React from "react"; +import { useQuery } from "@tanstack/react-query"; +import React, { useEffect } from "react"; const SidebarLayout = ({ children }: { children: React.ReactNode }) => { + const { data } = useQuery({ + queryKey: ["get_me"], + queryFn: () => user_api.getMe(), + select(data) { + return data.data.data; + }, + }); + const { addUser } = userStore(); + + useEffect(() => { + if (data) { + addUser(data); + } + }, [data]); + return ( diff --git a/src/features/districts/ui/TableDistrict.tsx b/src/features/districts/ui/TableDistrict.tsx index b4498ff..bb2a8a5 100644 --- a/src/features/districts/ui/TableDistrict.tsx +++ b/src/features/districts/ui/TableDistrict.tsx @@ -1,4 +1,5 @@ import type { DistrictListData } from "@/features/districts/lib/data"; +import { userStore } from "@/shared/hooks/user"; import { Button } from "@/shared/ui/button"; import { Table, @@ -30,6 +31,8 @@ const TableDistrict = ({ setEditingDistrict, currentPage, }: Props) => { + const { user } = userStore(); + return (
{isLoading && ( @@ -79,15 +82,17 @@ const TableDistrict = ({ > - - + {user?.is_superuser && ( + + )} ))} diff --git a/src/features/doctors/ui/TableDoctor.tsx b/src/features/doctors/ui/TableDoctor.tsx index 0542e79..5116e6a 100644 --- a/src/features/doctors/ui/TableDoctor.tsx +++ b/src/features/doctors/ui/TableDoctor.tsx @@ -1,4 +1,5 @@ import type { DoctorListResData } from "@/features/doctors/lib/data"; +import { userStore } from "@/shared/hooks/user"; import formatPhone from "@/shared/lib/formatPhone"; import { Badge } from "@/shared/ui/badge"; import { Button } from "@/shared/ui/button"; @@ -36,6 +37,7 @@ const TableDoctor = ({ handleDelete, isFetching, }: Props) => { + const { user: getme } = userStore(); return (
{(isLoading || isFetching) && ( @@ -112,14 +114,17 @@ const TableDoctor = ({ > - + {getme?.is_superuser && ( + + )} )) diff --git a/src/features/location/ui/LocationTable.tsx b/src/features/location/ui/LocationTable.tsx index c1bba06..5e17ad7 100644 --- a/src/features/location/ui/LocationTable.tsx +++ b/src/features/location/ui/LocationTable.tsx @@ -1,4 +1,5 @@ import type { LocationListDataRes } from "@/features/location/lib/data"; +import { userStore } from "@/shared/hooks/user"; import formatDate from "@/shared/lib/formatDate"; import { Button } from "@/shared/ui/button"; import { @@ -25,6 +26,7 @@ const LocationTable = ({ setDetailDialog, handleDelete, }: Props) => { + const { user: getme } = userStore(); return (
@@ -72,14 +74,17 @@ const LocationTable = ({ > - + {getme?.is_superuser && ( + + )} ))} diff --git a/src/features/location/ui/UserLocationTable.tsx b/src/features/location/ui/UserLocationTable.tsx index 7f85abe..d7ea619 100644 --- a/src/features/location/ui/UserLocationTable.tsx +++ b/src/features/location/ui/UserLocationTable.tsx @@ -1,4 +1,5 @@ import type { LocationListDataRes } from "@/features/location/lib/data"; +import { userStore } from "@/shared/hooks/user"; import formatDate from "@/shared/lib/formatDate"; import { Button } from "@/shared/ui/button"; import { @@ -25,6 +26,7 @@ const UserLocationTable = ({ setDetailDialog, handleDelete, }: Props) => { + const { user: getme } = userStore(); return (
@@ -72,14 +74,17 @@ const UserLocationTable = ({ > - + {getme?.is_superuser && ( + + )} ))} diff --git a/src/features/objects/ui/ObjectTable.tsx b/src/features/objects/ui/ObjectTable.tsx index 721df52..a3458d4 100644 --- a/src/features/objects/ui/ObjectTable.tsx +++ b/src/features/objects/ui/ObjectTable.tsx @@ -1,4 +1,5 @@ import type { ObjectListData } from "@/features/objects/lib/data"; +import { userStore } from "@/shared/hooks/user"; import { Badge } from "@/shared/ui/badge"; import { Button } from "@/shared/ui/button"; import { @@ -33,6 +34,7 @@ const ObjectTable = ({ isError, isLoading, }: Props) => { + const { user: getme } = userStore(); return (
{isLoading && ( @@ -96,14 +98,17 @@ const ObjectTable = ({ > - + {getme?.is_superuser && ( + + )} )) diff --git a/src/features/pharm/ui/PharmList.tsx b/src/features/pharm/ui/PharmList.tsx index 3b4b89b..a12b527 100644 --- a/src/features/pharm/ui/PharmList.tsx +++ b/src/features/pharm/ui/PharmList.tsx @@ -5,6 +5,7 @@ import { } from "@/features/pharm/lib/data"; import AddedPharm from "@/features/pharm/ui/AddedPharm"; import DeletePharm from "@/features/pharm/ui/DeletePharm"; +import { userStore } from "@/shared/hooks/user"; import { Button } from "@/shared/ui/button"; import { Dialog, @@ -28,6 +29,7 @@ import { Edit, Loader2, Plus, Trash } from "lucide-react"; import { useState } from "react"; const PharmList = () => { + const { user: getme } = userStore(); const [currentPage, setCurrentPage] = useState(1); const [nameFilter, setNameFilter] = useState(""); const limit = 20; @@ -140,14 +142,17 @@ const PharmList = () => { > - + {getme?.is_superuser && ( + + )} )) diff --git a/src/features/pharmacies/ui/PharmaciesTable.tsx b/src/features/pharmacies/ui/PharmaciesTable.tsx index e738926..f2c99d2 100644 --- a/src/features/pharmacies/ui/PharmaciesTable.tsx +++ b/src/features/pharmacies/ui/PharmaciesTable.tsx @@ -1,4 +1,5 @@ import type { PharmaciesListData } from "@/features/pharmacies/lib/data"; +import { userStore } from "@/shared/hooks/user"; import formatPhone from "@/shared/lib/formatPhone"; import { Button } from "@/shared/ui/button"; import { @@ -29,6 +30,7 @@ const PharmaciesTable = ({ setDialogOpen, handleDelete, }: Props) => { + const { user: getme } = userStore(); return (
@@ -82,14 +84,17 @@ const PharmaciesTable = ({ > - + {getme?.is_superuser && ( + + )} ))} diff --git a/src/features/pill/ui/PillList.tsx b/src/features/pill/ui/PillList.tsx index 12d5a01..f0810bd 100644 --- a/src/features/pill/ui/PillList.tsx +++ b/src/features/pill/ui/PillList.tsx @@ -2,6 +2,7 @@ import { pill_api } from "@/features/pill/lib/api"; import { type PillListData, type PillType } from "@/features/pill/lib/data"; import AddedPill from "@/features/pill/ui/AddedPill"; import DeletePill from "@/features/pill/ui/DeletePill"; +import { userStore } from "@/shared/hooks/user"; import formatPrice from "@/shared/lib/formatPrice"; import { Button } from "@/shared/ui/button"; import { @@ -26,6 +27,7 @@ import { Edit, Plus, Trash } from "lucide-react"; import { useState } from "react"; const PillList = () => { + const { user: getme } = userStore(); const [currentPage, setCurrentPage] = useState(1); const limit = 20; const [nameFilter, setNameFilter] = useState(""); @@ -123,14 +125,17 @@ const PillList = () => { > - + {getme?.is_superuser && ( + + )} ))} diff --git a/src/features/plans/ui/PalanTable.tsx b/src/features/plans/ui/PalanTable.tsx index 94ae8f2..313ae8d 100644 --- a/src/features/plans/ui/PalanTable.tsx +++ b/src/features/plans/ui/PalanTable.tsx @@ -1,4 +1,5 @@ import type { PlanListData } from "@/features/plans/lib/data"; +import { userStore } from "@/shared/hooks/user"; import { Button } from "@/shared/ui/button"; import { Table, @@ -33,6 +34,7 @@ const PalanTable = ({ setDialogOpen, handleDelete, }: Props) => { + const { user: getme } = userStore(); return (
{(isLoading || isFetching) && ( @@ -115,15 +117,19 @@ const PalanTable = ({ > - + {getme?.is_superuser && ( + + )} ))} diff --git a/src/features/region/ui/RegionTable.tsx b/src/features/region/ui/RegionTable.tsx index af4539c..901da40 100644 --- a/src/features/region/ui/RegionTable.tsx +++ b/src/features/region/ui/RegionTable.tsx @@ -1,4 +1,5 @@ import type { RegionListResData } from "@/features/region/lib/data"; +import { userStore } from "@/shared/hooks/user"; import { Button } from "@/shared/ui/button"; import { Table, @@ -26,6 +27,7 @@ const RegionTable = ({ setDialogOpen: Dispatch>; handleDelete: (user: RegionListResData) => void; }) => { + const { user: getme } = userStore(); return (
{isLoading && ( @@ -68,14 +70,17 @@ const RegionTable = ({ > - + {getme?.is_superuser && ( + + )} ))} diff --git a/src/features/specifications/ui/SpecificationsList.tsx b/src/features/specifications/ui/SpecificationsList.tsx index b92d8f7..8a6188b 100644 --- a/src/features/specifications/ui/SpecificationsList.tsx +++ b/src/features/specifications/ui/SpecificationsList.tsx @@ -5,6 +5,7 @@ import { type OrderListDataRes } from "@/features/specifications/lib/data"; import { AddedSpecification } from "@/features/specifications/ui/AddedSpecification"; import DeleteOrder from "@/features/specifications/ui/DeleteOrder"; import { SpecificationDetail } from "@/features/specifications/ui/SpecificationDetail "; +import { userStore } from "@/shared/hooks/user"; import formatPrice from "@/shared/lib/formatPrice"; import { Button } from "@/shared/ui/button"; import { @@ -34,6 +35,7 @@ const SpecificationsList = () => { const [dialogOpen, setDialogOpen] = useState(false); const [currentPage, setCurrentPage] = useState(1); const limit = 20; + const { user: getme } = userStore(); const { data: order, @@ -154,14 +156,17 @@ const SpecificationsList = () => { > - + {getme?.is_superuser && ( + + )} ))} diff --git a/src/features/tour-plan/ui/TourPlanTable.tsx b/src/features/tour-plan/ui/TourPlanTable.tsx index 1f25ca9..fbc3dc3 100644 --- a/src/features/tour-plan/ui/TourPlanTable.tsx +++ b/src/features/tour-plan/ui/TourPlanTable.tsx @@ -1,4 +1,5 @@ import type { PlanTourListDataRes } from "@/features/tour-plan/lib/data"; +import { userStore } from "@/shared/hooks/user"; import formatDate from "@/shared/lib/formatDate"; import { Button } from "@/shared/ui/button"; import { @@ -36,6 +37,7 @@ const TourPlanTable = ({ setDialogOpen, setDetailOpen, }: Props) => { + const { user: getme } = userStore(); return (
{(isLoading || isFetching) && ( @@ -107,14 +109,17 @@ const TourPlanTable = ({ > - + {getme?.is_superuser && ( + + )} )) diff --git a/src/features/users/ui/UserTable.tsx b/src/features/users/ui/UserTable.tsx index 0e21494..bfea1ad 100644 --- a/src/features/users/ui/UserTable.tsx +++ b/src/features/users/ui/UserTable.tsx @@ -1,5 +1,6 @@ import { user_api } from "@/features/users/lib/api"; import type { UserListData, UserListRes } from "@/features/users/lib/data"; +import { userStore } from "@/shared/hooks/user"; import { Button } from "@/shared/ui/button"; import { Checkbox } from "@/shared/ui/checkbox"; import { @@ -43,6 +44,7 @@ const UserTable = ({ currentPage, }: Props) => { const queryClient = useQueryClient(); + const { user: getme } = userStore(); const [pendingUserId, setPendingUserId] = useState(null); // TableHeader checkbox holati @@ -182,15 +184,17 @@ const UserTable = ({ > - + {getme?.is_superuser && ( + + )} )) diff --git a/src/shared/config/api/URLs.ts b/src/shared/config/api/URLs.ts index 1feb323..5fc76af 100644 --- a/src/shared/config/api/URLs.ts +++ b/src/shared/config/api/URLs.ts @@ -21,4 +21,5 @@ export const API_URLS = { SUPPORT: `${API_V}admin/support/list/`, DISTRIBUTED: `${API_V}admin/distributed_product/list/`, SEND_MESSAGE: `${API_V}admin/send_message/`, + GET_ME: `${API_V}accounts/user/me/`, }; diff --git a/src/shared/config/api/user/api.ts b/src/shared/config/api/user/api.ts new file mode 100644 index 0000000..361ea69 --- /dev/null +++ b/src/shared/config/api/user/api.ts @@ -0,0 +1,11 @@ +import httpClient from "@/shared/config/api/httpClient"; +import { API_URLS } from "@/shared/config/api/URLs"; +import type { GetMeRes } from "@/shared/config/api/user/type"; +import type { AxiosResponse } from "axios"; + +export const user_api = { + async getMe(): Promise> { + const res = httpClient.get(API_URLS.GET_ME); + return res; + }, +}; diff --git a/src/shared/config/api/user/type.ts b/src/shared/config/api/user/type.ts new file mode 100644 index 0000000..9cdc9b4 --- /dev/null +++ b/src/shared/config/api/user/type.ts @@ -0,0 +1,16 @@ +export interface GetMeRes { + status: string; + status_code: number; + data: GetMeResData; +} + +export interface GetMeResData { + created_at: string; + first_name: string; + id: number; + is_active: boolean; + is_superuser: boolean; + last_name: string; + region: null | number; + telegram_id: null | number; +} diff --git a/src/shared/hooks/user.ts b/src/shared/hooks/user.ts new file mode 100644 index 0000000..57817bb --- /dev/null +++ b/src/shared/hooks/user.ts @@ -0,0 +1,15 @@ +import type { GetMeResData } from "@/shared/config/api/user/type"; +import { create } from "zustand"; + +type State = { + user: GetMeResData | null; +}; + +type Actions = { + addUser: (user: GetMeResData) => void; +}; + +export const userStore = create((set) => ({ + user: null, + addUser: (user: GetMeResData | null) => set(() => ({ user })), +}));