Refactor Card component to introduce TaskButton for modal functionality and enhance layout with dynamic task data display.

This commit is contained in:
IluaAir
2025-12-06 23:54:32 +03:00
parent cb73cd219b
commit 461e9a8105
2 changed files with 127 additions and 14 deletions

View File

@@ -5,7 +5,7 @@ import { AuthLayout } from './layouts/AuthLayout'
import { Routes, Route, Navigate } from 'react-router'
import Dashboard from './pages/Dashboard'
import RootRedirect from './pages/RootRedirect'
import CardComponent from './components/Card'
import { TaskButton } from './components/Card'
function App() {
@@ -24,7 +24,7 @@ function App() {
<Dashboard />
} />
<Route path="/card" element={
<CardComponent />
<TaskButton />
} />
<Route path="*" element={<Navigate to="/auth/login" replace />} />
</Routes>

View File

@@ -1,21 +1,134 @@
import { useState } from 'react'
import styles from '@/lib/styles'
import { cn } from '@/lib/utils'
import { X } from 'lucide-react'
export function TaskButton({ taskData }) {
const [isModalOpen, setIsModalOpen] = useState(false)
const openModal = () => setIsModalOpen(true)
const closeModal = () => setIsModalOpen(false)
export default function CardComponent() {
return (
<div className={cn(styles.card.filled, "flex flex-col sm:w-1/2 h-[400px] overflow-hidden")}>
<>
<div
key={taskData?.id || 1}
className={cn(
styles.card.task,
"w-[110px] cursor-pointer hover:opacity-80 transition-opacity"
)}
onClick={openModal}
>
{taskData?.title || "Sample task card"}
</div>
{isModalOpen && (
<div
className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 p-4"
onClick={closeModal}
>
<div
className="relative w-fit h-fit"
onClick={(e) => e.stopPropagation()}
>
{/* Кнопка закрытия */}
<button
onClick={closeModal}
className="absolute -top-4 -right-4 z-10 bg-red-500 hover:bg-red-600 text-white rounded-full p-2 transition-colors"
aria-label="Close modal"
>
<X size={20} />
</button>
<CardComponent
status={taskData?.status}
time_spent={taskData?.time_spent}
description={taskData?.description}
title={taskData?.title}
priority={taskData?.priority}
due_date={taskData?.due_date}
/>
</div>
</div>
)}
</>
)
}
export function CardComponent({
status = "todo",
time_spent = 0,
description = "No description",
title = "Task",
priority = "medium",
due_date = null
}) {
const formatTimeSpent = (minutes) => {
if (!minutes || minutes === 0) return "0 min";
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
if (hours > 0) {
return `${hours}h ${mins > 0 ? `${mins}m` : ''}`;
}
return `${mins}m`;
};
const formatDate = (dateString) => {
if (!dateString) return "No due date";
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
});
};
const priorityBadgeStyle = {
low: styles.badge.low,
medium: styles.badge.medium,
high: styles.badge.high,
critical: styles.badge.critical,
};
const statusLabels = {
open: "Open",
closed: "Closed",
in_progress: "In Progress",
todo: "To Do"
};
return (
<div className={cn(styles.card.filled, "flex flex-col w-full min-w-[400px] max-w-[600px] h-[400px] overflow-hidden")}>
<div className="flex items-end flex-1 -mt-6 -mx-6 bg-center bg-cover bg-[url('/images/a.jpg')]">
<h1 className={cn(styles.text.h1, "pl-6")}>Card</h1>
<h1 className={cn(styles.text.h1, "pl-6")}>{title}</h1>
</div>
<div className="flex-1 flex flex-col gap-2 z-10 mt-6">
<div className="flex items-center gap-2">
<span className={cn(styles.badge.low)}>Low</span>
<span className={cn(styles.badge.medium)}>Medium</span>
<span className={cn(styles.badge.high)}>High</span>
<span className={cn(styles.badge.critical)}>Critical</span>
<div className="flex-1 flex flex-col gap-3 z-10 mt-6">
<div className="flex items-center gap-2 flex-wrap">
<span className={cn(priorityBadgeStyle[priority])}>
{priority.charAt(0).toUpperCase() + priority.slice(1)}
</span>
<span className={cn(styles.text.smallMuted)}>
Status: {statusLabels[status]}
</span>
</div>
<p className={cn(styles.text.smallMuted, "flex-2 line-clamp-2")}>Card description</p>
<button className={cn(styles.button.primaryNeon, "w-[110px]")}><span className={styles.text.neonBlue}>Complete</span></button>
<div className="flex items-center gap-4 text-sm">
<span className={cn(styles.text.smallMuted)}>
{formatTimeSpent(time_spent)}
</span>
<span className={cn(styles.text.smallMuted)}>
📅 {formatDate(due_date)}
</span>
</div>
<p className={cn(styles.text.smallMuted, "flex-1 line-clamp-3 overflow-auto")}>
{description}
</p>
<button className={cn(styles.button.primaryNeon, "w-[110px]")}>
<span className={styles.text.neonBlue}>Complete</span>
</button>
</div>
</div>
)