Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat]:Implement EditTaskModal component with enhanced task editing UI #3238

Merged
merged 3 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;

}
Innocent-Akim marked this conversation as resolved.
Show resolved Hide resolved
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
};
Innocent-Akim marked this conversation as resolved.
Show resolved Hide resolved

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>
Innocent-Akim marked this conversation as resolved.
Show resolved Hide resolved
<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>
Innocent-Akim marked this conversation as resolved.
Show resolved Hide resolved
<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>
Innocent-Akim marked this conversation as resolved.
Show resolved Hide resolved
</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" },
];
Innocent-Akim marked this conversation as resolved.
Show resolved Hide resolved
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 {
Innocent-Akim marked this conversation as resolved.
Show resolved Hide resolved
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
Loading