responsive task manager

This commit is contained in:
hamid zarghami
2026-06-20 09:21:13 +03:30
parent 19d60a9225
commit f836abd8f4
8 changed files with 80 additions and 71 deletions
@@ -3,7 +3,7 @@ import { type FC } from "react";
const AddNewColumn: FC = () => { const AddNewColumn: FC = () => {
return ( return (
<button type="button" className="bg-[#F0F3F7BF] bg-opacity-75 rounded-full px-6 py-4 flex items-center gap-2 shrink-0 self-start cursor-pointer"> <button type="button" className="bg-[#F0F3F7BF] bg-opacity-75 rounded-full px-4 sm:px-6 py-3 sm:py-4 flex items-center gap-2 shrink-0 self-start cursor-pointer w-[272px] sm:w-[288px] xl:w-[310px] justify-center">
<AddCircle size={16} color="black" /> <AddCircle size={16} color="black" />
<span className="text-xs text-black">اضافه کردن ستون جدید</span> <span className="text-xs text-black">اضافه کردن ستون جدید</span>
</button> </button>
+6 -8
View File
@@ -46,7 +46,7 @@ const Column: FC<Props> = ({
return ( return (
<div <div
className={`bg-[#F0F3F7] rounded-xl p-6 max-w-[310px] w-full flex flex-col self-start transition-colors ${ className={`bg-[#F0F3F7] rounded-xl p-3 sm:p-4 xl:p-6 w-[272px] sm:w-[288px] xl:w-[310px] shrink-0 flex flex-col max-h-[calc(100dvh-140px)] sm:max-h-[calc(100dvh-152px)] xl:max-h-[calc(100dvh-220px)] transition-colors ${
isDragOver ? "ring-2 ring-primary/40" : "" isDragOver ? "ring-2 ring-primary/40" : ""
}`} }`}
onDragOver={handleDragOver} onDragOver={handleDragOver}
@@ -54,12 +54,11 @@ const Column: FC<Props> = ({
onDrop={(e) => handleDrop(e, tasks.length)} onDrop={(e) => handleDrop(e, tasks.length)}
> >
<div className="flex justify-between items-center shrink-0"> <div className="flex justify-between items-center shrink-0">
<div className="font-bold text-sm">{column.title}</div> <div className="font-bold text-sm truncate">{column.title}</div>
<More size={20} color="black" /> <More size={20} color="black" className="shrink-0" />
</div> </div>
{tasks.length > 0 && ( <div className="mt-3 sm:mt-4 xl:mt-5 flex-1 min-h-0 overflow-y-auto overscroll-y-contain flex flex-col gap-3 sm:gap-4">
<div className="mt-5 flex flex-col gap-4">
{tasks.map((task, index) => ( {tasks.map((task, index) => (
<div <div
key={task.id} key={task.id}
@@ -79,10 +78,9 @@ const Column: FC<Props> = ({
</div> </div>
))} ))}
</div> </div>
)}
{isAdding ? ( {isAdding ? (
<div className="mt-5 shrink-0 flex flex-col gap-3"> <div className="mt-3 sm:mt-4 xl:mt-5 shrink-0 flex flex-col gap-3">
<input <input
type="text" type="text"
placeholder="عنوان تسک" placeholder="عنوان تسک"
@@ -111,7 +109,7 @@ const Column: FC<Props> = ({
<button <button
type="button" type="button"
onClick={() => setIsAdding(true)} onClick={() => setIsAdding(true)}
className="mt-5 flex gap-2 items-center shrink-0 cursor-pointer" className="mt-3 sm:mt-4 xl:mt-5 flex gap-2 items-center shrink-0 cursor-pointer"
> >
<AddCircle size={18} color="#888888" /> <AddCircle size={18} color="#888888" />
<span className="text-description text-[13px] mt-0.5">اضافه کردن تسک</span> <span className="text-description text-[13px] mt-0.5">اضافه کردن تسک</span>
@@ -2,19 +2,19 @@ import { InfoCircle, Setting2, UserAdd } from "iconsax-react";
const HeaderWorkspace = () => { const HeaderWorkspace = () => {
return ( return (
<div className="flex justify-between items-center bg-primary h-[102px] px-8"> <div className="flex justify-between items-center bg-primary h-14 sm:h-16 xl:h-[102px] px-3 sm:px-6 xl:px-8 shrink-0 gap-3">
<div className="flex gap-4 items-center"> <div className="flex gap-2 sm:gap-4 items-center min-w-0">
<div className="text-white text-xl">طراحی Dpage</div> <div className="text-white text-base sm:text-lg xl:text-xl truncate">طراحی Dpage</div>
<InfoCircle size={20} color="white" /> <InfoCircle size={18} color="white" />
</div> </div>
<div className="flex gap-4 items-center"> <div className="flex gap-2 sm:gap-4 items-center shrink-0">
<div className="flex gap-1 items-center px-2 rounded-lg bg-white h-8"> <div className="flex gap-1 items-center px-2 sm:px-3 rounded-lg bg-white h-8">
<UserAdd size={18} color="black" /> <UserAdd size={18} color="black" />
<div className="text-black text-xs">اشتراک گذاری</div> <div className="text-black text-xs hidden sm:block">اشتراک گذاری</div>
</div> </div>
<Setting2 size={20} color="white" /> <Setting2 size={18} color="white" />
</div> </div>
</div> </div>
); );
+6 -6
View File
@@ -41,17 +41,17 @@ const Task: FC<Props> = ({ task, isDragging, onDragStart, onDragEnd, onClick })
{task.tag} {task.tag}
</div> </div>
<div className="font-bold text-xs mt-2">{task.title}</div> <div className="font-bold text-xs mt-2">{task.title}</div>
<div className="mt-2 flex items-center gap-4 text-xs"> <div className="mt-2 flex flex-wrap items-center gap-x-3 gap-y-1.5 text-xs">
<div className="flex items-center gap-1 text-[10px]"> <div className="flex items-center gap-1 text-[10px] min-w-0">
<Calendar size={16} color="#292D32" /> <Calendar size={16} color="#292D32" className="shrink-0" />
<div>{task.dateRange}</div> <div className="truncate">{task.dateRange}</div>
</div> </div>
<div className="flex gap-1"> <div className="flex gap-1 shrink-0">
<AttachCircle size={16} color="#292D32" /> <AttachCircle size={16} color="#292D32" />
<div>{task.attachments}</div> <div>{task.attachments}</div>
</div> </div>
<div className="flex gap-1"> <div className="flex gap-1 shrink-0">
<TickSquare size={16} color="#292D32" /> <TickSquare size={16} color="#292D32" />
<div> <div>
{task.checklistDone}/{task.checklistTotal} {task.checklistDone}/{task.checklistTotal}
@@ -71,7 +71,7 @@ const TaskDetailActionBar: FC<Props> = ({ activeTab, onTabChange, labelsPopover,
className={`flex items-center gap-1.5 border rounded-lg px-3 py-2 text-[11px] cursor-pointer transition-colors ${isActive ? "bg-[#4A4A4A] text-white border-transparent" : "bg-white/50 text-[#292D32] border-white"}`} className={`flex items-center gap-1.5 border rounded-lg px-3 py-2 text-[11px] cursor-pointer transition-colors ${isActive ? "bg-[#4A4A4A] text-white border-transparent" : "bg-white/50 text-[#292D32] border-white"}`}
> >
<Icon size={16} color={isActive ? "#FFFFFF" : "#292D32"} /> <Icon size={16} color={isActive ? "#FFFFFF" : "#292D32"} />
<span>{t(labelKey)}</span> <span className="hidden sm:inline">{t(labelKey)}</span>
</button> </button>
</div> </div>
); );
@@ -19,8 +19,8 @@ const TaskDetailMetadataPreview: FC<Props> = ({ selectedLabels, selectedUsers, s
const dateLabel = formatDateRange(startDate, endDate); const dateLabel = formatDateRange(startDate, endDate);
return ( return (
<div className="mt-7 flex gap-10"> <div className="mt-5 sm:mt-7 grid grid-cols-[auto_auto] gap-x-6 gap-y-4 sm:flex sm:flex-wrap sm:gap-x-10 sm:gap-y-4">
<div> <div className="min-w-0">
<div className="text-xs font-medium mb-2">{t("taskmanager.task_detail.labels")}</div> <div className="text-xs font-medium mb-2">{t("taskmanager.task_detail.labels")}</div>
<div className="flex items-center gap-2 flex-wrap"> <div className="flex items-center gap-2 flex-wrap">
{selectedLabels.map((label) => ( {selectedLabels.map((label) => (
@@ -34,7 +34,7 @@ const TaskDetailMetadataPreview: FC<Props> = ({ selectedLabels, selectedUsers, s
))} ))}
<button <button
type="button" type="button"
className="size-7 rounded-lg border border-[#D0D0D0] bg-white/60 flex items-center justify-center cursor-pointer" className="size-7 rounded-lg border border-[#D0D0D0] bg-white/60 flex items-center justify-center cursor-pointer shrink-0"
aria-label={t("taskmanager.task_detail.new_label")} aria-label={t("taskmanager.task_detail.new_label")}
> >
<Add <Add
@@ -45,13 +45,13 @@ const TaskDetailMetadataPreview: FC<Props> = ({ selectedLabels, selectedUsers, s
</div> </div>
</div> </div>
<div> <div className="min-w-0">
<div className="text-xs font-medium mb-2">{t("taskmanager.users")}</div> <div className="text-xs font-medium mb-2">{t("taskmanager.users")}</div>
<div className="flex items-center gap-2 flex-wrap"> <div className="flex items-center gap-2 flex-wrap">
{selectedUsers.map((user) => ( {selectedUsers.map((user) => (
<div <div
key={user.id} key={user.id}
className="size-8 rounded-full bg-[#D0D0D0] overflow-hidden" className="size-8 rounded-full bg-[#D0D0D0] overflow-hidden shrink-0"
> >
<img <img
src={user.avatar ?? AvatarImage} src={user.avatar ?? AvatarImage}
@@ -62,7 +62,7 @@ const TaskDetailMetadataPreview: FC<Props> = ({ selectedLabels, selectedUsers, s
))} ))}
<button <button
type="button" type="button"
className="size-7 rounded-full border border-[#D0D0D0] bg-white/60 flex items-center justify-center cursor-pointer" className="size-7 rounded-full border border-[#D0D0D0] bg-white/60 flex items-center justify-center cursor-pointer shrink-0"
aria-label={t("taskmanager.users")} aria-label={t("taskmanager.users")}
> >
<Add <Add
@@ -73,9 +73,13 @@ const TaskDetailMetadataPreview: FC<Props> = ({ selectedLabels, selectedUsers, s
</div> </div>
</div> </div>
<div> <div className="col-span-2 sm:col-span-1 min-w-0">
<div className="text-xs font-medium mb-2">{t("taskmanager.task_detail.date")}</div> <div className="text-xs font-medium mb-2">{t("taskmanager.task_detail.date")}</div>
{dateLabel ? <span className="inline-flex items-center h-7 px-3 rounded-lg text-xs font-medium bg-[#E8E8E8] text-[#292D32]">{dateLabel}</span> : null} {dateLabel ? (
<span className="inline-flex items-center min-h-7 px-3 py-1 rounded-lg text-xs font-medium bg-[#E8E8E8] text-[#292D32] whitespace-nowrap">
{dateLabel}
</span>
) : null}
</div> </div>
</div> </div>
); );
+4 -2
View File
@@ -41,9 +41,10 @@ const Workspace: FC = () => {
: null; : null;
return ( return (
<div className="bg-[#01347A] h-full rounded-[32px] overflow-hidden flex flex-col"> <div className="bg-[#01347A] h-full rounded-none xl:rounded-[32px] overflow-hidden flex flex-col">
<HeaderWorkspace /> <HeaderWorkspace />
<div className="px-8 flex gap-7 mt-6 flex-1 min-h-0 overflow-auto pb-6 items-start"> <div className="flex-1 min-h-0 overflow-x-auto overflow-y-hidden overscroll-x-contain">
<div className="flex gap-3 sm:gap-4 xl:gap-7 px-3 sm:px-4 xl:px-8 pt-4 xl:pt-6 pb-4 xl:pb-6 h-full items-start w-max min-w-full">
{columns.map((column) => ( {columns.map((column) => (
<Column <Column
key={column.id} key={column.id}
@@ -59,6 +60,7 @@ const Workspace: FC = () => {
<AddNewColumn /> <AddNewColumn />
</div> </div>
</div>
<TaskDetailModal <TaskDetailModal
open={selectedTask !== null} open={selectedTask !== null}
+10 -5
View File
@@ -106,12 +106,17 @@ const MainRouter: FC = () => {
const isWorkspace = pathname.startsWith(Pages.taskmanager.workspace); const isWorkspace = pathname.startsWith(Pages.taskmanager.workspace);
return ( return (
<div className="p-4 overflow-hidden"> <div className={clx("overflow-hidden", isWorkspace ? "p-0 xl:p-4" : "p-4")}>
{!isWorkspace && <SideBar />} {!isWorkspace && <SideBar />}
<Header /> <Header />
<div className={clx("flex-1 mt-[68px] xl:mt-[81px]", !isWorkspace && "xl:ms-[269px]", !isWorkspace && hasSubMenu && "xl:ms-[305px]")}> <div className={clx("flex-1 mt-[68px] xl:mt-[81px]", !isWorkspace && "xl:ms-[269px]", !isWorkspace && hasSubMenu && "xl:ms-[305px]")}>
<div className={clx(`overflow-auto w-[${window.innerWidth}] h-[calc(100vh-113px)] flex flex-col`)}> <div
<div className="flex-1 min-h-0"> className={clx(
"flex flex-col",
isWorkspace ? "h-[calc(100dvh-68px)] xl:h-[calc(100dvh-97px)] overflow-hidden" : "overflow-auto h-[calc(100vh-113px)]",
)}
>
<div className={clx("flex-1 min-h-0", isWorkspace && "overflow-hidden")}>
<Routes> <Routes>
<Route path={Pages.dashboard} element={<Home />} /> <Route path={Pages.dashboard} element={<Home />} />
<Route path={Pages.services.mine} element={<MyServices />} /> <Route path={Pages.services.mine} element={<MyServices />} />
@@ -215,10 +220,10 @@ const MainRouter: FC = () => {
<Route path={Pages.taskmanager.projectList} element={<ProjectList />} /> <Route path={Pages.taskmanager.projectList} element={<ProjectList />} />
</Routes> </Routes>
</div> </div>
<div className="h-20 shrink-0 xl:hidden" /> {!isWorkspace && <div className="h-20 shrink-0 xl:hidden" />}
</div> </div>
</div> </div>
<Footer /> {!isWorkspace && <Footer />}
</div> </div>
); );
}; };