Files
meridyn-bot/src/features/plan/ui/plans.tsx
Samandar Turgunboyev 19d86d619b task update
2026-01-26 16:40:55 +05:00

304 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { plans_api } from "@/features/plan/lib/api";
import type { Task } from "@/features/plan/lib/data";
import { groupByDate } from "@/features/plan/lib/filteDareHelper";
import { AddPlans } from "@/features/plan/ui/addPlans";
import { PlanDetailsDialog } from "@/features/plan/ui/PlanDetailsDialogProps";
import formatDate from "@/shared/lib/formatDate";
import { Alert, AlertDescription, AlertTitle } from "@/shared/ui/alert";
import { Button } from "@/shared/ui/button";
import { Calendar } from "@/shared/ui/calendar";
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/ui/card";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/shared/ui/dialog";
import { Skeleton } from "@/shared/ui/skeleton";
import { DashboardLayout } from "@/widgets/dashboard-layout/ui";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import type { AxiosError } from "axios";
import clsx from "clsx";
import { CalendarIcon, Loader2, Pencil, TriangleAlert } from "lucide-react";
import { useState } from "react";
import { toast } from "sonner";
export default function Plans() {
const [taskEdit, setTaskEdit] = useState<Task | null>(null);
const queryClient = useQueryClient();
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
const [tempDate, setTempDate] = useState<Date | null>(null);
const { data, isLoading, isError } = useQuery({
queryKey: ["my_plans", selectedDate],
queryFn: () =>
plans_api.getMyPloans({
date: selectedDate ? formatDate.format(selectedDate, "YYYY-MM-DD") : "",
}),
});
const { mutate, isPending } = useMutation({
mutationFn: (id: number) => plans_api.delete(id),
onSuccess: () => {
toast.success("Reja ochirildi");
queryClient.refetchQueries({ queryKey: ["my_plans", selectedDate] });
setDeleteDialogOpen(false);
},
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 [isAddDialogOpen, setIsAddDialogOpen] = useState(false);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [isPlanDetailsOpen, setIsPlanDetailsOpen] = useState(false);
const [isDateDialogOpen, setIsDateDialogOpen] = useState(false);
const [newTask, setNewTask] = useState({
title: "",
description: "",
date: new Date(),
});
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
const openTaskDetails = (task: Task) => {
setSelectedTask(task);
setIsPlanDetailsOpen(true);
};
// const handleDeleteTask = (task: Task) => {
// setSelectedTask(task);
// setDeleteDialogOpen(true);
// };
const grouped = groupByDate(data?.data.data || []);
return (
<DashboardLayout link="/">
<div className="space-y-4">
<div className="flex items-center justify-between">
<h1 className="text-3xl font-bold text-foreground">Kunlik Reja</h1>
<Dialog open={isDateDialogOpen} onOpenChange={setIsDateDialogOpen}>
<DialogTrigger asChild>
<Button variant="outline" className="bg-transparent font-normal">
<CalendarIcon className="mr-2 h-4 w-4" />
{selectedDate
? formatDate.format(selectedDate, "dd.MM.yyyy")
: "Barchasi"}
</Button>
</DialogTrigger>
<DialogContent className="w-auto p-4">
<Calendar
mode="single"
selected={tempDate ?? selectedDate ?? undefined}
onSelect={(date) => setTempDate(date ?? null)}
initialFocus
/>
<div className="mt-4 flex justify-end gap-2">
<Button
variant="outline"
onClick={() => {
setIsDateDialogOpen(false);
setTempDate(null);
setSelectedDate(null);
}}
>
Bekor qilish
</Button>
<Button
onClick={() => {
if (tempDate) setSelectedDate(tempDate);
setIsDateDialogOpen(false);
}}
>
Tanlash
</Button>
</div>
</DialogContent>
</Dialog>
</div>
{isLoading && (
<div className="space-y-4">
{[1, 2, 3].map((i) => (
<Card key={i} className="px-0 py-3 gap-0">
<CardHeader className="px-3 py-0">
<Skeleton className="h-5 w-32" />
</CardHeader>
<CardContent className="space-y-3 px-2 py-0">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-3/4" />
<div className="grid grid-cols-2 gap-2">
<Skeleton className="h-10 w-full" />
<Skeleton className="h-10 w-full" />
</div>
</CardContent>
</Card>
))}
</div>
)}
{isError && (
<Alert variant="destructive">
<TriangleAlert className="h-4 w-4" />
<AlertTitle>Xatolik!</AlertTitle>
<AlertDescription>
Ma'lumotlarni yuklashda muammo yuz berdi. Qayta urinib koring.
</AlertDescription>
</Alert>
)}
{!isLoading &&
!isError &&
data &&
Object.keys(grouped).length === 0 && (
<Alert className="mt-4">
<AlertTitle>Ma'lumot yoq</AlertTitle>
<AlertDescription>
Tanlangan sana boyicha reja topilmadi.
</AlertDescription>
</Alert>
)}
{data &&
Object.entries(grouped).map(([date, items]) => (
<Card key={date} className="px-0 py-3 gap-0">
<CardHeader className="px-3 py-0">
<CardTitle className="text-lg">
{formatDate.format(new Date(date), "DD.MM.YYYY")}
</CardTitle>
</CardHeader>
<CardContent className="space-y-2 px-2 py-0">
{items.map((item) => (
<div
key={item.id}
onClick={() => openTaskDetails(item)}
className="flex flex-col gap-3 p-3 rounded-md border bg-white group hover:bg-gray-50 transition-colors"
>
<div className="flex gap-3">
<div className="flex-1 flex flex-col">
{/* <h3
className={clsx(
"font-semibold wrap-break-word",
item.comment ? "text-green-500" : "text-foreground",
)}
>
{item.title}
</h3> */}
<h3
className={clsx(
"font-semibold wrap-break-word",
item.comment ? "text-green-500" : "text-foreground",
)}
>
{item.description}
</h3>
</div>
</div>
<div className="grid grid-cols-1 gap-2">
<Button
size="sm"
variant="outline"
onClick={(e) => {
e.stopPropagation();
setTaskEdit(item);
setIsAddDialogOpen(true);
}}
className="p-4"
disabled={item.comment ? true : false}
>
<Pencil className="h-4 w-4" />
<p>Tahrirlash</p>
</Button>
{/* <Button
size="sm"
variant="destructive"
onClick={(e) => {
e.stopPropagation();
handleDeleteTask(item);
}}
className="p-4"
disabled={item.is_done}
>
<Trash className="h-4 w-4" />
<p>{"O'chirish"}</p>
</Button> */}
</div>
</div>
))}
</CardContent>
</Card>
))}
<AddPlans
isDialogOpen={isAddDialogOpen}
setIsDialogOpen={setIsAddDialogOpen}
newTask={newTask}
setNewTask={setNewTask}
setTaskEdit={setTaskEdit}
taskEdit={taskEdit}
/>
<PlanDetailsDialog
isOpen={isPlanDetailsOpen}
onOpenChange={setIsPlanDetailsOpen}
task={selectedTask}
/>
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Rejani ochirish</DialogTitle>
</DialogHeader>
<p>
Siz haqiqatdan ham <strong>{selectedTask?.title}</strong> rejani
ochirmoqchimisiz?
</p>
<DialogFooter className="flex justify-end gap-2">
<Button
variant="outline"
onClick={() => setDeleteDialogOpen(false)}
>
Bekor qilish
</Button>
<Button
variant="destructive"
disabled={isPending}
onClick={() => selectedTask && mutate(selectedTask.id)}
>
{isPending ? <Loader2 className="animate-spin" /> : "O'chirish"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</DashboardLayout>
);
}