Compare commits
6 Commits
feat/updat
...
0.2.1
Author | SHA1 | Date | |
---|---|---|---|
e2ef8a1a19 | |||
61cd376bfd | |||
ba9fc64fd9 | |||
03dafe9b7b | |||
12a767947a | |||
40557ba79f |
@ -1,5 +1,5 @@
|
|||||||
[general]
|
[general]
|
||||||
host = '0.0.0.0:3333'
|
hostname = '0.0.0.0:3333'
|
||||||
|
|
||||||
[email_notifications]
|
[email_notifications]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
@ -6,14 +6,6 @@
|
|||||||
"@apollo/client": "^3.0.0-rc.8",
|
"@apollo/client": "^3.0.0-rc.8",
|
||||||
"@apollo/react-common": "^3.1.4",
|
"@apollo/react-common": "^3.1.4",
|
||||||
"@apollo/react-hooks": "^3.1.3",
|
"@apollo/react-hooks": "^3.1.3",
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.27",
|
|
||||||
"@fortawesome/free-brands-svg-icons": "^5.12.1",
|
|
||||||
"@fortawesome/free-regular-svg-icons": "^5.12.1",
|
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.12.1",
|
|
||||||
"@fortawesome/react-fontawesome": "^0.1.8",
|
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
|
||||||
"@testing-library/react": "^9.3.2",
|
|
||||||
"@testing-library/user-event": "^7.1.2",
|
|
||||||
"@types/axios": "^0.14.0",
|
"@types/axios": "^0.14.0",
|
||||||
"@types/color": "^3.0.1",
|
"@types/color": "^3.0.1",
|
||||||
"@types/date-fns": "^2.6.0",
|
"@types/date-fns": "^2.6.0",
|
||||||
|
@ -59,7 +59,7 @@ const Install = () => {
|
|||||||
} else {
|
} else {
|
||||||
const response: RefreshTokenResponse = await x.data;
|
const response: RefreshTokenResponse = await x.data;
|
||||||
const { accessToken: newToken, isInstalled } = response;
|
const { accessToken: newToken, isInstalled } = response;
|
||||||
const claims: JWTToken = jwtDecode(accessToken);
|
const claims: JWTToken = jwtDecode(newToken);
|
||||||
const currentUser = {
|
const currentUser = {
|
||||||
id: claims.userId,
|
id: claims.userId,
|
||||||
roles: {
|
roles: {
|
||||||
@ -69,7 +69,7 @@ const Install = () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
setUser(currentUser);
|
setUser(currentUser);
|
||||||
setAccessToken(accessToken);
|
setAccessToken(newToken);
|
||||||
if (!isInstalled) {
|
if (!isInstalled) {
|
||||||
history.replace('/install');
|
history.replace('/install');
|
||||||
}
|
}
|
||||||
|
@ -323,7 +323,6 @@ const ProjectBoard: React.FC<ProjectBoardProps> = ({ projectID, onCardLabelClick
|
|||||||
const [updateTaskGroupName] = useUpdateTaskGroupNameMutation({});
|
const [updateTaskGroupName] = useUpdateTaskGroupNameMutation({});
|
||||||
const { loading, data } = useFindProjectQuery({
|
const { loading, data } = useFindProjectQuery({
|
||||||
variables: { projectID },
|
variables: { projectID },
|
||||||
pollInterval: 5000,
|
|
||||||
});
|
});
|
||||||
const [deleteTaskGroupTasks] = useDeleteTaskGroupTasksMutation({
|
const [deleteTaskGroupTasks] = useDeleteTaskGroupTasksMutation({
|
||||||
update: (client, resp) =>
|
update: (client, resp) =>
|
||||||
|
@ -3,7 +3,7 @@ import Modal from 'shared/components/Modal';
|
|||||||
import TaskDetails from 'shared/components/TaskDetails';
|
import TaskDetails from 'shared/components/TaskDetails';
|
||||||
import { Popup, usePopup } from 'shared/components/PopupMenu';
|
import { Popup, usePopup } from 'shared/components/PopupMenu';
|
||||||
import MemberManager from 'shared/components/MemberManager';
|
import MemberManager from 'shared/components/MemberManager';
|
||||||
import { useRouteMatch, useHistory, Redirect } from 'react-router';
|
import { useRouteMatch, useHistory } from 'react-router';
|
||||||
import {
|
import {
|
||||||
useDeleteTaskChecklistMutation,
|
useDeleteTaskChecklistMutation,
|
||||||
useUpdateTaskChecklistNameMutation,
|
useUpdateTaskChecklistNameMutation,
|
||||||
@ -32,7 +32,6 @@ import Input from 'shared/components/Input';
|
|||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import updateApolloCache from 'shared/utils/cache';
|
import updateApolloCache from 'shared/utils/cache';
|
||||||
import NOOP from 'shared/utils/noop';
|
import NOOP from 'shared/utils/noop';
|
||||||
import hasNotFoundError from 'shared/utils/error';
|
|
||||||
|
|
||||||
const calculateChecklistBadge = (checklists: Array<TaskChecklist>) => {
|
const calculateChecklistBadge = (checklists: Array<TaskChecklist>) => {
|
||||||
const total = checklists.reduce((prev: any, next: any) => {
|
const total = checklists.reduce((prev: any, next: any) => {
|
||||||
@ -270,8 +269,8 @@ const Details: React.FC<DetailsProps> = ({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { loading, data, refetch, error } = useFindTaskQuery({ variables: { taskID }, pollInterval: 5000 });
|
const { loading, data, refetch } = useFindTaskQuery({ variables: { taskID } });
|
||||||
const [setTaskComplete, { error: setTaskCompleteError }] = useSetTaskCompleteMutation();
|
const [setTaskComplete] = useSetTaskCompleteMutation();
|
||||||
const [updateTaskDueDate] = useUpdateTaskDueDateMutation({
|
const [updateTaskDueDate] = useUpdateTaskDueDateMutation({
|
||||||
onCompleted: () => {
|
onCompleted: () => {
|
||||||
refetch();
|
refetch();
|
||||||
@ -290,13 +289,9 @@ const Details: React.FC<DetailsProps> = ({
|
|||||||
refreshCache();
|
refreshCache();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (hasNotFoundError(error, setTaskCompleteError)) {
|
if (loading) {
|
||||||
return <Redirect to={projectURL} />;
|
return null;
|
||||||
}
|
}
|
||||||
if (setTaskCompleteError && setTaskCompleteError)
|
|
||||||
if (loading) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -351,11 +346,7 @@ const Details: React.FC<DetailsProps> = ({
|
|||||||
onTaskNameChange={onTaskNameChange}
|
onTaskNameChange={onTaskNameChange}
|
||||||
onTaskDescriptionChange={onTaskDescriptionChange}
|
onTaskDescriptionChange={onTaskDescriptionChange}
|
||||||
onToggleTaskComplete={task => {
|
onToggleTaskComplete={task => {
|
||||||
setTaskComplete({ variables: { taskID: task.id, complete: !task.complete } }).catch(r => {
|
setTaskComplete({ variables: { taskID: task.id, complete: !task.complete } });
|
||||||
if (hasNotFoundError(r)) {
|
|
||||||
history.push(projectURL);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
onDeleteTask={onDeleteTask}
|
onDeleteTask={onDeleteTask}
|
||||||
onChangeItemName={(itemID, itemName) => {
|
onChangeItemName={(itemID, itemName) => {
|
||||||
|
@ -8,6 +8,8 @@ import {
|
|||||||
useCreateProjectMutation,
|
useCreateProjectMutation,
|
||||||
GetProjectsDocument,
|
GetProjectsDocument,
|
||||||
GetProjectsQuery,
|
GetProjectsQuery,
|
||||||
|
MeQuery,
|
||||||
|
MeDocument,
|
||||||
} from 'shared/generated/graphql';
|
} from 'shared/generated/graphql';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
@ -234,7 +236,7 @@ type ShowNewProject = {
|
|||||||
|
|
||||||
const Projects = () => {
|
const Projects = () => {
|
||||||
const { showPopup, hidePopup } = usePopup();
|
const { showPopup, hidePopup } = usePopup();
|
||||||
const { loading, data } = useGetProjectsQuery({ fetchPolicy: 'network-only', pollInterval: 5000 });
|
const { loading, data } = useGetProjectsQuery({ fetchPolicy: 'network-only' });
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = 'Taskcafé';
|
document.title = 'Taskcafé';
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -154,7 +154,7 @@ type TeamProjectsProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const TeamProjects: React.FC<TeamProjectsProps> = ({ teamID }) => {
|
const TeamProjects: React.FC<TeamProjectsProps> = ({ teamID }) => {
|
||||||
const { loading, data } = useGetTeamQuery({ variables: { teamID }, pollInterval: 5000 });
|
const { loading, data } = useGetTeamQuery({ variables: { teamID } });
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <span>loading</span>;
|
return <span>loading</span>;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import styled, { css, keyframes } from 'styled-components';
|
import styled, { css, keyframes } from 'styled-components';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
import { mixin } from 'shared/utils/styles';
|
import { mixin } from 'shared/utils/styles';
|
||||||
import TextareaAutosize from 'react-autosize-textarea';
|
import TextareaAutosize from 'react-autosize-textarea';
|
||||||
import { CheckCircle, CheckSquareOutline } from 'shared/icons';
|
import { CheckCircle, CheckSquareOutline, Clock } from 'shared/icons';
|
||||||
import { RefObject } from 'react';
|
|
||||||
import TaskAssignee from 'shared/components/TaskAssignee';
|
import TaskAssignee from 'shared/components/TaskAssignee';
|
||||||
|
|
||||||
export const CardMember = styled(TaskAssignee)<{ zIndex: number }>`
|
export const CardMember = styled(TaskAssignee)<{ zIndex: number }>`
|
||||||
@ -20,7 +18,9 @@ export const ChecklistIcon = styled(CheckSquareOutline)<{ color: 'success' | 'no
|
|||||||
stroke: rgba(${props.theme.colors.success});
|
stroke: rgba(${props.theme.colors.success});
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
export const ClockIcon = styled(FontAwesomeIcon)``;
|
export const ClockIcon = styled(Clock)<{ color: string }>`
|
||||||
|
fill: ${props => props.color};
|
||||||
|
`;
|
||||||
|
|
||||||
export const EditorTextarea = styled(TextareaAutosize)`
|
export const EditorTextarea = styled(TextareaAutosize)`
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { Pencil, Eye, List } from 'shared/icons';
|
||||||
import { faPencilAlt, faList } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { faClock, faEye } from '@fortawesome/free-regular-svg-icons';
|
|
||||||
import {
|
import {
|
||||||
EditorTextarea,
|
EditorTextarea,
|
||||||
CardMember,
|
CardMember,
|
||||||
@ -155,7 +153,7 @@ const Card = React.forwardRef(
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon onClick={onOperationClick} color="#c2c6dc" size="xs" icon={faPencilAlt} />
|
<Pencil width={8} height={8} />
|
||||||
</ListCardOperation>
|
</ListCardOperation>
|
||||||
)}
|
)}
|
||||||
<ListCardDetails complete={complete ?? false}>
|
<ListCardDetails complete={complete ?? false}>
|
||||||
@ -218,18 +216,18 @@ const Card = React.forwardRef(
|
|||||||
<ListCardBadges>
|
<ListCardBadges>
|
||||||
{watched && (
|
{watched && (
|
||||||
<ListCardBadge>
|
<ListCardBadge>
|
||||||
<FontAwesomeIcon color="#6b778c" icon={faEye} size="xs" />
|
<Eye width={8} height={8} />
|
||||||
</ListCardBadge>
|
</ListCardBadge>
|
||||||
)}
|
)}
|
||||||
{dueDate && (
|
{dueDate && (
|
||||||
<DueDateCardBadge isPastDue={dueDate.isPastDue}>
|
<DueDateCardBadge isPastDue={dueDate.isPastDue}>
|
||||||
<ClockIcon color={dueDate.isPastDue ? '#fff' : '#6b778c'} icon={faClock} size="xs" />
|
<ClockIcon color={dueDate.isPastDue ? '#fff' : '#6b778c'} width={8} height={8} />
|
||||||
<ListCardBadgeText>{dueDate.formattedDate}</ListCardBadgeText>
|
<ListCardBadgeText>{dueDate.formattedDate}</ListCardBadgeText>
|
||||||
</DueDateCardBadge>
|
</DueDateCardBadge>
|
||||||
)}
|
)}
|
||||||
{description && (
|
{description && (
|
||||||
<DescriptionBadge>
|
<DescriptionBadge>
|
||||||
<FontAwesomeIcon color="#6b778c" icon={faList} size="xs" />
|
<List width={8} height={8} />
|
||||||
</DescriptionBadge>
|
</DescriptionBadge>
|
||||||
)}
|
)}
|
||||||
{checklists && (
|
{checklists && (
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Button from 'shared/components/Button';
|
import Button from 'shared/components/Button';
|
||||||
import TextareaAutosize from 'react-autosize-textarea';
|
import TextareaAutosize from 'react-autosize-textarea';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
import { mixin } from 'shared/utils/styles';
|
import { mixin } from 'shared/utils/styles';
|
||||||
|
|
||||||
export const CancelIcon = styled(FontAwesomeIcon)`
|
export const CancelIconWrapper = styled.div`
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 1.25em;
|
font-size: 1.25em;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CardComposerWrapper = styled.div<{ isOpen: boolean }>`
|
export const CardComposerWrapper = styled.div<{ isOpen: boolean }>`
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
display: ${props => (props.isOpen ? 'flex' : 'none')};
|
display: ${props => (props.isOpen ? 'flex' : 'none')};
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
import React, { useState, useRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import useOnEscapeKeyDown from 'shared/hooks/onEscapeKeyDown';
|
import useOnEscapeKeyDown from 'shared/hooks/onEscapeKeyDown';
|
||||||
import { faTimes } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
||||||
|
import { Cross } from 'shared/icons';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CardComposerWrapper,
|
CardComposerWrapper,
|
||||||
CancelIcon,
|
CancelIconWrapper,
|
||||||
AddCardButton,
|
AddCardButton,
|
||||||
ComposerControls,
|
ComposerControls,
|
||||||
ComposerControlsSaveSection,
|
ComposerControlsSaveSection,
|
||||||
@ -52,7 +52,9 @@ const CardComposer = ({ isOpen, onCreateCard, onClose }: Props) => {
|
|||||||
>
|
>
|
||||||
Add Card
|
Add Card
|
||||||
</AddCardButton>
|
</AddCardButton>
|
||||||
<CancelIcon onClick={onClose} icon={faTimes} color="#42526e" />
|
<CancelIconWrapper onClick={() => onClose()}>
|
||||||
|
<Cross width={12} height={12} />
|
||||||
|
</CancelIconWrapper>
|
||||||
</ComposerControlsSaveSection>
|
</ComposerControlsSaveSection>
|
||||||
<ComposerControlsActionsSection />
|
<ComposerControlsActionsSection />
|
||||||
</ComposerControls>
|
</ComposerControls>
|
||||||
|
@ -585,3 +585,30 @@ export const ActivityItemLog = styled.span`
|
|||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
color: rgba(${props => props.theme.colors.text.primary});
|
color: rgba(${props => props.theme.colors.text.primary});
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const ViewRawButton = styled.button`
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
right: 4px;
|
||||||
|
bottom: -24px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: rgba(${props => props.theme.colors.text.primary}, 0.25);
|
||||||
|
&:hover {
|
||||||
|
color: rgba(${props => props.theme.colors.text.primary});
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const TaskDetailsEditor = styled(TextareaAutosize)`
|
||||||
|
min-height: 108px;
|
||||||
|
color: #c2c6dc;
|
||||||
|
background: #262c49;
|
||||||
|
border-radius: 3px;
|
||||||
|
line-height: 20px;
|
||||||
|
margin-left: 32px;
|
||||||
|
margin-right: 32px;
|
||||||
|
padding: 9px 8px 7px 8px;
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
`;
|
||||||
|
@ -30,6 +30,7 @@ import {
|
|||||||
AssignUserLabel,
|
AssignUserLabel,
|
||||||
AssignUsersButton,
|
AssignUsersButton,
|
||||||
AssignedUsersSection,
|
AssignedUsersSection,
|
||||||
|
ViewRawButton,
|
||||||
DueDateTitle,
|
DueDateTitle,
|
||||||
Container,
|
Container,
|
||||||
LeftSidebar,
|
LeftSidebar,
|
||||||
@ -65,6 +66,7 @@ import {
|
|||||||
CommentProfile,
|
CommentProfile,
|
||||||
CommentInnerWrapper,
|
CommentInnerWrapper,
|
||||||
ActivitySection,
|
ActivitySection,
|
||||||
|
TaskDetailsEditor,
|
||||||
} from './Styles';
|
} from './Styles';
|
||||||
import Checklist, { ChecklistItem, ChecklistItems } from '../Checklist';
|
import Checklist, { ChecklistItem, ChecklistItems } from '../Checklist';
|
||||||
import onDragEnd from './onDragEnd';
|
import onDragEnd from './onDragEnd';
|
||||||
@ -153,6 +155,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
const [saveTimeout, setSaveTimeout] = useState<any>(null);
|
const [saveTimeout, setSaveTimeout] = useState<any>(null);
|
||||||
|
const [showRaw, setShowRaw] = useState(false);
|
||||||
const [showCommentActions, setShowCommentActions] = useState(false);
|
const [showCommentActions, setShowCommentActions] = useState(false);
|
||||||
const taskDescriptionRef = useRef(task.description ?? '');
|
const taskDescriptionRef = useRef(task.description ?? '');
|
||||||
const $noMemberBtn = useRef<HTMLDivElement>(null);
|
const $noMemberBtn = useRef<HTMLDivElement>(null);
|
||||||
@ -309,28 +312,34 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
</HeaderContainer>
|
</HeaderContainer>
|
||||||
<InnerContentContainer>
|
<InnerContentContainer>
|
||||||
<DescriptionContainer>
|
<DescriptionContainer>
|
||||||
<EditorContainer
|
{showRaw ? (
|
||||||
onClick={e => {
|
<TaskDetailsEditor value={taskDescriptionRef.current} />
|
||||||
if (!editTaskDescription) {
|
) : (
|
||||||
setEditTaskDescription(true);
|
<EditorContainer
|
||||||
}
|
onClick={e => {
|
||||||
}}
|
if (!editTaskDescription) {
|
||||||
>
|
setEditTaskDescription(true);
|
||||||
<Editor
|
}
|
||||||
defaultValue={task.description ?? ''}
|
|
||||||
theme={dark}
|
|
||||||
readOnly={!editTaskDescription}
|
|
||||||
autoFocus
|
|
||||||
onChange={value => {
|
|
||||||
setSaveTimeout(() => {
|
|
||||||
clearTimeout(saveTimeout);
|
|
||||||
return setTimeout(saveDescription, 2000);
|
|
||||||
});
|
|
||||||
const text = value();
|
|
||||||
taskDescriptionRef.current = text;
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
</EditorContainer>
|
<Editor
|
||||||
|
defaultValue={task.description ?? ''}
|
||||||
|
theme={dark}
|
||||||
|
readOnly={!editTaskDescription}
|
||||||
|
autoFocus
|
||||||
|
onChange={value => {
|
||||||
|
setSaveTimeout(() => {
|
||||||
|
clearTimeout(saveTimeout);
|
||||||
|
return setTimeout(saveDescription, 2000);
|
||||||
|
});
|
||||||
|
const text = value();
|
||||||
|
taskDescriptionRef.current = text;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</EditorContainer>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ViewRawButton onClick={() => setShowRaw(!showRaw)}>{showRaw ? 'Show editor' : 'Show raw'}</ViewRawButton>
|
||||||
</DescriptionContainer>
|
</DescriptionContainer>
|
||||||
<ChecklistSection>
|
<ChecklistSection>
|
||||||
<DragDropContext onDragEnd={result => onDragEnd(result, task, onChecklistDrop, onChecklistItemDrop)}>
|
<DragDropContext onDragEnd={result => onDragEnd(result, task, onChecklistDrop, onChecklistItemDrop)}>
|
||||||
|
@ -209,8 +209,7 @@ export enum ObjectType {
|
|||||||
Org = 'ORG',
|
Org = 'ORG',
|
||||||
Team = 'TEAM',
|
Team = 'TEAM',
|
||||||
Project = 'PROJECT',
|
Project = 'PROJECT',
|
||||||
Task = 'TASK',
|
Task = 'TASK'
|
||||||
TaskGroup = 'TASK_GROUP'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Query = {
|
export type Query = {
|
||||||
@ -723,7 +722,7 @@ export type UpdateProjectMemberRolePayload = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type NewTask = {
|
export type NewTask = {
|
||||||
taskGroupID: Scalars['UUID'];
|
taskGroupID: Scalars['String'];
|
||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
position: Scalars['Float'];
|
position: Scalars['Float'];
|
||||||
};
|
};
|
||||||
@ -1473,7 +1472,7 @@ export type UpdateProjectMemberRoleMutation = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export type CreateTaskMutationVariables = {
|
export type CreateTaskMutationVariables = {
|
||||||
taskGroupID: Scalars['UUID'];
|
taskGroupID: Scalars['String'];
|
||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
position: Scalars['Float'];
|
position: Scalars['Float'];
|
||||||
};
|
};
|
||||||
@ -3045,7 +3044,7 @@ export type UpdateProjectMemberRoleMutationHookResult = ReturnType<typeof useUpd
|
|||||||
export type UpdateProjectMemberRoleMutationResult = ApolloReactCommon.MutationResult<UpdateProjectMemberRoleMutation>;
|
export type UpdateProjectMemberRoleMutationResult = ApolloReactCommon.MutationResult<UpdateProjectMemberRoleMutation>;
|
||||||
export type UpdateProjectMemberRoleMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateProjectMemberRoleMutation, UpdateProjectMemberRoleMutationVariables>;
|
export type UpdateProjectMemberRoleMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateProjectMemberRoleMutation, UpdateProjectMemberRoleMutationVariables>;
|
||||||
export const CreateTaskDocument = gql`
|
export const CreateTaskDocument = gql`
|
||||||
mutation createTask($taskGroupID: UUID!, $name: String!, $position: Float!) {
|
mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) {
|
||||||
createTask(input: {taskGroupID: $taskGroupID, name: $name, position: $position}) {
|
createTask(input: {taskGroupID: $taskGroupID, name: $name, position: $position}) {
|
||||||
...TaskFields
|
...TaskFields
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import gql from 'graphql-tag';
|
|||||||
import TASK_FRAGMENT from '../fragments/task';
|
import TASK_FRAGMENT from '../fragments/task';
|
||||||
|
|
||||||
const CREATE_TASK_MUTATION = gql`
|
const CREATE_TASK_MUTATION = gql`
|
||||||
mutation createTask($taskGroupID: UUID!, $name: String!, $position: Float!) {
|
mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) {
|
||||||
createTask(input: { taskGroupID: $taskGroupID, name: $name, position: $position }) {
|
createTask(input: { taskGroupID: $taskGroupID, name: $name, position: $position }) {
|
||||||
...TaskFields
|
...TaskFields
|
||||||
}
|
}
|
||||||
|
12
frontend/src/shared/icons/Eye.tsx
Normal file
12
frontend/src/shared/icons/Eye.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Icon, { IconProps } from './Icon';
|
||||||
|
|
||||||
|
const Eye: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||||
|
return (
|
||||||
|
<Icon width={width} height={height} className={className} viewBox="0 0 576 512">
|
||||||
|
<path d="M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z" />
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Eye;
|
12
frontend/src/shared/icons/List.tsx
Normal file
12
frontend/src/shared/icons/List.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Icon, { IconProps } from './Icon';
|
||||||
|
|
||||||
|
const List: React.FC<IconProps> = ({ width = '16px', height = '16px', className }) => {
|
||||||
|
return (
|
||||||
|
<Icon width={width} height={height} className={className} viewBox="0 0 512 512">
|
||||||
|
<path d="M80 368H16a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-64a16 16 0 0 0-16-16zm0-320H16A16 16 0 0 0 0 64v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16V64a16 16 0 0 0-16-16zm0 160H16a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-64a16 16 0 0 0-16-16zm416 176H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z" />
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default List;
|
@ -1,5 +1,7 @@
|
|||||||
import Cross from './Cross';
|
import Cross from './Cross';
|
||||||
import Cog from './Cog';
|
import Cog from './Cog';
|
||||||
|
import Eye from './Eye';
|
||||||
|
import List from './List';
|
||||||
import At from './At';
|
import At from './At';
|
||||||
import Task from './Task';
|
import Task from './Task';
|
||||||
import Smile from './Smile';
|
import Smile from './Smile';
|
||||||
@ -85,4 +87,6 @@ export {
|
|||||||
Clone,
|
Clone,
|
||||||
Paperclip,
|
Paperclip,
|
||||||
Share,
|
Share,
|
||||||
|
Eye,
|
||||||
|
List,
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
let accessToken = '';
|
let accessToken = '';
|
||||||
|
|
||||||
export function setAccessToken(newToken: string) {
|
export function setAccessToken(newToken: string) {
|
||||||
|
console.log(newToken);
|
||||||
accessToken = newToken;
|
accessToken = newToken;
|
||||||
}
|
}
|
||||||
export function getAccessToken() {
|
export function getAccessToken() {
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
import { ApolloError } from '@apollo/client';
|
|
||||||
|
|
||||||
export default function hasNotFoundError(...errors: Array<ApolloError | undefined>) {
|
|
||||||
for (const error of errors) {
|
|
||||||
if (error && error.graphQLErrors.length !== 0) {
|
|
||||||
const notFound = error.graphQLErrors.find(e => e.extensions && e.extensions.code === '404');
|
|
||||||
if (notFound) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
@ -2648,7 +2648,6 @@ enum ObjectType {
|
|||||||
TEAM
|
TEAM
|
||||||
PROJECT
|
PROJECT
|
||||||
TASK
|
TASK
|
||||||
TASK_GROUP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION
|
directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION
|
||||||
@ -2852,20 +2851,20 @@ type UpdateProjectMemberRolePayload {
|
|||||||
|
|
||||||
extend type Mutation {
|
extend type Mutation {
|
||||||
createTask(input: NewTask!):
|
createTask(input: NewTask!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK_GROUP)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
deleteTask(input: DeleteTaskInput!):
|
deleteTask(input: DeleteTaskInput!):
|
||||||
DeleteTaskPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
DeleteTaskPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
|
|
||||||
updateTaskDescription(input: UpdateTaskDescriptionInput!):
|
updateTaskDescription(input: UpdateTaskDescriptionInput!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
updateTaskLocation(input: NewTaskLocation!):
|
updateTaskLocation(input: NewTaskLocation!):
|
||||||
UpdateTaskLocationPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
UpdateTaskLocationPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
updateTaskName(input: UpdateTaskName!):
|
updateTaskName(input: UpdateTaskName!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
setTaskComplete(input: SetTaskComplete!):
|
setTaskComplete(input: SetTaskComplete!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
updateTaskDueDate(input: UpdateTaskDueDate!):
|
updateTaskDueDate(input: UpdateTaskDueDate!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
|
|
||||||
assignTask(input: AssignTaskInput):
|
assignTask(input: AssignTaskInput):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
||||||
@ -2874,7 +2873,7 @@ extend type Mutation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input NewTask {
|
input NewTask {
|
||||||
taskGroupID: UUID!
|
taskGroupID: String!
|
||||||
name: String!
|
name: String!
|
||||||
position: Float!
|
position: Float!
|
||||||
}
|
}
|
||||||
@ -6534,7 +6533,7 @@ func (ec *executionContext) _Mutation_createTask(ctx context.Context, field grap
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK_GROUP")
|
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -6607,7 +6606,7 @@ func (ec *executionContext) _Mutation_deleteTask(ctx context.Context, field grap
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK")
|
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -6680,7 +6679,7 @@ func (ec *executionContext) _Mutation_updateTaskDescription(ctx context.Context,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK")
|
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -6753,7 +6752,7 @@ func (ec *executionContext) _Mutation_updateTaskLocation(ctx context.Context, fi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK")
|
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -6826,7 +6825,7 @@ func (ec *executionContext) _Mutation_updateTaskName(ctx context.Context, field
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK")
|
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -6899,7 +6898,7 @@ func (ec *executionContext) _Mutation_setTaskComplete(ctx context.Context, field
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK")
|
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -6972,7 +6971,7 @@ func (ec *executionContext) _Mutation_updateTaskDueDate(ctx context.Context, fie
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "TASK")
|
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -15195,7 +15194,7 @@ func (ec *executionContext) unmarshalInputNewTask(ctx context.Context, obj inter
|
|||||||
switch k {
|
switch k {
|
||||||
case "taskGroupID":
|
case "taskGroupID":
|
||||||
var err error
|
var err error
|
||||||
it.TaskGroupID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v)
|
it.TaskGroupID, err = ec.unmarshalNString2string(ctx, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
"github.com/jordanknott/taskcafe/internal/db"
|
"github.com/jordanknott/taskcafe/internal/db"
|
||||||
"github.com/jordanknott/taskcafe/internal/utils"
|
"github.com/jordanknott/taskcafe/internal/utils"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewHandler returns a new graphql endpoint handler.
|
// NewHandler returns a new graphql endpoint handler.
|
||||||
@ -52,8 +51,6 @@ func NewHandler(repo db.Repository) http.Handler {
|
|||||||
fieldName = "TeamID"
|
fieldName = "TeamID"
|
||||||
case ObjectTypeTask:
|
case ObjectTypeTask:
|
||||||
fieldName = "TaskID"
|
fieldName = "TaskID"
|
||||||
case ObjectTypeTaskGroup:
|
|
||||||
fieldName = "TaskGroupID"
|
|
||||||
default:
|
default:
|
||||||
fieldName = "ProjectID"
|
fieldName = "ProjectID"
|
||||||
}
|
}
|
||||||
@ -71,13 +68,6 @@ func NewHandler(repo db.Repository) http.Handler {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else if typeArg == ObjectTypeTaskGroup {
|
|
||||||
log.WithFields(log.Fields{"subjectID": subjectID}).Info("fetching project ID using task group ID")
|
|
||||||
taskGroup, err := repo.GetTaskGroupByID(ctx, subjectID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
subjectID = taskGroup.ProjectID
|
|
||||||
}
|
}
|
||||||
roles, err := GetProjectRoles(ctx, repo, subjectID)
|
roles, err := GetProjectRoles(ctx, repo, subjectID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -196,13 +186,3 @@ func GetActionType(actionType int32) ActionType {
|
|||||||
panic("Not a valid entity type!")
|
panic("Not a valid entity type!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotFoundError creates a 404 gqlerror
|
|
||||||
func NotFoundError(message string) error {
|
|
||||||
return &gqlerror.Error{
|
|
||||||
Message: message,
|
|
||||||
Extensions: map[string]interface{}{
|
|
||||||
"code": "404",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -229,9 +229,9 @@ type NewRefreshToken struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NewTask struct {
|
type NewTask struct {
|
||||||
TaskGroupID uuid.UUID `json:"taskGroupID"`
|
TaskGroupID string `json:"taskGroupID"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Position float64 `json:"position"`
|
Position float64 `json:"position"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NewTaskGroup struct {
|
type NewTaskGroup struct {
|
||||||
@ -648,11 +648,10 @@ func (e EntityType) MarshalGQL(w io.Writer) {
|
|||||||
type ObjectType string
|
type ObjectType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ObjectTypeOrg ObjectType = "ORG"
|
ObjectTypeOrg ObjectType = "ORG"
|
||||||
ObjectTypeTeam ObjectType = "TEAM"
|
ObjectTypeTeam ObjectType = "TEAM"
|
||||||
ObjectTypeProject ObjectType = "PROJECT"
|
ObjectTypeProject ObjectType = "PROJECT"
|
||||||
ObjectTypeTask ObjectType = "TASK"
|
ObjectTypeTask ObjectType = "TASK"
|
||||||
ObjectTypeTaskGroup ObjectType = "TASK_GROUP"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var AllObjectType = []ObjectType{
|
var AllObjectType = []ObjectType{
|
||||||
@ -660,12 +659,11 @@ var AllObjectType = []ObjectType{
|
|||||||
ObjectTypeTeam,
|
ObjectTypeTeam,
|
||||||
ObjectTypeProject,
|
ObjectTypeProject,
|
||||||
ObjectTypeTask,
|
ObjectTypeTask,
|
||||||
ObjectTypeTaskGroup,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ObjectType) IsValid() bool {
|
func (e ObjectType) IsValid() bool {
|
||||||
switch e {
|
switch e {
|
||||||
case ObjectTypeOrg, ObjectTypeTeam, ObjectTypeProject, ObjectTypeTask, ObjectTypeTaskGroup:
|
case ObjectTypeOrg, ObjectTypeTeam, ObjectTypeProject, ObjectTypeTask:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -174,7 +174,6 @@ enum ObjectType {
|
|||||||
TEAM
|
TEAM
|
||||||
PROJECT
|
PROJECT
|
||||||
TASK
|
TASK
|
||||||
TASK_GROUP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION
|
directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION
|
||||||
@ -378,20 +377,20 @@ type UpdateProjectMemberRolePayload {
|
|||||||
|
|
||||||
extend type Mutation {
|
extend type Mutation {
|
||||||
createTask(input: NewTask!):
|
createTask(input: NewTask!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK_GROUP)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
deleteTask(input: DeleteTaskInput!):
|
deleteTask(input: DeleteTaskInput!):
|
||||||
DeleteTaskPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
DeleteTaskPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
|
|
||||||
updateTaskDescription(input: UpdateTaskDescriptionInput!):
|
updateTaskDescription(input: UpdateTaskDescriptionInput!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
updateTaskLocation(input: NewTaskLocation!):
|
updateTaskLocation(input: NewTaskLocation!):
|
||||||
UpdateTaskLocationPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
UpdateTaskLocationPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
updateTaskName(input: UpdateTaskName!):
|
updateTaskName(input: UpdateTaskName!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
setTaskComplete(input: SetTaskComplete!):
|
setTaskComplete(input: SetTaskComplete!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
updateTaskDueDate(input: UpdateTaskDueDate!):
|
updateTaskDueDate(input: UpdateTaskDueDate!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
|
|
||||||
assignTask(input: AssignTaskInput):
|
assignTask(input: AssignTaskInput):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
||||||
@ -400,7 +399,7 @@ extend type Mutation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input NewTask {
|
input NewTask {
|
||||||
taskGroupID: UUID!
|
taskGroupID: String!
|
||||||
name: String!
|
name: String!
|
||||||
position: Float!
|
position: Float!
|
||||||
}
|
}
|
||||||
|
@ -179,9 +179,14 @@ func (r *mutationResolver) UpdateProjectMemberRole(ctx context.Context, input Up
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) CreateTask(ctx context.Context, input NewTask) (*db.Task, error) {
|
func (r *mutationResolver) CreateTask(ctx context.Context, input NewTask) (*db.Task, error) {
|
||||||
|
taskGroupID, err := uuid.Parse(input.TaskGroupID)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("issue while parsing task group ID")
|
||||||
|
return &db.Task{}, err
|
||||||
|
}
|
||||||
createdAt := time.Now().UTC()
|
createdAt := time.Now().UTC()
|
||||||
log.WithFields(log.Fields{"positon": input.Position, "taskGroupID": input.TaskGroupID}).Info("creating task")
|
log.WithFields(log.Fields{"positon": input.Position, "taskGroupID": taskGroupID}).Info("creating task")
|
||||||
task, err := r.Repository.CreateTask(ctx, db.CreateTaskParams{input.TaskGroupID, createdAt, input.Name, input.Position})
|
task, err := r.Repository.CreateTask(ctx, db.CreateTaskParams{taskGroupID, createdAt, input.Name, input.Position})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("issue while creating task")
|
log.WithError(err).Error("issue while creating task")
|
||||||
return &db.Task{}, err
|
return &db.Task{}, err
|
||||||
@ -233,9 +238,6 @@ func (r *mutationResolver) SetTaskComplete(ctx context.Context, input SetTaskCom
|
|||||||
completedAt := time.Now().UTC()
|
completedAt := time.Now().UTC()
|
||||||
task, err := r.Repository.SetTaskComplete(ctx, db.SetTaskCompleteParams{TaskID: input.TaskID, Complete: input.Complete, CompletedAt: sql.NullTime{Time: completedAt, Valid: true}})
|
task, err := r.Repository.SetTaskComplete(ctx, db.SetTaskCompleteParams{TaskID: input.TaskID, Complete: input.Complete, CompletedAt: sql.NullTime{Time: completedAt, Valid: true}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return &db.Task{}, NotFoundError("task does not exist")
|
|
||||||
}
|
|
||||||
return &db.Task{}, err
|
return &db.Task{}, err
|
||||||
}
|
}
|
||||||
return &task, nil
|
return &task, nil
|
||||||
@ -1031,14 +1033,6 @@ func (r *queryResolver) FindProject(ctx context.Context, input FindProject) (*db
|
|||||||
|
|
||||||
func (r *queryResolver) FindTask(ctx context.Context, input FindTask) (*db.Task, error) {
|
func (r *queryResolver) FindTask(ctx context.Context, input FindTask) (*db.Task, error) {
|
||||||
task, err := r.Repository.GetTaskByID(ctx, input.TaskID)
|
task, err := r.Repository.GetTaskByID(ctx, input.TaskID)
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return &db.Task{}, &gqlerror.Error{
|
|
||||||
Message: "Task does not exist",
|
|
||||||
Extensions: map[string]interface{}{
|
|
||||||
"code": "404",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &task, err
|
return &task, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1246,9 +1240,6 @@ func (r *taskResolver) Assigned(ctx context.Context, obj *db.Task) ([]Member, er
|
|||||||
taskMemberLinks, err := r.Repository.GetAssignedMembersForTask(ctx, obj.TaskID)
|
taskMemberLinks, err := r.Repository.GetAssignedMembersForTask(ctx, obj.TaskID)
|
||||||
taskMembers := []Member{}
|
taskMembers := []Member{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return taskMembers, nil
|
|
||||||
}
|
|
||||||
return taskMembers, err
|
return taskMembers, err
|
||||||
}
|
}
|
||||||
for _, taskMemberLink := range taskMemberLinks {
|
for _, taskMemberLink := range taskMemberLinks {
|
||||||
@ -1283,19 +1274,11 @@ func (r *taskResolver) Assigned(ctx context.Context, obj *db.Task) ([]Member, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *taskResolver) Labels(ctx context.Context, obj *db.Task) ([]db.TaskLabel, error) {
|
func (r *taskResolver) Labels(ctx context.Context, obj *db.Task) ([]db.TaskLabel, error) {
|
||||||
labels, err := r.Repository.GetTaskLabelsForTaskID(ctx, obj.TaskID)
|
return r.Repository.GetTaskLabelsForTaskID(ctx, obj.TaskID)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return []db.TaskLabel{}, err
|
|
||||||
}
|
|
||||||
return labels, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *taskResolver) Checklists(ctx context.Context, obj *db.Task) ([]db.TaskChecklist, error) {
|
func (r *taskResolver) Checklists(ctx context.Context, obj *db.Task) ([]db.TaskChecklist, error) {
|
||||||
checklists, err := r.Repository.GetTaskChecklistsForTask(ctx, obj.TaskID)
|
return r.Repository.GetTaskChecklistsForTask(ctx, obj.TaskID)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return []db.TaskChecklist{}, err
|
|
||||||
}
|
|
||||||
return checklists, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *taskResolver) Badges(ctx context.Context, obj *db.Task) (*TaskBadges, error) {
|
func (r *taskResolver) Badges(ctx context.Context, obj *db.Task) (*TaskBadges, error) {
|
||||||
|
@ -14,7 +14,6 @@ enum ObjectType {
|
|||||||
TEAM
|
TEAM
|
||||||
PROJECT
|
PROJECT
|
||||||
TASK
|
TASK
|
||||||
TASK_GROUP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION
|
directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
extend type Mutation {
|
extend type Mutation {
|
||||||
createTask(input: NewTask!):
|
createTask(input: NewTask!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK_GROUP)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
deleteTask(input: DeleteTaskInput!):
|
deleteTask(input: DeleteTaskInput!):
|
||||||
DeleteTaskPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
DeleteTaskPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
|
|
||||||
updateTaskDescription(input: UpdateTaskDescriptionInput!):
|
updateTaskDescription(input: UpdateTaskDescriptionInput!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
updateTaskLocation(input: NewTaskLocation!):
|
updateTaskLocation(input: NewTaskLocation!):
|
||||||
UpdateTaskLocationPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
UpdateTaskLocationPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
updateTaskName(input: UpdateTaskName!):
|
updateTaskName(input: UpdateTaskName!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
setTaskComplete(input: SetTaskComplete!):
|
setTaskComplete(input: SetTaskComplete!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
updateTaskDueDate(input: UpdateTaskDueDate!):
|
updateTaskDueDate(input: UpdateTaskDueDate!):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
|
||||||
|
|
||||||
assignTask(input: AssignTaskInput):
|
assignTask(input: AssignTaskInput):
|
||||||
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
Task! @hasRole(roles: [ADMIN], level: PROJECT, type: TASK)
|
||||||
@ -22,7 +22,7 @@ extend type Mutation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input NewTask {
|
input NewTask {
|
||||||
taskGroupID: UUID!
|
taskGroupID: String!
|
||||||
name: String!
|
name: String!
|
||||||
position: Float!
|
position: Float!
|
||||||
}
|
}
|
||||||
|
@ -251,10 +251,12 @@ func (h *TaskcafeHandler) InstallHandler(w http.ResponseWriter, r *http.Request)
|
|||||||
refreshExpiresAt := refreshCreatedAt.AddDate(0, 0, 1)
|
refreshExpiresAt := refreshCreatedAt.AddDate(0, 0, 1)
|
||||||
refreshTokenString, err := h.repo.CreateRefreshToken(r.Context(), db.CreateRefreshTokenParams{user.UserID, refreshCreatedAt, refreshExpiresAt})
|
refreshTokenString, err := h.repo.CreateRefreshToken(r.Context(), db.CreateRefreshTokenParams{user.UserID, refreshCreatedAt, refreshExpiresAt})
|
||||||
|
|
||||||
|
log.WithField("userID", user.UserID.String()).Info("creating install access token")
|
||||||
accessTokenString, err := auth.NewAccessToken(user.UserID.String(), auth.Unrestricted, user.RoleCode)
|
accessTokenString, err := auth.NewAccessToken(user.UserID.String(), auth.Unrestricted, user.RoleCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
log.Info(accessTokenString)
|
||||||
|
|
||||||
w.Header().Set("Content-type", "application/json")
|
w.Header().Set("Content-type", "application/json")
|
||||||
http.SetCookie(w, &http.Cookie{
|
http.SetCookie(w, &http.Cookie{
|
||||||
|
@ -61,9 +61,9 @@ func (h *TaskcafeHandler) ProfileImageUpload(w http.ResponseWriter, r *http.Requ
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
h.repo.UpdateUserAccountProfileAvatarURL(r.Context(), db.UpdateUserAccountProfileAvatarURLParams{UserID: userID, ProfileAvatarUrl: sql.NullString{String: "http://localhost:3333/uploads/" + handler.Filename, Valid: true}})
|
h.repo.UpdateUserAccountProfileAvatarURL(r.Context(), db.UpdateUserAccountProfileAvatarURLParams{UserID: userID, ProfileAvatarUrl: sql.NullString{String: "/uploads/" + handler.Filename, Valid: true}})
|
||||||
// return that we have successfully uploaded our file!
|
// return that we have successfully uploaded our file!
|
||||||
log.Info("file uploaded")
|
log.Info("file uploaded")
|
||||||
json.NewEncoder(w).Encode(AvatarUploadResponseData{URL: "http://localhost:3333/uploads/" + handler.Filename, UserID: userID.String()})
|
json.NewEncoder(w).Encode(AvatarUploadResponseData{URL: "/uploads/" + handler.Filename, UserID: userID.String()})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user