feature: add labels & remove old types
This commit is contained in:
@ -14,28 +14,17 @@ export default {
|
||||
},
|
||||
};
|
||||
|
||||
const labelData = [
|
||||
const labelData: Array<ProjectLabel> = [
|
||||
{
|
||||
labelId: 'development',
|
||||
id: 'development',
|
||||
name: 'Development',
|
||||
createdDate: new Date().toString(),
|
||||
labelColor: {
|
||||
id: '1',
|
||||
colorHex: LabelColors.BLUE,
|
||||
name: 'blue',
|
||||
position: 1,
|
||||
},
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
labelId: 'general',
|
||||
name: 'General',
|
||||
labelColor: {
|
||||
id: '2',
|
||||
colorHex: LabelColors.PINK,
|
||||
name: 'pink',
|
||||
position: 2,
|
||||
},
|
||||
active: false,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -47,10 +47,10 @@ const Member: React.FC<MemberProps> = ({ onCardMemberClick, taskID, member }) =>
|
||||
onClick={e => {
|
||||
if (onCardMemberClick) {
|
||||
e.stopPropagation();
|
||||
onCardMemberClick($targetRef, taskID, member.userID);
|
||||
onCardMemberClick($targetRef, taskID, member.id);
|
||||
}
|
||||
}}
|
||||
key={member.userID}
|
||||
key={member.id}
|
||||
bgColor={member.profileIcon.bgColor ?? '#7367F0'}
|
||||
>
|
||||
<CardMemberInitials>{member.profileIcon.initials}</CardMemberInitials>
|
||||
@ -68,7 +68,7 @@ type Props = {
|
||||
dueDate?: DueDate;
|
||||
checklists?: Checklist;
|
||||
watched?: boolean;
|
||||
labels?: Label[];
|
||||
labels?: Array<ProjectLabel>;
|
||||
wrapperProps?: any;
|
||||
members?: Array<TaskUser> | null;
|
||||
onCardMemberClick?: OnCardMemberClick;
|
||||
@ -136,7 +136,7 @@ const Card = React.forwardRef(
|
||||
<ListCardLabels>
|
||||
{labels &&
|
||||
labels.map(label => (
|
||||
<ListCardLabel color={label.labelColor.colorHex} key={label.name}>
|
||||
<ListCardLabel color={label.labelColor.colorHex} key={label.id}>
|
||||
{label.name}
|
||||
</ListCardLabel>
|
||||
))}
|
||||
@ -169,7 +169,7 @@ const Card = React.forwardRef(
|
||||
<CardMembers>
|
||||
{members &&
|
||||
members.map(member => (
|
||||
<Member key={member.userID} taskID={taskID} member={member} onCardMemberClick={onCardMemberClick} />
|
||||
<Member key={member.id} taskID={taskID} member={member} onCardMemberClick={onCardMemberClick} />
|
||||
))}
|
||||
</CardMembers>
|
||||
</ListCardDetails>
|
||||
|
@ -17,21 +17,35 @@ export const Default = () => {
|
||||
return (
|
||||
<DueDateManager
|
||||
task={{
|
||||
taskID: '1',
|
||||
taskGroup: { name: 'General', taskGroupID: '1' },
|
||||
id: '1',
|
||||
taskGroup: { name: 'General', id: '1', position: 1 },
|
||||
name: 'Hello, world',
|
||||
position: 1,
|
||||
labels: [
|
||||
{
|
||||
labelId: 'soft-skills',
|
||||
labelColor: { id: '1', colorHex: '#fff', name: 'white', position: 1 },
|
||||
active: true,
|
||||
name: 'Soft Skills',
|
||||
id: 'soft-skills',
|
||||
assignedDate: new Date().toString(),
|
||||
projectLabel: {
|
||||
createdDate: new Date().toString(),
|
||||
id: 'label-soft-skills',
|
||||
name: 'Soft Skills',
|
||||
labelColor: {
|
||||
id: '1',
|
||||
name: 'white',
|
||||
colorHex: '#fff',
|
||||
position: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
description: 'hello!',
|
||||
members: [
|
||||
{ userID: '1', profileIcon: { url: null, initials: null, bgColor: null }, displayName: 'Jordan Knott' },
|
||||
assigned: [
|
||||
{
|
||||
id: '1',
|
||||
profileIcon: { url: null, initials: null, bgColor: null },
|
||||
firstName: 'Jordan',
|
||||
lastName: 'Knott',
|
||||
},
|
||||
],
|
||||
}}
|
||||
onCancel={action('cancel')}
|
||||
|
@ -16,28 +16,17 @@ export default {
|
||||
},
|
||||
};
|
||||
|
||||
const labelData = [
|
||||
const labelData: Array<ProjectLabel> = [
|
||||
{
|
||||
labelId: 'development',
|
||||
id: 'development',
|
||||
name: 'Development',
|
||||
createdDate: new Date().toString(),
|
||||
labelColor: {
|
||||
id: '1',
|
||||
colorHex: LabelColors.BLUE,
|
||||
name: 'blue',
|
||||
position: 1,
|
||||
},
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
labelId: 'general',
|
||||
name: 'General',
|
||||
labelColor: {
|
||||
id: '2',
|
||||
colorHex: LabelColors.PINK,
|
||||
name: 'pink',
|
||||
position: 2,
|
||||
},
|
||||
active: false,
|
||||
},
|
||||
];
|
||||
|
||||
@ -68,7 +57,6 @@ export const Default = () => {
|
||||
isComposerOpen={false}
|
||||
onSaveName={action('on save name')}
|
||||
onOpenComposer={action('on open composer')}
|
||||
tasks={[]}
|
||||
onExtraMenuOpen={action('extra menu open')}
|
||||
>
|
||||
<ListCards>
|
||||
@ -94,7 +82,6 @@ export const WithCardComposer = () => {
|
||||
isComposerOpen
|
||||
onSaveName={action('on save name')}
|
||||
onOpenComposer={action('on open composer')}
|
||||
tasks={[]}
|
||||
onExtraMenuOpen={action('extra menu open')}
|
||||
>
|
||||
<ListCards>
|
||||
@ -121,7 +108,6 @@ export const WithCard = () => {
|
||||
isComposerOpen={false}
|
||||
onSaveName={action('on save name')}
|
||||
onOpenComposer={action('on open composer')}
|
||||
tasks={[]}
|
||||
onExtraMenuOpen={action('extra menu open')}
|
||||
>
|
||||
<ListCards>
|
||||
@ -160,7 +146,6 @@ export const WithCardAndComposer = () => {
|
||||
isComposerOpen
|
||||
onSaveName={action('on save name')}
|
||||
onOpenComposer={action('on open composer')}
|
||||
tasks={[]}
|
||||
onExtraMenuOpen={action('extra menu open')}
|
||||
>
|
||||
<ListCards>
|
||||
|
@ -97,9 +97,7 @@ export const Header = styled.div<{ isEditing: boolean }>`
|
||||
props.isEditing &&
|
||||
css`
|
||||
& ${HeaderName} {
|
||||
background: #fff;
|
||||
border: none;
|
||||
box-shadow: inset 0 0 0 2px #0079bf;
|
||||
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
@ -22,7 +22,6 @@ type Props = {
|
||||
onSaveName: (name: string) => void;
|
||||
isComposerOpen: boolean;
|
||||
onOpenComposer: (id: string) => void;
|
||||
tasks: Task[];
|
||||
wrapperProps?: any;
|
||||
headerProps?: any;
|
||||
index?: number;
|
||||
|
@ -70,14 +70,12 @@ export const Default = () => {
|
||||
...listsData,
|
||||
tasks: {
|
||||
...listsData.tasks,
|
||||
[droppedTask.taskID]: droppedTask,
|
||||
[droppedTask.id]: droppedTask,
|
||||
},
|
||||
};
|
||||
console.log(newState);
|
||||
setListsData(newState);
|
||||
};
|
||||
const onListDrop = (droppedColumn: any) => {
|
||||
console.log(droppedColumn);
|
||||
const newState = {
|
||||
...listsData,
|
||||
columns: {
|
||||
@ -85,44 +83,9 @@ export const Default = () => {
|
||||
[droppedColumn.taskGroupID]: droppedColumn,
|
||||
},
|
||||
};
|
||||
console.log(newState);
|
||||
setListsData(newState);
|
||||
};
|
||||
return (
|
||||
<Lists
|
||||
{...listsData}
|
||||
onCardClick={action('card click')}
|
||||
onExtraMenuOpen={action('extra menu open')}
|
||||
onQuickEditorOpen={action('card composer open')}
|
||||
onCardDrop={onCardDrop}
|
||||
onListDrop={onListDrop}
|
||||
onCardMemberClick={action('card member click')}
|
||||
onCardCreate={action('card create')}
|
||||
onCreateList={listName => {
|
||||
const [lastColumn] = Object.values(listsData.columns)
|
||||
.sort((a, b) => a.position - b.position)
|
||||
.slice(-1);
|
||||
let position = 1;
|
||||
if (lastColumn) {
|
||||
position = lastColumn.position + 1;
|
||||
}
|
||||
const taskGroupID = Math.random().toString();
|
||||
const newListsData = {
|
||||
...listsData,
|
||||
columns: {
|
||||
...listsData.columns,
|
||||
[taskGroupID]: {
|
||||
taskGroupID,
|
||||
name: listName,
|
||||
position,
|
||||
tasks: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
setListsData(newListsData);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
return <span />;
|
||||
};
|
||||
|
||||
const createColumn = (id: any, name: any, position: any) => {
|
||||
@ -202,13 +165,13 @@ export const ListsWithManyList = () => {
|
||||
};
|
||||
return (
|
||||
<Lists
|
||||
{...listsData}
|
||||
onCardClick={action('card click')}
|
||||
taskGroups={[]}
|
||||
onTaskClick={action('card click')}
|
||||
onQuickEditorOpen={action('card composer open')}
|
||||
onCardCreate={action('card create')}
|
||||
onCardDrop={onCardDrop}
|
||||
onListDrop={onListDrop}
|
||||
onCreateList={action('create list')}
|
||||
onCreateTask={action('card create')}
|
||||
onTaskDrop={onCardDrop}
|
||||
onTaskGroupDrop={onListDrop}
|
||||
onCreateTaskGroup={action('create list')}
|
||||
onExtraMenuOpen={action('extra menu open')}
|
||||
onCardMemberClick={action('card member click')}
|
||||
/>
|
||||
|
@ -11,5 +11,7 @@ export const Container = styled.div`
|
||||
|
||||
export const BoardWrapper = styled.div`
|
||||
display: flex;
|
||||
margin-top: 12px;
|
||||
margin-left: 8px;
|
||||
`;
|
||||
export default Container;
|
||||
|
@ -13,37 +13,29 @@ import {
|
||||
|
||||
import { Container, BoardWrapper } from './Styles';
|
||||
|
||||
interface Columns {
|
||||
[key: string]: TaskGroup;
|
||||
}
|
||||
interface Tasks {
|
||||
[key: string]: Task;
|
||||
}
|
||||
interface SimpleProps {
|
||||
taskGroups: Array<TaskGroup>;
|
||||
onTaskDrop: (task: Task) => void;
|
||||
onTaskGroupDrop: (taskGroup: TaskGroup) => void;
|
||||
|
||||
type Props = {
|
||||
columns: Columns;
|
||||
tasks: Tasks;
|
||||
onCardClick: (task: Task) => void;
|
||||
onCardDrop: (task: Task) => void;
|
||||
onListDrop: (taskGroup: TaskGroup) => void;
|
||||
onCardCreate: (taskGroupID: string, name: string) => void;
|
||||
onTaskClick: (task: Task) => void;
|
||||
onCreateTask: (taskGroupID: string, name: string) => void;
|
||||
onQuickEditorOpen: (e: ContextMenuEvent) => void;
|
||||
onCreateList: (listName: string) => void;
|
||||
onCreateTaskGroup: (listName: string) => void;
|
||||
onExtraMenuOpen: (taskGroupID: string, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||
onCardMemberClick: OnCardMemberClick;
|
||||
};
|
||||
}
|
||||
|
||||
const Lists: React.FC<Props> = ({
|
||||
columns,
|
||||
tasks,
|
||||
onCardClick,
|
||||
onCardDrop,
|
||||
onListDrop,
|
||||
onCardCreate,
|
||||
const SimpleLists: React.FC<SimpleProps> = ({
|
||||
taskGroups,
|
||||
onTaskDrop,
|
||||
onTaskGroupDrop,
|
||||
onTaskClick,
|
||||
onCreateTask,
|
||||
onQuickEditorOpen,
|
||||
onCreateList,
|
||||
onCardMemberClick,
|
||||
onCreateTaskGroup,
|
||||
onExtraMenuOpen,
|
||||
onCardMemberClick,
|
||||
}) => {
|
||||
const onDragEnd = ({ draggableId, source, destination, type }: DropResult) => {
|
||||
if (typeof destination === 'undefined') return;
|
||||
@ -51,64 +43,78 @@ const Lists: React.FC<Props> = ({
|
||||
|
||||
const isList = type === 'column';
|
||||
const isSameList = destination.droppableId === source.droppableId;
|
||||
const droppedDraggable: DraggableElement = isList
|
||||
? {
|
||||
id: draggableId,
|
||||
position: columns[draggableId].position,
|
||||
}
|
||||
: {
|
||||
id: draggableId,
|
||||
position: tasks[draggableId].position,
|
||||
};
|
||||
const beforeDropDraggables = isList
|
||||
? getSortedDraggables(
|
||||
Object.values(columns).map(column => {
|
||||
return { id: column.taskGroupID, position: column.position };
|
||||
}),
|
||||
)
|
||||
: getSortedDraggables(
|
||||
Object.values(tasks)
|
||||
.filter((t: any) => t.taskGroup.taskGroupID === destination.droppableId)
|
||||
.map(task => {
|
||||
return { id: task.taskID, position: task.position };
|
||||
}),
|
||||
);
|
||||
|
||||
const afterDropDraggables = getAfterDropDraggableList(
|
||||
beforeDropDraggables,
|
||||
droppedDraggable,
|
||||
isList,
|
||||
isSameList,
|
||||
destination,
|
||||
);
|
||||
const newPosition = getNewDraggablePosition(afterDropDraggables, destination.index);
|
||||
let droppedDraggable: DraggableElement | null = null;
|
||||
let beforeDropDraggables: Array<DraggableElement> | null = null;
|
||||
|
||||
if (isList) {
|
||||
const droppedList = columns[droppedDraggable.id];
|
||||
onListDrop({
|
||||
...droppedList,
|
||||
position: newPosition,
|
||||
});
|
||||
const droppedGroup = taskGroups.find(taskGroup => taskGroup.id === draggableId);
|
||||
if (droppedGroup) {
|
||||
droppedDraggable = {
|
||||
id: draggableId,
|
||||
position: droppedGroup.position,
|
||||
};
|
||||
beforeDropDraggables = getSortedDraggables(
|
||||
taskGroups.map(taskGroup => {
|
||||
return { id: taskGroup.id, position: taskGroup.position };
|
||||
}),
|
||||
);
|
||||
if (droppedDraggable === null || beforeDropDraggables === null) {
|
||||
throw new Error('before drop draggables is null');
|
||||
}
|
||||
const afterDropDraggables = getAfterDropDraggableList(
|
||||
beforeDropDraggables,
|
||||
droppedDraggable,
|
||||
isList,
|
||||
isSameList,
|
||||
destination,
|
||||
);
|
||||
const newPosition = getNewDraggablePosition(afterDropDraggables, destination.index);
|
||||
onTaskGroupDrop({
|
||||
...droppedGroup,
|
||||
position: newPosition,
|
||||
});
|
||||
} else {
|
||||
throw { error: 'task group can not be found' };
|
||||
}
|
||||
} else {
|
||||
const droppedCard = tasks[droppedDraggable.id];
|
||||
const newCard = {
|
||||
...droppedCard,
|
||||
position: newPosition,
|
||||
taskGroup: {
|
||||
taskGroupID: destination.droppableId,
|
||||
},
|
||||
};
|
||||
onCardDrop(newCard);
|
||||
const targetGroup = taskGroups.findIndex(
|
||||
taskGroup => taskGroup.tasks.findIndex(task => task.id === draggableId) !== -1,
|
||||
);
|
||||
const droppedTask = taskGroups[targetGroup].tasks.find(task => task.id === draggableId);
|
||||
|
||||
if (droppedTask) {
|
||||
droppedDraggable = {
|
||||
id: draggableId,
|
||||
position: droppedTask.position,
|
||||
};
|
||||
beforeDropDraggables = getSortedDraggables(
|
||||
taskGroups[targetGroup].tasks.map(task => {
|
||||
return { id: task.id, position: task.position };
|
||||
}),
|
||||
);
|
||||
if (droppedDraggable === null || beforeDropDraggables === null) {
|
||||
throw new Error('before drop draggables is null');
|
||||
}
|
||||
const afterDropDraggables = getAfterDropDraggableList(
|
||||
beforeDropDraggables,
|
||||
droppedDraggable,
|
||||
isList,
|
||||
isSameList,
|
||||
destination,
|
||||
);
|
||||
const newPosition = getNewDraggablePosition(afterDropDraggables, destination.index);
|
||||
const newTask = {
|
||||
...droppedTask,
|
||||
position: newPosition,
|
||||
taskGroup: {
|
||||
id: destination.droppableId,
|
||||
},
|
||||
};
|
||||
onTaskDrop(newTask);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const orderedColumns = getSortedDraggables(
|
||||
Object.values(columns).map(column => {
|
||||
return { id: column.taskGroupID, position: column.position };
|
||||
}),
|
||||
);
|
||||
console.log(orderedColumns);
|
||||
|
||||
const [currentComposer, setCurrentComposer] = useState('');
|
||||
return (
|
||||
<BoardWrapper>
|
||||
@ -116,85 +122,92 @@ const Lists: React.FC<Props> = ({
|
||||
<Droppable direction="horizontal" type="column" droppableId="root">
|
||||
{provided => (
|
||||
<Container {...provided.droppableProps} ref={provided.innerRef}>
|
||||
{orderedColumns.map((columnDraggable, index: number) => {
|
||||
const column = columns[columnDraggable.id];
|
||||
const columnCards = Object.values(tasks)
|
||||
.filter((t: Task) => t.taskGroup.taskGroupID === column.taskGroupID)
|
||||
.sort((a, b) => a.position - b.position);
|
||||
return (
|
||||
<Draggable draggableId={column.taskGroupID} key={column.taskGroupID} index={index}>
|
||||
{columnDragProvided => (
|
||||
<Droppable type="tasks" droppableId={column.taskGroupID}>
|
||||
{(columnDropProvided, snapshot) => (
|
||||
<List
|
||||
name={column.name}
|
||||
onOpenComposer={id => setCurrentComposer(id)}
|
||||
isComposerOpen={currentComposer === column.taskGroupID}
|
||||
onSaveName={name => {}}
|
||||
tasks={columnCards}
|
||||
ref={columnDragProvided.innerRef}
|
||||
wrapperProps={columnDragProvided.draggableProps}
|
||||
headerProps={columnDragProvided.dragHandleProps}
|
||||
onExtraMenuOpen={onExtraMenuOpen}
|
||||
id={column.taskGroupID}
|
||||
key={column.taskGroupID}
|
||||
index={index}
|
||||
>
|
||||
<ListCards ref={columnDropProvided.innerRef} {...columnDropProvided.droppableProps}>
|
||||
{columnCards.map((task: Task, taskIndex: any) => {
|
||||
return (
|
||||
<Draggable key={task.taskID} draggableId={task.taskID} index={taskIndex}>
|
||||
{taskProvided => {
|
||||
return (
|
||||
<Card
|
||||
wrapperProps={{
|
||||
...taskProvided.draggableProps,
|
||||
...taskProvided.dragHandleProps,
|
||||
}}
|
||||
ref={taskProvided.innerRef}
|
||||
taskID={task.taskID}
|
||||
taskGroupID={column.taskGroupID}
|
||||
description=""
|
||||
title={task.name}
|
||||
labels={task.labels}
|
||||
members={task.members}
|
||||
onClick={() => onCardClick(task)}
|
||||
onCardMemberClick={onCardMemberClick}
|
||||
onContextMenu={onQuickEditorOpen}
|
||||
/>
|
||||
);
|
||||
{taskGroups
|
||||
.slice()
|
||||
.sort((a: any, b: any) => a.position - b.position)
|
||||
.map((taskGroup: TaskGroup, index: number) => {
|
||||
return (
|
||||
<Draggable draggableId={taskGroup.id} key={taskGroup.id} index={index}>
|
||||
{columnDragProvided => (
|
||||
<Droppable type="tasks" droppableId={taskGroup.id}>
|
||||
{(columnDropProvided, snapshot) => (
|
||||
<List
|
||||
name={taskGroup.name}
|
||||
onOpenComposer={id => setCurrentComposer(id)}
|
||||
isComposerOpen={currentComposer === taskGroup.id}
|
||||
onSaveName={name => {}}
|
||||
ref={columnDragProvided.innerRef}
|
||||
wrapperProps={columnDragProvided.draggableProps}
|
||||
headerProps={columnDragProvided.dragHandleProps}
|
||||
onExtraMenuOpen={onExtraMenuOpen}
|
||||
id={taskGroup.id}
|
||||
key={taskGroup.id}
|
||||
index={index}
|
||||
>
|
||||
<ListCards ref={columnDropProvided.innerRef} {...columnDropProvided.droppableProps}>
|
||||
{taskGroup.tasks
|
||||
.slice()
|
||||
.sort((a: any, b: any) => a.position - b.position)
|
||||
.map((task: Task, taskIndex: any) => {
|
||||
return (
|
||||
<Draggable key={task.id} draggableId={task.id} index={taskIndex}>
|
||||
{taskProvided => {
|
||||
return (
|
||||
<Card
|
||||
wrapperProps={{
|
||||
...taskProvided.draggableProps,
|
||||
...taskProvided.dragHandleProps,
|
||||
}}
|
||||
ref={taskProvided.innerRef}
|
||||
taskID={task.id}
|
||||
taskGroupID={taskGroup.id}
|
||||
description=""
|
||||
labels={task.labels.map(label => label.projectLabel)}
|
||||
title={task.name}
|
||||
members={task.assigned}
|
||||
onClick={() => {
|
||||
onTaskClick(task);
|
||||
}}
|
||||
onCardMemberClick={onCardMemberClick}
|
||||
onContextMenu={onQuickEditorOpen}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{columnDropProvided.placeholder}
|
||||
{currentComposer === taskGroup.id && (
|
||||
<CardComposer
|
||||
onClose={() => {
|
||||
setCurrentComposer('');
|
||||
}}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{columnDropProvided.placeholder}
|
||||
{currentComposer === column.taskGroupID && (
|
||||
<CardComposer
|
||||
onClose={() => {
|
||||
setCurrentComposer('');
|
||||
}}
|
||||
onCreateCard={name => {
|
||||
onCardCreate(column.taskGroupID, name);
|
||||
}}
|
||||
isOpen
|
||||
/>
|
||||
)}
|
||||
</ListCards>
|
||||
</List>
|
||||
)}
|
||||
</Droppable>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
onCreateCard={name => {
|
||||
onCreateTask(taskGroup.id, name);
|
||||
}}
|
||||
isOpen
|
||||
/>
|
||||
)}
|
||||
</ListCards>
|
||||
</List>
|
||||
)}
|
||||
</Droppable>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{provided.placeholder}
|
||||
</Container>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
<AddList onSave={onCreateList} />
|
||||
<AddList
|
||||
onSave={listName => {
|
||||
onCreateTaskGroup(listName);
|
||||
}}
|
||||
/>
|
||||
</BoardWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Lists;
|
||||
export default SimpleLists;
|
||||
|
@ -39,16 +39,18 @@ const MemberManager: React.FC<MemberManagerProps> = ({
|
||||
<BoardMembersList>
|
||||
{availableMembers
|
||||
.filter(
|
||||
member => currentSearch === '' || member.displayName.toLowerCase().startsWith(currentSearch.toLowerCase()),
|
||||
member =>
|
||||
currentSearch === '' ||
|
||||
`${member.firstName} ${member.lastName}`.toLowerCase().startsWith(currentSearch.toLowerCase()),
|
||||
)
|
||||
.map(member => {
|
||||
return (
|
||||
<BoardMembersListItem key={member.userID}>
|
||||
<BoardMembersListItem key={member.id}>
|
||||
<BoardMemberListItemContent
|
||||
onClick={() => {
|
||||
const isActive = activeMembers.findIndex(m => m.userID === member.userID) !== -1;
|
||||
const isActive = activeMembers.findIndex(m => m.id === member.id) !== -1;
|
||||
if (isActive) {
|
||||
setActiveMembers(activeMembers.filter(m => m.userID !== member.userID));
|
||||
setActiveMembers(activeMembers.filter(m => m.id !== member.id));
|
||||
} else {
|
||||
setActiveMembers([...activeMembers, member]);
|
||||
}
|
||||
@ -56,8 +58,8 @@ const MemberManager: React.FC<MemberManagerProps> = ({
|
||||
}}
|
||||
>
|
||||
<ProfileIcon>JK</ProfileIcon>
|
||||
<MemberName>{member.displayName}</MemberName>
|
||||
{activeMembers.findIndex(m => m.userID === member.userID) !== -1 && (
|
||||
<MemberName>{`${member.firstName} ${member.lastName}`}</MemberName>
|
||||
{activeMembers.findIndex(m => m.id === member.id) !== -1 && (
|
||||
<ActiveIconWrapper>
|
||||
<Checkmark size={16} color="#42526e" />
|
||||
</ActiveIconWrapper>
|
||||
|
@ -16,14 +16,14 @@ type MiniProfileProps = {
|
||||
displayName: string;
|
||||
username: string;
|
||||
bio: string;
|
||||
profileIcon: ProfileIcon;
|
||||
profileIcon: ProfileIcon | null;
|
||||
onRemoveFromTask: () => void;
|
||||
};
|
||||
const MiniProfile: React.FC<MiniProfileProps> = ({ displayName, username, bio, profileIcon, onRemoveFromTask }) => {
|
||||
return (
|
||||
<>
|
||||
<Profile>
|
||||
<ProfileIcon bgColor={profileIcon.bgColor ?? ''}>{profileIcon.initials}</ProfileIcon>
|
||||
{profileIcon && <ProfileIcon bgColor={profileIcon.bgColor ?? ''}>{profileIcon.initials}</ProfileIcon>}
|
||||
<ProfileInfo>
|
||||
<InfoTitle>{displayName}</InfoTitle>
|
||||
<InfoUsername>{username}</InfoUsername>
|
||||
|
@ -5,7 +5,7 @@ import { SaveButton, DeleteButton, LabelBox, EditLabelForm, FieldLabel, FieldNam
|
||||
|
||||
type Props = {
|
||||
labelColors: Array<LabelColor>;
|
||||
label: Label | null;
|
||||
label: ProjectLabel | null;
|
||||
onLabelEdit: (labelId: string | null, labelName: string, labelColor: LabelColor) => void;
|
||||
onLabelDelete?: (labelId: string) => void;
|
||||
};
|
||||
@ -32,7 +32,7 @@ const LabelManager = ({ labelColors, label, onLabelEdit, onLabelDelete }: Props)
|
||||
onChange={e => {
|
||||
setCurrentLabel(e.currentTarget.value);
|
||||
}}
|
||||
value={currentLabel}
|
||||
value={currentLabel ?? ''}
|
||||
/>
|
||||
<FieldLabel>Select a color</FieldLabel>
|
||||
<div>
|
||||
@ -56,7 +56,7 @@ const LabelManager = ({ labelColors, label, onLabelEdit, onLabelDelete }: Props)
|
||||
e.preventDefault();
|
||||
console.log(currentColor);
|
||||
if (currentColor) {
|
||||
onLabelEdit(label ? label.labelId : null, currentLabel, currentColor);
|
||||
onLabelEdit(label ? label.id : null, currentLabel ?? '', currentColor);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@ -66,7 +66,7 @@ const LabelManager = ({ labelColors, label, onLabelEdit, onLabelDelete }: Props)
|
||||
type="submit"
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
onLabelDelete(label.labelId);
|
||||
onLabelDelete(label.id);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
@ -14,12 +14,14 @@ import {
|
||||
} from './Styles';
|
||||
|
||||
type Props = {
|
||||
labels?: Label[];
|
||||
labels?: Array<ProjectLabel>;
|
||||
taskLabels?: Array<TaskLabel>;
|
||||
onLabelToggle: (labelId: string) => void;
|
||||
onLabelEdit: (labelId: string) => void;
|
||||
onLabelCreate: () => void;
|
||||
};
|
||||
const LabelManager: React.FC<Props> = ({ labels, onLabelToggle, onLabelEdit, onLabelCreate }) => {
|
||||
|
||||
const LabelManager: React.FC<Props> = ({ labels, taskLabels, onLabelToggle, onLabelEdit, onLabelCreate }) => {
|
||||
const $fieldName = useRef<HTMLInputElement>(null);
|
||||
const [currentLabel, setCurrentLabel] = useState('');
|
||||
const [currentSearch, setCurrentSearch] = useState('');
|
||||
@ -44,27 +46,31 @@ const LabelManager: React.FC<Props> = ({ labels, onLabelToggle, onLabelEdit, onL
|
||||
<Labels>
|
||||
{labels &&
|
||||
labels
|
||||
.filter(label => currentSearch === '' || label.name.toLowerCase().startsWith(currentSearch.toLowerCase()))
|
||||
.filter(
|
||||
label =>
|
||||
currentSearch === '' ||
|
||||
(label.name && label.name.toLowerCase().startsWith(currentSearch.toLowerCase())),
|
||||
)
|
||||
.map(label => (
|
||||
<Label key={label.labelId}>
|
||||
<Label key={label.id}>
|
||||
<LabelIcon
|
||||
onClick={() => {
|
||||
onLabelEdit(label.labelId);
|
||||
onLabelEdit(label.id);
|
||||
}}
|
||||
>
|
||||
<Pencil color="#c2c6dc" />
|
||||
</LabelIcon>
|
||||
<CardLabel
|
||||
key={label.labelId}
|
||||
key={label.id}
|
||||
color={label.labelColor.colorHex}
|
||||
active={currentLabel === label.labelId}
|
||||
active={currentLabel === label.id}
|
||||
onMouseEnter={() => {
|
||||
setCurrentLabel(label.labelId);
|
||||
setCurrentLabel(label.id);
|
||||
}}
|
||||
onClick={() => onLabelToggle(label.labelId)}
|
||||
onClick={() => onLabelToggle(label.id)}
|
||||
>
|
||||
{label.name}
|
||||
{label.active && (
|
||||
{taskLabels && taskLabels.find(t => t.projectLabel.id === label.id) && (
|
||||
<ActiveIcon>
|
||||
<Checkmark color="#fff" />
|
||||
</ActiveIcon>
|
||||
|
@ -7,12 +7,13 @@ import ListActions from 'shared/components/ListActions';
|
||||
import MemberManager from 'shared/components/MemberManager';
|
||||
import DueDateManager from 'shared/components/DueDateManager';
|
||||
import MiniProfile from 'shared/components/MiniProfile';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import PopupMenu, { PopupProvider, usePopup, Popup } from '.';
|
||||
import styled from 'styled-components';
|
||||
import produce from 'immer';
|
||||
import NormalizeStyles from 'App/NormalizeStyles';
|
||||
import BaseStyles from 'App/BaseStyles';
|
||||
import produce from 'immer';
|
||||
|
||||
import PopupMenu, { PopupProvider, usePopup, Popup } from '.';
|
||||
|
||||
export default {
|
||||
component: PopupMenu,
|
||||
@ -24,28 +25,17 @@ export default {
|
||||
],
|
||||
},
|
||||
};
|
||||
const labelData = [
|
||||
const labelData: Array<ProjectLabel> = [
|
||||
{
|
||||
labelId: 'development',
|
||||
id: 'development',
|
||||
name: 'Development',
|
||||
createdDate: new Date().toString(),
|
||||
labelColor: {
|
||||
id: '1',
|
||||
name: 'white',
|
||||
colorHex: LabelColors.BLUE,
|
||||
name: 'blue',
|
||||
position: 1,
|
||||
},
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
labelId: 'general',
|
||||
name: 'General',
|
||||
labelColor: {
|
||||
id: '1',
|
||||
name: 'white',
|
||||
colorHex: LabelColors.PINK,
|
||||
position: 1,
|
||||
},
|
||||
active: false,
|
||||
},
|
||||
];
|
||||
|
||||
@ -74,9 +64,9 @@ const LabelManagerEditor = () => {
|
||||
onLabelToggle={labelId => {
|
||||
setLabels(
|
||||
produce(labels, draftState => {
|
||||
const idx = labels.findIndex(label => label.labelId === labelId);
|
||||
const idx = labels.findIndex(label => label.id === labelId);
|
||||
if (idx !== -1) {
|
||||
draftState[idx] = { ...draftState[idx], active: !labels[idx].active };
|
||||
draftState[idx] = { ...draftState[idx] };
|
||||
}
|
||||
}),
|
||||
);
|
||||
@ -86,13 +76,21 @@ const LabelManagerEditor = () => {
|
||||
<Popup onClose={action('on close')} title="Edit label" tab={1}>
|
||||
<LabelEditor
|
||||
labelColors={[{ id: '1', colorHex: '#c2c6dc', position: 1, name: 'gray' }]}
|
||||
label={labels.find(label => label.labelId === currentLabel) ?? null}
|
||||
label={labels.find(label => label.id === currentLabel) ?? null}
|
||||
onLabelEdit={(_labelId, name, color) => {
|
||||
setLabels(
|
||||
produce(labels, draftState => {
|
||||
const idx = labels.findIndex(label => label.labelId === currentLabel);
|
||||
const idx = labels.findIndex(label => label.id === currentLabel);
|
||||
if (idx !== -1) {
|
||||
draftState[idx] = { ...draftState[idx], name, labelColor: color };
|
||||
draftState[idx] = {
|
||||
...draftState[idx],
|
||||
name,
|
||||
labelColor: {
|
||||
...draftState[idx].labelColor,
|
||||
name: color.name ?? '',
|
||||
colorHex: color.colorHex,
|
||||
},
|
||||
};
|
||||
}
|
||||
}),
|
||||
);
|
||||
@ -105,7 +103,20 @@ const LabelManagerEditor = () => {
|
||||
label={null}
|
||||
labelColors={[{ id: '1', colorHex: '#c2c6dc', position: 1, name: 'gray' }]}
|
||||
onLabelEdit={(_labelId, name, color) => {
|
||||
setLabels([...labels, { labelId: name, name, labelColor: color, active: false }]);
|
||||
setLabels([
|
||||
...labels,
|
||||
{
|
||||
id: name,
|
||||
name,
|
||||
createdDate: new Date().toString(),
|
||||
labelColor: {
|
||||
id: color.id,
|
||||
colorHex: color.colorHex,
|
||||
name: color.name ?? '',
|
||||
position: 1,
|
||||
},
|
||||
},
|
||||
]);
|
||||
setTab(0);
|
||||
}}
|
||||
/>
|
||||
@ -214,7 +225,12 @@ export const MemberManagerPopup = () => {
|
||||
<PopupMenu title="Members" top={popupData.top} onClose={() => setPopupData(initalState)} left={popupData.left}>
|
||||
<MemberManager
|
||||
availableMembers={[
|
||||
{ userID: '1', displayName: 'Jordan Knott', profileIcon: { bgColor: null, url: null, initials: null } },
|
||||
{
|
||||
id: '1',
|
||||
firstName: 'Jordan',
|
||||
lastName: 'Knott',
|
||||
profileIcon: { bgColor: null, url: null, initials: null },
|
||||
},
|
||||
]}
|
||||
activeMembers={[]}
|
||||
onMemberChange={action('member change')}
|
||||
@ -251,26 +267,35 @@ export const DueDateManagerPopup = () => {
|
||||
<PopupMenu title="Due Date" top={popupData.top} onClose={() => setPopupData(initalState)} left={popupData.left}>
|
||||
<DueDateManager
|
||||
task={{
|
||||
taskID: '1',
|
||||
taskGroup: { name: 'General', taskGroupID: '1' },
|
||||
id: '1',
|
||||
taskGroup: { name: 'General', id: '1', position: 1 },
|
||||
name: 'Hello, world',
|
||||
position: 1,
|
||||
labels: [
|
||||
{
|
||||
labelId: 'soft-skills',
|
||||
labelColor: {
|
||||
id: '1',
|
||||
name: 'white',
|
||||
colorHex: '#fff',
|
||||
position: 1,
|
||||
id: 'soft-skills',
|
||||
assignedDate: new Date().toString(),
|
||||
projectLabel: {
|
||||
createdDate: new Date().toString(),
|
||||
id: 'label-soft-skills',
|
||||
name: 'Soft Skills',
|
||||
labelColor: {
|
||||
id: '1',
|
||||
name: 'white',
|
||||
colorHex: '#fff',
|
||||
position: 1,
|
||||
},
|
||||
},
|
||||
active: true,
|
||||
name: 'Soft Skills',
|
||||
},
|
||||
],
|
||||
description: 'hello!',
|
||||
members: [
|
||||
{ userID: '1', profileIcon: { bgColor: null, url: null, initials: null }, displayName: 'Jordan Knott' },
|
||||
assigned: [
|
||||
{
|
||||
id: '1',
|
||||
profileIcon: { bgColor: null, url: null, initials: null },
|
||||
firstName: 'Jordan',
|
||||
lastName: 'Knott',
|
||||
},
|
||||
],
|
||||
}}
|
||||
onCancel={action('cancel')}
|
||||
|
@ -17,28 +17,17 @@ export default {
|
||||
},
|
||||
};
|
||||
|
||||
const labelData = [
|
||||
const labelData: Array<ProjectLabel> = [
|
||||
{
|
||||
labelId: 'development',
|
||||
id: 'development',
|
||||
name: 'Development',
|
||||
createdDate: 'date',
|
||||
labelColor: {
|
||||
id: '1',
|
||||
name: 'white',
|
||||
id: 'label-color-blue',
|
||||
colorHex: LabelColors.BLUE,
|
||||
name: 'blue',
|
||||
position: 1,
|
||||
},
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
labelId: 'general',
|
||||
name: 'General',
|
||||
labelColor: {
|
||||
id: '1',
|
||||
name: 'white',
|
||||
colorHex: LabelColors.PINK,
|
||||
position: 1,
|
||||
},
|
||||
active: false,
|
||||
},
|
||||
];
|
||||
|
||||
@ -70,7 +59,6 @@ export const Default = () => {
|
||||
isComposerOpen={false}
|
||||
onSaveName={action('on save name')}
|
||||
onOpenComposer={action('on open composer')}
|
||||
tasks={[]}
|
||||
onExtraMenuOpen={(taskGroupID, $targetRef) => console.log(taskGroupID, $targetRef)}
|
||||
>
|
||||
<ListCards>
|
||||
|
@ -22,7 +22,7 @@ type Props = {
|
||||
onEditCard: (taskGroupID: string, taskID: string, cardName: string) => void;
|
||||
onOpenPopup: (popupType: number, top: number, left: number) => void;
|
||||
onArchiveCard: (taskGroupID: string, taskID: string) => void;
|
||||
labels?: Label[];
|
||||
labels?: Array<ProjectLabel>;
|
||||
isOpen: boolean;
|
||||
top: number;
|
||||
left: number;
|
||||
@ -72,7 +72,7 @@ const QuickCardEditor = ({
|
||||
<ListCardLabels>
|
||||
{labels &&
|
||||
labels.map(label => (
|
||||
<ListCardLabel color={label.labelColor.colorHex} key={label.name}>
|
||||
<ListCardLabel color={label.labelColor.colorHex} key={label.id}>
|
||||
{label.name}
|
||||
</ListCardLabel>
|
||||
))}
|
||||
|
@ -31,7 +31,7 @@ type TaskAssigneeProps = {
|
||||
const TaskAssignee: React.FC<TaskAssigneeProps> = ({ member, onMemberProfile, size }) => {
|
||||
const $memberRef = useRef<HTMLDivElement>(null);
|
||||
return (
|
||||
<TaskDetailAssignee ref={$memberRef} onClick={() => onMemberProfile($memberRef, member.userID)} key={member.userID}>
|
||||
<TaskDetailAssignee ref={$memberRef} onClick={() => onMemberProfile($memberRef, member.id)} key={member.id}>
|
||||
<ProfileIcon size={size}>{member.profileIcon.initials ?? ''}</ProfileIcon>
|
||||
</TaskDetailAssignee>
|
||||
);
|
||||
|
@ -244,11 +244,11 @@ export const TaskDetailLabels = styled.div`
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
export const TaskDetailLabel = styled.div`
|
||||
export const TaskDetailLabel = styled.div<{ color: string }>`
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
background-color: #00c2e0;
|
||||
background-color: ${props => props.color};
|
||||
color: #fff;
|
||||
|
||||
cursor: pointer;
|
||||
|
@ -29,29 +29,34 @@ export const Default = () => {
|
||||
return (
|
||||
<TaskDetails
|
||||
task={{
|
||||
taskID: '1',
|
||||
taskGroup: { name: 'General', taskGroupID: '1' },
|
||||
id: '1',
|
||||
taskGroup: { name: 'General', id: '1' },
|
||||
name: 'Hello, world',
|
||||
position: 1,
|
||||
labels: [
|
||||
{
|
||||
labelId: 'soft-skills',
|
||||
labelColor: {
|
||||
id: '1',
|
||||
name: 'white',
|
||||
colorHex: '#fff',
|
||||
position: 1,
|
||||
id: 'soft-skills',
|
||||
assignedDate: new Date().toString(),
|
||||
projectLabel: {
|
||||
createdDate: new Date().toString(),
|
||||
id: 'label-soft-skills',
|
||||
name: 'Soft Skills',
|
||||
labelColor: {
|
||||
id: '1',
|
||||
name: 'white',
|
||||
colorHex: '#fff',
|
||||
position: 1,
|
||||
},
|
||||
},
|
||||
active: true,
|
||||
name: 'Soft Skills',
|
||||
},
|
||||
],
|
||||
description,
|
||||
members: [
|
||||
assigned: [
|
||||
{
|
||||
userID: '1',
|
||||
id: '1',
|
||||
profileIcon: { bgColor: null, url: null, initials: null },
|
||||
displayName: 'Jordan Knott',
|
||||
firstName: 'Jordan',
|
||||
lastName: 'Knott',
|
||||
},
|
||||
],
|
||||
}}
|
||||
|
@ -193,15 +193,15 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
||||
<TaskDetailsSidebar>
|
||||
<TaskDetailSectionTitle>Assignees</TaskDetailSectionTitle>
|
||||
<TaskDetailAssignees>
|
||||
{task.members && task.members.length === 0 ? (
|
||||
{task.assigned && task.assigned.length === 0 ? (
|
||||
<UnassignedLabel ref={$unassignedRef} onClick={onUnassignedClick}>
|
||||
Unassigned
|
||||
</UnassignedLabel>
|
||||
) : (
|
||||
<>
|
||||
{task.members &&
|
||||
task.members.map(member => (
|
||||
<TaskAssignee size={32} member={member} onMemberProfile={onMemberProfile} />
|
||||
{task.assigned &&
|
||||
task.assigned.map(member => (
|
||||
<TaskAssignee key={member.id} size={32} member={member} onMemberProfile={onMemberProfile} />
|
||||
))}
|
||||
<TaskDetailsAddMember ref={$addMemberRef} onClick={onAddMember}>
|
||||
<TaskDetailsAddMemberIcon>
|
||||
@ -214,7 +214,11 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
||||
<TaskDetailSectionTitle>Labels</TaskDetailSectionTitle>
|
||||
<TaskDetailLabels>
|
||||
{task.labels.map(label => {
|
||||
return <TaskDetailLabel>{label.name}</TaskDetailLabel>;
|
||||
return (
|
||||
<TaskDetailLabel key={label.projectLabel.id} color={label.projectLabel.labelColor.colorHex}>
|
||||
{label.projectLabel.name}
|
||||
</TaskDetailLabel>
|
||||
);
|
||||
})}
|
||||
<TaskDetailsAddLabel ref={$addLabelRef} onClick={onAddLabel}>
|
||||
<TaskDetailsAddLabelIcon>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import styled, { css } from 'styled-components';
|
||||
import TextareaAutosize from 'react-autosize-textarea';
|
||||
import { mixin } from 'shared/utils/styles';
|
||||
|
||||
export const NavbarWrapper = styled.div`
|
||||
@ -135,7 +136,36 @@ export const ProjectName = styled.h1`
|
||||
color: #c2c6dc;
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
padding: 6px 10px 6px 8px;
|
||||
padding: 3px 10px 3px 8px;
|
||||
font-family: 'Droid Sans';
|
||||
margin: -4px 0;
|
||||
`;
|
||||
export const ProjectNameTextarea = styled(TextareaAutosize)`
|
||||
font-family: 'Droid Sans';
|
||||
border: none;
|
||||
resize: none;
|
||||
overflow: hidden;
|
||||
overflow-wrap: break-word;
|
||||
background: transparent;
|
||||
border-radius: 3px;
|
||||
box-shadow: none;
|
||||
margin: -4px 0;
|
||||
|
||||
letter-spacing: normal;
|
||||
word-spacing: normal;
|
||||
text-transform: none;
|
||||
text-indent: 0px;
|
||||
text-shadow: none;
|
||||
flex-direction: column;
|
||||
text-align: start;
|
||||
|
||||
color: #c2c6dc;
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
padding: 3px 10px 3px 8px;
|
||||
&:focus {
|
||||
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const ProjectSwitcher = styled.button`
|
||||
|
@ -1,8 +1,9 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { Star, Bell, Cog, AngleDown } from 'shared/icons';
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import { Star, Ellipsis, Bell, Cog, AngleDown } from 'shared/icons';
|
||||
|
||||
import {
|
||||
NotificationContainer,
|
||||
ProjectNameTextarea,
|
||||
InviteButton,
|
||||
GlobalActions,
|
||||
ProjectActions,
|
||||
@ -28,9 +29,72 @@ import TaskAssignee from 'shared/components/TaskAssignee';
|
||||
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
||||
import MiniProfile from 'shared/components/MiniProfile';
|
||||
|
||||
type ProjectHeadingProps = {
|
||||
projectName: string;
|
||||
onSaveProjectName?: (projectName: string) => void;
|
||||
};
|
||||
|
||||
const ProjectHeading: React.FC<ProjectHeadingProps> = ({ projectName: initialProjectName, onSaveProjectName }) => {
|
||||
const [isEditProjectName, setEditProjectName] = useState(false);
|
||||
const [projectName, setProjectName] = useState(initialProjectName);
|
||||
const $projectName = useRef<HTMLTextAreaElement>(null);
|
||||
useEffect(() => {
|
||||
if (isEditProjectName && $projectName && $projectName.current) {
|
||||
$projectName.current.focus();
|
||||
$projectName.current.select();
|
||||
}
|
||||
}, [isEditProjectName]);
|
||||
useEffect(() => {
|
||||
setProjectName(initialProjectName);
|
||||
}, [initialProjectName]);
|
||||
|
||||
const onProjectNameChange = (event: React.FormEvent<HTMLTextAreaElement>): void => {
|
||||
setProjectName(event.currentTarget.value);
|
||||
};
|
||||
const onProjectNameBlur = () => {
|
||||
if (onSaveProjectName) {
|
||||
onSaveProjectName(projectName);
|
||||
}
|
||||
setEditProjectName(false);
|
||||
};
|
||||
const onProjectNameKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
if ($projectName && $projectName.current) {
|
||||
$projectName.current.blur();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Separator>»</Separator>
|
||||
{isEditProjectName ? (
|
||||
<ProjectNameTextarea
|
||||
ref={$projectName}
|
||||
onChange={onProjectNameChange}
|
||||
onKeyDown={onProjectNameKeyDown}
|
||||
onBlur={onProjectNameBlur}
|
||||
spellCheck={false}
|
||||
value={projectName}
|
||||
/>
|
||||
) : (
|
||||
<ProjectName
|
||||
onClick={() => {
|
||||
setEditProjectName(true);
|
||||
}}
|
||||
>
|
||||
{projectName}
|
||||
</ProjectName>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
type NavBarProps = {
|
||||
projectName: string;
|
||||
onProfileClick: (bottom: number, right: number) => void;
|
||||
onSaveProjectName?: (projectName: string) => void;
|
||||
onNotificationClick: () => void;
|
||||
bgColor: string;
|
||||
firstName: string;
|
||||
@ -38,8 +102,10 @@ type NavBarProps = {
|
||||
initials: string;
|
||||
projectMembers?: Array<TaskUser> | null;
|
||||
};
|
||||
|
||||
const NavBar: React.FC<NavBarProps> = ({
|
||||
projectName,
|
||||
onSaveProjectName,
|
||||
onProfileClick,
|
||||
onNotificationClick,
|
||||
firstName,
|
||||
@ -68,14 +134,14 @@ const NavBar: React.FC<NavBarProps> = ({
|
||||
</Popup>,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<NavbarWrapper>
|
||||
<NavbarHeader>
|
||||
<ProjectActions>
|
||||
<ProjectMeta>
|
||||
<ProjectSwitcher>Projects</ProjectSwitcher>
|
||||
<Separator>»</Separator>
|
||||
<ProjectName>{projectName}</ProjectName>
|
||||
<ProjectHeading projectName={projectName} onSaveProjectName={onSaveProjectName} />
|
||||
<ProjectSettingsButton>
|
||||
<AngleDown color="#c2c6dc" />
|
||||
</ProjectSettingsButton>
|
||||
@ -94,7 +160,7 @@ const NavBar: React.FC<NavBarProps> = ({
|
||||
{projectMembers && (
|
||||
<ProjectMembers>
|
||||
{projectMembers.map(member => (
|
||||
<TaskAssignee key={member.userID} size={28} member={member} onMemberProfile={onMemberProfile} />
|
||||
<TaskAssignee key={member.id} size={28} member={member} onMemberProfile={onMemberProfile} />
|
||||
))}
|
||||
<InviteButton>Invite</InviteButton>
|
||||
</ProjectMembers>
|
||||
@ -104,9 +170,7 @@ const NavBar: React.FC<NavBarProps> = ({
|
||||
</NotificationContainer>
|
||||
<ProfileContainer>
|
||||
<ProfileNameWrapper>
|
||||
<ProfileNamePrimary>
|
||||
{firstName} {lastName}
|
||||
</ProfileNamePrimary>
|
||||
<ProfileNamePrimary>{`${firstName} ${lastName}`}</ProfileNamePrimary>
|
||||
<ProfileNameSecondary>Manager</ProfileNameSecondary>
|
||||
</ProfileNameWrapper>
|
||||
<ProfileIcon ref={$profileRef} onClick={handleProfileClick} bgColor={bgColor}>
|
||||
|
@ -34,10 +34,8 @@ export type LabelColor = {
|
||||
export type TaskLabel = {
|
||||
__typename?: 'TaskLabel';
|
||||
id: Scalars['ID'];
|
||||
projectLabelID: Scalars['UUID'];
|
||||
projectLabel: ProjectLabel;
|
||||
assignedDate: Scalars['Time'];
|
||||
colorHex: Scalars['String'];
|
||||
name?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type ProfileIcon = {
|
||||
@ -255,11 +253,10 @@ export type UpdateTaskDescriptionInput = {
|
||||
|
||||
export type AddTaskLabelInput = {
|
||||
taskID: Scalars['UUID'];
|
||||
labelColorID: Scalars['UUID'];
|
||||
projectLabelID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type RemoveTaskLabelInput = {
|
||||
taskID: Scalars['UUID'];
|
||||
taskLabelID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
@ -289,12 +286,29 @@ export type UpdateProjectLabelColor = {
|
||||
labelColorID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type ToggleTaskLabelInput = {
|
||||
taskID: Scalars['UUID'];
|
||||
projectLabelID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type ToggleTaskLabelPayload = {
|
||||
__typename?: 'ToggleTaskLabelPayload';
|
||||
active: Scalars['Boolean'];
|
||||
task: Task;
|
||||
};
|
||||
|
||||
export type UpdateProjectName = {
|
||||
projectID: Scalars['UUID'];
|
||||
name: Scalars['String'];
|
||||
};
|
||||
|
||||
export type Mutation = {
|
||||
__typename?: 'Mutation';
|
||||
createRefreshToken: RefreshToken;
|
||||
createUserAccount: UserAccount;
|
||||
createTeam: Team;
|
||||
createProject: Project;
|
||||
updateProjectName: Project;
|
||||
createProjectLabel: ProjectLabel;
|
||||
deleteProjectLabel: ProjectLabel;
|
||||
updateProjectLabel: ProjectLabel;
|
||||
@ -305,6 +319,7 @@ export type Mutation = {
|
||||
deleteTaskGroup: DeleteTaskGroupPayload;
|
||||
addTaskLabel: Task;
|
||||
removeTaskLabel: Task;
|
||||
toggleTaskLabel: ToggleTaskLabelPayload;
|
||||
createTask: Task;
|
||||
updateTaskDescription: Task;
|
||||
updateTaskLocation: Task;
|
||||
@ -336,6 +351,11 @@ export type MutationCreateProjectArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationUpdateProjectNameArgs = {
|
||||
input?: Maybe<UpdateProjectName>;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateProjectLabelArgs = {
|
||||
input: NewProjectLabel;
|
||||
};
|
||||
@ -386,6 +406,11 @@ export type MutationRemoveTaskLabelArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationToggleTaskLabelArgs = {
|
||||
input: ToggleTaskLabelInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateTaskArgs = {
|
||||
input: NewTask;
|
||||
};
|
||||
@ -476,8 +501,19 @@ export type CreateTaskMutation = (
|
||||
& Pick<Task, 'id' | 'name' | 'position' | 'description'>
|
||||
& { taskGroup: (
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'id'>
|
||||
), assigned: Array<(
|
||||
& Pick<TaskGroup, 'id' | 'name' | 'position'>
|
||||
), labels: Array<(
|
||||
{ __typename?: 'TaskLabel' }
|
||||
& Pick<TaskLabel, 'id' | 'assignedDate'>
|
||||
& { projectLabel: (
|
||||
{ __typename?: 'ProjectLabel' }
|
||||
& Pick<ProjectLabel, 'id' | 'name' | 'createdDate'>
|
||||
& { labelColor: (
|
||||
{ __typename?: 'LabelColor' }
|
||||
& Pick<LabelColor, 'id' | 'colorHex' | 'position' | 'name'>
|
||||
) }
|
||||
) }
|
||||
)>, assigned: Array<(
|
||||
{ __typename?: 'ProjectMember' }
|
||||
& Pick<ProjectMember, 'id' | 'firstName' | 'lastName'>
|
||||
& { profileIcon: (
|
||||
@ -580,7 +616,21 @@ export type FindProjectQuery = (
|
||||
& { tasks: Array<(
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'id' | 'name' | 'position' | 'description'>
|
||||
& { assigned: Array<(
|
||||
& { taskGroup: (
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'id' | 'name' | 'position'>
|
||||
), labels: Array<(
|
||||
{ __typename?: 'TaskLabel' }
|
||||
& Pick<TaskLabel, 'id' | 'assignedDate'>
|
||||
& { projectLabel: (
|
||||
{ __typename?: 'ProjectLabel' }
|
||||
& Pick<ProjectLabel, 'id' | 'name' | 'createdDate'>
|
||||
& { labelColor: (
|
||||
{ __typename?: 'LabelColor' }
|
||||
& Pick<LabelColor, 'id' | 'colorHex' | 'position' | 'name'>
|
||||
) }
|
||||
) }
|
||||
)>, assigned: Array<(
|
||||
{ __typename?: 'ProjectMember' }
|
||||
& Pick<ProjectMember, 'id' | 'firstName' | 'lastName'>
|
||||
& { profileIcon: (
|
||||
@ -609,7 +659,18 @@ export type FindTaskQuery = (
|
||||
& { taskGroup: (
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'id'>
|
||||
), assigned: Array<(
|
||||
), labels: Array<(
|
||||
{ __typename?: 'TaskLabel' }
|
||||
& Pick<TaskLabel, 'id' | 'assignedDate'>
|
||||
& { projectLabel: (
|
||||
{ __typename?: 'ProjectLabel' }
|
||||
& Pick<ProjectLabel, 'id' | 'name' | 'createdDate'>
|
||||
& { labelColor: (
|
||||
{ __typename?: 'LabelColor' }
|
||||
& Pick<LabelColor, 'id' | 'colorHex' | 'position' | 'name'>
|
||||
) }
|
||||
) }
|
||||
)>, assigned: Array<(
|
||||
{ __typename?: 'ProjectMember' }
|
||||
& Pick<ProjectMember, 'id' | 'firstName' | 'lastName'>
|
||||
& { profileIcon: (
|
||||
@ -650,6 +711,36 @@ export type MeQuery = (
|
||||
) }
|
||||
);
|
||||
|
||||
export type ToggleTaskLabelMutationVariables = {
|
||||
taskID: Scalars['UUID'];
|
||||
projectLabelID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
|
||||
export type ToggleTaskLabelMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { toggleTaskLabel: (
|
||||
{ __typename?: 'ToggleTaskLabelPayload' }
|
||||
& Pick<ToggleTaskLabelPayload, 'active'>
|
||||
& { task: (
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'id'>
|
||||
& { labels: Array<(
|
||||
{ __typename?: 'TaskLabel' }
|
||||
& Pick<TaskLabel, 'id' | 'assignedDate'>
|
||||
& { projectLabel: (
|
||||
{ __typename?: 'ProjectLabel' }
|
||||
& Pick<ProjectLabel, 'id' | 'createdDate' | 'name'>
|
||||
& { labelColor: (
|
||||
{ __typename?: 'LabelColor' }
|
||||
& Pick<LabelColor, 'id' | 'colorHex' | 'name' | 'position'>
|
||||
) }
|
||||
) }
|
||||
)> }
|
||||
) }
|
||||
) }
|
||||
);
|
||||
|
||||
export type UnassignTaskMutationVariables = {
|
||||
taskID: Scalars['UUID'];
|
||||
userID: Scalars['UUID'];
|
||||
@ -687,6 +778,20 @@ export type UpdateProjectLabelMutation = (
|
||||
) }
|
||||
);
|
||||
|
||||
export type UpdateProjectNameMutationVariables = {
|
||||
projectID: Scalars['UUID'];
|
||||
name: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type UpdateProjectNameMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { updateProjectName: (
|
||||
{ __typename?: 'Project' }
|
||||
& Pick<Project, 'id' | 'name'>
|
||||
) }
|
||||
);
|
||||
|
||||
export type UpdateTaskDescriptionMutationVariables = {
|
||||
taskID: Scalars['UUID'];
|
||||
description: Scalars['String'];
|
||||
@ -834,6 +939,23 @@ export const CreateTaskDocument = gql`
|
||||
description
|
||||
taskGroup {
|
||||
id
|
||||
name
|
||||
position
|
||||
}
|
||||
labels {
|
||||
id
|
||||
assignedDate
|
||||
projectLabel {
|
||||
id
|
||||
name
|
||||
createdDate
|
||||
labelColor {
|
||||
id
|
||||
colorHex
|
||||
position
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
assigned {
|
||||
id
|
||||
@ -1032,13 +1154,13 @@ export const FindProjectDocument = gql`
|
||||
labels {
|
||||
id
|
||||
createdDate
|
||||
name
|
||||
labelColor {
|
||||
id
|
||||
name
|
||||
colorHex
|
||||
position
|
||||
}
|
||||
name
|
||||
}
|
||||
taskGroups {
|
||||
id
|
||||
@ -1049,6 +1171,26 @@ export const FindProjectDocument = gql`
|
||||
name
|
||||
position
|
||||
description
|
||||
taskGroup {
|
||||
id
|
||||
name
|
||||
position
|
||||
}
|
||||
labels {
|
||||
id
|
||||
assignedDate
|
||||
projectLabel {
|
||||
id
|
||||
name
|
||||
createdDate
|
||||
labelColor {
|
||||
id
|
||||
colorHex
|
||||
position
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
assigned {
|
||||
id
|
||||
firstName
|
||||
@ -1106,6 +1248,21 @@ export const FindTaskDocument = gql`
|
||||
taskGroup {
|
||||
id
|
||||
}
|
||||
labels {
|
||||
id
|
||||
assignedDate
|
||||
projectLabel {
|
||||
id
|
||||
name
|
||||
createdDate
|
||||
labelColor {
|
||||
id
|
||||
colorHex
|
||||
position
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
assigned {
|
||||
id
|
||||
firstName
|
||||
@ -1219,6 +1376,57 @@ export function useMeLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptio
|
||||
export type MeQueryHookResult = ReturnType<typeof useMeQuery>;
|
||||
export type MeLazyQueryHookResult = ReturnType<typeof useMeLazyQuery>;
|
||||
export type MeQueryResult = ApolloReactCommon.QueryResult<MeQuery, MeQueryVariables>;
|
||||
export const ToggleTaskLabelDocument = gql`
|
||||
mutation toggleTaskLabel($taskID: UUID!, $projectLabelID: UUID!) {
|
||||
toggleTaskLabel(input: {taskID: $taskID, projectLabelID: $projectLabelID}) {
|
||||
active
|
||||
task {
|
||||
id
|
||||
labels {
|
||||
id
|
||||
assignedDate
|
||||
projectLabel {
|
||||
id
|
||||
createdDate
|
||||
labelColor {
|
||||
id
|
||||
colorHex
|
||||
name
|
||||
position
|
||||
}
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type ToggleTaskLabelMutationFn = ApolloReactCommon.MutationFunction<ToggleTaskLabelMutation, ToggleTaskLabelMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useToggleTaskLabelMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useToggleTaskLabelMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useToggleTaskLabelMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [toggleTaskLabelMutation, { data, loading, error }] = useToggleTaskLabelMutation({
|
||||
* variables: {
|
||||
* taskID: // value for 'taskID'
|
||||
* projectLabelID: // value for 'projectLabelID'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useToggleTaskLabelMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<ToggleTaskLabelMutation, ToggleTaskLabelMutationVariables>) {
|
||||
return ApolloReactHooks.useMutation<ToggleTaskLabelMutation, ToggleTaskLabelMutationVariables>(ToggleTaskLabelDocument, baseOptions);
|
||||
}
|
||||
export type ToggleTaskLabelMutationHookResult = ReturnType<typeof useToggleTaskLabelMutation>;
|
||||
export type ToggleTaskLabelMutationResult = ApolloReactCommon.MutationResult<ToggleTaskLabelMutation>;
|
||||
export type ToggleTaskLabelMutationOptions = ApolloReactCommon.BaseMutationOptions<ToggleTaskLabelMutation, ToggleTaskLabelMutationVariables>;
|
||||
export const UnassignTaskDocument = gql`
|
||||
mutation unassignTask($taskID: UUID!, $userID: UUID!) {
|
||||
unassignTask(input: {taskID: $taskID, userID: $userID}) {
|
||||
@ -1299,6 +1507,40 @@ export function useUpdateProjectLabelMutation(baseOptions?: ApolloReactHooks.Mut
|
||||
export type UpdateProjectLabelMutationHookResult = ReturnType<typeof useUpdateProjectLabelMutation>;
|
||||
export type UpdateProjectLabelMutationResult = ApolloReactCommon.MutationResult<UpdateProjectLabelMutation>;
|
||||
export type UpdateProjectLabelMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateProjectLabelMutation, UpdateProjectLabelMutationVariables>;
|
||||
export const UpdateProjectNameDocument = gql`
|
||||
mutation updateProjectName($projectID: UUID!, $name: String!) {
|
||||
updateProjectName(input: {projectID: $projectID, name: $name}) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type UpdateProjectNameMutationFn = ApolloReactCommon.MutationFunction<UpdateProjectNameMutation, UpdateProjectNameMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useUpdateProjectNameMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useUpdateProjectNameMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useUpdateProjectNameMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [updateProjectNameMutation, { data, loading, error }] = useUpdateProjectNameMutation({
|
||||
* variables: {
|
||||
* projectID: // value for 'projectID'
|
||||
* name: // value for 'name'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useUpdateProjectNameMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<UpdateProjectNameMutation, UpdateProjectNameMutationVariables>) {
|
||||
return ApolloReactHooks.useMutation<UpdateProjectNameMutation, UpdateProjectNameMutationVariables>(UpdateProjectNameDocument, baseOptions);
|
||||
}
|
||||
export type UpdateProjectNameMutationHookResult = ReturnType<typeof useUpdateProjectNameMutation>;
|
||||
export type UpdateProjectNameMutationResult = ApolloReactCommon.MutationResult<UpdateProjectNameMutation>;
|
||||
export type UpdateProjectNameMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateProjectNameMutation, UpdateProjectNameMutationVariables>;
|
||||
export const UpdateTaskDescriptionDocument = gql`
|
||||
mutation updateTaskDescription($taskID: UUID!, $description: String!) {
|
||||
updateTaskDescription(input: {taskID: $taskID, description: $description}) {
|
||||
|
@ -6,6 +6,23 @@ mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) {
|
||||
description
|
||||
taskGroup {
|
||||
id
|
||||
name
|
||||
position
|
||||
}
|
||||
labels {
|
||||
id
|
||||
assignedDate
|
||||
projectLabel {
|
||||
id
|
||||
name
|
||||
createdDate
|
||||
labelColor {
|
||||
id
|
||||
colorHex
|
||||
position
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
assigned {
|
||||
id
|
||||
|
@ -14,13 +14,13 @@ query findProject($projectId: String!) {
|
||||
labels {
|
||||
id
|
||||
createdDate
|
||||
name
|
||||
labelColor {
|
||||
id
|
||||
name
|
||||
colorHex
|
||||
position
|
||||
}
|
||||
name
|
||||
}
|
||||
taskGroups {
|
||||
id
|
||||
@ -31,6 +31,26 @@ query findProject($projectId: String!) {
|
||||
name
|
||||
position
|
||||
description
|
||||
taskGroup {
|
||||
id
|
||||
name
|
||||
position
|
||||
}
|
||||
labels {
|
||||
id
|
||||
assignedDate
|
||||
projectLabel {
|
||||
id
|
||||
name
|
||||
createdDate
|
||||
labelColor {
|
||||
id
|
||||
colorHex
|
||||
position
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
assigned {
|
||||
id
|
||||
firstName
|
||||
|
@ -7,6 +7,21 @@ query findTask($taskID: UUID!) {
|
||||
taskGroup {
|
||||
id
|
||||
}
|
||||
labels {
|
||||
id
|
||||
assignedDate
|
||||
projectLabel {
|
||||
id
|
||||
name
|
||||
createdDate
|
||||
labelColor {
|
||||
id
|
||||
colorHex
|
||||
position
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
assigned {
|
||||
id
|
||||
firstName
|
||||
|
29
web/src/shared/graphql/toggleTaskLabel.graphqls
Normal file
29
web/src/shared/graphql/toggleTaskLabel.graphqls
Normal file
@ -0,0 +1,29 @@
|
||||
mutation toggleTaskLabel($taskID: UUID!, $projectLabelID: UUID!) {
|
||||
toggleTaskLabel(
|
||||
input: {
|
||||
taskID: $taskID,
|
||||
projectLabelID: $projectLabelID
|
||||
}
|
||||
) {
|
||||
active
|
||||
task {
|
||||
id
|
||||
labels {
|
||||
id
|
||||
assignedDate
|
||||
projectLabel {
|
||||
id
|
||||
createdDate
|
||||
labelColor {
|
||||
id
|
||||
colorHex
|
||||
name
|
||||
position
|
||||
}
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
6
web/src/shared/graphql/updateProjectName.graphqls
Normal file
6
web/src/shared/graphql/updateProjectName.graphqls
Normal file
@ -0,0 +1,6 @@
|
||||
mutation updateProjectName($projectID: UUID!, $name: String!) {
|
||||
updateProjectName(input: {projectID: $projectID, name: $name}) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
@ -3,9 +3,17 @@ import React from 'react';
|
||||
type Props = {
|
||||
size: number | string;
|
||||
color: string;
|
||||
vertical: boolean;
|
||||
};
|
||||
|
||||
const Ellipsis = ({ size, color }: Props) => {
|
||||
const Ellipsis = ({ size, color, vertical }: Props) => {
|
||||
if (vertical) {
|
||||
return (
|
||||
<svg fill={color} xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 192 512">
|
||||
<path d="M96 184c39.8 0 72 32.2 72 72s-32.2 72-72 72-72-32.2-72-72 32.2-72 72-72zM24 80c0 39.8 32.2 72 72 72s72-32.2 72-72S135.8 8 96 8 24 40.2 24 80zm0 352c0 39.8 32.2 72 72 72s72-32.2 72-72-32.2-72-72-72-72 32.2-72 72z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<svg fill={color} xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 512 512">
|
||||
<path d="M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z" />
|
||||
@ -16,6 +24,7 @@ const Ellipsis = ({ size, color }: Props) => {
|
||||
Ellipsis.defaultProps = {
|
||||
size: 16,
|
||||
color: '#000',
|
||||
vertical: false,
|
||||
};
|
||||
|
||||
export default Ellipsis;
|
||||
|
@ -1,50 +0,0 @@
|
||||
import produce from 'immer';
|
||||
|
||||
export const addTask = (currentState: BoardState, newTask: Task) => {
|
||||
return produce(currentState, (draftState: BoardState) => {
|
||||
draftState.tasks[newTask.taskID] = newTask;
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteTask = (currentState: BoardState, taskID: string) => {
|
||||
return produce(currentState, (draftState: BoardState) => {
|
||||
delete draftState.tasks[taskID];
|
||||
});
|
||||
};
|
||||
|
||||
export const addTaskGroup = (currentState: BoardState, newTaskGroup: TaskGroup) => {
|
||||
return produce(currentState, (draftState: BoardState) => {
|
||||
draftState.columns[newTaskGroup.taskGroupID] = newTaskGroup;
|
||||
});
|
||||
};
|
||||
|
||||
export const updateTaskGroup = (currentState: BoardState, newTaskGroup: TaskGroup) => {
|
||||
return produce(currentState, (draftState: BoardState) => {
|
||||
draftState.columns[newTaskGroup.taskGroupID] = newTaskGroup;
|
||||
});
|
||||
};
|
||||
|
||||
export const updateTask = (currentState: BoardState, newTask: Task) => {
|
||||
return produce(currentState, (draftState: BoardState) => {
|
||||
draftState.tasks[newTask.taskID] = newTask;
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteTaskGroup = (currentState: BoardState, deletedTaskGroupID: string) => {
|
||||
return produce(currentState, (draftState: BoardState) => {
|
||||
delete draftState.columns[deletedTaskGroupID];
|
||||
const filteredTasks = Object.keys(currentState.tasks)
|
||||
.filter(taskID => currentState.tasks[taskID].taskGroup.taskGroupID !== deletedTaskGroupID)
|
||||
.reduce((obj: TaskState, key: string) => {
|
||||
obj[key] = currentState.tasks[key];
|
||||
return obj;
|
||||
}, {});
|
||||
draftState.tasks = filteredTasks;
|
||||
});
|
||||
};
|
||||
|
||||
export const updateTaskName = (currentState: BoardState, taskID: string, newName: string) => {
|
||||
return produce(currentState, (draftState: BoardState) => {
|
||||
draftState.tasks[taskID].name = newName;
|
||||
});
|
||||
};
|
Reference in New Issue
Block a user