Skip to content

Commit

Permalink
[Feat]:Implement EditTaskModal component with enhanced task editing UI (
Browse files Browse the repository at this point in the history
#3238)

* feat:Implement EditTaskModal component with enhanced task editing UI and state management

* refact: coderabbitai[bot]

* refact: coderabbitai[bot]
  • Loading branch information
Innocent-Akim authored Nov 7, 2024
1 parent c3afbd1 commit 3740f52
Show file tree
Hide file tree
Showing 6 changed files with 350 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import { Button, Modal, statusColor } from "@/lib/components";
import { IoMdArrowDropdown } from "react-icons/io";
import { FaRegClock } from "react-icons/fa";
import { DatePickerFilter } from "./TimesheetFilterDate";
import { useState } from "react";
import { useTranslations } from "next-intl";
import { clsxm } from "@/app/utils";
import { Item, ManageOrMemberComponent, getNestedValue } from "@/lib/features/manual-time/manage-member-component";
import { useTeamTasks } from "@/app/hooks";
import { CustomSelect } from "@/lib/features";
import { statusTable } from "./TimesheetAction";

export interface IEditTaskModalProps {
isOpen: boolean;
closeModal: () => void;

}
export function EditTaskModal({ isOpen, closeModal }: IEditTaskModalProps) {
const { activeTeam } = useTeamTasks();
const t = useTranslations();
const [dateRange, setDateRange] = useState<{ from: Date | null }>({
from: new Date(),
});
const [endTime, setEndTime] = useState<string>('');
const [startTime, setStartTime] = useState<string>('');
const [isBillable, setIsBillable] = useState<boolean>(false);
const [notes, setNotes] = useState('');
const memberItemsLists = {
Project: activeTeam?.projects as [],
};
const handleSelectedValuesChange = (values: { [key: string]: Item | null }) => {
// Handle value changes
};
const selectedValues = {
Teams: null,
};

const handleChange = (field: string, selectedItem: Item | null) => {
// Handle field changes
};

const fields = [
{
label: 'Project',
placeholder: 'Select a project',
isRequired: true,
valueKey: 'id',
displayKey: 'name',
element: 'Project'
},
];

const handleFromChange = (fromDate: Date | null) => {
setDateRange((prev) => ({ ...prev, from: fromDate }));
};
return (
<Modal
closeModal={closeModal}
isOpen={isOpen}
showCloseIcon
title={'Edit Task'}
className="bg-light--theme-light dark:bg-dark--theme-light p-5 rounded-xl w-full md:w-40 md:min-w-[30rem] justify-start h-[auto]"
titleClass="font-bold">
<div className="flex flex-col w-full">
<div className="flex flex-col border-b border-b-slate-100 dark:border-b-gray-700">
<span> #321 Spike for creating calendar views on mobile</span>
<div className="flex items-center gap-x-1 ">
<span className="text-gray-400">for</span>
<span className="text-primary dark:text-primary-light">Savannah Nguyen </span>
<IoMdArrowDropdown className="cursor-pointer" />
</div>
</div>
<div className="flex items-start flex-col justify-center gap-4">
<div>
<span>Task Time</span>
<div className="flex items-center gap-x-2 ">
<FaRegClock className="text-[#30B366]" />
<span>08:10h</span>
</div>
</div>
<div className="flex items-center w-full">
<div className=" w-[48%] mr-[4%]">
<label className="block text-gray-500 mb-1">
{t('manualTime.START_TIME')}
<span className="text-[#de5505e1] ml-1">*</span>
</label>
<input
aria-label="Start time"
aria-describedby="start-time-error"
type="time"
value={startTime}
onChange={(e) => setStartTime(e.target.value)}
className="w-full p-1 border font-normal border-slate-300 dark:border-slate-600 dark:bg-dark--theme-light rounded-md"
required
/>
</div>

<div className=" w-[48%]">
<label className="block text-gray-500 mb-1">
{t('manualTime.END_TIME')}
<span className="text-[#de5505e1] ml-1">*</span>
</label>

<input
aria-label="End time"
aria-describedby="end-time-error"
type="time"
value={endTime}
onChange={(e) => setEndTime(e.target.value)}
className="w-full p-1 border font-normal border-slate-300 dark:border-slate-600 dark:bg-dark--theme-light rounded-md"
required
/>
</div>

</div>
<div>
<span className="block text-gray-500 mr-2">Date</span>
<DatePickerFilter
date={dateRange.from}
setDate={handleFromChange}
label="Oct 01 2024"
/>
</div>
<div className="w-full flex flex-col">
<ManageOrMemberComponent
fields={fields}
itemsLists={memberItemsLists}
selectedValues={selectedValues}
onSelectedValuesChange={handleSelectedValuesChange}
handleChange={handleChange}
itemToString={(item, displayKey) => getNestedValue(item, displayKey) || ''}
itemToValue={(item, valueKey) => getNestedValue(item, valueKey) || ''}
/>
</div>
<div className=" flex flex-col items-center">
<label className="text-gray-500 mr-6 ">Billable</label>
<div className="flex items-center gap-3">
<ToggleButton
isActive={isBillable}
onClick={() => setIsBillable(!isBillable)}
label={t('pages.timesheet.BILLABLE.YES')}
/>
<ToggleButton
isActive={!isBillable}
onClick={() => setIsBillable(!isBillable)}
label={t('pages.timesheet.BILLABLE.NO')}
/>
</div>
</div>
<div className="w-full flex flex-col">
<span>Notes</span>
<textarea
value={notes}
onChange={(e) => setNotes(e.target.value)}
placeholder="Insert notes here..."
className={clsxm(
"bg-transparent focus:border-transparent focus:ring-2 focus:ring-transparent",
"placeholder-gray-300 placeholder:font-normal resize-none p-2 grow w-full",
"border border-gray-200 dark:border-slate-600 dark:bg-dark--theme-light rounded-md h-40",
notes.trim().length === 0 && "border-red-500"
)}
maxLength={120}
minLength={0}
aria-label="Insert notes here"
required
/>
<div className="text-sm text-gray-500 text-right">
{notes.length}/{120}
</div>
</div>
<div className="border-t border-t-gray-200 dark:border-t-gray-700 w-full"></div>
<div className="flex items-center justify-between gap-2 ">
<div className="flex flex-col items-start justify-center">
<CustomSelect
options={statusTable?.flatMap((status) => status.label)}
renderOption={(option) => (
<div className="flex items-center gap-x-2">
<div className={clsxm("p-2 rounded-full", statusColor(option).bg)}></div>
<span className={clsxm("ml-1", statusColor(option).text,)}>{option}</span>
</div>
)}
/>
</div>
<div className="flex items-center gap-x-2">
<Button
variant="outline"
type="button"
className={clsxm("dark:text-primary h-[2.3rem] border-gray-300 dark:border-slate-600 font-normal dark:bg-dark--theme-light")}>
Cancel
</Button>

<Button
type="submit"
className={clsxm(
'bg-[#3826A6] h-[2.3rem] font-normal flex items-center text-white',
)}
>
Save
</Button>
</div>

</div>
</div>
</div>

</Modal>
)
}

interface ToggleButtonProps {
isActive: boolean;
onClick: () => void;
label: string;
}

const ToggleButton = ({ isActive, onClick, label }: ToggleButtonProps) => (
<div className="flex items-center gap-x-2">
<div
className="w-6 h-6 flex items-center bg-[#6c57f4b7] rounded-full p-1 cursor-pointer"
onClick={onClick}
style={{
background: isActive
? 'linear-gradient(to right, #9d91efb7, #8a7bedb7)'
: '#6c57f4b7'
}}
>
<div className="bg-[#3826A6] w-4 h-4 rounded-full shadow-md transform transition-transform translate-x-0" />
</div>
<span>{label}</span>
</div>
);
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,9 @@ export const getTimesheetButtons = (status: StatusType, t: TranslationHooks, onC
/>
));
};

