Skip to content

Commit

Permalink
FEAT : adds modal to issue card
Browse files Browse the repository at this point in the history
  • Loading branch information
Ajeyakrishna-k committed Oct 18, 2023
1 parent 9851e57 commit ef44943
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 22 deletions.
31 changes: 29 additions & 2 deletions __tests__/Unit/Components/Issues/Card.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render } from '@testing-library/react';
import { fireEvent, render } from '@testing-library/react';
import Card from '@/components/issues/Card';
import MarkdownRenderer from '@/components/MarkdownRenderer/MarkdownRenderer';

Expand Down Expand Up @@ -122,6 +122,33 @@ describe('Issue card', () => {
query: { dev: 'true' },
}
);
expect(screen.getByRole('button')).toHaveTextContent('Assign Task');
expect(screen.getByRole('button')).toHaveTextContent('Convert to Task');
});
test('should render issue card with convert to task button', () => {
const screen = renderWithRouter(
<Provider store={store()}>
<Card issue={issuesResponseSearchedWithQuery[0]} />
</Provider>,
{
query: { dev: 'true' },
}
);
expect(screen.getByRole('button')).toHaveTextContent('Convert to Task');
});
test('should open a modal when button is clicked', () => {
const screen = renderWithRouter(
<Provider store={store()}>
<Card issue={issuesResponseSearchedWithQuery[0]} />
</Provider>,
{
query: { dev: 'true' },
}
);
const convertToTaskButton = screen.getByText(/Convert to Task/i);
fireEvent.click(convertToTaskButton);
const taskRequestModalTitle = screen.getByText(/Task Request/i);
expect(taskRequestModalTitle).toBeInTheDocument();
const taskAssignmentModalTitle = screen.getByText(/Task Assignment/i);
expect(taskAssignmentModalTitle).toBeInTheDocument();
});
});
77 changes: 71 additions & 6 deletions src/components/issues/Card.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ $card-text: #78716c;

&:hover {
border-color: #e5e5e5;
filter: drop-shadow(0 20px 13px rgba(0, 0, 0, 0.02))
drop-shadow(0 8px 5px rgba(0, 0, 0, 0.06));
}

@media (max-width: 64rem) {
Expand All @@ -35,26 +33,93 @@ $card-text: #78716c;
width: 100%;
}
}
.inputContainer {
display: flex;
flex-direction: column;
}

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

.request_form {
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 1rem;
min-height: 22rem;
min-width: 20rem;
max-width: 20rem;
}

.form_container {
display: flex;
flex-direction: column;
gap: 1rem;
}

.assign {
width: 9rem;
margin-left: 0.5rem;
margin-bottom: 1rem;
line-height: 2rem;
display: inline-block;
}
.description_box {
background-color: rgb(29 18 255 / 10%);
border-radius: 2px;
min-height: 5rem;
height: 6rem;
border: none;
resize: none;
}

.assign_label {
margin-left: 0.5rem;
line-height: 1rem;
margin-bottom: 0;
font-size: 0.8rem;
color: #a9a9a9;
font-weight: 700;
}

.taskTabs {
/* 1s delay after the page loads */
display: flex;
gap: 1rem;
justify-content: space-evenly;
font-size: 1rem;
font-weight: 700;
}
.input_select {
padding: 0 4px 0 4px;
height: 2rem;
border: 1px solid #1f1f1f;
border-radius: 4px;
outline: none;
}
.input_date {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
padding: 0 4px 0 4px;
height: 2rem;
border: 1px solid #1f1f1f;
border-radius: 4px;
outline: none;
}

.taskTab {
background: none;
color: inherit;
border: none;
padding: 4px;
font: inherit;
cursor: pointer;
outline: inherit;
}

