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

[Feature] : User should be able to update progress through progress bar slider #646

Merged
merged 11 commits into from
Jul 12, 2023
39 changes: 39 additions & 0 deletions src/components/tasks/card/ProgressBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ProgressBarProps } from '@/interfaces/task.type';
import { FC } from 'react';
import ProgressSlider from './ProgressSlider';
import ProgressIndicator from './ProgressIndicator';

const HandleProgressbar: FC<ProgressBarProps> = ({
progress,
progressValue,
percentCompleted,
handleProgressChange,
debounceSlider,
startedOn,
endsOn,
}) => {
if (progress) {
return (
<>
<ProgressSlider
value={progressValue}
debounceSlider={debounceSlider}
handleProgressChange={handleProgressChange}
/>
<span>{progressValue}%</span>
</>
);
}
return (
<>
<ProgressIndicator
percentCompleted={percentCompleted}
startedOn={startedOn}
endsOn={endsOn}
/>
<span>{percentCompleted}% </span>
</>
);
};

export default HandleProgressbar;
32 changes: 32 additions & 0 deletions src/components/tasks/card/ProgressIndicator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { FC } from 'react';

import classNames from '@/components/tasks/card/card.module.scss';
import { ProgressIndicatorProps } from '@/interfaces/task.type';
import handleProgressColor from '@/utils/handleProgressColor';

const ProgressIndicator: FC<ProgressIndicatorProps> = ({
percentCompleted,
startedOn,
endsOn,
}) => {
const progressColor = handleProgressColor(
percentCompleted,
startedOn,
endsOn
);
return (
<div className={classNames.progressIndicator}>
<div
className={`
${progressColor}
${classNames.progressStyle}
`}
style={{
width: `${percentCompleted}%`,
}}
></div>
</div>
);
};

export default ProgressIndicator;
23 changes: 23 additions & 0 deletions src/components/tasks/card/ProgressSlider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { FC } from 'react';

import { ProgressSliderProps } from '@/interfaces/task.type';

const ProgressSlider: FC<ProgressSliderProps> = ({
value,
debounceSlider,
handleProgressChange,
}) => {
return (
<input
type="range"
value={value}
min="0"
max="100"
step="10"
onChange={(e) => handleProgressChange(e)}
onMouseUp={() => debounceSlider(1000)}
/>
);
};

export default ProgressSlider;
30 changes: 30 additions & 0 deletions src/components/tasks/card/ProgressText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { FC } from 'react';
import classNames from '@/components/tasks/card/card.module.scss';
import { handleProgressTextProps } from '@/interfaces/task.type';

const HandleProgressText: FC<handleProgressTextProps> = ({
progress,
handleSaveProgressUpdate,
handleProgressUpdate,
}) => {
if (progress) {
return (
<div
className={classNames.changeProgressText}
onClick={() => handleSaveProgressUpdate()}
>
save Progress
</div>
);
}
return (
<div
className={classNames.changeProgressText}
onClick={() => handleProgressUpdate()}
>
Progress update
</div>
);
yesyash marked this conversation as resolved.
Show resolved Hide resolved
};

export default HandleProgressText;
12 changes: 12 additions & 0 deletions src/components/tasks/card/card.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,18 @@ $seeMoreTasksShadow: #595959cc;
border-radius: 1rem;
}

.progressBarContainer {
display: flex;
flex-direction: column;
}

.changeProgressText {
cursor: pointer;
color: $taskTagLevelBorder;
font-weight: 700;
padding-bottom: 0.5rem;
}

