api ulandi
This commit is contained in:
@@ -69,7 +69,29 @@ export default function LoginForm() {
|
||||
},
|
||||
onError: (error: AxiosError) => {
|
||||
const data = error.response?.data as { message?: string };
|
||||
toast.error(data?.message || "Xatolik yuz berdi");
|
||||
const errorData = error.response?.data as {
|
||||
messages?: {
|
||||
token_class: string;
|
||||
token_type: string;
|
||||
message: string;
|
||||
}[];
|
||||
};
|
||||
const errorName = error.response?.data as {
|
||||
data?: {
|
||||
name: string[];
|
||||
};
|
||||
};
|
||||
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -95,12 +95,16 @@ export default function District() {
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
errorName.data?.name[0] ||
|
||||
data.message ||
|
||||
errorData?.messages?.[0].message ||
|
||||
"Xatolik yuz berdi",
|
||||
);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -128,12 +132,16 @@ export default function District() {
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
errorName.data?.name[0] ||
|
||||
data.message ||
|
||||
errorData?.messages?.[0].message ||
|
||||
"Xatolik yuz berdi",
|
||||
);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -160,12 +168,16 @@ export default function District() {
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
errorName.data?.name[0] ||
|
||||
data.message ||
|
||||
errorData?.messages?.[0].message ||
|
||||
"Xatolik yuz berdi",
|
||||
);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -119,12 +119,16 @@ const CreateDoctor = () => {
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
errorName.data?.name[0] ||
|
||||
data.message ||
|
||||
errorData?.messages?.[0].message ||
|
||||
"Xatolik yuz berdi",
|
||||
);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -150,12 +154,16 @@ const CreateDoctor = () => {
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
errorName.data?.name[0] ||
|
||||
data.message ||
|
||||
errorData?.messages?.[0].message ||
|
||||
"Xatolik yuz berdi",
|
||||
);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -55,12 +55,16 @@ const Doctor = () => {
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
errorName.data?.name[0] ||
|
||||
data.message ||
|
||||
errorData?.messages?.[0].message ||
|
||||
"Xatolik yuz berdi",
|
||||
);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -78,12 +78,16 @@ export default function Home() {
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
errorName.data?.name[0] ||
|
||||
data.message ||
|
||||
errorData?.messages?.[0].message ||
|
||||
"Xatolik yuz berdi",
|
||||
);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -68,13 +68,16 @@ const MyLocation: React.FC = () => {
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
errorName.data?.name?.[0] ||
|
||||
data.message ||
|
||||
errorData?.messages?.[0]?.message ||
|
||||
"Xatolik yuz berdi",
|
||||
);
|
||||
setLoadingButtonId(null);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -87,12 +87,16 @@ const CreateObject = () => {
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
errorName.data?.name[0] ||
|
||||
data.message ||
|
||||
errorData?.messages?.[0].message ||
|
||||
"Xatolik yuz berdi",
|
||||
);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -85,12 +85,16 @@ const CreatePharmacy = () => {
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
errorName.data?.name[0] ||
|
||||
data.message ||
|
||||
errorData?.messages?.[0].message ||
|
||||
"Xatolik yuz berdi",
|
||||
);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
9
src/features/plan-tour/lib/api.ts
Normal file
9
src/features/plan-tour/lib/api.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import httpClient from "@/shared/config/api/httpClient";
|
||||
import { TOUR_PLAN } from "@/shared/config/api/URLs";
|
||||
|
||||
export const tour_plan_api = {
|
||||
async list() {
|
||||
const res = await httpClient.get(TOUR_PLAN);
|
||||
return res;
|
||||
},
|
||||
};
|
||||
0
src/features/plan-tour/lib/data.ts
Normal file
0
src/features/plan-tour/lib/data.ts
Normal file
@@ -1,60 +1,41 @@
|
||||
import { order_api } from "@/features/specification/lib/api";
|
||||
import type { OrderListData } from "@/features/specification/lib/data";
|
||||
import { LanguageRoutes } from "@/shared/config/i18n/types";
|
||||
import { formatPrice } from "@/shared/lib/formatPrice";
|
||||
import { Alert, AlertDescription } from "@/shared/ui/alert";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/ui/card";
|
||||
import { Input } from "@/shared/ui/input";
|
||||
import { Building2, Check, DollarSign, Edit2, MapPin, X } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FalkeDataPlanTour } from "../lib/mock";
|
||||
|
||||
// TYPES
|
||||
interface MonthData {
|
||||
amount: number;
|
||||
locked: boolean;
|
||||
}
|
||||
|
||||
interface Pharmacy {
|
||||
id: number;
|
||||
name: string;
|
||||
district: string;
|
||||
address: string;
|
||||
monthlyData: Record<string, MonthData>;
|
||||
}
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { AxiosError } from "axios";
|
||||
import {
|
||||
Banknote,
|
||||
Building2,
|
||||
Check,
|
||||
DollarSign,
|
||||
Edit2,
|
||||
Loader2,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface PlanPriceProps {
|
||||
selectedMonth: string;
|
||||
pharmacies: OrderListData[];
|
||||
}
|
||||
|
||||
const PlanPrice = ({ selectedMonth }: PlanPriceProps) => {
|
||||
const [pharmacies, setPharmacies] = useState(FalkeDataPlanTour);
|
||||
const PlanPrice = ({ selectedMonth, pharmacies }: PlanPriceProps) => {
|
||||
const currentDate = new Date();
|
||||
const queryClient = useQueryClient();
|
||||
const currentMonthKey = `${currentDate.getFullYear()}-${String(
|
||||
currentDate.getMonth() + 1,
|
||||
).padStart(2, "0")}`;
|
||||
|
||||
useEffect(() => {
|
||||
setPharmacies((prev) =>
|
||||
prev.map((pharmacy) => {
|
||||
if (pharmacy.monthlyData[selectedMonth]) return pharmacy;
|
||||
|
||||
return {
|
||||
...pharmacy,
|
||||
monthlyData: {
|
||||
...pharmacy.monthlyData,
|
||||
[selectedMonth]: { amount: 0, locked: false },
|
||||
},
|
||||
};
|
||||
}),
|
||||
);
|
||||
}, [selectedMonth]);
|
||||
|
||||
// Editing
|
||||
const [editingId, setEditingId] = useState<number | null>(null);
|
||||
const [tempAmount, setTempAmount] = useState<string>("");
|
||||
const [displayPrice, setDisplayPrice] = useState("");
|
||||
|
||||
// === HELPERS ===
|
||||
const formatCurrency = (amount: number): string =>
|
||||
new Intl.NumberFormat("uz-UZ").format(amount) + " so'm";
|
||||
|
||||
@@ -77,6 +58,43 @@ const PlanPrice = ({ selectedMonth }: PlanPriceProps) => {
|
||||
return `${months[parseInt(month) - 1]} ${year}`;
|
||||
};
|
||||
|
||||
const { mutate, isPending } = useMutation({
|
||||
mutationFn: ({ body, id }: { id: number; body: { paid_price: number } }) =>
|
||||
order_api.update({ body, id }),
|
||||
onSuccess: () => {
|
||||
toast.success("Lokatsiya jo'natildi");
|
||||
queryClient.refetchQueries({ queryKey: ["order_list"] });
|
||||
setEditingId(null);
|
||||
setTempAmount("");
|
||||
},
|
||||
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[];
|
||||
};
|
||||
};
|
||||
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
const handleEdit = (pharmacyId: number) => {
|
||||
const pharmacy = pharmacies.find((p) => p.id === pharmacyId);
|
||||
const currentAmount = pharmacy?.monthlyData[selectedMonth]?.amount ?? 0;
|
||||
@@ -88,25 +106,12 @@ const PlanPrice = ({ selectedMonth }: PlanPriceProps) => {
|
||||
const handleSave = (pharmacyId: number) => {
|
||||
const amount = parseInt(tempAmount) || 0;
|
||||
|
||||
setPharmacies((prev) =>
|
||||
prev.map((pharmacy) =>
|
||||
pharmacy.id === pharmacyId
|
||||
? {
|
||||
...pharmacy,
|
||||
monthlyData: {
|
||||
...pharmacy.monthlyData,
|
||||
[selectedMonth]: {
|
||||
amount,
|
||||
locked: selectedMonth !== currentMonthKey,
|
||||
mutate({
|
||||
body: {
|
||||
paid_price: amount,
|
||||
},
|
||||
},
|
||||
}
|
||||
: pharmacy,
|
||||
),
|
||||
);
|
||||
|
||||
setEditingId(null);
|
||||
setTempAmount("");
|
||||
id: pharmacyId,
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@@ -114,7 +119,7 @@ const PlanPrice = ({ selectedMonth }: PlanPriceProps) => {
|
||||
setTempAmount("");
|
||||
};
|
||||
|
||||
const isLocked = (pharmacy: Pharmacy): boolean =>
|
||||
const isLocked = (pharmacy: OrderListData): boolean =>
|
||||
pharmacy.monthlyData[selectedMonth]?.locked === true ||
|
||||
selectedMonth !== currentMonthKey;
|
||||
|
||||
@@ -158,18 +163,20 @@ const PlanPrice = ({ selectedMonth }: PlanPriceProps) => {
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<CardTitle className="text-xl text-gray-900 mb-2">
|
||||
{pharmacy.name}
|
||||
{pharmacy.employee_name}
|
||||
</CardTitle>
|
||||
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center text-sm text-gray-600">
|
||||
<MapPin className="h-4 w-4 mr-2 text-blue-600" />
|
||||
{pharmacy.district}
|
||||
<Building2 className="h-4 w-4 mr-2 text-blue-600" />
|
||||
Farmasevtika: {pharmacy.factory.name}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center text-sm text-gray-600">
|
||||
<Building2 className="h-4 w-4 mr-2 text-blue-600" />
|
||||
{pharmacy.address}
|
||||
<Banknote className="h-4 w-4 mr-2 text-blue-600" />
|
||||
Qolgan summa: {formatPrice(pharmacy.overdue_price)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -182,13 +189,9 @@ const PlanPrice = ({ selectedMonth }: PlanPriceProps) => {
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="pt-6">
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 mb-2 block">
|
||||
Oylik summa
|
||||
</label>
|
||||
|
||||
{isEditing ? (
|
||||
<div className="space-y-3 flex flex-col">
|
||||
<Input
|
||||
@@ -213,7 +216,11 @@ const PlanPrice = ({ selectedMonth }: PlanPriceProps) => {
|
||||
className="flex-1 bg-green-600 hover:bg-green-700"
|
||||
>
|
||||
<Check className="h-4 w-4 mr-2" />
|
||||
Saqlash
|
||||
{isPending ? (
|
||||
<Loader2 className="animate-spin" />
|
||||
) : (
|
||||
"Saqlash"
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
|
||||
@@ -1,32 +1,62 @@
|
||||
"use client";
|
||||
|
||||
import { order_api } from "@/features/specification/lib/api";
|
||||
import type { OrderListData } from "@/features/specification/lib/data";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/ui/card";
|
||||
import { DashboardLayout } from "@/widgets/dashboard-layout/ui";
|
||||
import { Calendar } from "lucide-react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Calendar, Loader2 } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FalkeDataPlanTour } from "../lib/mock";
|
||||
import PlanPrice from "./PlanPrice";
|
||||
|
||||
const PlanTour = () => {
|
||||
const [pharmacies, setPharmacies] = useState(FalkeDataPlanTour);
|
||||
const currentDate = new Date();
|
||||
const currentMonthKey = `${currentDate.getFullYear()}-${String(
|
||||
currentDate.getMonth() + 1,
|
||||
).padStart(2, "0")}`;
|
||||
|
||||
const { data, isLoading, isError } = useQuery({
|
||||
queryKey: ["order_list"],
|
||||
queryFn: () => order_api.order_list(),
|
||||
select(data) {
|
||||
return data.data.data as OrderListData[];
|
||||
},
|
||||
});
|
||||
|
||||
// 🌟 monthlyData frontendda qo‘shilmoqda
|
||||
const [pharmacies, setPharmacies] = useState<OrderListData[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!data) return;
|
||||
|
||||
const enhanced = data.map((item) => ({
|
||||
...item,
|
||||
monthlyData: item.monthlyData || {}, // mavjud bo'lmasa yaratamiz
|
||||
}));
|
||||
|
||||
setPharmacies(enhanced);
|
||||
}, [data]);
|
||||
|
||||
// 🔥 Joriy oy bo‘yicha monthlyData bo‘lmasa — qo‘shamiz
|
||||
useEffect(() => {
|
||||
if (!pharmacies.length) return;
|
||||
|
||||
setPharmacies((prev) =>
|
||||
prev.map((pharmacy) => {
|
||||
if (pharmacy.monthlyData[currentMonthKey]) return pharmacy;
|
||||
prev.map((p) => {
|
||||
if (p.monthlyData[currentMonthKey]) return p;
|
||||
|
||||
return {
|
||||
...pharmacy,
|
||||
...p,
|
||||
monthlyData: {
|
||||
...pharmacy.monthlyData,
|
||||
...p.monthlyData,
|
||||
[currentMonthKey]: { amount: 0, locked: false },
|
||||
},
|
||||
};
|
||||
}),
|
||||
);
|
||||
}, [currentMonthKey]);
|
||||
}, [currentMonthKey, pharmacies.length]);
|
||||
|
||||
const [selectedMonth, setSelectedMonth] = useState<string>(currentMonthKey);
|
||||
|
||||
const getMonthName = (monthKey: string): string => {
|
||||
@@ -49,16 +79,17 @@ const PlanTour = () => {
|
||||
};
|
||||
|
||||
const availableMonths = (): string[] => {
|
||||
const months = new Set<string>();
|
||||
const set = new Set<string>();
|
||||
|
||||
pharmacies.forEach((p) => {
|
||||
Object.keys(p.monthlyData).forEach((m) => months.add(m));
|
||||
Object.keys(p.monthlyData).forEach((m) => set.add(m));
|
||||
});
|
||||
return Array.from(months).sort();
|
||||
|
||||
return Array.from(set).sort();
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<div>
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<h1 className="text-4xl font-bold text-gray-900 mb-2">
|
||||
Oylik hisobotlar
|
||||
@@ -66,13 +97,27 @@ const PlanTour = () => {
|
||||
<p className="text-gray-600">
|
||||
Dorixonalar uchun oylik summalarni boshqaring
|
||||
</p>
|
||||
|
||||
{isLoading && (
|
||||
<div className="flex justify-center py-10">
|
||||
<Loader2 className="w-10 h-10 animate-spin text-blue-600" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isError && (
|
||||
<div className="flex justify-center py-10 text-red-600 font-semibold">
|
||||
Ma'lumotlarni yuklashda xatolik yuz berdi!
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Card className="mb-4 border-0 p-0 mt-5">
|
||||
<CardHeader className="bg-blue-500 p-3 from-blue-600 to-indigo-600 text-white">
|
||||
<CardHeader className="bg-blue-500 p-3 text-white">
|
||||
<CardTitle className="flex items-center gap-2 justify-center">
|
||||
<Calendar className="h-5 w-5" />
|
||||
Oy tanlash
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="pb-6">
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{availableMonths().map((month) => (
|
||||
@@ -80,11 +125,11 @@ const PlanTour = () => {
|
||||
key={month}
|
||||
onClick={() => setSelectedMonth(month)}
|
||||
variant={selectedMonth === month ? "default" : "outline"}
|
||||
className={`${
|
||||
className={
|
||||
selectedMonth === month
|
||||
? "bg-blue-600 hover:bg-blue-700 text-white"
|
||||
: "hover:bg-blue-50"
|
||||
}`}
|
||||
}
|
||||
>
|
||||
{getMonthName(month)}
|
||||
{month === currentMonthKey && (
|
||||
@@ -97,8 +142,9 @@ const PlanTour = () => {
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<PlanPrice selectedMonth={selectedMonth} />
|
||||
</div>
|
||||
|
||||
{/* 🔥 Oylik narxlar */}
|
||||
<PlanPrice selectedMonth={selectedMonth} pharmacies={pharmacies} />
|
||||
</div>
|
||||
</DashboardLayout>
|
||||
);
|
||||
|
||||
@@ -49,10 +49,22 @@ export function PlanDetailsDialog({
|
||||
message: string;
|
||||
}[];
|
||||
};
|
||||
const errorName = error.response?.data as {
|
||||
data?: {
|
||||
name: string[];
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
data.message || errorData?.messages?.[0].message || "Xatolik yuz berdi",
|
||||
);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -94,10 +94,22 @@ export const AddPlans = ({
|
||||
message: string;
|
||||
}[];
|
||||
};
|
||||
const errorName = error.response?.data as {
|
||||
data?: {
|
||||
name: string[];
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
data.message || errorData?.messages?.[0].message || "Xatolik yuz berdi",
|
||||
);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -119,10 +131,22 @@ export const AddPlans = ({
|
||||
message: string;
|
||||
}[];
|
||||
};
|
||||
const errorName = error.response?.data as {
|
||||
data?: {
|
||||
name: string[];
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
data.message || errorData?.messages?.[0].message || "Xatolik yuz berdi",
|
||||
);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -61,10 +61,22 @@ export default function Plans() {
|
||||
message: string;
|
||||
}[];
|
||||
};
|
||||
const errorName = error.response?.data as {
|
||||
data?: {
|
||||
name: string[];
|
||||
};
|
||||
};
|
||||
|
||||
toast.error(
|
||||
data.message || errorData?.messages?.[0].message || "Xatolik yuz berdi",
|
||||
);
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
36
src/features/specification/lib/api.ts
Normal file
36
src/features/specification/lib/api.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type {
|
||||
FactoryListRes,
|
||||
OrderCreateReq,
|
||||
OrderList,
|
||||
ProductListRes,
|
||||
} from "@/features/specification/lib/data";
|
||||
import httpClient from "@/shared/config/api/httpClient";
|
||||
import { FACTORY, ORDER, PRODUCT } from "@/shared/config/api/URLs";
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
export const order_api = {
|
||||
async factory_list(): Promise<AxiosResponse<FactoryListRes>> {
|
||||
const res = await httpClient.get(FACTORY);
|
||||
return res;
|
||||
},
|
||||
|
||||
async product_list(): Promise<AxiosResponse<ProductListRes>> {
|
||||
const res = await httpClient.get(PRODUCT);
|
||||
return res;
|
||||
},
|
||||
|
||||
async order_list(): Promise<AxiosResponse<OrderList>> {
|
||||
const res = await httpClient.get(`${ORDER}list/`);
|
||||
return res;
|
||||
},
|
||||
|
||||
async order_create(body: OrderCreateReq) {
|
||||
const res = await httpClient.post(`${ORDER}create/`, body);
|
||||
return res;
|
||||
},
|
||||
|
||||
async update({ body, id }: { id: number; body: { paid_price: number } }) {
|
||||
const res = await httpClient.patch(`${ORDER}${id}/update/`, body);
|
||||
return res;
|
||||
},
|
||||
};
|
||||
72
src/features/specification/lib/data.ts
Normal file
72
src/features/specification/lib/data.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
export interface FactoryListRes {
|
||||
status_code: number;
|
||||
status: string;
|
||||
message: string;
|
||||
data: FactoryListData[];
|
||||
}
|
||||
|
||||
export interface FactoryListData {
|
||||
id: number;
|
||||
name: string;
|
||||
created_at: string;
|
||||
}
|
||||
export interface ProductListRes {
|
||||
status_code: number;
|
||||
status: string;
|
||||
message: string;
|
||||
data: ProductListData[];
|
||||
}
|
||||
|
||||
export interface ProductListData {
|
||||
id: number;
|
||||
name: string;
|
||||
price: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface OrderCreateReq {
|
||||
factory_id: number;
|
||||
paid_price: string;
|
||||
total_price: string;
|
||||
advance: number;
|
||||
employee_name: string;
|
||||
items: {
|
||||
product: number;
|
||||
quantity: number;
|
||||
total_price: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface OrderList {
|
||||
status_code: number;
|
||||
status: string;
|
||||
message: string;
|
||||
data: OrderListData[];
|
||||
}
|
||||
|
||||
export interface OrderListData {
|
||||
id: number;
|
||||
factory: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
total_price: string;
|
||||
paid_price: string;
|
||||
advance: number;
|
||||
employee_name: string;
|
||||
overdue_price: string;
|
||||
order_items: {
|
||||
id: number;
|
||||
product: number;
|
||||
quantity: number;
|
||||
total_price: string;
|
||||
}[];
|
||||
file: string;
|
||||
|
||||
monthlyData: {
|
||||
[key: string]: {
|
||||
amount: number;
|
||||
locked: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,81 +1,35 @@
|
||||
"use client";
|
||||
|
||||
import formatDate from "@/shared/lib/formatDate";
|
||||
import { order_api } from "@/features/specification/lib/api";
|
||||
import { formatPrice } from "@/shared/lib/formatPrice";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import { DashboardLayout } from "@/widgets/dashboard-layout/ui";
|
||||
import { Building2, Calendar, User } from "lucide-react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Building2, User } from "lucide-react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
interface MedicineItem {
|
||||
id: number;
|
||||
name: string;
|
||||
price: number;
|
||||
quantity: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
interface SpecificationHistory {
|
||||
id: string;
|
||||
date: string;
|
||||
buyerName: string;
|
||||
pharmacy: string;
|
||||
priceType: "regular" | "special";
|
||||
paymentPercentage: number;
|
||||
medicines: MedicineItem[];
|
||||
totalAmount: number;
|
||||
paymentAmount: number;
|
||||
}
|
||||
|
||||
// Sample data
|
||||
const SAMPLE_HISTORY: SpecificationHistory[] = [
|
||||
{
|
||||
id: "1",
|
||||
date: "2024-11-15 14:30",
|
||||
buyerName: "Abdullayev Aziz",
|
||||
pharmacy: "Farmaciya #1",
|
||||
priceType: "special",
|
||||
paymentPercentage: 100,
|
||||
medicines: [
|
||||
{ id: 1, name: "Aspirin", price: 3500, quantity: 2, total: 7000 },
|
||||
{ id: 2, name: "Paracetamol", price: 5500, quantity: 3, total: 16500 },
|
||||
],
|
||||
totalAmount: 23500,
|
||||
paymentAmount: 23500,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
date: "2024-11-14 10:15",
|
||||
buyerName: "Karimova Malika",
|
||||
pharmacy: "Farmaciya #2",
|
||||
priceType: "regular",
|
||||
paymentPercentage: 50,
|
||||
medicines: [
|
||||
{ id: 3, name: "Ibuprofen", price: 10000, quantity: 1, total: 10000 },
|
||||
{ id: 4, name: "Amoxicillin", price: 15000, quantity: 2, total: 30000 },
|
||||
{ id: 5, name: "Metformin", price: 20000, quantity: 1, total: 20000 },
|
||||
],
|
||||
totalAmount: 60000,
|
||||
paymentAmount: 30000,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
date: "2024-11-13 16:45",
|
||||
buyerName: "Tursunov Bobur",
|
||||
pharmacy: "Farmaciya #3",
|
||||
priceType: "special",
|
||||
paymentPercentage: 75,
|
||||
medicines: [
|
||||
{ id: 6, name: "Lisinopril", price: 8500, quantity: 4, total: 34000 },
|
||||
],
|
||||
totalAmount: 34000,
|
||||
paymentAmount: 25500,
|
||||
},
|
||||
];
|
||||
|
||||
export function DetailViewPage() {
|
||||
const { id } = useParams();
|
||||
|
||||
const item = SAMPLE_HISTORY.find((h) => h.id === id);
|
||||
const { data } = useQuery({
|
||||
queryKey: ["order_list"],
|
||||
queryFn: () => order_api.order_list(),
|
||||
select(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
});
|
||||
|
||||
const { data: product } = useQuery({
|
||||
queryKey: ["product_list"],
|
||||
queryFn: () => order_api.product_list(),
|
||||
select(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
});
|
||||
|
||||
const item = data && data.find((h) => h.id === Number(id));
|
||||
|
||||
const handleDownloadPDF = async () => {};
|
||||
|
||||
if (!item) return <div>{"Ma'lumot topilmadi"}</div>;
|
||||
|
||||
@@ -92,22 +46,24 @@ export function DetailViewPage() {
|
||||
|
||||
{/* Info Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 p-6 bg-gray-50">
|
||||
<div className="bg-white p-4 rounded-lg shadow-sm">
|
||||
{/* <div className="bg-white p-4 rounded-lg shadow-sm">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<Calendar className="text-blue-600" size={20} />
|
||||
<span className="text-sm text-gray-600">Sana</span>
|
||||
</div>
|
||||
<p className="font-semibold text-gray-900">
|
||||
{formatDate.format(item.date, "DD-MM-YYYY")}
|
||||
{formatDate.format(item., "DD-MM-YYYY")}
|
||||
</p>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div className="bg-white p-4 rounded-lg shadow-sm">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<User className="text-green-600" size={20} />
|
||||
<span className="text-sm text-gray-600">Xaridor ismi</span>
|
||||
</div>
|
||||
<p className="font-semibold text-gray-900">{item.buyerName}</p>
|
||||
<p className="font-semibold text-gray-900">
|
||||
{item.employee_name}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-4 rounded-lg shadow-sm">
|
||||
@@ -117,7 +73,9 @@ export function DetailViewPage() {
|
||||
Farmasevtikaga tegishli
|
||||
</span>
|
||||
</div>
|
||||
<p className="font-semibold text-gray-900">{item.pharmacy}</p>
|
||||
<p className="font-semibold text-gray-900">
|
||||
{item.factory.name}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -143,22 +101,28 @@ export function DetailViewPage() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
{item.medicines.map((medicine) => (
|
||||
{item.order_items.map((medicine) => {
|
||||
const pro =
|
||||
product &&
|
||||
product.find((e) => medicine.product === e.id);
|
||||
|
||||
return (
|
||||
<tr key={medicine.id} className="hover:bg-gray-50">
|
||||
<td className="px-4 py-3 text-sm text-gray-900 font-medium">
|
||||
{medicine.name}
|
||||
{pro && pro.name}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-gray-700 text-right">
|
||||
{formatPrice(medicine.price)}
|
||||
{pro && formatPrice(pro.price)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-gray-700 text-right">
|
||||
{medicine.quantity}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-gray-900 font-semibold text-right">
|
||||
{formatPrice(medicine.total)}
|
||||
{formatPrice(medicine.total_price)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -170,22 +134,24 @@ export function DetailViewPage() {
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-700">Jami summa:</span>
|
||||
<span className="text-xl font-bold text-gray-900">
|
||||
{formatPrice(item.totalAmount)}
|
||||
{formatPrice(item.total_price)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center pt-3 border-t-2 border-gray-200">
|
||||
<span className="text-gray-700">
|
||||
{"To'langan"} ({item.paymentPercentage}%):
|
||||
{"To'langan"} ({item.advance}%):
|
||||
</span>
|
||||
<span className="text-2xl font-bold text-green-600">
|
||||
{formatPrice(item.paymentAmount)}
|
||||
{formatPrice(item.paid_price)}
|
||||
</span>
|
||||
</div>
|
||||
{item.paymentPercentage < 100 && (
|
||||
{item.advance < 100 && (
|
||||
<div className="flex justify-between items-center text-sm pt-2">
|
||||
<span className="text-gray-600">Qoldiq:</span>
|
||||
<span className="font-semibold text-red-600">
|
||||
{formatPrice(item.totalAmount - item.paymentAmount)}
|
||||
{formatPrice(
|
||||
Number(item.total_price) - Number(item.paid_price),
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -193,6 +159,13 @@ export function DetailViewPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleDownloadPDF}
|
||||
className="w-full h-14 mt-5 text-md bg-green-600 hover:bg-green-700 text-white"
|
||||
size="lg"
|
||||
>
|
||||
Yuklab olish
|
||||
</Button>
|
||||
</div>
|
||||
</DashboardLayout>
|
||||
);
|
||||
|
||||
@@ -1,16 +1,48 @@
|
||||
"use client";
|
||||
|
||||
import { order_api } from "@/features/specification/lib/api";
|
||||
import { formatPrice } from "@/shared/lib/formatPrice";
|
||||
import AddedButton from "@/shared/ui/added-button";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import { DashboardLayout } from "@/widgets/dashboard-layout/ui";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Eye } from "lucide-react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { SAMPLE_HISTORY } from "../lib/mock";
|
||||
|
||||
export function HistoryListPage() {
|
||||
const router = useNavigate();
|
||||
|
||||
const { data, isLoading, isError } = useQuery({
|
||||
queryKey: ["order_list"],
|
||||
queryFn: () => order_api.order_list(),
|
||||
select(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<div className="min-h-screen flex justify-center items-center">
|
||||
<p className="text-gray-500 text-lg">Yuklanmoqda...</p>
|
||||
</div>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<div className="min-h-screen flex flex-col justify-center items-center">
|
||||
<p className="text-red-600 text-lg mb-2">Xatolik yuz berdi</p>
|
||||
<Button onClick={() => window.location.reload()}>
|
||||
Qayta yuklash
|
||||
</Button>
|
||||
</div>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
@@ -29,8 +61,8 @@ export function HistoryListPage() {
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{SAMPLE_HISTORY.length > 0 ? (
|
||||
SAMPLE_HISTORY.map((item) => (
|
||||
{data && data.length > 0 ? (
|
||||
data.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="bg-white border rounded-xl p-5 shadow-sm hover:shadow-md transition"
|
||||
@@ -40,27 +72,24 @@ export function HistoryListPage() {
|
||||
<p className="text-lg font-semibold text-gray-900">
|
||||
Buyurtma №{item.id}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
Sana: {item.date}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4">
|
||||
<div>
|
||||
<p className="text-gray-500 text-sm">Mijoz</p>
|
||||
<p className="font-medium">{item.buyerName}</p>
|
||||
<p className="font-medium">{item.employee_name}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-gray-500 text-sm">Farmasevtika</p>
|
||||
<p className="font-medium">{item.pharmacy}</p>
|
||||
<p className="font-medium">{item.factory.name}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-gray-500 text-sm">Hisoblangan narxi</p>
|
||||
<p className="font-medium">
|
||||
{formatPrice(item.totalAmount)}
|
||||
{formatPrice(item.total_price)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -68,7 +97,7 @@ export function HistoryListPage() {
|
||||
<p className="text-gray-500 text-sm">
|
||||
{"To'langan foizi"}
|
||||
</p>
|
||||
<p className="font-medium">{item.paymentPercentage}%</p>
|
||||
<p className="font-medium">{item.advance}%</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -76,7 +105,7 @@ export function HistoryListPage() {
|
||||
{"To'langan narxi"}
|
||||
</p>
|
||||
<p className="font-medium">
|
||||
{formatPrice(item.paymentAmount)}
|
||||
{formatPrice(item.paid_price)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
"use client";
|
||||
|
||||
import type { ProductListData } from "@/features/specification/lib/data";
|
||||
import { LanguageRoutes } from "@/shared/config/i18n/types";
|
||||
import { formatPrice } from "@/shared/lib/formatPrice";
|
||||
import { Input } from "@/shared/ui/input";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
interface Medicine {
|
||||
id: number;
|
||||
name: string;
|
||||
regularPrice: number;
|
||||
specialPrice: number;
|
||||
quantity: number;
|
||||
}
|
||||
type MedicineItem = ProductListData & { quantity: number };
|
||||
|
||||
interface MedicineRowProps {
|
||||
medicine: Medicine;
|
||||
medicine: MedicineItem;
|
||||
onQuantityChange: (id: number, quantity: number) => void;
|
||||
}
|
||||
|
||||
export function MedicineRow({ medicine, onQuantityChange }: MedicineRowProps) {
|
||||
const { locale } = useParams();
|
||||
const total = medicine.regularPrice * medicine.quantity;
|
||||
const total = Number(medicine.price) * medicine.quantity;
|
||||
|
||||
const handleQuantityChange = (value: string) => {
|
||||
const numValue = parseInt(value, 10) || 0;
|
||||
@@ -33,8 +28,7 @@ export function MedicineRow({ medicine, onQuantityChange }: MedicineRowProps) {
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-foreground">{medicine.name}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Narxi:{" "}
|
||||
{formatPrice(medicine.regularPrice, locale as LanguageRoutes, true)}
|
||||
Narxi: {formatPrice(medicine.price, locale as LanguageRoutes, true)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { order_api } from "@/features/specification/lib/api";
|
||||
import type {
|
||||
OrderCreateReq,
|
||||
ProductListData,
|
||||
} from "@/features/specification/lib/data";
|
||||
import type { LanguageRoutes } from "@/shared/config/i18n/types";
|
||||
import { formatPrice } from "@/shared/lib/formatPrice";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
@@ -13,40 +18,100 @@ import {
|
||||
SelectValue,
|
||||
} from "@/shared/ui/select";
|
||||
import { DashboardLayout } from "@/widgets/dashboard-layout/ui";
|
||||
import { useState } from "react";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import type { AxiosError } from "axios";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import { MedicineRow } from "./MedicineRow";
|
||||
import { PaymentPercentage } from "./PaymentPercentageProps";
|
||||
|
||||
// Sample medicines data
|
||||
const MEDICINES_DATA = [
|
||||
{ id: 1, name: "Aspirin", regularPrice: 5000, specialPrice: 3500 },
|
||||
{ id: 2, name: "Paracetamol", regularPrice: 8000, specialPrice: 5500 },
|
||||
{ id: 3, name: "Ibuprofen", regularPrice: 10000, specialPrice: 7000 },
|
||||
{ id: 4, name: "Amoxicillin", regularPrice: 15000, specialPrice: 10500 },
|
||||
{ id: 5, name: "Metformin", regularPrice: 20000, specialPrice: 14000 },
|
||||
{ id: 6, name: "Lisinopril", regularPrice: 12000, specialPrice: 8500 },
|
||||
];
|
||||
|
||||
const PHARMACIES = [
|
||||
"Farmasevtika #1",
|
||||
"Farmasevtika #2",
|
||||
"Farmasevtika #3",
|
||||
"Farmasevtika #4",
|
||||
];
|
||||
|
||||
interface MedicineItem {
|
||||
id: number;
|
||||
name: string;
|
||||
regularPrice: number;
|
||||
specialPrice: number;
|
||||
quantity: number;
|
||||
}
|
||||
type MedicineItem = ProductListData & { quantity: number };
|
||||
|
||||
export function SpecificationPage() {
|
||||
const navigate = useNavigate();
|
||||
const queryClient = useQueryClient();
|
||||
const { data: product } = useQuery({
|
||||
queryKey: ["product_list"],
|
||||
queryFn: () => order_api.product_list(),
|
||||
select(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
});
|
||||
const { data } = useQuery({
|
||||
queryKey: ["factory_list"],
|
||||
queryFn: () => order_api.factory_list(),
|
||||
select(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
});
|
||||
|
||||
// const { data: pharmacy } = useQuery({
|
||||
// queryKey: ["pharmacy_list"],
|
||||
// queryFn: () => pharmacy_api.list(),
|
||||
// select(data) {
|
||||
// return data.data.data;
|
||||
// },
|
||||
// });
|
||||
|
||||
const { mutate, isPending } = useMutation({
|
||||
mutationFn: (body: OrderCreateReq) => order_api.order_create(body),
|
||||
onSuccess: () => {
|
||||
navigate("/specification");
|
||||
queryClient.refetchQueries({ queryKey: ["order_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[];
|
||||
};
|
||||
};
|
||||
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
const [medicines, setMedicines] = useState<MedicineItem[]>(
|
||||
MEDICINES_DATA.map((m) => ({ ...m, quantity: 0 })),
|
||||
product?.map((m) => ({ ...m, quantity: 0 })) || [],
|
||||
);
|
||||
const [selectedPharmacy, setSelectedPharmacy] = useState(PHARMACIES[0]);
|
||||
|
||||
const [selectedPharmacy, setSelectedPharmacy] = useState(
|
||||
data ? data[0].id : "",
|
||||
);
|
||||
|
||||
// const [selectedPharm, setSelectedPharm] = useState(
|
||||
// pharmacy ? pharmacy[0].id : "",
|
||||
// );
|
||||
|
||||
useEffect(() => {
|
||||
if (product) {
|
||||
setMedicines(product?.map((m) => ({ ...m, quantity: 0 })));
|
||||
}
|
||||
if (data) {
|
||||
setSelectedPharmacy(data[0].id);
|
||||
}
|
||||
// if (pharmacy) {
|
||||
// setSelectedPharm(pharmacy[0].id);
|
||||
// }
|
||||
}, [product, data]);
|
||||
|
||||
const [buyerName, setBuyerName] = useState("");
|
||||
const [paymentPercentage, setPaymentPercentage] = useState(100);
|
||||
|
||||
@@ -58,8 +123,8 @@ export function SpecificationPage() {
|
||||
);
|
||||
};
|
||||
|
||||
const getPrice = (medicine: MedicineItem) => {
|
||||
return medicine.regularPrice;
|
||||
const getPrice = (medicine: ProductListData) => {
|
||||
return Number(medicine.price);
|
||||
};
|
||||
|
||||
const calculateTotal = () => {
|
||||
@@ -80,16 +145,21 @@ export function SpecificationPage() {
|
||||
pharmacy: selectedPharmacy,
|
||||
paymentPercentage,
|
||||
medicines: selectedMedicines.map((m) => ({
|
||||
id: m.id,
|
||||
name: m.name,
|
||||
price: getPrice(m),
|
||||
product: m.id,
|
||||
quantity: m.quantity,
|
||||
total: getPrice(m) * m.quantity,
|
||||
total_price: String(Number(m.price) * m.quantity),
|
||||
})),
|
||||
totalAmount: calculateTotal(),
|
||||
paymentAmount: calculatePaymentAmount(),
|
||||
};
|
||||
console.log("PDF Data:", data);
|
||||
mutate({
|
||||
employee_name: data.buyerName,
|
||||
factory_id: Number(data.pharmacy),
|
||||
items: data.medicines,
|
||||
paid_price: String(data.paymentAmount),
|
||||
total_price: String(data.totalAmount),
|
||||
advance: data.paymentPercentage,
|
||||
});
|
||||
};
|
||||
|
||||
const hasSelectedMedicines = medicines.some((m) => m.quantity > 0);
|
||||
@@ -128,22 +198,49 @@ export function SpecificationPage() {
|
||||
<Card className="p-3 shadow-sm gap-2">
|
||||
<h3 className="font-semibold text-foreground">Farmasevtika</h3>
|
||||
<Select
|
||||
value={selectedPharmacy}
|
||||
value={String(selectedPharmacy)}
|
||||
onValueChange={setSelectedPharmacy}
|
||||
>
|
||||
<SelectTrigger className="w-full h-12">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{PHARMACIES.map((pharmacy) => (
|
||||
<SelectItem key={pharmacy} value={pharmacy}>
|
||||
{pharmacy}
|
||||
{data &&
|
||||
data.map((pharmacy) => (
|
||||
<SelectItem
|
||||
key={pharmacy.id}
|
||||
value={String(pharmacy.id)}
|
||||
>
|
||||
{pharmacy.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</Card>
|
||||
|
||||
{/* <Card className="p-3 shadow-sm gap-2">
|
||||
<h3 className="font-semibold text-foreground">Dorixa</h3>
|
||||
<Select
|
||||
value={String(selectedPharm)}
|
||||
onValueChange={setSelectedPharm}
|
||||
>
|
||||
<SelectTrigger className="w-full h-12">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{pharmacy &&
|
||||
pharmacy.map((pharmacy) => (
|
||||
<SelectItem
|
||||
key={pharmacy.id}
|
||||
value={String(pharmacy.id)}
|
||||
>
|
||||
{pharmacy.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</Card> */}
|
||||
|
||||
{/* Buyer name */}
|
||||
<Card className="p-3 shadow-sm gap-2">
|
||||
<h3 className="font-semibold text-foreground">Xaridor Nomi</h3>
|
||||
@@ -204,7 +301,11 @@ export function SpecificationPage() {
|
||||
className="w-full h-14 text-md bg-green-600 hover:bg-green-700 text-white"
|
||||
size="lg"
|
||||
>
|
||||
PDF ga yuklab olish
|
||||
{isPending ? (
|
||||
<Loader2 className="animate-spin" />
|
||||
) : (
|
||||
"Saqlash va yuklab olish"
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
import type { TourItem } from "@/features/tour-plan/lib/types";
|
||||
import httpClient from "@/shared/config/api/httpClient";
|
||||
import { TOUR_PLAN } from "@/shared/config/api/URLs";
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
export const tour_plan_api = {
|
||||
async list() {
|
||||
const res = await httpClient.get(TOUR_PLAN);
|
||||
async list(): Promise<AxiosResponse<TourItem>> {
|
||||
const res = await httpClient.get(`${TOUR_PLAN}list/`);
|
||||
return res;
|
||||
},
|
||||
|
||||
async send_location({
|
||||
id,
|
||||
body,
|
||||
}: {
|
||||
body: {
|
||||
longitude: number;
|
||||
latitude: number;
|
||||
};
|
||||
id: number;
|
||||
}): Promise<AxiosResponse<TourItem>> {
|
||||
const res = await httpClient.patch(`${TOUR_PLAN}${id}/update/`, body);
|
||||
return res;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type { TourItem } from "@/features/tour-plan/lib/types";
|
||||
import type { TourItemData } from "@/features/tour-plan/lib/types";
|
||||
import formatDate from "@/shared/lib/formatDate";
|
||||
import type { ColumnDef } from "@tanstack/react-table";
|
||||
import { Send } from "lucide-react";
|
||||
@@ -9,9 +9,9 @@ export const getColumns = ({
|
||||
sendLocation,
|
||||
canSend,
|
||||
}: {
|
||||
sendLocation: (tour: TourItem) => void;
|
||||
sendLocation: (tour: TourItemData) => void;
|
||||
canSend: (date: string) => boolean;
|
||||
}): ColumnDef<TourItem>[] => [
|
||||
}): ColumnDef<TourItemData>[] => [
|
||||
{
|
||||
accessorKey: "date",
|
||||
header: "Sana",
|
||||
@@ -24,6 +24,9 @@ export const getColumns = ({
|
||||
{
|
||||
accessorKey: "district",
|
||||
header: "Tuman",
|
||||
cell: ({ row }) => (
|
||||
<div className="text-center">{row.original.place_name}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
@@ -35,9 +38,13 @@ export const getColumns = ({
|
||||
<div className="flex gap-3 items-center justify-center">
|
||||
<Send
|
||||
className={`cursor-pointer ${
|
||||
canSend(tour.date) ? "text-green-600" : "text-gray-400"
|
||||
canSend(tour.date) && !tour.location_send
|
||||
? "text-green-600"
|
||||
: "text-gray-400"
|
||||
}`}
|
||||
onClick={() => canSend(tour.date) && sendLocation(tour)}
|
||||
onClick={() =>
|
||||
canSend(tour.date) && !tour.location_send && sendLocation(tour)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type { TourItem } from "@/features/tour-plan/lib/types";
|
||||
import type { TourItemData } from "@/features/tour-plan/lib/types";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -16,12 +16,11 @@ import {
|
||||
type ColumnDef,
|
||||
} from "@tanstack/react-table";
|
||||
|
||||
interface DataTableProps<TourItem> {
|
||||
columns: ColumnDef<TourItem>[];
|
||||
data: TourItem[];
|
||||
interface DataTableProps<TourItemData> {
|
||||
columns: ColumnDef<TourItemData>[];
|
||||
data: TourItemData[];
|
||||
}
|
||||
|
||||
export function DataTable({ columns, data }: DataTableProps<TourItem>) {
|
||||
export function DataTable({ columns, data }: DataTableProps<TourItemData>) {
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
export interface TourItem {
|
||||
export interface TourItemData {
|
||||
id: number;
|
||||
place_name: string;
|
||||
longitude: number;
|
||||
latitude: number;
|
||||
location_send: boolean;
|
||||
created_at: string;
|
||||
date: string;
|
||||
district: string;
|
||||
}
|
||||
|
||||
export const mockTourData: TourItem[] = [
|
||||
{ id: 1, date: "2025-11-01", district: "Yunusobod tumani" },
|
||||
{ id: 2, date: "2025-11-15", district: "Mirzo Ulug'bek tumani" },
|
||||
{ id: 3, date: "2025-11-20", district: "Chilonzor tumani" },
|
||||
];
|
||||
export interface TourItem {
|
||||
status_code: number;
|
||||
status: string;
|
||||
message: string;
|
||||
data: TourItemData[];
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { DataTable } from "@/features/doctor/lib/data-table";
|
||||
import type { TourItem } from "@/features/tour-plan/lib/types";
|
||||
import { tour_plan_api } from "@/features/tour-plan/lib/api";
|
||||
import { DataTable } from "@/features/tour-plan/lib/data-table";
|
||||
import type { TourItemData } from "@/features/tour-plan/lib/types";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -11,42 +12,89 @@ import {
|
||||
SelectValue,
|
||||
} from "@/shared/ui/select";
|
||||
import { DashboardLayout } from "@/widgets/dashboard-layout/ui";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import type { AxiosError } from "axios";
|
||||
import { Loader2 } from "lucide-react"; // 🔥 spinner
|
||||
import { useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { getColumns } from "../lib/column";
|
||||
|
||||
const mockTourData: TourItem[] = [
|
||||
{
|
||||
id: 1,
|
||||
date: "2025-11-01",
|
||||
district: "Yunusobod tumani",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: "2025-11-15",
|
||||
district: "Mirzo Ulug'bek tumani",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: "2025-11-20",
|
||||
district: "Chilonzor tumani",
|
||||
},
|
||||
];
|
||||
|
||||
export default function TourPlan() {
|
||||
const currentYear = new Date().getFullYear().toString();
|
||||
const currentMonth = (new Date().getMonth() + 1).toString().padStart(2, "0");
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { data, isLoading, isError } = useQuery({
|
||||
queryKey: ["tour_plan_list"],
|
||||
queryFn: () => tour_plan_api.list(),
|
||||
select(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
});
|
||||
|
||||
const { mutate } = useMutation({
|
||||
mutationFn: ({
|
||||
id,
|
||||
body,
|
||||
}: {
|
||||
body: {
|
||||
longitude: number;
|
||||
latitude: number;
|
||||
};
|
||||
id: number;
|
||||
}) => tour_plan_api.send_location({ body, id }),
|
||||
onSuccess: () => {
|
||||
toast.success("Lokatsiya jo'natildi");
|
||||
queryClient.refetchQueries({ queryKey: ["tour_plan_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[];
|
||||
};
|
||||
};
|
||||
|
||||
const message =
|
||||
Array.isArray(errorName.data?.name) && errorName.data.name.length
|
||||
? errorName.data.name[0]
|
||||
: data?.message ||
|
||||
(Array.isArray(errorData?.messages) && errorData.messages.length
|
||||
? errorData.messages[0].message
|
||||
: undefined) ||
|
||||
"Xatolik yuz berdi";
|
||||
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
const [year, setYear] = useState(currentYear);
|
||||
const [month, setMonth] = useState(currentMonth);
|
||||
const sendLocation = () => {
|
||||
|
||||
const sendLocation = (tour: TourItemData) => {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(pos) => {
|
||||
console.log("📌 Lokatsiya olindi:");
|
||||
console.log("Latitude:", pos.coords.latitude);
|
||||
console.log("Longitude:", pos.coords.longitude);
|
||||
const latitude = pos.coords.latitude;
|
||||
const longitude = pos.coords.longitude;
|
||||
|
||||
mutate({
|
||||
id: tour.id,
|
||||
body: {
|
||||
latitude,
|
||||
longitude,
|
||||
},
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
console.error("Lokatsiya olishda xatolik:", err);
|
||||
toast.error("Lokatsiya olishga ruxsat berilmadi!");
|
||||
},
|
||||
{
|
||||
enableHighAccuracy: true,
|
||||
@@ -57,17 +105,25 @@ export default function TourPlan() {
|
||||
};
|
||||
|
||||
const canSend = (date: string) => {
|
||||
return new Date(date) >= new Date();
|
||||
const d = new Date(date);
|
||||
const today = new Date();
|
||||
|
||||
d.setHours(0, 0, 0, 0);
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
return d.getTime() >= today.getTime();
|
||||
};
|
||||
|
||||
const columnsProps = getColumns({
|
||||
sendLocation: sendLocation,
|
||||
canSend: canSend,
|
||||
sendLocation,
|
||||
canSend,
|
||||
});
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
return mockTourData.filter((item) => {
|
||||
const d = new Date(item.date);
|
||||
if (!data) return [];
|
||||
|
||||
return data.filter((item) => {
|
||||
const d = new Date(item.date + "T00:00:00");
|
||||
|
||||
const itemYear = d.getFullYear().toString();
|
||||
const itemMonth = (d.getMonth() + 1).toString().padStart(2, "0");
|
||||
@@ -77,18 +133,19 @@ export default function TourPlan() {
|
||||
|
||||
return yearMatch && monthMatch;
|
||||
});
|
||||
}, [year, month]);
|
||||
}, [data, year, month]);
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<div className="space-y-6">
|
||||
<h1 className="text-3xl font-bold">Tur Plan</h1>
|
||||
|
||||
{/* 🔹 FILTER UI */}
|
||||
{/* FILTERS */}
|
||||
<div className="flex gap-2 justify-end items-end">
|
||||
{/* Year */}
|
||||
<Select onValueChange={(e) => setYear(e)} value={year}>
|
||||
<SelectTrigger className="w-fit !h-10">
|
||||
<SelectValue />
|
||||
<SelectTrigger className="w-fit h-10">
|
||||
<SelectValue placeholder="Yil" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
@@ -101,9 +158,10 @@ export default function TourPlan() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{/* Month */}
|
||||
<Select onValueChange={(e) => setMonth(e)} value={month}>
|
||||
<SelectTrigger className="w-fit !h-10">
|
||||
<SelectValue />
|
||||
<SelectTrigger className="w-fit h-10">
|
||||
<SelectValue placeholder="Oy" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
@@ -124,9 +182,26 @@ export default function TourPlan() {
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* LOADING UI */}
|
||||
{isLoading && (
|
||||
<div className="w-full flex justify-center py-10">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-primary" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ERROR UI */}
|
||||
{isError && (
|
||||
<div className="w-full flex justify-center py-10 text-red-500 font-semibold">
|
||||
Ma'lumotlarni yuklashda xatolik yuz berdi!
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TABLE */}
|
||||
{!isLoading && !isError && (
|
||||
<div className="overflow-x-auto">
|
||||
<DataTable columns={columnsProps} data={filteredData} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DashboardLayout>
|
||||
);
|
||||
|
||||
1
src/global.d.ts
vendored
1
src/global.d.ts
vendored
@@ -8,6 +8,7 @@ interface Window {
|
||||
last_name?: string;
|
||||
};
|
||||
};
|
||||
sendData?: (data: string) => void;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { SpecificationPage } from "@/features/specification/ui/Specification";
|
||||
import TokenLayout from "@/token-layaout";
|
||||
|
||||
const SpecificationAdded = () => {
|
||||
return <SpecificationPage />;
|
||||
return (
|
||||
<TokenLayout>
|
||||
<SpecificationPage />
|
||||
</TokenLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default SpecificationAdded;
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { DetailViewPage } from "@/features/specification/ui/DetailViewPage";
|
||||
import TokenLayout from "@/token-layaout";
|
||||
|
||||
const SpecificationDetail = () => {
|
||||
return <DetailViewPage />;
|
||||
return (
|
||||
<TokenLayout>
|
||||
<DetailViewPage />
|
||||
</TokenLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default SpecificationDetail;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { HistoryListPage } from "@/features/specification/ui/HistoryListPage";
|
||||
import TokenLayout from "@/token-layaout";
|
||||
|
||||
export default function Specification() {
|
||||
return <HistoryListPage />;
|
||||
return (
|
||||
<TokenLayout>
|
||||
<HistoryListPage />
|
||||
</TokenLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import TourPlan from "@/features/tour-plan/ui/TourPlan";
|
||||
import TokenLayout from "@/token-layaout";
|
||||
|
||||
export const TourPlanPage = () => {
|
||||
return <TourPlan />;
|
||||
return (
|
||||
<TokenLayout>
|
||||
<TourPlan />
|
||||
</TokenLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default TourPlanPage;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import PlanTour from "@/features/plan-tour/ui/PlanTour";
|
||||
import TokenLayout from "@/token-layaout";
|
||||
|
||||
export const TypePlan = () => {
|
||||
return <PlanTour />;
|
||||
return (
|
||||
<TokenLayout>
|
||||
<PlanTour />
|
||||
</TokenLayout>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,18 +10,24 @@ const OBJECT = "/api/v1/shared/place/";
|
||||
const DOCTOR = "/api/v1/shared/doctor/";
|
||||
const PHARMACY = "/api/v1/shared/pharmacy/";
|
||||
const LOCATION = "/api/v1/shared/location/";
|
||||
const TOUR_PLAN = "/api/v1/shared/tour_plan/list";
|
||||
const TOUR_PLAN = "/api/v1/shared/tour_plan/";
|
||||
const FACTORY = "/api/v1/shared/factory/list/";
|
||||
const PRODUCT = "/api/v1/orders/product/list/";
|
||||
const ORDER = "/api/v1/orders/order/";
|
||||
|
||||
export {
|
||||
BASE_URL,
|
||||
CREATE_USER,
|
||||
DISCTRICT,
|
||||
DOCTOR,
|
||||
FACTORY,
|
||||
LOCATION,
|
||||
LOGIN_USER,
|
||||
OBJECT,
|
||||
ORDER,
|
||||
PHARMACY,
|
||||
PLANS,
|
||||
PRODUCT,
|
||||
REGIONS,
|
||||
TOUR_PLAN,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user