bug fix loading

This commit is contained in:
Samandar Turgunboyev
2025-11-11 16:05:22 +05:00
parent 8a4618b454
commit 64f8467f41
11 changed files with 271 additions and 155 deletions

View File

@@ -41,7 +41,7 @@ export default function TourAgenciesPage() {
queryFn: () => getAllAgency({ page: currentPage, page_size: itemsPerPage }), queryFn: () => getAllAgency({ page: currentPage, page_size: itemsPerPage }),
}); });
const { mutate } = useMutation({ const { mutate, isPending } = useMutation({
mutationFn: ({ id }: { id: number }) => { mutationFn: ({ id }: { id: number }) => {
return deleteAgency({ id }); return deleteAgency({ id });
}, },
@@ -293,7 +293,11 @@ export default function TourAgenciesPage() {
onClick={() => handleDelete(agency.id)} onClick={() => handleDelete(agency.id)}
className="bg-red-600 hover:bg-red-700 text-white" className="bg-red-600 hover:bg-red-700 text-white"
> >
{t("O'chirish")} {isPending ? (
<Loader2 className="animate-spin" />
) : (
t("O'chirish")
)}
</Button> </Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>

View File

@@ -8,6 +8,7 @@ import {
updateAgencyStatus, updateAgencyStatus,
} from "@/pages/agencies/lib/api"; } from "@/pages/agencies/lib/api";
import { AgencyUsersSection } from "@/pages/agencies/ui/AgencyUsersSection"; import { AgencyUsersSection } from "@/pages/agencies/ui/AgencyUsersSection";
import { createTourAdmin } from "@/pages/support/lib/api";
import formatPhone from "@/shared/lib/formatPhone"; import formatPhone from "@/shared/lib/formatPhone";
import formatPrice from "@/shared/lib/formatPrice"; import formatPrice from "@/shared/lib/formatPrice";
import { Badge } from "@/shared/ui/badge"; import { Badge } from "@/shared/ui/badge";
@@ -50,6 +51,7 @@ export default function AgencyDetailPage() {
const router = useNavigate(); const router = useNavigate();
const { t } = useTranslation(); const { t } = useTranslation();
const [edit, setEdit] = useState<boolean>(false); const [edit, setEdit] = useState<boolean>(false);
const [added, setAdded] = useState<boolean>(false);
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [openUser, setOpenUser] = useState<boolean>(false); const [openUser, setOpenUser] = useState<boolean>(false);
const [user, setUser] = useState<{ const [user, setUser] = useState<{
@@ -105,6 +107,22 @@ export default function AgencyDetailPage() {
}, },
}); });
const createAdmin = useMutation({
mutationFn: (id: number) => createTourAdmin(id),
onSuccess: (res) => {
queryClient.refetchQueries({ queryKey: ["agency_user"] });
setOpenUser(true);
setUser(res.data);
setAdded(false);
},
onError: () => {
toast.error(t("Xatolik yuz berdi"), {
richColors: true,
position: "top-center",
});
},
});
const deleteUserMutation = useMutation({ const deleteUserMutation = useMutation({
mutationFn: (userId: number) => agencyUserDelete(userId), mutationFn: (userId: number) => agencyUserDelete(userId),
onSuccess: () => { onSuccess: () => {
@@ -126,6 +144,10 @@ export default function AgencyDetailPage() {
updateUserMutation.mutate(user); updateUserMutation.mutate(user);
}; };
const handleAddeUser = () => {
createAdmin.mutate(Number(params.id!));
};
const handleDeleteUser = (userId: number) => { const handleDeleteUser = (userId: number) => {
deleteUserMutation.mutate(userId); deleteUserMutation.mutate(userId);
}; };
@@ -409,8 +431,14 @@ export default function AgencyDetailPage() {
{agency?.data.data && ( {agency?.data.data && (
<div className="mb-8"> <div className="mb-8">
<AgencyUsersSection <AgencyUsersSection
agencyId={Number(params.id)}
added={added}
setAdded={setAdded}
edit={edit} edit={edit}
handleAddeUser={handleAddeUser}
setEdit={setEdit} setEdit={setEdit}
isPending={updateUserMutation.isPending}
createAdminPending={createAdmin.isPending}
users={ users={
Array.isArray(agency?.data.data) Array.isArray(agency?.data.data)
? agency?.data.data ? agency?.data.data
@@ -419,6 +447,7 @@ export default function AgencyDetailPage() {
onEdit={handleEditUser} onEdit={handleEditUser}
onDelete={handleDeleteUser} onDelete={handleDeleteUser}
isLoading={isLoadingUsers} isLoading={isLoadingUsers}
deletePending={deleteUserMutation.isPending}
/> />
</div> </div>
)} )}

View File

@@ -13,7 +13,15 @@ import {
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
} from "@/shared/ui/dialog"; } from "@/shared/ui/dialog";
import { Pencil, Phone, Shield, Trash2, User } from "lucide-react"; import {
Loader2,
Pencil,
Phone,
Shield,
Trash2,
User,
UserPlus,
} from "lucide-react";
import { type Dispatch, type SetStateAction } from "react"; import { type Dispatch, type SetStateAction } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -27,22 +35,36 @@ interface AgencyUser {
interface AgencyUsersProps { interface AgencyUsersProps {
users: AgencyUser[]; users: AgencyUser[];
agencyId: number;
onEdit: (userId: number) => void; onEdit: (userId: number) => void;
handleAddeUser: () => void;
onDelete: (userId: number) => void; onDelete: (userId: number) => void;
isLoading?: boolean; isLoading?: boolean;
isPending: boolean;
createAdminPending: boolean;
edit: boolean; edit: boolean;
added: boolean;
deletePending: boolean;
setEdit: Dispatch<SetStateAction<boolean>>; setEdit: Dispatch<SetStateAction<boolean>>;
setAdded: Dispatch<SetStateAction<boolean>>;
} }
export function AgencyUsersSection({ export function AgencyUsersSection({
users, users,
onEdit, onEdit,
setAdded,
added,
edit, edit,
setEdit, setEdit,
handleAddeUser,
createAdminPending,
onDelete, onDelete,
isLoading = false, isLoading = false,
deletePending,
isPending = false,
}: AgencyUsersProps) { }: AgencyUsersProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const getRoleBadge = (role: string) => { const getRoleBadge = (role: string) => {
const roleColors: Record<string, string> = { const roleColors: Record<string, string> = {
admin: "bg-purple-500/20 text-purple-300 border-purple-500/40", admin: "bg-purple-500/20 text-purple-300 border-purple-500/40",
@@ -75,160 +97,197 @@ export function AgencyUsersSection({
); );
} }
if (!users || users.length === 0) { return (
return ( <Card className="border border-gray-700 shadow-lg bg-gray-800">
<Card className="border border-gray-700 shadow-lg bg-gray-800"> <CardHeader className="flex justify-between items-center">
<CardHeader> <div>
<CardTitle className="text-2xl text-white flex items-center gap-2"> <CardTitle className="text-2xl text-white flex items-center gap-2">
<User className="w-6 h-6" /> <User className="w-6 h-6" />
{t("Agentlik foydalanuvchilari")} {t("Agentlik foydalanuvchilari")}
</CardTitle> </CardTitle>
</CardHeader> <p className="text-gray-400">
<CardContent> {t("Agentlik bilan bog'langan foydalanuvchilar ro'yxati")}
</p>
</div>
<Dialog open={added} onOpenChange={setAdded}>
<DialogTrigger asChild>
<Button
variant="outline"
size="icon"
className="border-gray-600 bg-gray-700 hover:bg-gray-600 text-blue-400 hover:text-blue-300"
>
<UserPlus className="w-4 h-4" onClick={() => setAdded(true)} />
</Button>
</DialogTrigger>
<DialogContent className="bg-gray-800 border-gray-700 text-white">
<DialogHeader>
<DialogTitle>{t("Foydalanuvchi qo'shish")}</DialogTitle>
<DialogDescription className="text-gray-400">
{t("Siz agentlikga yangi foydalanuvchi qo'shmoqchimisiz")}
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button
className="border-gray-600 bg-gray-700 hover:bg-gray-600 text-white"
onClick={() => setAdded(false)}
>
{t("Bekor qilish")}
</Button>
<Button
onClick={() => handleAddeUser()}
className="bg-blue-600 hover:bg-blue-700 text-white"
>
{createAdminPending ? (
<Loader2 className="animate-spin" />
) : (
t("Qo'shish")
)}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</CardHeader>
<CardContent>
{users.length === 0 ? (
<div className="text-center text-gray-400 py-8"> <div className="text-center text-gray-400 py-8">
{t("Hozircha foydalanuvchilar yo'q")} {t("Hozircha foydalanuvchilar yo'q")}
</div> </div>
</CardContent> ) : (
</Card> <div className="space-y-3">
); {users.map((user) => (
} <div
key={user.id}
return ( className="p-5 border border-gray-700 rounded-xl bg-gray-800 hover:bg-gray-750 transition-all"
<Card className="border border-gray-700 shadow-lg bg-gray-800"> >
<CardHeader> <div className="flex items-center justify-between">
<CardTitle className="text-2xl text-white flex items-center gap-2"> <div className="flex items-center gap-4 flex-1">
<User className="w-6 h-6" /> <div className="w-12 h-12 rounded-full bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center">
{t("Agentlik foydalanuvchilari")} <User className="w-6 h-6 text-white" />
</CardTitle>
<p className="text-gray-400">
{t("Agentlik bilan bog'langan foydalanuvchilar ro'yxati")}
</p>
</CardHeader>
<CardContent>
<div className="space-y-3">
{users.map((user) => (
<div
key={user.id}
className="p-5 border border-gray-700 rounded-xl bg-gray-800 hover:bg-gray-750 transition-all"
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-4 flex-1">
<div className="w-12 h-12 rounded-full bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center">
<User className="w-6 h-6 text-white" />
</div>
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h3 className="text-lg font-semibold text-white">
{user.first_name} {user.last_name}
</h3>
{getRoleBadge(user.role)}
</div> </div>
<div className="flex flex-wrap items-center gap-4 text-sm"> <div className="flex-1">
<div className="flex items-center gap-2 text-gray-400"> <div className="flex items-center gap-3 mb-2">
<Phone className="w-4 h-4" /> <h3 className="text-lg font-semibold text-white">
<span>{formatPhone(user.phone)}</span> {user.first_name} {user.last_name}
</h3>
{getRoleBadge(user.role)}
</div> </div>
<div className="flex items-center gap-2 text-gray-400">
<Shield className="w-4 h-4" /> <div className="flex flex-wrap items-center gap-4 text-sm">
<span>ID: {user.id}</span> <div className="flex items-center gap-2 text-gray-400">
<Phone className="w-4 h-4" />
<span>{formatPhone(user.phone)}</span>
</div>
<div className="flex items-center gap-2 text-gray-400">
<Shield className="w-4 h-4" />
<span>ID: {user.id}</span>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
{/* Actions */} <div className="flex items-center gap-2">
<div className="flex items-center gap-2"> {/* ✏️ Edit */}
{/* Edit Confirm Dialog */} <Dialog open={edit} onOpenChange={setEdit}>
<Dialog open={edit} onOpenChange={setEdit}> <DialogTrigger asChild>
<DialogTrigger asChild> <Button
<Button variant="outline"
variant="outline" size="icon"
size="icon" className="border-gray-600 bg-gray-700 hover:bg-gray-600 text-blue-400 hover:text-blue-300"
className="border-gray-600 bg-gray-700 hover:bg-gray-600 text-blue-400 hover:text-blue-300" >
> <Pencil
<Pencil className="w-4 h-4"
className="w-4 h-4" onClick={() => setEdit(true)}
onClick={() => setEdit(true)} />
/> </Button>
</Button> </DialogTrigger>
</DialogTrigger> <DialogContent className="bg-gray-800 border-gray-700 text-white">
<DialogContent className="bg-gray-800 border-gray-700 text-white"> <DialogHeader>
<DialogHeader> <DialogTitle>
<DialogTitle> {t("Foydalanuvchini tahrirlash")}
{t("Foydalanuvchini tahrirlash")} </DialogTitle>
</DialogTitle> <DialogDescription className="text-gray-400">
<DialogDescription className="text-gray-400"> {t("Haqiqatan ham")}{" "}
{t("Haqiqatan ham")}{" "} <span className="font-semibold text-white">
<span className="font-semibold text-white"> {user.first_name} {user.last_name}
{user.first_name} {user.last_name} </span>{" "}
</span>{" "} {t("ni tahrirlamoqchimisiz?")}
{t("ni tahrirlamoqchimisiz?")} </DialogDescription>
</DialogDescription> </DialogHeader>
</DialogHeader>
<DialogFooter> <DialogFooter>
<Button <Button
className="border-gray-600 bg-gray-700 hover:bg-gray-600 text-white" className="border-gray-600 bg-gray-700 hover:bg-gray-600 text-white"
onClick={() => setEdit(true)} onClick={() => setEdit(false)}
> >
{t("Bekor qilish")} {t("Bekor qilish")}
</Button> </Button>
<Button <Button
onClick={() => onEdit(user.id)} onClick={() => onEdit(user.id)}
className="bg-blue-600 hover:bg-blue-700 text-white" className="bg-blue-600 hover:bg-blue-700 text-white"
> >
{t("Tahrirlash")} {isPending ? (
</Button> <Loader2 className="animate-spin" />
</DialogFooter> ) : (
</DialogContent> t("Tahrirlash")
</Dialog> )}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Delete Confirm Dialog */} {/* 🗑 Delete */}
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button
variant="outline"
size="icon"
className="border-gray-600 bg-gray-700 hover:bg-red-900/20 hover:border-red-500 text-red-400 hover:text-red-300"
>
<Trash2 className="w-4 h-4" />
</Button>
</DialogTrigger>
<DialogContent className="bg-gray-800 border-gray-700 text-white">
<DialogHeader>
<DialogTitle>
{t("Foydalanuvchini o'chirish")}
</DialogTitle>
<DialogDescription className="text-gray-400">
{t("Haqiqatan ham")}{" "}
<span className="font-semibold text-white">
{user.first_name} {user.last_name}
</span>{" "}
{t(
"ni o'chirmoqchimisiz? Bu amalni qaytarib bo'lmaydi.",
)}
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button className="border-gray-600 bg-gray-700 hover:bg-gray-600 text-white">
{t("Bekor qilish")}
</Button>
<Button <Button
onClick={() => onDelete(user.id)} variant="outline"
className="bg-red-600 hover:bg-red-700 text-white" size="icon"
className="border-gray-600 bg-gray-700 hover:bg-red-900/20 hover:border-red-500 text-red-400 hover:text-red-300"
> >
{t("O'chirish")} <Trash2 className="w-4 h-4" />
</Button> </Button>
</DialogFooter> </DialogTrigger>
</DialogContent> <DialogContent className="bg-gray-800 border-gray-700 text-white">
</Dialog> <DialogHeader>
<DialogTitle>
{t("Foydalanuvchini o'chirish")}
</DialogTitle>
<DialogDescription className="text-gray-400">
{t("Haqiqatan ham")}{" "}
<span className="font-semibold text-white">
{user.first_name} {user.last_name}
</span>{" "}
{t(
"ni o'chirmoqchimisiz? Bu amalni qaytarib bo'lmaydi.",
)}
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button className="border-gray-600 bg-gray-700 hover:bg-gray-600 text-white">
{t("Bekor qilish")}
</Button>
<Button
onClick={() => onDelete(user.id)}
className="bg-red-600 hover:bg-red-700 text-white"
>
{deletePending ? (
<Loader2 className="animate-spin" />
) : (
t("O'chirish")
)}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div> </div>
</div> </div>
</div> ))}
))} </div>
</div> )}
</CardContent> </CardContent>
</Card> </Card>
); );

View File

@@ -40,7 +40,7 @@ const formSchema = z.object({
addres: z.string().min(1, "Manzil kiritish shart"), addres: z.string().min(1, "Manzil kiritish shart"),
email: z.string().email("Email notogri"), email: z.string().email("Email notogri"),
phone: z.string().min(3, "Telefon raqami notogri"), phone: z.string().min(3, "Telefon raqami notogri"),
web_site: z.string().url("URL notogri"), web_site: z.string().min(1, "URL notogri"),
}); });
type FormData = z.infer<typeof formSchema>; type FormData = z.infer<typeof formSchema>;
@@ -63,12 +63,12 @@ const EditAgency = () => {
}); });
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { data, isPending } = useQuery({ const { data } = useQuery({
queryKey: ["detail_agency", params.id], queryKey: ["detail_agency", params.id],
queryFn: () => getDetailAgency({ id: Number(params.id) }), queryFn: () => getDetailAgency({ id: Number(params.id) }),
}); });
const { mutate } = useMutation({ const { mutate, isPending } = useMutation({
mutationFn: (body: { mutationFn: (body: {
status: "pending" | "approved" | "cancelled"; status: "pending" | "approved" | "cancelled";
custom_id?: string; custom_id?: string;

View File

@@ -95,7 +95,7 @@ const NewsCategory = () => {
setIsDialogOpen(true); setIsDialogOpen(true);
}; };
const { mutate: added } = useMutation({ const { mutate: added, isPending } = useMutation({
mutationFn: (body: { name: string; name_ru: string }) => mutationFn: (body: { name: string; name_ru: string }) =>
addNewsCategory(body), addNewsCategory(body),
onSuccess: () => { onSuccess: () => {
@@ -111,7 +111,7 @@ const NewsCategory = () => {
}, },
}); });
const { mutate: edit } = useMutation({ const { mutate: edit, isPending: editPending } = useMutation({
mutationFn: ({ mutationFn: ({
body, body,
id, id,
@@ -349,7 +349,11 @@ const NewsCategory = () => {
type="submit" type="submit"
className="bg-blue-600 px-5 py-5 hover:bg-blue-700 text-white mt-4 cursor-pointer" className="bg-blue-600 px-5 py-5 hover:bg-blue-700 text-white mt-4 cursor-pointer"
> >
{t("Saqlash")} {isPending || editPending ? (
<Loader2 className="animate-spin" />
) : (
t("Saqlash")
)}
</Button> </Button>
</div> </div>
</form> </form>

View File

@@ -16,7 +16,7 @@ import { Label } from "@/shared/ui/label";
import { Textarea } from "@/shared/ui/textarea"; import { Textarea } from "@/shared/ui/textarea";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { ImagePlus, PlusCircle, Trash2 } from "lucide-react"; import { ImagePlus, Loader2, PlusCircle, Trash2 } from "lucide-react";
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import { useFieldArray, useForm } from "react-hook-form"; import { useFieldArray, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -134,7 +134,7 @@ const StepTwo = ({
if (file) form.setValue(`sections.${index}.image`, file); if (file) form.setValue(`sections.${index}.image`, file);
}; };
const { mutate: added } = useMutation({ const { mutate: added, isPending } = useMutation({
mutationFn: (body: FormData) => addNews(body), mutationFn: (body: FormData) => addNews(body),
onSuccess: () => { onSuccess: () => {
queryClient.refetchQueries({ queryKey: ["all_news"] }); queryClient.refetchQueries({ queryKey: ["all_news"] });
@@ -150,7 +150,7 @@ const StepTwo = ({
}, },
}); });
const { mutate: update } = useMutation({ const { mutate: update, isPending: updatePending } = useMutation({
mutationFn: ({ body, id }: { id: number; body: FormData }) => mutationFn: ({ body, id }: { id: number; body: FormData }) =>
updateNews({ id, body }), updateNews({ id, body }),
onSuccess: () => { onSuccess: () => {
@@ -404,7 +404,11 @@ const StepTwo = ({
type="submit" type="submit"
className="mt-6 px-8 py-3 bg-blue-600 text-white rounded-md hover:bg-blue-700 cursor-pointer" className="mt-6 px-8 py-3 bg-blue-600 text-white rounded-md hover:bg-blue-700 cursor-pointer"
> >
{t("Saqlash")} {isPending || updatePending ? (
<Loader2 className="animate-spin" />
) : (
t("Saqlash")
)}
</Button> </Button>
</div> </div>
</form> </form>

View File

@@ -35,7 +35,7 @@ import { RadioGroup, RadioGroupItem } from "@/shared/ui/radio-group";
import { Textarea } from "@/shared/ui/textarea"; import { Textarea } from "@/shared/ui/textarea";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation, useQuery } from "@tanstack/react-query"; import { useMutation, useQuery } from "@tanstack/react-query";
import { ChevronDownIcon, SquareCheckBig, XIcon } from "lucide-react"; import { ChevronDownIcon, Loader2, SquareCheckBig, XIcon } from "lucide-react";
import { useEffect, useState, type Dispatch, type SetStateAction } from "react"; import { useEffect, useState, type Dispatch, type SetStateAction } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -231,7 +231,7 @@ const StepOne = ({
const selectedDate = watch("departureDateTime.date"); const selectedDate = watch("departureDateTime.date");
const selectedDateTravel = watch("travelDateTime.date"); const selectedDateTravel = watch("travelDateTime.date");
const { mutate: create } = useMutation({ const { mutate: create, isPending } = useMutation({
mutationFn: (body: FormData) => { mutationFn: (body: FormData) => {
return createTours({ body }); return createTours({ body });
}, },
@@ -2204,7 +2204,7 @@ const StepOne = ({
type="submit" type="submit"
className="mt-6 px-8 py-3 bg-blue-600 text-white rounded-md hover:bg-blue-600 cursor-pointer" className="mt-6 px-8 py-3 bg-blue-600 text-white rounded-md hover:bg-blue-600 cursor-pointer"
> >
{t("Saqlash")} {isPending ? <Loader2 className="animate-spin" /> : t("Saqlash")}
</Button> </Button>
</div> </div>
</form> </form>

View File

@@ -68,7 +68,7 @@ const Tours = ({ user }: { user: Role }) => {
getAllTours({ page: 1, page_size: 10, featured_tickets: true }), getAllTours({ page: 1, page_size: 10, featured_tickets: true }),
}); });
const { mutate } = useMutation({ const { mutate, isPending } = useMutation({
mutationFn: (id: number) => deleteTours({ id }), mutationFn: (id: number) => deleteTours({ id }),
onSuccess: () => { onSuccess: () => {
queryClient.refetchQueries({ queryKey: ["all_tours"] }); queryClient.refetchQueries({ queryKey: ["all_tours"] });
@@ -270,8 +270,14 @@ const Tours = ({ user }: { user: Role }) => {
variant="destructive" variant="destructive"
onClick={() => confirmDelete(deleteId!)} onClick={() => confirmDelete(deleteId!)}
> >
<Trash2 className="w-4 h-4 mr-2" /> {isPending ? (
{t("O'chirish")} <Loader2 className="animate-spin" />
) : (
<>
<Trash2 className="w-4 h-4 mr-2" />
{t("O'chirish")}
</>
)}
</Button> </Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>

View File

@@ -19,7 +19,7 @@ import {
FormMessage, FormMessage,
} from "@/shared/ui/form"; } from "@/shared/ui/form";
import { Input } from "@/shared/ui/input"; import { Input } from "@/shared/ui/input";
import { ArrowLeft, Mail, Phone, Save, User } from "lucide-react"; import { ArrowLeft, Loader2, Mail, Phone, Save, User } from "lucide-react";
import { useEffect } from "react"; import { useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
@@ -250,8 +250,14 @@ export default function EditUser() {
disabled={isPending} disabled={isPending}
className="flex-1 h-11 rounded-lg bg-gradient-to-r from-emerald-600 to-teal-700 hover:from-emerald-700 hover:to-teal-800 text-white font-medium" className="flex-1 h-11 rounded-lg bg-gradient-to-r from-emerald-600 to-teal-700 hover:from-emerald-700 hover:to-teal-800 text-white font-medium"
> >
<Save className="w-5 h-5 mr-2" /> {isPending ? (
{t("Yangilash")} <Loader2 className="animate-spin" />
) : (
<>
<Save className="w-5 h-5 mr-2" />
{t("Yangilash")}
</>
)}
</Button> </Button>
</div> </div>
</form> </form>

View File

@@ -8,6 +8,8 @@
"Foydalanuvchilar": "Пользователи", "Foydalanuvchilar": "Пользователи",
"Tur firmalar": "Турфирмы", "Tur firmalar": "Турфирмы",
"Xodimlar": "Сотрудники", "Xodimlar": "Сотрудники",
"Siz agentlikga yangi foydalanuvchi qo'shmoqchimisiz": "Вы хотите добавить нового пользователя в агентство",
"Foydalanuvchi qo'shish": "Добавить пользователя",
"Byudjet": "Бюджет", "Byudjet": "Бюджет",
"Turlar": "Туры", "Turlar": "Туры",
"Tur sozlamalari": "Настройки туров", "Tur sozlamalari": "Настройки туров",

View File

@@ -10,6 +10,8 @@
"Xodimlar": "Xodimlar", "Xodimlar": "Xodimlar",
"Byudjet": "Byudjet", "Byudjet": "Byudjet",
"Turlar": "Turlar", "Turlar": "Turlar",
"Siz agentlikga yangi foydalanuvchi qo'shmoqchimisiz": "Siz agentlikga yangi foydalanuvchi qo'shmoqchimisiz",
"Foydalanuvchi qo'shish": "Foydalanuvchi qo'shish",
"Tur sozlamalari": "Tur sozlamalari", "Tur sozlamalari": "Tur sozlamalari",
"Bronlar": "Bronlar", "Bronlar": "Bronlar",
"Yangiliklar": "Yangiliklar", "Yangiliklar": "Yangiliklar",