.progressGreen {
background-color: $green;
}
Expand Down
150 changes: 78 additions & 72 deletions src/components/tasks/card/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import { useEditMode } from '@/hooks/useEditMode';
import { useGetUsersByUsernameQuery } from '@/app/services/usersApi';
import { ConditionalLinkWrapper } from './ConditionalLinkWrapper';
import { isNewCardDesignEnabled } from '@/constants/FeatureFlags';
import { useGetUserQuery } from '@/app/services/userApi';
import HandleProgressText from './ProgressText';
import HandleProgressbar from './ProgressBar';
import ProgressIndicator from './ProgressIndicator';
import useUserData from '@/hooks/useUserData';
import { isTaskDetailsPageLinkEnabled } from '@/constants/FeatureFlags';
import { useUpdateTaskMutation } from '@/app/services/tasksApi';
Expand All @@ -54,7 +58,14 @@ const Card: FC<Props> = ({
TASK_STATUS.VERIFIED,
TASK_STATUS.AVAILABLE,
];

const cardDetails = content;
const { data } = useGetUserQuery();
const [progress, setProgress] = useState<boolean>(false);
const [progressValue, setProgressValue] = useState<number>(0);
const [updateTasks] = useUpdateTaskMutation();
const [debounceTimeOut, setDebounceTimeOut] = useState<number>(0);

const { data: userResponse } = useGetUsersByUsernameQuery({
searchString: cardDetails.assignee,
size: MAX_SEARCH_RESULTS,
Expand All @@ -69,8 +80,6 @@ const Card: FC<Props> = ({

const [keyLongPressed] = useKeyLongPressed();

// const [assigneeName, setAssigneeName] = useState('');

const { data: taskTagLevel, isLoading } = useGetTaskTagsQuery({
itemId: cardDetails.id,
});
Expand Down Expand Up @@ -167,55 +176,6 @@ const Card: FC<Props> = ({
}
}

function inputParser(input: string) {
const parsedDate = moment(new Date(parseInt(input, 10) * 1000));
return parsedDate;
}

function getPercentageOfDaysLeft(
startedOn: string,
endsOn: string
): number {
const startDate = inputParser(startedOn);
const endDate = inputParser(endsOn);

// It provides us with total days that are there for the the project and number of days left
const totalDays = endDate.diff(startDate, 'days');
const daysLeft = endDate.diff(new Date(), 'days');

// It provides the percentage of days left
const percentageOfDaysLeft = (daysLeft / totalDays) * 100;
return percentageOfDaysLeft;
}

function handleProgressColor(
percentCompleted: number,
startedOn: string,
endsOn: string
): string {
const percentageOfDaysLeft = getPercentageOfDaysLeft(startedOn, endsOn);
const percentIncomplete = 100 - percentCompleted;
if (
percentCompleted === 100 ||
percentageOfDaysLeft >= percentIncomplete
) {
return classNames.progressGreen;
}

if (
(percentageOfDaysLeft < 25 && percentIncomplete > 35) ||
(percentageOfDaysLeft <= 0 && percentIncomplete > 0)
) {
return classNames.progressRed;
}

if (percentageOfDaysLeft < 50 && percentIncomplete > 75) {
return classNames.progressOrange;
}

return classNames.progressYellow;
}

function renderDate(fromNowEndsOn: string, shouldEdit: boolean) {
if (shouldEdit) {
return (
Expand Down Expand Up @@ -321,23 +281,50 @@ const Card: FC<Props> = ({
</div>
);

const ProgressIndicator = () => (
<div className={classNames.progressIndicator}>
<div
className={`
${handleProgressColor(
content.percentCompleted,
content.startedOn,
content.endsOn
)}
${classNames.progressStyle}
`}
style={{
width: `${content.percentCompleted}%`,
}}
></div>
</div>
);
const handleProgressUpdate = () => {
if (
Pratiyushkumar marked this conversation as resolved.
Show resolved Hide resolved
content.assignee === data?.username ||
data?.roles.super_user === true
) {
setProgress(true);
} else {
toast(ERROR, 'You cannot update progress');
}
};

const debounceSlider = (debounceTimeOut: number) => {
if (debounceTimeOut) {
clearTimeout(debounceTimeOut);
}
const timer = setTimeout(() => {
handleSliderChangeComplete(cardDetails.id, progressValue);
}, 1000);
setDebounceTimeOut(Number(timer));
};
yesyash marked this conversation as resolved.
Show resolved Hide resolved

const handleSliderChangeComplete = async (
id: string,
percentCompleted: number
) => {
const data = {
percentCompleted: percentCompleted,
};
await updateTasks({
task: data,
id: id,
});
toast(SUCCESS, 'Progress Updated Successfully');
};

const handleProgressChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
setProgressValue(Number(event.target.value));
};

const handleSaveProgressUpdate = () => {
setProgress(false);
};

const AssigneeButton = () => {
return (
Expand Down Expand Up @@ -457,9 +444,23 @@ const Card: FC<Props> = ({
</ConditionalLinkWrapper>

{/* progress bar */}
<div className={classNames.progressContainerUpdated}>
<ProgressIndicator />
<span>{content.percentCompleted}% </span>
<div>
<div className={classNames.progressContainerUpdated}>
<HandleProgressbar
progress={progress}
progressValue={progressValue}
percentCompleted={content.percentCompleted}
handleProgressChange={handleProgressChange}
debounceSlider={debounceSlider}
startedOn={content.startedOn}
endsOn={content.endsOn}
/>
</div>
<HandleProgressText
progress={progress}
handleSaveProgressUpdate={handleSaveProgressUpdate}
handleProgressUpdate={handleProgressUpdate}
/>
</div>
</div>
<div className={classNames.taskStatusAndDateContainer}>
Expand Down Expand Up @@ -633,11 +634,16 @@ const Card: FC<Props> = ({
</div>
<div className={classNames.cardItems}>
<span className={classNames.progressContainer}>
<ProgressIndicator />
<ProgressIndicator
percentCompleted={content.percentCompleted}
startedOn={content.startedOn}
endsOn={content.endsOn}
/>

<span>{content.percentCompleted}% completed</span>
</span>
</div>

<div
className={`${classNames.taskTagLevelWrapper} ${
shouldEdit && classNames.editMode
Expand Down
28 changes: 28 additions & 0 deletions src/interfaces/task.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,36 @@ type task = {
};
};

export type ProgressSliderProps = {
value: number;
debounceSlider: (debounceTimeOut: number) => void;
handleProgressChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
};
export type updateTaskDetails = Partial<Omit<task, 'startedOn'>> & {
startedOn?: number;
percentCompleted?: number;
};

export type ProgressBarProps = {
progress: boolean;
progressValue: number;
percentCompleted: number;
startedOn: string;
endsOn: string;
handleProgressChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
debounceSlider: (debounceTimeOut: number) => void;
};

export type ProgressIndicatorProps = {
percentCompleted: number;
startedOn: string;
endsOn: string;
};

export type handleProgressTextProps = {
progress: boolean;
handleSaveProgressUpdate: () => void;
handleProgressUpdate: () => void;
};

enum Tab {
Expand Down