export const statusTable: { label: StatusType; description: string }[] = [
{ label: "Pending", description: "Awaiting approval or review" },
{ label: "Approved", description: "The item has been approved" },
{ label: "Rejected", description: "The item has been rejected" },
];
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ const DatePickerInput: React.FC<DatePickerInputProps> = ({ date, label }) => (
<Button
variant="outline"
className={cn(
"w-[150px] justify-start text-left font-normal bg-transparent hover:bg-transparent text-black h-8 border border-transparent dark:border-transparent",
"w-[150px] justify-start text-left font-normal bg-transparent hover:bg-transparent text-black dark:text-gray-100 h-8 border border-transparent dark:border-transparent",
!date && "text-muted-foreground"
)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export * from './FilterWithStatus';
export * from './TimesheetFilterDate';
export * from './TimeSheetFilterPopover'
export * from './TimesheetAction';
export * from './RejectSelectedModal'
export * from './RejectSelectedModal';
export * from './EditTaskModal';
60 changes: 37 additions & 23 deletions apps/web/lib/features/integrations/calendar/table-time-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ import { clsxm } from "@/app/utils"
import { statusColor } from "@/lib/components"
import { Badge } from '@components/ui/badge'
import { IDailyPlan } from "@/app/interfaces"
import { RejectSelectedModal, StatusType, getTimesheetButtons } from "@/app/[locale]/timesheet/[memberId]/components"
import { EditTaskModal, RejectSelectedModal, StatusType, getTimesheetButtons } from "@/app/[locale]/timesheet/[memberId]/components"
import { useTranslations } from "next-intl"
import { formatDate } from "@/app/helpers"
import { TaskNameInfoDisplay } from "../../task/task-displays"
Expand Down Expand Up @@ -225,18 +225,19 @@ export function DataTableTimeSheet({ data }: { data?: IDailyPlan[] }) {
console.error(`Unsupported action: ${action}`);
}
};

console.log("================>", data)

return (
<div className="w-full dark:bg-dark--theme">
{<RejectSelectedModal
<RejectSelectedModal
onReject={() => {
// Pending implementation
}}
maxReasonLength={120}
minReasonLength={0}
closeModal={closeModal}
isOpen={isOpen} />}
isOpen={isOpen}
/>
<div className="rounded-md">
<Table className="order rounded-md dark:bg-dark--theme-light">
<TableBody className="w-full rounded-md h-[400px] overflow-y-auto dark:bg-dark--theme">
Expand Down Expand Up @@ -425,26 +426,39 @@ export function SelectFilter({ selectedStatus }: { selectedStatus?: string }) {
);
}

const TaskActionMenu = ({ idTasks }: { idTasks: any }) => {
const handleCopyPaymentId = () => navigator.clipboard.writeText(idTasks);

const TaskActionMenu = ({ idTasks }: { idTasks: string | number }) => {
const {
isOpen: isEditTask,
openModal: isOpenModalEditTask,
closeModal: isCloseModalEditTask
} = useModal();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0 text-sm sm:text-base">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem className="cursor-pointer" onClick={handleCopyPaymentId}>
Edit
</DropdownMenuItem>
<DropdownMenuSeparator />
<StatusTask />
<DropdownMenuItem className="text-red-600 hover:!text-red-600 cursor-pointer">Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<>
{
<EditTaskModal
closeModal={isCloseModalEditTask}
isOpen={isEditTask}
/>
}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0 text-sm sm:text-base">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem className="cursor-pointer" onClick={isOpenModalEditTask}>
Edit
</DropdownMenuItem>
<DropdownMenuSeparator />
<StatusTask />
<DropdownMenuItem className="text-red-600 hover:!text-red-600 cursor-pointer">Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

</>

);
};

Expand Down
Loading

0 comments on commit 3740f52

Please sign in to comment.