.highlightTaskTab {
border-bottom: styles.$color-blue solid 2px;
transition: 150ms ease-in-out;
}
.suggestions_container {
position: relative;
display: inline-block;
Expand Down
193 changes: 179 additions & 14 deletions src/components/issues/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,45 @@ import { toast, ToastTypes } from '@/helperFunctions/toast';

import fetch from '@/helperFunctions/fetch';
import { IssueCardProps } from '@/interfaces/issueProps.type';
import { TASKS_URL } from '../../constants/url';
import { TASKS_URL, TASK_REQUEST_URL } from '../../constants/url';
import useUserData from '@/hooks/useUserData';
import ActionForm from './ActionForm';
import { useRouter } from 'next/router';
import TaskRequestForm from './TaskRequestForm';
import Modal from '../Modal';
import { useUpdateTaskMutation } from '@/app/services/tasksApi';
import { REQUEST_TABS } from './constants';
import { TASK_REQUEST_TYPES } from '@/constants/tasks';
import { FEATURE } from '@/constants/task-type';
import { AVAILABLE } from '@/constants/task-status';
import { TBD } from '@/constants/constants';
const { SUCCESS, ERROR } = ToastTypes;
type TaskData = {
assignee?: string;
endsOn?: number;
startedOn?: number;
status?: string;
};

const Card: FC<IssueCardProps> = ({ issue }) => {
const date = new Date(issue.created_at).toDateString();
const [taskExists, setTaskExists] = useState(issue.taskExists ?? false);
const [isLoading, setIsLoading] = useState(false);
const router = useRouter();
const devMode = router.query.dev === 'true' ? true : false;
const { isUserAuthorized } = useUserData();
const { data: userData, isUserAuthorized } = useUserData();
const [taskId, setTaskId] = useState(issue.taskId);

const [isTaskModalOpen, setIsTaskModalOpen] = useState(false);
const [requestId, setRequestId] = useState();
const [assignee, setAssignee] = useState<string | undefined>();
const defaultTaskConversionTab = isUserAuthorized
? REQUEST_TABS.TASK_CREATION
: REQUEST_TABS.CREATION_REQUEST;
const [selectedTab, setSelectedTab] = useState(defaultTaskConversionTab);
const [updateTask] = useUpdateTaskMutation();
const toggle = () => {
setIsTaskModalOpen(!isTaskModalOpen);
};
const getIssueInfo = () => {
const issueInfo: any = {
status: issue.state,
Expand Down Expand Up @@ -69,6 +93,87 @@ const Card: FC<IssueCardProps> = ({ issue }) => {
}
};

const onTaskRequestClick = () => {
setSelectedTab(REQUEST_TABS.CREATION_REQUEST);
};
const onTaskAssignmentClick = () => {
setSelectedTab(REQUEST_TABS.TASK_CREATION);
};
const handleUpdateTask = async (taskData: TaskData, taskId: string) => {
try {
await updateTask({
task: taskData,
id: taskId,
}).unwrap();
toast(SUCCESS, 'Task updated successfully.');
setAssignee(taskData.assignee);
toggle();
} catch (error: any) {
toast(ERROR, error.data.message);
}
};
const handleCreateTaskRequest = async (data: any) => {
const requestData = {
externalIssueUrl: issue.url,
userId: userData?.id,
requestType: TASK_REQUEST_TYPES.CREATION,
proposedStartDate: data.startedOn,
proposedDeadline: data.endsOn,
description: data.description,
};
try {
const url = TASK_REQUEST_URL;
const { requestPromise } = fetch({
url,
method: 'post',
data: requestData,
});
const response = await requestPromise;
setRequestId(response.data.data.id);
toast(SUCCESS, 'Task Request created successfully');
toggle();
} catch (error: any) {
if ('response' in error) {
toast(ERROR, error.response.data.message);
return;
}
toast(ERROR, error.message);
}
};
const handleCreateTask = async (taskData: TaskData) => {
try {
if (!taskData.assignee) delete taskData.assignee;
const url = TASKS_URL;
const data = {
title: issue.title,
type: FEATURE,
status: taskData.status || AVAILABLE,
percentCompleted: 0,
priority: TBD,
github: {
issue: getIssueInfo(),
},
...taskData,
};
const { requestPromise } = fetch({
url,
method: 'post',
data,
});
const response = await requestPromise;
setTaskId(response.data.task.id);
toast(SUCCESS, 'Task created successfully');
setTaskExists(true);
toggle();
} catch (error: any) {
setIsLoading(false);
if ('response' in error) {
toast(ERROR, error.response.data.message);
return;
}
toast(ERROR, error.message);
}
};
return (
<div className={styles.card}>
<div className={styles.card_details}>
Expand Down Expand Up @@ -126,17 +231,77 @@ const Card: FC<IssueCardProps> = ({ issue }) => {
</div>
</div>
<div className={styles.actions}>
{(!taskExists || !isUserAuthorized || !devMode) && (
<button
className={styles.card__top__button}
disabled={taskExists || isLoading || !isUserAuthorized}
onClick={handleClick}
>
Convert to task
</button>
)}
{isUserAuthorized && taskExists && devMode && (
<ActionForm taskId={taskId || ''} />
{devMode ? (
<>
<button
className={styles.card__top__button}
disabled={
isLoading ||
(!isUserAuthorized &&
(taskExists || !!requestId))
}
onClick={toggle}
>
{isUserAuthorized
? 'Convert to Task'
: 'Request as Task'}
</button>
<Modal isOpen={isTaskModalOpen} toggle={toggle}>
<div className={styles.taskTabs}>
<button
onClick={onTaskRequestClick}
className={`${styles.taskTab} ${
selectedTab ===
REQUEST_TABS.CREATION_REQUEST
? styles.highlightTaskTab
: ''
}`}
>
Task Request
</button>
{isUserAuthorized && (
<button
onClick={onTaskAssignmentClick}
className={`${styles.taskTab} ${
selectedTab ===
REQUEST_TABS.TASK_CREATION
? styles.highlightTaskTab
: ''
}`}
>
Task Assignment
</button>
)}
</div>
{selectedTab === REQUEST_TABS.CREATION_REQUEST && (
<TaskRequestForm
requestId={requestId}
taskId={taskId}
createTaskRequest={handleCreateTaskRequest}
/>
)}
{selectedTab === REQUEST_TABS.TASK_CREATION && (
<ActionForm
taskId={taskId || ''}
taskAssignee={assignee}
createTask={handleCreateTask}
updateTask={handleUpdateTask}
/>
)}
</Modal>
</>
) : (
<>
<button
className={styles.card__top__button}
disabled={
taskExists || isLoading || !isUserAuthorized
}
onClick={handleClick}
>
Convert to task
</button>
</>
)}
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export const STANDUP_ALREADY_SUBMITTED =
export const DATEFORMAT = 'MMMM DD, YYYY ';
export const STANDUPTIME = 6;
export const TASK_RESULT_SIZE = 5;
export const TBD = 'TBD';
5 changes: 5 additions & 0 deletions src/constants/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ export const EMPTY_TASKS_DATA = {
OVERDUE: [],
DONE: [],
};

export const TASK_REQUEST_TYPES = {
CREATION: 'CREATION',
ASSIGNMENT: 'ASSIGNMENT',
};
1 change: 1 addition & 0 deletions src/constants/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const ALL_TAGS_URL = `${process.env.NEXT_PUBLIC_BASE_URL}/tags`;
export const ALL_LEVELS_URL = `${process.env.NEXT_PUBLIC_BASE_URL}/levels`;
export const ITEM_TYPES = { task: 'TASK' };
export const TASKS_URL = `${process.env.NEXT_PUBLIC_BASE_URL}/tasks`;
export const TASK_REQUEST_URL = `${process.env.NEXT_PUBLIC_BASE_URL}/taskRequests`;
export const ISSUES_URL = `${process.env.NEXT_PUBLIC_BASE_URL}/issues`;
export const USERS_URL = `${BASE_URL}/users`;
export const MAIN_SITE_URL = 'https://www.realdevsquad.com';
Expand Down

0 comments on commit ef44943

Please sign in to comment.