feature: update task details design
This commit is contained in:
		@@ -24,6 +24,7 @@
 | 
				
			|||||||
    "@types/node": "^12.0.0",
 | 
					    "@types/node": "^12.0.0",
 | 
				
			||||||
    "@types/react": "^16.9.21",
 | 
					    "@types/react": "^16.9.21",
 | 
				
			||||||
    "@types/react-beautiful-dnd": "^12.1.1",
 | 
					    "@types/react-beautiful-dnd": "^12.1.1",
 | 
				
			||||||
 | 
					    "@types/react-datepicker": "^2.11.0",
 | 
				
			||||||
    "@types/react-dom": "^16.9.5",
 | 
					    "@types/react-dom": "^16.9.5",
 | 
				
			||||||
    "@types/react-router": "^5.1.4",
 | 
					    "@types/react-router": "^5.1.4",
 | 
				
			||||||
    "@types/react-router-dom": "^5.1.3",
 | 
					    "@types/react-router-dom": "^5.1.3",
 | 
				
			||||||
@@ -41,10 +42,12 @@
 | 
				
			|||||||
    "history": "^4.10.1",
 | 
					    "history": "^4.10.1",
 | 
				
			||||||
    "immer": "^6.0.3",
 | 
					    "immer": "^6.0.3",
 | 
				
			||||||
    "lodash": "^4.17.15",
 | 
					    "lodash": "^4.17.15",
 | 
				
			||||||
 | 
					    "moment": "^2.24.0",
 | 
				
			||||||
    "prop-types": "^15.7.2",
 | 
					    "prop-types": "^15.7.2",
 | 
				
			||||||
    "react": "^16.12.0",
 | 
					    "react": "^16.12.0",
 | 
				
			||||||
    "react-autosize-textarea": "^7.0.0",
 | 
					    "react-autosize-textarea": "^7.0.0",
 | 
				
			||||||
    "react-beautiful-dnd": "^13.0.0",
 | 
					    "react-beautiful-dnd": "^13.0.0",
 | 
				
			||||||
 | 
					    "react-datepicker": "^2.14.1",
 | 
				
			||||||
    "react-dom": "^16.12.0",
 | 
					    "react-dom": "^16.12.0",
 | 
				
			||||||
    "react-hook-form": "^5.2.0",
 | 
					    "react-hook-form": "^5.2.0",
 | 
				
			||||||
    "react-router": "^5.1.2",
 | 
					    "react-router": "^5.1.2",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -106,5 +106,9 @@ export default createGlobalStyle`
 | 
				
			|||||||
    touch-action: manipulation;
 | 
					    touch-action: manipulation;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  textarea {
 | 
				
			||||||
 | 
					    resize: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ${mixin.placeholderColor(color.textLight)}
 | 
					  ${mixin.placeholderColor(color.textLight)}
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										0
									
								
								web/src/Projects/Project/Lists/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								web/src/Projects/Project/Lists/index.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -19,6 +19,10 @@ import Lists from 'shared/components/Lists';
 | 
				
			|||||||
import QuickCardEditor from 'shared/components/QuickCardEditor';
 | 
					import QuickCardEditor from 'shared/components/QuickCardEditor';
 | 
				
			||||||
import PopupMenu from 'shared/components/PopupMenu';
 | 
					import PopupMenu from 'shared/components/PopupMenu';
 | 
				
			||||||
import ListActions from 'shared/components/ListActions';
 | 
					import ListActions from 'shared/components/ListActions';
 | 
				
			||||||
 | 
					import Modal from 'shared/components/Modal';
 | 
				
			||||||
 | 
					import TaskDetails from 'shared/components/TaskDetails';
 | 
				
			||||||
 | 
					import MemberManager from 'shared/components/MemberManager';
 | 
				
			||||||
 | 
					import { LabelsPopup } from 'shared/components/PopupMenu/PopupMenu.stories';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ColumnState {
 | 
					interface ColumnState {
 | 
				
			||||||
  [key: string]: TaskGroup;
 | 
					  [key: string]: TaskGroup;
 | 
				
			||||||
@@ -73,11 +77,16 @@ interface ProjectParams {
 | 
				
			|||||||
const initialState: State = { tasks: {}, columns: {} };
 | 
					const initialState: State = { tasks: {}, columns: {} };
 | 
				
			||||||
const initialPopupState = { left: 0, top: 0, isOpen: false, taskGroupID: '' };
 | 
					const initialPopupState = { left: 0, top: 0, isOpen: false, taskGroupID: '' };
 | 
				
			||||||
const initialQuickCardEditorState: QuickCardEditorState = { isOpen: false, top: 0, left: 0 };
 | 
					const initialQuickCardEditorState: QuickCardEditorState = { isOpen: false, top: 0, left: 0 };
 | 
				
			||||||
 | 
					const initialMemberPopupState = { taskID: '', isOpen: false, top: 0, left: 0 };
 | 
				
			||||||
 | 
					const initialLabelsPopupState = { taskID: '', isOpen: false, top: 0, left: 0 };
 | 
				
			||||||
 | 
					const initialTaskDetailsState = { isOpen: false, taskID: '' };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Project = () => {
 | 
					const Project = () => {
 | 
				
			||||||
  const { projectId } = useParams<ProjectParams>();
 | 
					  const { projectId } = useParams<ProjectParams>();
 | 
				
			||||||
  const [listsData, setListsData] = useState(initialState);
 | 
					  const [listsData, setListsData] = useState(initialState);
 | 
				
			||||||
  const [popupData, setPopupData] = useState(initialPopupState);
 | 
					  const [popupData, setPopupData] = useState(initialPopupState);
 | 
				
			||||||
 | 
					  const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
 | 
				
			||||||
 | 
					  const [taskDetails, setTaskDetails] = useState(initialTaskDetailsState);
 | 
				
			||||||
  const [quickCardEditor, setQuickCardEditor] = useState(initialQuickCardEditorState);
 | 
					  const [quickCardEditor, setQuickCardEditor] = useState(initialQuickCardEditorState);
 | 
				
			||||||
  const [updateTaskLocation] = useUpdateTaskLocationMutation();
 | 
					  const [updateTaskLocation] = useUpdateTaskLocationMutation();
 | 
				
			||||||
  const [updateTaskGroupLocation] = useUpdateTaskGroupLocationMutation();
 | 
					  const [updateTaskGroupLocation] = useUpdateTaskGroupLocationMutation();
 | 
				
			||||||
@@ -256,6 +265,9 @@ const Project = () => {
 | 
				
			|||||||
          </TitleWrapper>
 | 
					          </TitleWrapper>
 | 
				
			||||||
          <Board>
 | 
					          <Board>
 | 
				
			||||||
            <Lists
 | 
					            <Lists
 | 
				
			||||||
 | 
					              onCardClick={task => {
 | 
				
			||||||
 | 
					                setTaskDetails({ isOpen: true, taskID: task.taskID });
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
              onExtraMenuOpen={(taskGroupID, pos, size) => {
 | 
					              onExtraMenuOpen={(taskGroupID, pos, size) => {
 | 
				
			||||||
                setPopupData({
 | 
					                setPopupData({
 | 
				
			||||||
                  isOpen: true,
 | 
					                  isOpen: true,
 | 
				
			||||||
@@ -315,6 +327,57 @@ const Project = () => {
 | 
				
			|||||||
            />
 | 
					            />
 | 
				
			||||||
          </PopupMenu>
 | 
					          </PopupMenu>
 | 
				
			||||||
        )}
 | 
					        )}
 | 
				
			||||||
 | 
					        {memberPopupData.isOpen && (
 | 
				
			||||||
 | 
					          <PopupMenu
 | 
				
			||||||
 | 
					            title="Members"
 | 
				
			||||||
 | 
					            onClose={() => setMemberPopupData(initialMemberPopupState)}
 | 
				
			||||||
 | 
					            top={memberPopupData.top}
 | 
				
			||||||
 | 
					            left={memberPopupData.left}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <MemberManager
 | 
				
			||||||
 | 
					              availableMembers={[{ displayName: 'Jordan Knott', userID: '21345076-6423-4a00-a6bd-cd9f830e2764' }]}
 | 
				
			||||||
 | 
					              activeMembers={[]}
 | 
				
			||||||
 | 
					              onMemberChange={(member, isActive) => console.log(member, isActive)}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </PopupMenu>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					        {taskDetails.isOpen && (
 | 
				
			||||||
 | 
					          <Modal
 | 
				
			||||||
 | 
					            width={1040}
 | 
				
			||||||
 | 
					            onClose={() => {
 | 
				
			||||||
 | 
					              setTaskDetails(initialTaskDetailsState);
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            renderContent={() => {
 | 
				
			||||||
 | 
					              const task = listsData.tasks[taskDetails.taskID];
 | 
				
			||||||
 | 
					              return (
 | 
				
			||||||
 | 
					                <TaskDetails
 | 
				
			||||||
 | 
					                  task={task}
 | 
				
			||||||
 | 
					                  onTaskNameChange={(updatedTask, newName) => {
 | 
				
			||||||
 | 
					                    updateTaskName({ variables: { taskID: updatedTask.taskID, name: newName } });
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  onTaskDescriptionChange={(updatedTask, newDescription) => {
 | 
				
			||||||
 | 
					                    console.log(updatedTask, newDescription);
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  onDeleteTask={deletedTask => {
 | 
				
			||||||
 | 
					                    setTaskDetails(initialTaskDetailsState);
 | 
				
			||||||
 | 
					                    deleteTask({ variables: { taskID: deletedTask.taskID } });
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  onCloseModal={() => setTaskDetails(initialTaskDetailsState)}
 | 
				
			||||||
 | 
					                  onOpenAddMemberPopup={(task, bounds) => {
 | 
				
			||||||
 | 
					                    console.log(task, bounds);
 | 
				
			||||||
 | 
					                    setMemberPopupData({
 | 
				
			||||||
 | 
					                      isOpen: true,
 | 
				
			||||||
 | 
					                      taskID: task.taskID,
 | 
				
			||||||
 | 
					                      top: bounds.position.top + bounds.size.height + 10,
 | 
				
			||||||
 | 
					                      left: bounds.position.left,
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  onOpenAddLabelPopup={(task, bounds) => {}}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
      </>
 | 
					      </>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								web/src/citadel.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								web/src/citadel.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -16,6 +16,11 @@ type InnerTaskGroup = {
 | 
				
			|||||||
  position?: number;
 | 
					  position?: number;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TaskUser = {
 | 
				
			||||||
 | 
					  userID: string;
 | 
				
			||||||
 | 
					  displayName: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Task = {
 | 
					type Task = {
 | 
				
			||||||
  taskID: string;
 | 
					  taskID: string;
 | 
				
			||||||
  taskGroup: InnerTaskGroup;
 | 
					  taskGroup: InnerTaskGroup;
 | 
				
			||||||
@@ -23,6 +28,7 @@ type Task = {
 | 
				
			|||||||
  position: number;
 | 
					  position: number;
 | 
				
			||||||
  labels: Label[];
 | 
					  labels: Label[];
 | 
				
			||||||
  description?: string;
 | 
					  description?: string;
 | 
				
			||||||
 | 
					  members?: Array<TaskUser>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TaskGroup = {
 | 
					type TaskGroup = {
 | 
				
			||||||
@@ -85,3 +91,8 @@ type ElementSize = {
 | 
				
			|||||||
  width: number;
 | 
					  width: number;
 | 
				
			||||||
  height: number;
 | 
					  height: number;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ElementBounds = {
 | 
				
			||||||
 | 
					  size: ElementSize;
 | 
				
			||||||
 | 
					  position: ElementPosition;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					import React, { useRef } from 'react';
 | 
				
			||||||
 | 
					import { action } from '@storybook/addon-actions';
 | 
				
			||||||
 | 
					import DueDateManager from '.';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  component: DueDateManager,
 | 
				
			||||||
 | 
					  title: 'DueDateManager',
 | 
				
			||||||
 | 
					  parameters: {
 | 
				
			||||||
 | 
					    backgrounds: [
 | 
				
			||||||
 | 
					      { name: 'gray', value: '#f8f8f8', default: true },
 | 
				
			||||||
 | 
					      { name: 'white', value: '#ffffff' },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Default = () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <DueDateManager
 | 
				
			||||||
 | 
					      task={{
 | 
				
			||||||
 | 
					        taskID: '1',
 | 
				
			||||||
 | 
					        taskGroup: { name: 'General', taskGroupID: '1' },
 | 
				
			||||||
 | 
					        name: 'Hello, world',
 | 
				
			||||||
 | 
					        position: 1,
 | 
				
			||||||
 | 
					        labels: [{ labelId: 'soft-skills', color: '#fff', active: true, name: 'Soft Skills' }],
 | 
				
			||||||
 | 
					        description: 'hello!',
 | 
				
			||||||
 | 
					        members: [{ userID: '1', displayName: 'Jordan Knott' }],
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      onCancel={action('cancel')}
 | 
				
			||||||
 | 
					      onDueDateChange={action('due date change')}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										45
									
								
								web/src/shared/components/DueDateManager/Styles.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								web/src/shared/components/DueDateManager/Styles.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					import styled from 'styled-components';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Wrapper = styled.div`
 | 
				
			||||||
 | 
					display: flex
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DueDatePickerWrapper = styled.div`
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ConfirmAddDueDate = styled.div`
 | 
				
			||||||
 | 
					  background-color: #5aac44;
 | 
				
			||||||
 | 
					  box-shadow: none;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  float: left;
 | 
				
			||||||
 | 
					  margin: 0 4px 0 0;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					  font-weight: 400;
 | 
				
			||||||
 | 
					  line-height: 20px;
 | 
				
			||||||
 | 
					  padding: 6px 12px;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  border-radius: 3px;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const CancelDueDate = styled.div`
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  height: 32px;
 | 
				
			||||||
 | 
					  width: 32px;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ActionWrapper = styled.div`
 | 
				
			||||||
 | 
					  padding-top: 8px;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
							
								
								
									
										32
									
								
								web/src/shared/components/DueDateManager/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								web/src/shared/components/DueDateManager/index.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					import React, { useState } from 'react';
 | 
				
			||||||
 | 
					import DatePicker from 'react-datepicker';
 | 
				
			||||||
 | 
					import { Cross } from 'shared/icons';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Wrapper, ActionWrapper, DueDatePickerWrapper, ConfirmAddDueDate, CancelDueDate } from './Styles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'react-datepicker/dist/react-datepicker.css';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DueDateManagerProps = {
 | 
				
			||||||
 | 
					  task: Task;
 | 
				
			||||||
 | 
					  onDueDateChange: (task: Task, newDueDate: Date) => void;
 | 
				
			||||||
 | 
					  onCancel: () => void;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					const DueDateManager: React.FC<DueDateManagerProps> = ({ task, onDueDateChange, onCancel }) => {
 | 
				
			||||||
 | 
					  const [startDate, setStartDate] = useState(new Date());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Wrapper>
 | 
				
			||||||
 | 
					      <DueDatePickerWrapper>
 | 
				
			||||||
 | 
					        <DatePicker inline selected={startDate} onChange={date => setStartDate(date ?? new Date())} />
 | 
				
			||||||
 | 
					      </DueDatePickerWrapper>
 | 
				
			||||||
 | 
					      <ActionWrapper>
 | 
				
			||||||
 | 
					        <ConfirmAddDueDate onClick={() => onDueDateChange(task, startDate)}>Save</ConfirmAddDueDate>
 | 
				
			||||||
 | 
					        <CancelDueDate onClick={onCancel}>
 | 
				
			||||||
 | 
					          <Cross size={16} color="#c2c6dc" />
 | 
				
			||||||
 | 
					        </CancelDueDate>
 | 
				
			||||||
 | 
					      </ActionWrapper>
 | 
				
			||||||
 | 
					    </Wrapper>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default DueDateManager;
 | 
				
			||||||
@@ -113,7 +113,7 @@ export const ListCards = styled.div`
 | 
				
			|||||||
  margin: 0 4px;
 | 
					  margin: 0 4px;
 | 
				
			||||||
  padding: 0 4px;
 | 
					  padding: 0 4px;
 | 
				
			||||||
  flex: 1 1 auto;
 | 
					  flex: 1 1 auto;
 | 
				
			||||||
  min-height: 30px;
 | 
					  min-height: 45px;
 | 
				
			||||||
  overflow-y: auto;
 | 
					  overflow-y: auto;
 | 
				
			||||||
  overflow-x: hidden;
 | 
					  overflow-x: hidden;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,13 +64,13 @@ const initialListsData = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const Default = () => {
 | 
					export const Default = () => {
 | 
				
			||||||
  const [listsData, setListsData] = useState(initialListsData);
 | 
					  const [listsData, setListsData] = useState(initialListsData);
 | 
				
			||||||
  const onCardDrop = (droppedTask: any) => {
 | 
					  const onCardDrop = (droppedTask: Task) => {
 | 
				
			||||||
    console.log(droppedTask);
 | 
					    console.log(droppedTask);
 | 
				
			||||||
    const newState = {
 | 
					    const newState = {
 | 
				
			||||||
      ...listsData,
 | 
					      ...listsData,
 | 
				
			||||||
      tasks: {
 | 
					      tasks: {
 | 
				
			||||||
        ...listsData.tasks,
 | 
					        ...listsData.tasks,
 | 
				
			||||||
        [droppedTask.taskGroupID]: droppedTask,
 | 
					        [droppedTask.taskID]: droppedTask,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    console.log(newState);
 | 
					    console.log(newState);
 | 
				
			||||||
@@ -91,6 +91,7 @@ export const Default = () => {
 | 
				
			|||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Lists
 | 
					    <Lists
 | 
				
			||||||
      {...listsData}
 | 
					      {...listsData}
 | 
				
			||||||
 | 
					      onCardClick={action('card click')}
 | 
				
			||||||
      onExtraMenuOpen={action('extra menu open')}
 | 
					      onExtraMenuOpen={action('extra menu open')}
 | 
				
			||||||
      onQuickEditorOpen={action('card composer open')}
 | 
					      onQuickEditorOpen={action('card composer open')}
 | 
				
			||||||
      onCardDrop={onCardDrop}
 | 
					      onCardDrop={onCardDrop}
 | 
				
			||||||
@@ -201,6 +202,7 @@ export const ListsWithManyList = () => {
 | 
				
			|||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Lists
 | 
					    <Lists
 | 
				
			||||||
      {...listsData}
 | 
					      {...listsData}
 | 
				
			||||||
 | 
					      onCardClick={action('card click')}
 | 
				
			||||||
      onQuickEditorOpen={action('card composer open')}
 | 
					      onQuickEditorOpen={action('card composer open')}
 | 
				
			||||||
      onCardCreate={action('card create')}
 | 
					      onCardCreate={action('card create')}
 | 
				
			||||||
      onCardDrop={onCardDrop}
 | 
					      onCardDrop={onCardDrop}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@ interface Tasks {
 | 
				
			|||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
  columns: Columns;
 | 
					  columns: Columns;
 | 
				
			||||||
  tasks: Tasks;
 | 
					  tasks: Tasks;
 | 
				
			||||||
 | 
					  onCardClick: (task: Task) => void;
 | 
				
			||||||
  onCardDrop: (task: Task) => void;
 | 
					  onCardDrop: (task: Task) => void;
 | 
				
			||||||
  onListDrop: (taskGroup: TaskGroup) => void;
 | 
					  onListDrop: (taskGroup: TaskGroup) => void;
 | 
				
			||||||
  onCardCreate: (taskGroupID: string, name: string) => void;
 | 
					  onCardCreate: (taskGroupID: string, name: string) => void;
 | 
				
			||||||
@@ -34,6 +35,7 @@ type Props = {
 | 
				
			|||||||
const Lists: React.FC<Props> = ({
 | 
					const Lists: React.FC<Props> = ({
 | 
				
			||||||
  columns,
 | 
					  columns,
 | 
				
			||||||
  tasks,
 | 
					  tasks,
 | 
				
			||||||
 | 
					  onCardClick,
 | 
				
			||||||
  onCardDrop,
 | 
					  onCardDrop,
 | 
				
			||||||
  onListDrop,
 | 
					  onListDrop,
 | 
				
			||||||
  onCardCreate,
 | 
					  onCardCreate,
 | 
				
			||||||
@@ -72,7 +74,6 @@ const Lists: React.FC<Props> = ({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    console.log(beforeDropDraggables);
 | 
					    console.log(beforeDropDraggables);
 | 
				
			||||||
    console.log(destination);
 | 
					    console.log(destination);
 | 
				
			||||||
    console.log(droppedDraggable);
 | 
					 | 
				
			||||||
    const afterDropDraggables = getAfterDropDraggableList(
 | 
					    const afterDropDraggables = getAfterDropDraggableList(
 | 
				
			||||||
      beforeDropDraggables,
 | 
					      beforeDropDraggables,
 | 
				
			||||||
      droppedDraggable,
 | 
					      droppedDraggable,
 | 
				
			||||||
@@ -85,16 +86,20 @@ const Lists: React.FC<Props> = ({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (isList) {
 | 
					    if (isList) {
 | 
				
			||||||
      const droppedList = columns[droppedDraggable.id];
 | 
					      const droppedList = columns[droppedDraggable.id];
 | 
				
			||||||
 | 
					      console.log(`is list ${droppedList}`);
 | 
				
			||||||
      onListDrop({
 | 
					      onListDrop({
 | 
				
			||||||
        ...droppedList,
 | 
					        ...droppedList,
 | 
				
			||||||
        position: newPosition,
 | 
					        position: newPosition,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      const droppedCard = tasks[droppedDraggable.id];
 | 
					      const droppedCard = tasks[droppedDraggable.id];
 | 
				
			||||||
 | 
					      console.log(`is card ${droppedCard}`);
 | 
				
			||||||
      const newCard = {
 | 
					      const newCard = {
 | 
				
			||||||
        ...droppedCard,
 | 
					        ...droppedCard,
 | 
				
			||||||
        position: newPosition,
 | 
					        position: newPosition,
 | 
				
			||||||
 | 
					        taskGroup: {
 | 
				
			||||||
          taskGroupID: destination.droppableId,
 | 
					          taskGroupID: destination.droppableId,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      onCardDrop(newCard);
 | 
					      onCardDrop(newCard);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -121,22 +126,22 @@ const Lists: React.FC<Props> = ({
 | 
				
			|||||||
                return (
 | 
					                return (
 | 
				
			||||||
                  <Draggable draggableId={column.taskGroupID} key={column.taskGroupID} index={index}>
 | 
					                  <Draggable draggableId={column.taskGroupID} key={column.taskGroupID} index={index}>
 | 
				
			||||||
                    {columnDragProvided => (
 | 
					                    {columnDragProvided => (
 | 
				
			||||||
 | 
					                      <Droppable type="tasks" droppableId={column.taskGroupID}>
 | 
				
			||||||
 | 
					                        {(columnDropProvided, snapshot) => (
 | 
				
			||||||
                          <List
 | 
					                          <List
 | 
				
			||||||
                        id={column.taskGroupID}
 | 
					 | 
				
			||||||
                            name={column.name}
 | 
					                            name={column.name}
 | 
				
			||||||
                        key={column.taskGroupID}
 | 
					 | 
				
			||||||
                            onOpenComposer={id => setCurrentComposer(id)}
 | 
					                            onOpenComposer={id => setCurrentComposer(id)}
 | 
				
			||||||
                            isComposerOpen={currentComposer === column.taskGroupID}
 | 
					                            isComposerOpen={currentComposer === column.taskGroupID}
 | 
				
			||||||
                            onSaveName={name => console.log(name)}
 | 
					                            onSaveName={name => console.log(name)}
 | 
				
			||||||
                        index={index}
 | 
					 | 
				
			||||||
                            tasks={columnCards}
 | 
					                            tasks={columnCards}
 | 
				
			||||||
                            ref={columnDragProvided.innerRef}
 | 
					                            ref={columnDragProvided.innerRef}
 | 
				
			||||||
                            wrapperProps={columnDragProvided.draggableProps}
 | 
					                            wrapperProps={columnDragProvided.draggableProps}
 | 
				
			||||||
                            headerProps={columnDragProvided.dragHandleProps}
 | 
					                            headerProps={columnDragProvided.dragHandleProps}
 | 
				
			||||||
                            onExtraMenuOpen={onExtraMenuOpen}
 | 
					                            onExtraMenuOpen={onExtraMenuOpen}
 | 
				
			||||||
 | 
					                            id={column.taskGroupID}
 | 
				
			||||||
 | 
					                            key={column.taskGroupID}
 | 
				
			||||||
 | 
					                            index={index}
 | 
				
			||||||
                          >
 | 
					                          >
 | 
				
			||||||
                        <Droppable type="tasks" droppableId={column.taskGroupID}>
 | 
					 | 
				
			||||||
                          {columnDropProvided => (
 | 
					 | 
				
			||||||
                            <ListCards ref={columnDropProvided.innerRef} {...columnDropProvided.droppableProps}>
 | 
					                            <ListCards ref={columnDropProvided.innerRef} {...columnDropProvided.droppableProps}>
 | 
				
			||||||
                              {columnCards.map((task: Task, taskIndex: any) => {
 | 
					                              {columnCards.map((task: Task, taskIndex: any) => {
 | 
				
			||||||
                                return (
 | 
					                                return (
 | 
				
			||||||
@@ -154,7 +159,7 @@ const Lists: React.FC<Props> = ({
 | 
				
			|||||||
                                          description=""
 | 
					                                          description=""
 | 
				
			||||||
                                          title={task.name}
 | 
					                                          title={task.name}
 | 
				
			||||||
                                          labels={task.labels}
 | 
					                                          labels={task.labels}
 | 
				
			||||||
                                          onClick={e => console.log(e)}
 | 
					                                          onClick={() => onCardClick(task)}
 | 
				
			||||||
                                          onContextMenu={onQuickEditorOpen}
 | 
					                                          onContextMenu={onQuickEditorOpen}
 | 
				
			||||||
                                        />
 | 
					                                        />
 | 
				
			||||||
                                      );
 | 
					                                      );
 | 
				
			||||||
@@ -163,7 +168,6 @@ const Lists: React.FC<Props> = ({
 | 
				
			|||||||
                                );
 | 
					                                );
 | 
				
			||||||
                              })}
 | 
					                              })}
 | 
				
			||||||
                              {columnDropProvided.placeholder}
 | 
					                              {columnDropProvided.placeholder}
 | 
				
			||||||
 | 
					 | 
				
			||||||
                              {currentComposer === column.taskGroupID && (
 | 
					                              {currentComposer === column.taskGroupID && (
 | 
				
			||||||
                                <CardComposer
 | 
					                                <CardComposer
 | 
				
			||||||
                                  onClose={() => {
 | 
					                                  onClose={() => {
 | 
				
			||||||
@@ -176,9 +180,9 @@ const Lists: React.FC<Props> = ({
 | 
				
			|||||||
                                />
 | 
					                                />
 | 
				
			||||||
                              )}
 | 
					                              )}
 | 
				
			||||||
                            </ListCards>
 | 
					                            </ListCards>
 | 
				
			||||||
 | 
					                          </List>
 | 
				
			||||||
                        )}
 | 
					                        )}
 | 
				
			||||||
                      </Droppable>
 | 
					                      </Droppable>
 | 
				
			||||||
                      </List>
 | 
					 | 
				
			||||||
                    )}
 | 
					                    )}
 | 
				
			||||||
                  </Draggable>
 | 
					                  </Draggable>
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { action } from '@storybook/addon-actions';
 | 
				
			||||||
 | 
					import MemberManager from '.';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  component: MemberManager,
 | 
				
			||||||
 | 
					  title: 'MemberManager',
 | 
				
			||||||
 | 
					  parameters: {
 | 
				
			||||||
 | 
					    backgrounds: [
 | 
				
			||||||
 | 
					      { name: 'white', value: '#ffffff', default: true },
 | 
				
			||||||
 | 
					      { name: 'gray', value: '#f8f8f8' },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Default = () => {
 | 
				
			||||||
 | 
					  return <MemberManager availableMembers={[]} activeMembers={[]} onMemberChange={action('member change')} />;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										81
									
								
								web/src/shared/components/MemberManager/Styles.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								web/src/shared/components/MemberManager/Styles.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					import styled from 'styled-components';
 | 
				
			||||||
 | 
					import TextareaAutosize from 'react-autosize-textarea/lib';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const MemberManagerWrapper = styled.div``;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const MemberManagerSearchWrapper = styled.div`
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const MemberManagerSearch = styled(TextareaAutosize)`
 | 
				
			||||||
 | 
					  margin: 4px 0 12px;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  background-color: #ebecf0;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  box-shadow: inset 0 0 0 2px #dfe1e6;
 | 
				
			||||||
 | 
					  line-height: 20px;
 | 
				
			||||||
 | 
					  padding: 8px 12px;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					  color: #172b4d;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const BoardMembersLabel = styled.h4`
 | 
				
			||||||
 | 
					  color: #5e6c84;
 | 
				
			||||||
 | 
					  font-size: 12px;
 | 
				
			||||||
 | 
					  font-weight: 500;
 | 
				
			||||||
 | 
					  letter-spacing: 0.04em;
 | 
				
			||||||
 | 
					  line-height: 16px;
 | 
				
			||||||
 | 
					  text-transform: uppercase;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const BoardMembersList = styled.ul`
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  list-style-type: none;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const BoardMembersListItem = styled.li``;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const BoardMemberListItemContent = styled.div`
 | 
				
			||||||
 | 
					  background-color: rgba(9, 30, 66, 0.04);
 | 
				
			||||||
 | 
					  padding-right: 28px;
 | 
				
			||||||
 | 
					  border-radius: 3px;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  height: 40px;
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  text-overflow: ellipsis;
 | 
				
			||||||
 | 
					  text-decoration: none;
 | 
				
			||||||
 | 
					  white-space: nowrap;
 | 
				
			||||||
 | 
					  padding: 4px;
 | 
				
			||||||
 | 
					  margin-bottom: 2px;
 | 
				
			||||||
 | 
					  color: #172b4d;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ProfileIcon = styled.div`
 | 
				
			||||||
 | 
					  width: 32px;
 | 
				
			||||||
 | 
					  height: 32px;
 | 
				
			||||||
 | 
					  border-radius: 9999px;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  font-weight: 700;
 | 
				
			||||||
 | 
					  background: rgb(115, 103, 240);
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  margin-right: 6px;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const MemberName = styled.span`
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ActiveIconWrapper = styled.div`
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  top: 0;
 | 
				
			||||||
 | 
					  right: 0;
 | 
				
			||||||
 | 
					  padding: 11px;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
							
								
								
									
										73
									
								
								web/src/shared/components/MemberManager/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								web/src/shared/components/MemberManager/index.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					import React, { useState } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  MemberName,
 | 
				
			||||||
 | 
					  ProfileIcon,
 | 
				
			||||||
 | 
					  MemberManagerWrapper,
 | 
				
			||||||
 | 
					  MemberManagerSearchWrapper,
 | 
				
			||||||
 | 
					  MemberManagerSearch,
 | 
				
			||||||
 | 
					  BoardMembersLabel,
 | 
				
			||||||
 | 
					  BoardMembersList,
 | 
				
			||||||
 | 
					  BoardMembersListItem,
 | 
				
			||||||
 | 
					  BoardMemberListItemContent,
 | 
				
			||||||
 | 
					  ActiveIconWrapper,
 | 
				
			||||||
 | 
					} from './Styles';
 | 
				
			||||||
 | 
					import { Checkmark } from 'shared/icons';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MemberManagerProps = {
 | 
				
			||||||
 | 
					  availableMembers: Array<TaskUser>;
 | 
				
			||||||
 | 
					  activeMembers: Array<TaskUser>;
 | 
				
			||||||
 | 
					  onMemberChange: (member: TaskUser, isActive: boolean) => void;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					const MemberManager: React.FC<MemberManagerProps> = ({
 | 
				
			||||||
 | 
					  availableMembers,
 | 
				
			||||||
 | 
					  activeMembers: initialActiveMembers,
 | 
				
			||||||
 | 
					  onMemberChange,
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
 | 
					  const [activeMembers, setActiveMembers] = useState(initialActiveMembers);
 | 
				
			||||||
 | 
					  const [currentSearch, setCurrentSearch] = useState('');
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <MemberManagerWrapper>
 | 
				
			||||||
 | 
					      <MemberManagerSearchWrapper>
 | 
				
			||||||
 | 
					        <MemberManagerSearch
 | 
				
			||||||
 | 
					          onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
 | 
				
			||||||
 | 
					            setCurrentSearch(e.currentTarget.value);
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </MemberManagerSearchWrapper>
 | 
				
			||||||
 | 
					      <BoardMembersLabel>Board Members</BoardMembersLabel>
 | 
				
			||||||
 | 
					      <BoardMembersList>
 | 
				
			||||||
 | 
					        {availableMembers
 | 
				
			||||||
 | 
					          .filter(
 | 
				
			||||||
 | 
					            member => currentSearch === '' || member.displayName.toLowerCase().startsWith(currentSearch.toLowerCase()),
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          .map(member => {
 | 
				
			||||||
 | 
					            return (
 | 
				
			||||||
 | 
					              <BoardMembersListItem>
 | 
				
			||||||
 | 
					                <BoardMemberListItemContent
 | 
				
			||||||
 | 
					                  onClick={() => {
 | 
				
			||||||
 | 
					                    const isActive = activeMembers.findIndex(m => m.userID === member.userID) !== -1;
 | 
				
			||||||
 | 
					                    if (isActive) {
 | 
				
			||||||
 | 
					                      setActiveMembers(activeMembers.filter(m => m.userID !== member.userID));
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                      setActiveMembers([...activeMembers, member]);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    onMemberChange(member, !isActive);
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <ProfileIcon>JK</ProfileIcon>
 | 
				
			||||||
 | 
					                  <MemberName>{member.displayName}</MemberName>
 | 
				
			||||||
 | 
					                  {activeMembers.findIndex(m => m.userID === member.userID) !== -1 && (
 | 
				
			||||||
 | 
					                    <ActiveIconWrapper>
 | 
				
			||||||
 | 
					                      <Checkmark size={16} color="#42526e" />
 | 
				
			||||||
 | 
					                    </ActiveIconWrapper>
 | 
				
			||||||
 | 
					                  )}
 | 
				
			||||||
 | 
					                </BoardMemberListItemContent>
 | 
				
			||||||
 | 
					              </BoardMembersListItem>
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          })}
 | 
				
			||||||
 | 
					      </BoardMembersList>
 | 
				
			||||||
 | 
					    </MemberManagerWrapper>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export default MemberManager;
 | 
				
			||||||
@@ -14,7 +14,7 @@ export const ScrollOverlay = styled.div`
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const ClickableOverlay = styled.div`
 | 
					export const ClickableOverlay = styled.div`
 | 
				
			||||||
  min-height: 100%;
 | 
					  min-height: 100%;
 | 
				
			||||||
  background: rgba(9, 30, 66, 0.54);
 | 
					  background: rgba(0, 0, 0, 0.4);
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  justify-content: center;
 | 
					  justify-content: center;
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
@@ -25,7 +25,7 @@ export const StyledModal = styled.div<{ width: number }>`
 | 
				
			|||||||
  display: inline-block;
 | 
					  display: inline-block;
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
  background: #fff;
 | 
					  background: #262c49;
 | 
				
			||||||
  max-width: ${props => props.width}px;
 | 
					  max-width: ${props => props.width}px;
 | 
				
			||||||
  vertical-align: middle;
 | 
					  vertical-align: middle;
 | 
				
			||||||
  border-radius: 3px;
 | 
					  border-radius: 3px;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,8 +4,12 @@ import LabelColors from 'shared/constants/labelColors';
 | 
				
			|||||||
import LabelManager from 'shared/components/PopupMenu/LabelManager';
 | 
					import LabelManager from 'shared/components/PopupMenu/LabelManager';
 | 
				
			||||||
import LabelEditor from 'shared/components/PopupMenu/LabelEditor';
 | 
					import LabelEditor from 'shared/components/PopupMenu/LabelEditor';
 | 
				
			||||||
import ListActions from 'shared/components/ListActions';
 | 
					import ListActions from 'shared/components/ListActions';
 | 
				
			||||||
 | 
					import MemberManager from 'shared/components/MemberManager';
 | 
				
			||||||
 | 
					import DueDateManager from 'shared/components/DueDateManager';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import PopupMenu from '.';
 | 
					import PopupMenu from '.';
 | 
				
			||||||
 | 
					import NormalizeStyles from 'App/NormalizeStyles';
 | 
				
			||||||
 | 
					import BaseStyles from 'App/BaseStyles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  component: PopupMenu,
 | 
					  component: PopupMenu,
 | 
				
			||||||
@@ -99,3 +103,93 @@ export const ListActionsPopup = () => {
 | 
				
			|||||||
    </>
 | 
					    </>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const MemberManagerPopup = () => {
 | 
				
			||||||
 | 
					  const $buttonRef = useRef<HTMLButtonElement>(null);
 | 
				
			||||||
 | 
					  const [popupData, setPopupData] = useState(initalState);
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <NormalizeStyles />
 | 
				
			||||||
 | 
					      <BaseStyles />
 | 
				
			||||||
 | 
					      {popupData.isOpen && (
 | 
				
			||||||
 | 
					        <PopupMenu title="Members" top={popupData.top} onClose={() => setPopupData(initalState)} left={popupData.left}>
 | 
				
			||||||
 | 
					          <MemberManager
 | 
				
			||||||
 | 
					            availableMembers={[{ userID: '1', displayName: 'Jordan Knott' }]}
 | 
				
			||||||
 | 
					            activeMembers={[]}
 | 
				
			||||||
 | 
					            onMemberChange={action('member change')}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </PopupMenu>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					      <span
 | 
				
			||||||
 | 
					        style={{
 | 
				
			||||||
 | 
					          width: '60px',
 | 
				
			||||||
 | 
					          textAlign: 'center',
 | 
				
			||||||
 | 
					          margin: '25px auto',
 | 
				
			||||||
 | 
					          cursor: 'pointer',
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        ref={$buttonRef}
 | 
				
			||||||
 | 
					        onClick={() => {
 | 
				
			||||||
 | 
					          if ($buttonRef && $buttonRef.current) {
 | 
				
			||||||
 | 
					            const pos = $buttonRef.current.getBoundingClientRect();
 | 
				
			||||||
 | 
					            setPopupData({
 | 
				
			||||||
 | 
					              isOpen: true,
 | 
				
			||||||
 | 
					              left: pos.left,
 | 
				
			||||||
 | 
					              top: pos.top + pos.height + 10,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        Open
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DueDateManagerPopup = () => {
 | 
				
			||||||
 | 
					  const $buttonRef = useRef<HTMLButtonElement>(null);
 | 
				
			||||||
 | 
					  const [popupData, setPopupData] = useState(initalState);
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <NormalizeStyles />
 | 
				
			||||||
 | 
					      <BaseStyles />
 | 
				
			||||||
 | 
					      {popupData.isOpen && (
 | 
				
			||||||
 | 
					        <PopupMenu title="Due Date" top={popupData.top} onClose={() => setPopupData(initalState)} left={popupData.left}>
 | 
				
			||||||
 | 
					          <DueDateManager
 | 
				
			||||||
 | 
					            task={{
 | 
				
			||||||
 | 
					              taskID: '1',
 | 
				
			||||||
 | 
					              taskGroup: { name: 'General', taskGroupID: '1' },
 | 
				
			||||||
 | 
					              name: 'Hello, world',
 | 
				
			||||||
 | 
					              position: 1,
 | 
				
			||||||
 | 
					              labels: [{ labelId: 'soft-skills', color: '#fff', active: true, name: 'Soft Skills' }],
 | 
				
			||||||
 | 
					              description: 'hello!',
 | 
				
			||||||
 | 
					              members: [{ userID: '1', displayName: 'Jordan Knott' }],
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            onCancel={action('cancel')}
 | 
				
			||||||
 | 
					            onDueDateChange={action('due date change')}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </PopupMenu>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					      <span
 | 
				
			||||||
 | 
					        style={{
 | 
				
			||||||
 | 
					          width: '60px',
 | 
				
			||||||
 | 
					          textAlign: 'center',
 | 
				
			||||||
 | 
					          margin: '25px auto',
 | 
				
			||||||
 | 
					          cursor: 'pointer',
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        ref={$buttonRef}
 | 
				
			||||||
 | 
					        onClick={() => {
 | 
				
			||||||
 | 
					          if ($buttonRef && $buttonRef.current) {
 | 
				
			||||||
 | 
					            const pos = $buttonRef.current.getBoundingClientRect();
 | 
				
			||||||
 | 
					            setPopupData({
 | 
				
			||||||
 | 
					              isOpen: true,
 | 
				
			||||||
 | 
					              left: pos.left,
 | 
				
			||||||
 | 
					              top: pos.top + pos.height + 10,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        Open
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,10 +8,9 @@ export const Container = styled.div<{ top: number; left: number; ref: any }>`
 | 
				
			|||||||
  border-radius: 3px;
 | 
					  border-radius: 3px;
 | 
				
			||||||
  box-shadow: 0 8px 16px -4px rgba(9, 30, 66, 0.25), 0 0 0 1px rgba(9, 30, 66, 0.08);
 | 
					  box-shadow: 0 8px 16px -4px rgba(9, 30, 66, 0.25), 0 0 0 1px rgba(9, 30, 66, 0.08);
 | 
				
			||||||
  display: block;
 | 
					  display: block;
 | 
				
			||||||
  overflow: hidden;
 | 
					 | 
				
			||||||
  position: absolute;
 | 
					  position: absolute;
 | 
				
			||||||
  width: 304px;
 | 
					  width: 304px;
 | 
				
			||||||
  z-index: 70;
 | 
					  z-index: 100000000000;
 | 
				
			||||||
  &:focus {
 | 
					  &:focus {
 | 
				
			||||||
    outline: none;
 | 
					    outline: none;
 | 
				
			||||||
    border: none;
 | 
					    border: none;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +1,38 @@
 | 
				
			|||||||
import styled from 'styled-components';
 | 
					import styled from 'styled-components';
 | 
				
			||||||
import TextareaAutosize from 'react-autosize-textarea/lib';
 | 
					import TextareaAutosize from 'react-autosize-textarea/lib';
 | 
				
			||||||
 | 
					import { mixin } from 'shared/utils/styles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TaskHeader = styled.div`
 | 
					export const TaskHeader = styled.div`
 | 
				
			||||||
 | 
					  padding: 21px 30px 0px;
 | 
				
			||||||
 | 
					  margin-right: 70px;
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  -webkit-box-pack: justify;
 | 
					  flex-direction: column;
 | 
				
			||||||
  justify-content: space-between;
 | 
					 | 
				
			||||||
  padding: 21px 18px 0px;
 | 
					 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TaskMeta = styled.div`
 | 
					export const TaskMeta = styled.div`
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
  font-size: 14px;
 | 
					  font-size: 14px;
 | 
				
			||||||
  display: inline-block;
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
  border-radius: 4px;
 | 
					  border-radius: 4px;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TaskGroupLabel = styled.span`
 | 
				
			||||||
 | 
					  color: #c2c6dc;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					export const TaskGroupLabelName = styled.span`
 | 
				
			||||||
 | 
					  color: #c2c6dc;
 | 
				
			||||||
 | 
					  text-decoration: underline;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TaskActions = styled.div`
 | 
					export const TaskActions = styled.div`
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  top: 0;
 | 
				
			||||||
 | 
					  right: 0;
 | 
				
			||||||
 | 
					  padding: 21px 18px 0px;
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
@@ -26,19 +42,8 @@ export const TaskAction = styled.button`
 | 
				
			|||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  justify-content: center;
 | 
					  justify-content: center;
 | 
				
			||||||
  height: 32px;
 | 
					  height: 32px;
 | 
				
			||||||
  vertical-align: middle;
 | 
					 | 
				
			||||||
  line-height: 1;
 | 
					 | 
				
			||||||
  white-space: nowrap;
 | 
					 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
  user-select: none;
 | 
					 | 
				
			||||||
  font-size: 14.5px;
 | 
					 | 
				
			||||||
  color: rgb(66, 82, 110);
 | 
					 | 
				
			||||||
  font-family: CircularStdBook;
 | 
					 | 
				
			||||||
  font-weight: normal;
 | 
					 | 
				
			||||||
  padding: 0px 9px;
 | 
					  padding: 0px 9px;
 | 
				
			||||||
  border-radius: 3px;
 | 
					 | 
				
			||||||
  transition: all 0.1s ease 0s;
 | 
					 | 
				
			||||||
  background: rgb(255, 255, 255);
 | 
					 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TaskDetailsWrapper = styled.div`
 | 
					export const TaskDetailsWrapper = styled.div`
 | 
				
			||||||
@@ -53,13 +58,12 @@ export const TaskDetailsContent = styled.div`
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const TaskDetailsSidebar = styled.div`
 | 
					export const TaskDetailsSidebar = styled.div`
 | 
				
			||||||
  width: 35%;
 | 
					  width: 35%;
 | 
				
			||||||
  padding-top: 5px;
 | 
					 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TaskDetailsTitleWrapper = styled.div`
 | 
					export const TaskDetailsTitleWrapper = styled.div`
 | 
				
			||||||
  height: 44px;
 | 
					  height: 44px;
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
  margin: 18px 0px 0px -8px;
 | 
					  margin: 0 0 0 -8px;
 | 
				
			||||||
  display: inline-block;
 | 
					  display: inline-block;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -70,8 +74,8 @@ export const TaskDetailsTitle = styled(TextareaAutosize)`
 | 
				
			|||||||
  font-size: 24px;
 | 
					  font-size: 24px;
 | 
				
			||||||
  font-family: 'Droid Sans';
 | 
					  font-family: 'Droid Sans';
 | 
				
			||||||
  font-weight: 700;
 | 
					  font-weight: 700;
 | 
				
			||||||
  padding: 7px 7px 8px;
 | 
					  padding: 4px;
 | 
				
			||||||
  background: rgb(255, 255, 255);
 | 
					  background: #262c49;
 | 
				
			||||||
  border-width: 1px;
 | 
					  border-width: 1px;
 | 
				
			||||||
  border-style: solid;
 | 
					  border-style: solid;
 | 
				
			||||||
  border-color: transparent;
 | 
					  border-color: transparent;
 | 
				
			||||||
@@ -79,28 +83,22 @@ export const TaskDetailsTitle = styled(TextareaAutosize)`
 | 
				
			|||||||
  transition: background 0.1s ease 0s;
 | 
					  transition: background 0.1s ease 0s;
 | 
				
			||||||
  overflow-y: hidden;
 | 
					  overflow-y: hidden;
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
  color: rgb(23, 43, 77);
 | 
					  color: #c2c6dc;
 | 
				
			||||||
  &:hover {
 | 
					 | 
				
			||||||
    background: rgb(235, 236, 240);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  &:focus {
 | 
					  &:focus {
 | 
				
			||||||
    box-shadow: rgb(76, 154, 255) 0px 0px 0px 1px;
 | 
					    box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
 | 
				
			||||||
    background: rgb(255, 255, 255);
 | 
					    background: ${mixin.darken('#262c49', 0.15)};
 | 
				
			||||||
    border-width: 1px;
 | 
					 | 
				
			||||||
    border-style: solid;
 | 
					 | 
				
			||||||
    border-color: rgb(76, 154, 255);
 | 
					 | 
				
			||||||
    border-image: initial;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TaskDetailsLabel = styled.div`
 | 
					export const TaskDetailsLabel = styled.div`
 | 
				
			||||||
  padding: 20px 0px 12px;
 | 
					  padding: 24px 0px 12px;
 | 
				
			||||||
  font-size: 15px;
 | 
					  font-size: 15px;
 | 
				
			||||||
  font-weight: 600;
 | 
					  font-weight: 600;
 | 
				
			||||||
 | 
					  color: #c2c6dc;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TaskDetailsAddDetailsButton = styled.div`
 | 
					export const TaskDetailsAddDetailsButton = styled.div`
 | 
				
			||||||
  background-color: rgba(9, 30, 66, 0.04);
 | 
					  background: ${mixin.darken('#262c49', 0.15)};
 | 
				
			||||||
  box-shadow: none;
 | 
					  box-shadow: none;
 | 
				
			||||||
  border: none;
 | 
					  border: none;
 | 
				
			||||||
  border-radius: 3px;
 | 
					  border-radius: 3px;
 | 
				
			||||||
@@ -110,8 +108,9 @@ export const TaskDetailsAddDetailsButton = styled.div`
 | 
				
			|||||||
  text-decoration: none;
 | 
					  text-decoration: none;
 | 
				
			||||||
  font-size: 14px;
 | 
					  font-size: 14px;
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  color: #c2c6dc;
 | 
				
			||||||
  &:hover {
 | 
					  &:hover {
 | 
				
			||||||
    background-color: rgba(9, 30, 66, 0.08);
 | 
					    background: ${mixin.darken('#262c49', 0.25)};
 | 
				
			||||||
    box-shadow: none;
 | 
					    box-shadow: none;
 | 
				
			||||||
    border: none;
 | 
					    border: none;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -128,9 +127,9 @@ export const TaskDetailsEditorWrapper = styled.div`
 | 
				
			|||||||
export const TaskDetailsEditor = styled(TextareaAutosize)`
 | 
					export const TaskDetailsEditor = styled(TextareaAutosize)`
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
  min-height: 108px;
 | 
					  min-height: 108px;
 | 
				
			||||||
  background: #fff;
 | 
					  color: #c2c6dc;
 | 
				
			||||||
  box-shadow: none;
 | 
					  background: #262c49;
 | 
				
			||||||
  border-color: rgba(9, 30, 66, 0.13);
 | 
					  box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
 | 
				
			||||||
  border-radius: 3px;
 | 
					  border-radius: 3px;
 | 
				
			||||||
  line-height: 20px;
 | 
					  line-height: 20px;
 | 
				
			||||||
  padding: 8px 12px;
 | 
					  padding: 8px 12px;
 | 
				
			||||||
@@ -138,15 +137,15 @@ export const TaskDetailsEditor = styled(TextareaAutosize)`
 | 
				
			|||||||
  border: none;
 | 
					  border: none;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &:focus {
 | 
					  &:focus {
 | 
				
			||||||
    background: #fff;
 | 
					    box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
 | 
				
			||||||
    border: none;
 | 
					    background: ${mixin.darken('#262c49', 0.05)};
 | 
				
			||||||
    box-shadow: inset 0 0 0 2px #0079bf;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TaskDetailsMarkdown = styled.div`
 | 
					export const TaskDetailsMarkdown = styled.div`
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  color: #c2c6dc;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TaskDetailsControls = styled.div`
 | 
					export const TaskDetailsControls = styled.div`
 | 
				
			||||||
@@ -179,3 +178,106 @@ export const CancelEdit = styled.div`
 | 
				
			|||||||
  width: 32px;
 | 
					  width: 32px;
 | 
				
			||||||
  cursor: pointer;
 | 
					  cursor: pointer;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TaskDetailSectionTitle = styled.div`
 | 
				
			||||||
 | 
					  text-transform: uppercase;
 | 
				
			||||||
 | 
					  color: #c2c6dc;
 | 
				
			||||||
 | 
					  font-size: 12.5px;
 | 
				
			||||||
 | 
					  font-weight: 600;
 | 
				
			||||||
 | 
					  margin: 24px 0px 5px;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TaskDetailAssignees = styled.div`
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-wrap: wrap;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TaskDetailAssignee = styled.div`
 | 
				
			||||||
 | 
					  &:hover {
 | 
				
			||||||
 | 
					    opacity: 0.8;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  margin-right: 4px;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					export const ProfileIcon = styled.div`
 | 
				
			||||||
 | 
					  width: 32px;
 | 
				
			||||||
 | 
					  height: 32px;
 | 
				
			||||||
 | 
					  border-radius: 9999px;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  font-weight: 700;
 | 
				
			||||||
 | 
					  background: rgb(115, 103, 240);
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TaskDetailsAddMember = styled.div`
 | 
				
			||||||
 | 
					  border-radius: 100%;
 | 
				
			||||||
 | 
					  background: ${mixin.darken('#262c49', 0.15)};
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  &:hover {
 | 
				
			||||||
 | 
					    opacity: 0.8;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TaskDetailsAddMemberIcon = styled.div`
 | 
				
			||||||
 | 
					  height: 32px;
 | 
				
			||||||
 | 
					  width: 32px;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TaskDetailLabels = styled.div`
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  flex-wrap: wrap;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TaskDetailLabel = styled.div`
 | 
				
			||||||
 | 
					  &:hover {
 | 
				
			||||||
 | 
					    opacity: 0.8;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  background-color: #00c2e0;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  border-radius: 3px;
 | 
				
			||||||
 | 
					  box-sizing: border-box;
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  float: left;
 | 
				
			||||||
 | 
					  font-weight: 600;
 | 
				
			||||||
 | 
					  height: 32px;
 | 
				
			||||||
 | 
					  line-height: 32px;
 | 
				
			||||||
 | 
					  margin: 0 4px 4px 0;
 | 
				
			||||||
 | 
					  min-width: 40px;
 | 
				
			||||||
 | 
					  padding: 0 12px;
 | 
				
			||||||
 | 
					  width: auto;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TaskDetailsAddLabel = styled.div`
 | 
				
			||||||
 | 
					  border-radius: 3px;
 | 
				
			||||||
 | 
					  background: ${mixin.darken('#262c49', 0.15)};
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  &:hover {
 | 
				
			||||||
 | 
					    opacity: 0.8;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TaskDetailsAddLabelIcon = styled.div`
 | 
				
			||||||
 | 
					  height: 32px;
 | 
				
			||||||
 | 
					  width: 32px;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const NoDueDateLabel = styled.span`
 | 
				
			||||||
 | 
					  color: rgb(137, 147, 164);
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,15 +30,19 @@ export const Default = () => {
 | 
				
			|||||||
            <TaskDetails
 | 
					            <TaskDetails
 | 
				
			||||||
              task={{
 | 
					              task={{
 | 
				
			||||||
                taskID: '1',
 | 
					                taskID: '1',
 | 
				
			||||||
                taskGroup: { taskGroupID: '1' },
 | 
					                taskGroup: { name: 'General', taskGroupID: '1' },
 | 
				
			||||||
                name: 'Hello, world',
 | 
					                name: 'Hello, world',
 | 
				
			||||||
                position: 1,
 | 
					                position: 1,
 | 
				
			||||||
                labels: [],
 | 
					                labels: [{ labelId: 'soft-skills', color: '#fff', active: true, name: 'Soft Skills' }],
 | 
				
			||||||
                description,
 | 
					                description,
 | 
				
			||||||
 | 
					                members: [{ userID: '1', displayName: 'Jordan Knott' }],
 | 
				
			||||||
              }}
 | 
					              }}
 | 
				
			||||||
 | 
					              onTaskNameChange={action('task name change')}
 | 
				
			||||||
              onTaskDescriptionChange={(_task, desc) => setDescription(desc)}
 | 
					              onTaskDescriptionChange={(_task, desc) => setDescription(desc)}
 | 
				
			||||||
              onDeleteTask={action('delete task')}
 | 
					              onDeleteTask={action('delete task')}
 | 
				
			||||||
              onCloseModal={action('close modal')}
 | 
					              onCloseModal={action('close modal')}
 | 
				
			||||||
 | 
					              onOpenAddMemberPopup={action('open add member popup')}
 | 
				
			||||||
 | 
					              onOpenAddLabelPopup={action('open add label popup')}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        }}
 | 
					        }}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,19 @@
 | 
				
			|||||||
import React, { useState, useRef, useEffect } from 'react';
 | 
					import React, { useState, useRef, useEffect } from 'react';
 | 
				
			||||||
import { Bin, Cross } from 'shared/icons';
 | 
					import { Bin, Cross, Plus } from 'shared/icons';
 | 
				
			||||||
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
 | 
					import useOnOutsideClick from 'shared/hooks/onOutsideClick';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
 | 
					  NoDueDateLabel,
 | 
				
			||||||
 | 
					  TaskDetailsAddMember,
 | 
				
			||||||
 | 
					  TaskGroupLabel,
 | 
				
			||||||
 | 
					  TaskGroupLabelName,
 | 
				
			||||||
  TaskActions,
 | 
					  TaskActions,
 | 
				
			||||||
 | 
					  TaskDetailsAddLabel,
 | 
				
			||||||
 | 
					  TaskDetailsAddLabelIcon,
 | 
				
			||||||
  TaskAction,
 | 
					  TaskAction,
 | 
				
			||||||
  TaskMeta,
 | 
					  TaskMeta,
 | 
				
			||||||
  TaskHeader,
 | 
					  TaskHeader,
 | 
				
			||||||
 | 
					  ProfileIcon,
 | 
				
			||||||
  TaskDetailsContent,
 | 
					  TaskDetailsContent,
 | 
				
			||||||
  TaskDetailsWrapper,
 | 
					  TaskDetailsWrapper,
 | 
				
			||||||
  TaskDetailsSidebar,
 | 
					  TaskDetailsSidebar,
 | 
				
			||||||
@@ -20,8 +27,16 @@ import {
 | 
				
			|||||||
  TaskDetailsControls,
 | 
					  TaskDetailsControls,
 | 
				
			||||||
  ConfirmSave,
 | 
					  ConfirmSave,
 | 
				
			||||||
  CancelEdit,
 | 
					  CancelEdit,
 | 
				
			||||||
 | 
					  TaskDetailSectionTitle,
 | 
				
			||||||
 | 
					  TaskDetailLabel,
 | 
				
			||||||
 | 
					  TaskDetailLabels,
 | 
				
			||||||
 | 
					  TaskDetailAssignee,
 | 
				
			||||||
 | 
					  TaskDetailAssignees,
 | 
				
			||||||
 | 
					  TaskDetailsAddMemberIcon,
 | 
				
			||||||
} from './Styles';
 | 
					} from './Styles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import convertDivElementRefToBounds from 'shared/utils/boundingRect';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TaskContentProps = {
 | 
					type TaskContentProps = {
 | 
				
			||||||
  onEditContent: () => void;
 | 
					  onEditContent: () => void;
 | 
				
			||||||
  description: string;
 | 
					  description: string;
 | 
				
			||||||
@@ -70,7 +85,7 @@ const DetailsEditor: React.FC<DetailsEditorProps> = ({
 | 
				
			|||||||
      <TaskDetailsControls>
 | 
					      <TaskDetailsControls>
 | 
				
			||||||
        <ConfirmSave onClick={handleOutsideClick}>Save</ConfirmSave>
 | 
					        <ConfirmSave onClick={handleOutsideClick}>Save</ConfirmSave>
 | 
				
			||||||
        <CancelEdit onClick={onCancel}>
 | 
					        <CancelEdit onClick={onCancel}>
 | 
				
			||||||
          <Cross size={16} />
 | 
					          <Plus size={16} color="#c2c6dc" />
 | 
				
			||||||
        </CancelEdit>
 | 
					        </CancelEdit>
 | 
				
			||||||
      </TaskDetailsControls>
 | 
					      </TaskDetailsControls>
 | 
				
			||||||
    </TaskDetailsEditorWrapper>
 | 
					    </TaskDetailsEditorWrapper>
 | 
				
			||||||
@@ -79,37 +94,81 @@ const DetailsEditor: React.FC<DetailsEditorProps> = ({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type TaskDetailsProps = {
 | 
					type TaskDetailsProps = {
 | 
				
			||||||
  task: Task;
 | 
					  task: Task;
 | 
				
			||||||
 | 
					  onTaskNameChange: (task: Task, newName: string) => void;
 | 
				
			||||||
  onTaskDescriptionChange: (task: Task, newDescription: string) => void;
 | 
					  onTaskDescriptionChange: (task: Task, newDescription: string) => void;
 | 
				
			||||||
  onDeleteTask: (task: Task) => void;
 | 
					  onDeleteTask: (task: Task) => void;
 | 
				
			||||||
  onCloseModal: () => void;
 | 
					  onCloseModal: () => void;
 | 
				
			||||||
 | 
					  onOpenAddMemberPopup: (task: Task, bounds: ElementBounds) => void;
 | 
				
			||||||
 | 
					  onOpenAddLabelPopup: (task: Task, bounds: ElementBounds) => void;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TaskDetails: React.FC<TaskDetailsProps> = ({ task, onTaskDescriptionChange, onDeleteTask, onCloseModal }) => {
 | 
					const TaskDetails: React.FC<TaskDetailsProps> = ({
 | 
				
			||||||
 | 
					  task,
 | 
				
			||||||
 | 
					  onTaskNameChange,
 | 
				
			||||||
 | 
					  onTaskDescriptionChange,
 | 
				
			||||||
 | 
					  onDeleteTask,
 | 
				
			||||||
 | 
					  onCloseModal,
 | 
				
			||||||
 | 
					  onOpenAddMemberPopup,
 | 
				
			||||||
 | 
					  onOpenAddLabelPopup,
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
  const [editorOpen, setEditorOpen] = useState(false);
 | 
					  const [editorOpen, setEditorOpen] = useState(false);
 | 
				
			||||||
 | 
					  const [taskName, setTaskName] = useState(task.name);
 | 
				
			||||||
  const handleClick = () => {
 | 
					  const handleClick = () => {
 | 
				
			||||||
    setEditorOpen(!editorOpen);
 | 
					    setEditorOpen(!editorOpen);
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  const handleDeleteTask = () => {
 | 
					  const handleDeleteTask = () => {
 | 
				
			||||||
    onDeleteTask(task);
 | 
					    onDeleteTask(task);
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					  const onKeyDown = (e: React.KeyboardEvent) => {
 | 
				
			||||||
 | 
					    if (e.key === 'Enter') {
 | 
				
			||||||
 | 
					      e.preventDefault();
 | 
				
			||||||
 | 
					      onTaskNameChange(task, taskName);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  const $addMemberRef = useRef<HTMLDivElement>(null);
 | 
				
			||||||
 | 
					  const onAddMember = () => {
 | 
				
			||||||
 | 
					    console.log('beep!');
 | 
				
			||||||
 | 
					    const bounds = convertDivElementRefToBounds($addMemberRef);
 | 
				
			||||||
 | 
					    console.log(bounds);
 | 
				
			||||||
 | 
					    if (bounds) {
 | 
				
			||||||
 | 
					      onOpenAddMemberPopup(task, bounds);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  const $addLabelRef = useRef<HTMLDivElement>(null);
 | 
				
			||||||
 | 
					  const onAddLabel = () => {
 | 
				
			||||||
 | 
					    const bounds = convertDivElementRefToBounds($addLabelRef);
 | 
				
			||||||
 | 
					    if (bounds) {
 | 
				
			||||||
 | 
					      onOpenAddLabelPopup(task, bounds);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <>
 | 
					    <>
 | 
				
			||||||
      <TaskHeader>
 | 
					 | 
				
			||||||
        <TaskMeta />
 | 
					 | 
				
			||||||
      <TaskActions>
 | 
					      <TaskActions>
 | 
				
			||||||
        <TaskAction onClick={handleDeleteTask}>
 | 
					        <TaskAction onClick={handleDeleteTask}>
 | 
				
			||||||
            <Bin size={20} />
 | 
					          <Bin size={20} color="#c2c6dc" />
 | 
				
			||||||
        </TaskAction>
 | 
					        </TaskAction>
 | 
				
			||||||
        <TaskAction onClick={onCloseModal}>
 | 
					        <TaskAction onClick={onCloseModal}>
 | 
				
			||||||
            <Cross size={20} />
 | 
					          <Cross size={20} color="#c2c6dc" />
 | 
				
			||||||
        </TaskAction>
 | 
					        </TaskAction>
 | 
				
			||||||
      </TaskActions>
 | 
					      </TaskActions>
 | 
				
			||||||
 | 
					      <TaskHeader>
 | 
				
			||||||
 | 
					        <TaskDetailsTitleWrapper>
 | 
				
			||||||
 | 
					          <TaskDetailsTitle
 | 
				
			||||||
 | 
					            value={taskName}
 | 
				
			||||||
 | 
					            onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setTaskName(e.currentTarget.value)}
 | 
				
			||||||
 | 
					            onKeyDown={onKeyDown}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </TaskDetailsTitleWrapper>
 | 
				
			||||||
 | 
					        <TaskMeta>
 | 
				
			||||||
 | 
					          {task.taskGroup.name && (
 | 
				
			||||||
 | 
					            <TaskGroupLabel>
 | 
				
			||||||
 | 
					              in list <TaskGroupLabelName>{task.taskGroup.name}</TaskGroupLabelName>
 | 
				
			||||||
 | 
					            </TaskGroupLabel>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					        </TaskMeta>
 | 
				
			||||||
      </TaskHeader>
 | 
					      </TaskHeader>
 | 
				
			||||||
      <TaskDetailsWrapper>
 | 
					      <TaskDetailsWrapper>
 | 
				
			||||||
        <TaskDetailsContent>
 | 
					        <TaskDetailsContent>
 | 
				
			||||||
          <TaskDetailsTitleWrapper>
 | 
					 | 
				
			||||||
            <TaskDetailsTitle value="Hello darkness my old friend" />
 | 
					 | 
				
			||||||
          </TaskDetailsTitleWrapper>
 | 
					 | 
				
			||||||
          <TaskDetailsLabel>Description</TaskDetailsLabel>
 | 
					          <TaskDetailsLabel>Description</TaskDetailsLabel>
 | 
				
			||||||
          {editorOpen ? (
 | 
					          {editorOpen ? (
 | 
				
			||||||
            <DetailsEditor
 | 
					            <DetailsEditor
 | 
				
			||||||
@@ -126,7 +185,38 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({ task, onTaskDescriptionChange
 | 
				
			|||||||
            <TaskContent description={task.description ?? ''} onEditContent={handleClick} />
 | 
					            <TaskContent description={task.description ?? ''} onEditContent={handleClick} />
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
        </TaskDetailsContent>
 | 
					        </TaskDetailsContent>
 | 
				
			||||||
        <TaskDetailsSidebar />
 | 
					        <TaskDetailsSidebar>
 | 
				
			||||||
 | 
					          <TaskDetailSectionTitle>Assignees</TaskDetailSectionTitle>
 | 
				
			||||||
 | 
					          <TaskDetailAssignees>
 | 
				
			||||||
 | 
					            {task.members &&
 | 
				
			||||||
 | 
					              task.members.map(member => {
 | 
				
			||||||
 | 
					                const initials = 'JK';
 | 
				
			||||||
 | 
					                return (
 | 
				
			||||||
 | 
					                  <TaskDetailAssignee key={member.userID}>
 | 
				
			||||||
 | 
					                    <ProfileIcon>{initials}</ProfileIcon>
 | 
				
			||||||
 | 
					                  </TaskDetailAssignee>
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					              })}
 | 
				
			||||||
 | 
					            <TaskDetailsAddMember ref={$addMemberRef} onClick={onAddMember}>
 | 
				
			||||||
 | 
					              <TaskDetailsAddMemberIcon>
 | 
				
			||||||
 | 
					                <Plus size={16} color="#c2c6dc" />
 | 
				
			||||||
 | 
					              </TaskDetailsAddMemberIcon>
 | 
				
			||||||
 | 
					            </TaskDetailsAddMember>
 | 
				
			||||||
 | 
					          </TaskDetailAssignees>
 | 
				
			||||||
 | 
					          <TaskDetailSectionTitle>Labels</TaskDetailSectionTitle>
 | 
				
			||||||
 | 
					          <TaskDetailLabels>
 | 
				
			||||||
 | 
					            {task.labels.map(label => {
 | 
				
			||||||
 | 
					              return <TaskDetailLabel>{label.name}</TaskDetailLabel>;
 | 
				
			||||||
 | 
					            })}
 | 
				
			||||||
 | 
					            <TaskDetailsAddLabel ref={$addLabelRef} onClick={onAddLabel}>
 | 
				
			||||||
 | 
					              <TaskDetailsAddLabelIcon>
 | 
				
			||||||
 | 
					                <Plus size={16} color="#c2c6dc" />
 | 
				
			||||||
 | 
					              </TaskDetailsAddLabelIcon>
 | 
				
			||||||
 | 
					            </TaskDetailsAddLabel>
 | 
				
			||||||
 | 
					          </TaskDetailLabels>
 | 
				
			||||||
 | 
					          <TaskDetailSectionTitle>Due Date</TaskDetailSectionTitle>
 | 
				
			||||||
 | 
					          <NoDueDateLabel>No due date</NoDueDateLabel>
 | 
				
			||||||
 | 
					        </TaskDetailsSidebar>
 | 
				
			||||||
      </TaskDetailsWrapper>
 | 
					      </TaskDetailsWrapper>
 | 
				
			||||||
    </>
 | 
					    </>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										104
									
								
								web/src/shared/undraw/NoData.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								web/src/shared/undraw/NoData.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					  width: number;
 | 
				
			||||||
 | 
					  height: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const AccessAccount = ({ width, height }: Props) => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <svg data-name="Layer 1" width={width} height={height} viewBox="0 0 820.16 780.81">
 | 
				
			||||||
 | 
					      <defs>
 | 
				
			||||||
 | 
					        <linearGradient
 | 
				
			||||||
 | 
					          id="a"
 | 
				
			||||||
 | 
					          x1="539.63"
 | 
				
			||||||
 | 
					          y1="734.6"
 | 
				
			||||||
 | 
					          x2="539.63"
 | 
				
			||||||
 | 
					          y2="151.19"
 | 
				
			||||||
 | 
					          gradientTransform="translate(-3.62 1.57)"
 | 
				
			||||||
 | 
					          gradientUnits="userSpaceOnUse"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <stop offset="0" stop-color="gray" stop-opacity=".25" />
 | 
				
			||||||
 | 
					          <stop offset=".54" stop-color="gray" stop-opacity=".12" />
 | 
				
			||||||
 | 
					          <stop offset="1" stop-color="gray" stop-opacity=".1" />
 | 
				
			||||||
 | 
					        </linearGradient>
 | 
				
			||||||
 | 
					        <linearGradient
 | 
				
			||||||
 | 
					          id="b"
 | 
				
			||||||
 | 
					          x1="540.17"
 | 
				
			||||||
 | 
					          y1="180.2"
 | 
				
			||||||
 | 
					          x2="540.17"
 | 
				
			||||||
 | 
					          y2="130.75"
 | 
				
			||||||
 | 
					          gradientTransform="translate(-63.92 7.85)"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <linearGradient
 | 
				
			||||||
 | 
					          id="c"
 | 
				
			||||||
 | 
					          x1="540.17"
 | 
				
			||||||
 | 
					          y1="140.86"
 | 
				
			||||||
 | 
					          x2="540.17"
 | 
				
			||||||
 | 
					          y2="82.43"
 | 
				
			||||||
 | 
					          gradientTransform="rotate(-12.11 545.066 460.65)"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <linearGradient id="d" x1="476.4" y1="710.53" x2="476.4" y2="127.12" />
 | 
				
			||||||
 | 
					        <linearGradient id="e" x1="476.94" y1="156.13" x2="476.94" y2="106.68" />
 | 
				
			||||||
 | 
					        <linearGradient id="f" x1="666.86" y1="176.39" x2="666.86" y2="117.95" />
 | 
				
			||||||
 | 
					      </defs>
 | 
				
			||||||
 | 
					      <path fill="#e0e0e0" d="M69.12 135.49l427.295-91.682L623.09 634.19l-427.295 91.682z" />
 | 
				
			||||||
 | 
					      <path
 | 
				
			||||||
 | 
					        transform="rotate(-12.11 160.03 1309.797)"
 | 
				
			||||||
 | 
					        fill="url(#a)"
 | 
				
			||||||
 | 
					        d="M324.89 152.76h422.25v583.41H324.89z"
 | 
				
			||||||
 | 
					        opacity=".5"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <path fill="#fafafa" d="M84.639 146.993L486.98 60.665l119.69 557.824-402.344 86.328z" />
 | 
				
			||||||
 | 
					      <path transform="rotate(-12.11 100.28 1028.707)" fill="url(#b)" d="M374.18 138.6h204.14v49.45H374.18z" />
 | 
				
			||||||
 | 
					      <path
 | 
				
			||||||
 | 
					        d="M460.93 91.9c-15.41 3.31-25.16 18.78-21.77 34.55s18.62 25.89 34 22.58 25.16-18.78 21.77-34.55-18.59-25.89-34-22.58zm9.67 45.1a16.86 16.86 0 1112.56-20 16.66 16.66 0 01-12.56 20z"
 | 
				
			||||||
 | 
					        transform="translate(-189.92 -59.59)"
 | 
				
			||||||
 | 
					        fill="url(#c)"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <path fill="#6c63ff" d="M183.007 98.422L378.4 56.498l9.917 46.218-195.393 41.924z" />
 | 
				
			||||||
 | 
					      <path
 | 
				
			||||||
 | 
					        d="M271.01 32.31a27.93 27.93 0 1033.17 21.45 27.93 27.93 0 00-33.17-21.45zm9.24 43.1a16.12 16.12 0 1112.38-19.14 16.12 16.12 0 01-12.38 19.14z"
 | 
				
			||||||
 | 
					        fill="#6c63ff"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <path fill="#e0e0e0" d="M257.89 116.91h437.02v603.82H257.89z" />
 | 
				
			||||||
 | 
					      <path fill="url(#d)" d="M265.28 127.12h422.25v583.41H265.28z" opacity=".5" />
 | 
				
			||||||
 | 
					      <path fill="#fff" d="M270.65 131.42h411.5v570.52h-411.5z" />
 | 
				
			||||||
 | 
					      <path fill="url(#e)" d="M374.87 106.68h204.14v49.45H374.87z" />
 | 
				
			||||||
 | 
					      <path
 | 
				
			||||||
 | 
					        d="M666.86 118c-15.76 0-28.54 13.08-28.54 29.22s12.78 29.22 28.54 29.22 28.54-13.08 28.54-29.22S682.62 118 666.86 118zm0 46.08a16.86 16.86 0 1116.46-16.86A16.66 16.66 0 01666.86 164z"
 | 
				
			||||||
 | 
					        transform="translate(-189.92 -59.59)"
 | 
				
			||||||
 | 
					        fill="url(#f)"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <path fill="#6c63ff" d="M377.02 104.56h199.84v47.27H377.02z" />
 | 
				
			||||||
 | 
					      <path
 | 
				
			||||||
 | 
					        d="M476.94 58.41a27.93 27.93 0 1027.93 27.93 27.93 27.93 0 00-27.93-27.93zm0 44.05a16.12 16.12 0 1116.14-16.16 16.12 16.12 0 01-16.14 16.11z"
 | 
				
			||||||
 | 
					        fill="#6c63ff"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <g opacity=".5" fill="#47e6b1">
 | 
				
			||||||
 | 
					        <path d="M15.27 737.05h3.76v21.33h-3.76z" />
 | 
				
			||||||
 | 
					        <path d="M27.82 745.84v3.76H6.49v-3.76z" />
 | 
				
			||||||
 | 
					      </g>
 | 
				
			||||||
 | 
					      <g opacity=".5" fill="#47e6b1">
 | 
				
			||||||
 | 
					        <path d="M451.49 0h3.76v21.33h-3.76z" />
 | 
				
			||||||
 | 
					        <path d="M464.04 8.78v3.76h-21.33V8.78z" />
 | 
				
			||||||
 | 
					      </g>
 | 
				
			||||||
 | 
					      <path
 | 
				
			||||||
 | 
					        d="M771.08 772.56a4.61 4.61 0 01-2.57-5.57 2.22 2.22 0 00.1-.51 2.31 2.31 0 00-4.15-1.53 2.22 2.22 0 00-.26.45 4.61 4.61 0 01-5.57 2.57 2.22 2.22 0 00-.51-.1 2.31 2.31 0 00-1.53 4.15 2.22 2.22 0 00.45.26 4.61 4.61 0 012.57 5.57 2.22 2.22 0 00-.1.51 2.31 2.31 0 004.15 1.53 2.22 2.22 0 00.26-.45 4.61 4.61 0 015.57-2.57 2.22 2.22 0 00.51.1 2.31 2.31 0 001.53-4.15 2.22 2.22 0 00-.45-.26z"
 | 
				
			||||||
 | 
					        fill="#4d8af0"
 | 
				
			||||||
 | 
					        opacity=".5"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <path
 | 
				
			||||||
 | 
					        d="M136.67 567.5a4.61 4.61 0 01-2.57-5.57 2.22 2.22 0 00.1-.51 2.31 2.31 0 00-4.15-1.53 2.22 2.22 0 00-.26.45 4.61 4.61 0 01-5.57 2.57 2.22 2.22 0 00-.51-.1 2.31 2.31 0 00-1.53 4.15 2.22 2.22 0 00.45.26 4.61 4.61 0 012.57 5.57 2.22 2.22 0 00-.1.51 2.31 2.31 0 004.15 1.53 2.22 2.22 0 00.26-.45 4.61 4.61 0 015.57-2.57 2.22 2.22 0 00.51.1 2.31 2.31 0 001.53-4.15 2.22 2.22 0 00-.45-.26zM665.08 68.18a4.61 4.61 0 01-2.57-5.57 2.22 2.22 0 00.1-.51 2.31 2.31 0 00-4.15-1.53 2.22 2.22 0 00-.26.45 4.61 4.61 0 01-5.57 2.57 2.22 2.22 0 00-.51-.1 2.31 2.31 0 00-1.53 4.15 2.22 2.22 0 00.45.26 4.61 4.61 0 012.57 5.57 2.22 2.22 0 00-.1.51 2.31 2.31 0 004.15 1.53 2.22 2.22 0 00.26-.45 4.61 4.61 0 015.57-2.57 2.22 2.22 0 00.51.1 2.31 2.31 0 001.53-4.15 2.22 2.22 0 00-.45-.26z"
 | 
				
			||||||
 | 
					        fill="#fdd835"
 | 
				
			||||||
 | 
					        opacity=".5"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <circle cx="812.64" cy="314.47" r="7.53" fill="#f55f44" opacity=".5" />
 | 
				
			||||||
 | 
					      <circle cx="230.73" cy="746.65" r="7.53" fill="#f55f44" opacity=".5" />
 | 
				
			||||||
 | 
					      <circle cx="735.31" cy="477.23" r="7.53" fill="#f55f44" opacity=".5" />
 | 
				
			||||||
 | 
					      <circle cx="87.14" cy="96.35" r="7.53" fill="#4d8af0" opacity=".5" />
 | 
				
			||||||
 | 
					      <circle cx="7.53" cy="301.76" r="7.53" fill="#47e6b1" opacity=".5" />
 | 
				
			||||||
 | 
					    </svg>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export default AccessAccount;
 | 
				
			||||||
							
								
								
									
										20
									
								
								web/src/shared/utils/boundingRect.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								web/src/shared/utils/boundingRect.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					export const convertDivElementRefToBounds = ($ref: React.RefObject<HTMLDivElement>) => {
 | 
				
			||||||
 | 
					  if ($ref && $ref.current) {
 | 
				
			||||||
 | 
					    const bounds = $ref.current.getBoundingClientRect();
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      size: {
 | 
				
			||||||
 | 
					        width: bounds.width,
 | 
				
			||||||
 | 
					        height: bounds.height,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      position: {
 | 
				
			||||||
 | 
					        left: bounds.left,
 | 
				
			||||||
 | 
					        right: bounds.right,
 | 
				
			||||||
 | 
					        top: bounds.top,
 | 
				
			||||||
 | 
					        bottom: bounds.bottom,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return null;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default convertDivElementRefToBounds;
 | 
				
			||||||
@@ -9,7 +9,9 @@ export const moveItemWithinArray = (arr: Array<DraggableElement>, item: Draggabl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const insertItemIntoArray = (arr: Array<DraggableElement>, item: DraggableElement, index: number) => {
 | 
					export const insertItemIntoArray = (arr: Array<DraggableElement>, item: DraggableElement, index: number) => {
 | 
				
			||||||
  const arrClone = [...arr];
 | 
					  const arrClone = [...arr];
 | 
				
			||||||
 | 
					  console.log(arrClone, index, item);
 | 
				
			||||||
  arrClone.splice(index, 0, item);
 | 
					  arrClone.splice(index, 0, item);
 | 
				
			||||||
 | 
					  console.log(arrClone);
 | 
				
			||||||
  return arrClone;
 | 
					  return arrClone;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -56,6 +58,7 @@ export const isPositionChanged = (source: DraggableLocation, destination: Dragga
 | 
				
			|||||||
  if (!destination) return false;
 | 
					  if (!destination) return false;
 | 
				
			||||||
  const isSameList = destination.droppableId === source.droppableId;
 | 
					  const isSameList = destination.droppableId === source.droppableId;
 | 
				
			||||||
  const isSamePosition = destination.index === source.index;
 | 
					  const isSamePosition = destination.index === source.index;
 | 
				
			||||||
 | 
					  console.log(`isSameList: ${isSameList} : isSamePosition: ${isSamePosition}`);
 | 
				
			||||||
  return !isSameList || !isSamePosition;
 | 
					  return !isSameList || !isSamePosition;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3179,6 +3179,15 @@
 | 
				
			|||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    "@types/react" "*"
 | 
					    "@types/react" "*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@types/react-datepicker@^2.11.0":
 | 
				
			||||||
 | 
					  version "2.11.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@types/react-datepicker/-/react-datepicker-2.11.0.tgz#aa6faa66de17b0ff96bc0af9d5d2506d5dd99703"
 | 
				
			||||||
 | 
					  integrity sha512-eagG8BE3TFgPYyZb2/hG4+2delLH9z/4OWzT7wuTCKLHDDXIXgvkb2O2cW8q4/wuqnTnMxo+vl3vgPTENkofzw==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    "@types/react" "*"
 | 
				
			||||||
 | 
					    date-fns "^2.0.1"
 | 
				
			||||||
 | 
					    popper.js "^1.14.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"@types/react-dom@*", "@types/react-dom@^16.9.5":
 | 
					"@types/react-dom@*", "@types/react-dom@^16.9.5":
 | 
				
			||||||
  version "16.9.5"
 | 
					  version "16.9.5"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.5.tgz#5de610b04a35d07ffd8f44edad93a71032d9aaa7"
 | 
					  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.5.tgz#5de610b04a35d07ffd8f44edad93a71032d9aaa7"
 | 
				
			||||||
@@ -5480,7 +5489,7 @@ class-utils@^0.3.5:
 | 
				
			|||||||
    isobject "^3.0.0"
 | 
					    isobject "^3.0.0"
 | 
				
			||||||
    static-extend "^0.1.1"
 | 
					    static-extend "^0.1.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
classnames@^2.2.5:
 | 
					classnames@^2.2.5, classnames@^2.2.6:
 | 
				
			||||||
  version "2.2.6"
 | 
					  version "2.2.6"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
 | 
					  resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
 | 
				
			||||||
  integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
 | 
					  integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
 | 
				
			||||||
@@ -6319,6 +6328,11 @@ date-fns@^1.27.2:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
 | 
					  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
 | 
				
			||||||
  integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
 | 
					  integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					date-fns@^2.0.1:
 | 
				
			||||||
 | 
					  version "2.12.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.12.0.tgz#01754c8a2f3368fc1119cf4625c3dad8c1845ee6"
 | 
				
			||||||
 | 
					  integrity sha512-qJgn99xxKnFgB1qL4jpxU7Q2t0LOn1p8KMIveef3UZD7kqjT3tpFNNdXJelEHhE+rUgffriXriw/sOSU+cS1Hw==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
de-indent@^1.0.2:
 | 
					de-indent@^1.0.2:
 | 
				
			||||||
  version "1.0.2"
 | 
					  version "1.0.2"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
 | 
					  resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
 | 
				
			||||||
@@ -12032,7 +12046,7 @@ polished@^3.3.1:
 | 
				
			|||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    "@babel/runtime" "^7.6.3"
 | 
					    "@babel/runtime" "^7.6.3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
popper.js@^1.14.4, popper.js@^1.14.7:
 | 
					popper.js@^1.14.1, popper.js@^1.14.4, popper.js@^1.14.7:
 | 
				
			||||||
  version "1.16.1"
 | 
					  version "1.16.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
 | 
					  resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
 | 
				
			||||||
  integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
 | 
					  integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
 | 
				
			||||||
@@ -13250,6 +13264,17 @@ react-color@^2.17.0:
 | 
				
			|||||||
    reactcss "^1.2.0"
 | 
					    reactcss "^1.2.0"
 | 
				
			||||||
    tinycolor2 "^1.4.1"
 | 
					    tinycolor2 "^1.4.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					react-datepicker@^2.14.1:
 | 
				
			||||||
 | 
					  version "2.14.1"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-2.14.1.tgz#83463beb85235a575475955f554290a95f89c65b"
 | 
				
			||||||
 | 
					  integrity sha512-8eWHvrjXfKVkt5rERXC6/c/eEdcE2stIsl+QmTO5Efgpacf8MOCyVpBisJLVLDYjVlENczhOcRlIzvraODHKxA==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    classnames "^2.2.6"
 | 
				
			||||||
 | 
					    date-fns "^2.0.1"
 | 
				
			||||||
 | 
					    prop-types "^15.7.2"
 | 
				
			||||||
 | 
					    react-onclickoutside "^6.9.0"
 | 
				
			||||||
 | 
					    react-popper "^1.3.4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
react-dev-utils@^10.2.0:
 | 
					react-dev-utils@^10.2.0:
 | 
				
			||||||
  version "10.2.0"
 | 
					  version "10.2.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-10.2.0.tgz#b11cc48aa2be2502fb3c27a50d1dfa95cfa9dfe0"
 | 
					  resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-10.2.0.tgz#b11cc48aa2be2502fb3c27a50d1dfa95cfa9dfe0"
 | 
				
			||||||
@@ -13442,6 +13467,11 @@ react-lifecycles-compat@^3.0.4:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
 | 
					  resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
 | 
				
			||||||
  integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
 | 
					  integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					react-onclickoutside@^6.9.0:
 | 
				
			||||||
 | 
					  version "6.9.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.9.0.tgz#a54bc317ae8cf6131a5d78acea55a11067f37a1f"
 | 
				
			||||||
 | 
					  integrity sha512-8ltIY3bC7oGhj2nPAvWOGi+xGFybPNhJM0V1H8hY/whNcXgmDeaeoCMPPd8VatrpTsUWjb/vGzrmu6SrXVty3A==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
react-popper-tooltip@^2.8.3:
 | 
					react-popper-tooltip@^2.8.3:
 | 
				
			||||||
  version "2.10.1"
 | 
					  version "2.10.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-2.10.1.tgz#e10875f31916297c694d64a677d6f8fa0a48b4d1"
 | 
					  resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-2.10.1.tgz#e10875f31916297c694d64a677d6f8fa0a48b4d1"
 | 
				
			||||||
@@ -13450,7 +13480,7 @@ react-popper-tooltip@^2.8.3:
 | 
				
			|||||||
    "@babel/runtime" "^7.7.4"
 | 
					    "@babel/runtime" "^7.7.4"
 | 
				
			||||||
    react-popper "^1.3.6"
 | 
					    react-popper "^1.3.6"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
react-popper@^1.3.6:
 | 
					react-popper@^1.3.4, react-popper@^1.3.6:
 | 
				
			||||||
  version "1.3.7"
 | 
					  version "1.3.7"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.7.tgz#f6a3471362ef1f0d10a4963673789de1baca2324"
 | 
					  resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.7.tgz#f6a3471362ef1f0d10a4963673789de1baca2324"
 | 
				
			||||||
  integrity sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==
 | 
					  integrity sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user