diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json index 8b2f4c6..4089399 100644 --- a/frontend/.eslintrc.json +++ b/frontend/.eslintrc.json @@ -30,6 +30,7 @@ "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-unused-vars": "off", "react/jsx-filename-extension": [2, { "extensions": [".js", ".jsx", ".ts", ".tsx"] }], + "react/require-default-props": "off", "no-case-declarations": "off", "no-plusplus": "off", "react/prop-types": 0, diff --git a/frontend/package.json b/frontend/package.json index bddae34..9c82ca2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,7 +24,7 @@ "@types/react-router-dom": "^5.1.3", "@types/react-select": "^3.0.13", "@types/react-timeago": "^4.1.1", - "@types/styled-components": "^5.0.0", + "@types/styled-components": "^5.1.0", "apollo-cache-inmemory": "^1.6.5", "apollo-client": "^2.6.8", "apollo-link": "^1.2.13", @@ -64,7 +64,7 @@ "react-timeago": "^4.4.0", "react-toastify": "^6.0.8", "rich-markdown-editor": "^10.6.5", - "styled-components": "^5.0.1", + "styled-components": "^5.1.0", "typescript": "~3.7.2" }, "proxy": "http://localhost:3333", @@ -93,10 +93,10 @@ ] }, "devDependencies": { - "@graphql-codegen/cli": "^1.13.2", - "@graphql-codegen/typescript": "^1.13.2", - "@graphql-codegen/typescript-operations": "^1.13.2", - "@graphql-codegen/typescript-react-apollo": "^1.13.2", + "@graphql-codegen/cli": "^1.21.4", + "@graphql-codegen/typescript": "^1.22.0", + "@graphql-codegen/typescript-operations": "^1.17.16", + "@graphql-codegen/typescript-react-apollo": "^2.2.4", "@typescript-eslint/eslint-plugin": "^2.20.0", "@typescript-eslint/parser": "^2.20.0", "eslint": "^6.8.0", diff --git a/frontend/src/App/Routes.tsx b/frontend/src/App/Routes.tsx index 3c4f227..3dd9a37 100644 --- a/frontend/src/App/Routes.tsx +++ b/frontend/src/App/Routes.tsx @@ -41,9 +41,7 @@ const AuthorizedRoutes = () => { const { status } = x; const response: ValidateTokenResponse = await x.json(); const { valid, userID } = response; - if (!valid) { - history.replace(`/login`); - } else { + if (valid) { setUser(userID); } setLoading(false); diff --git a/frontend/src/App/TopNavbar/ProjectPopup.tsx b/frontend/src/App/TopNavbar/ProjectPopup.tsx index e27e2e0..1e6983c 100644 --- a/frontend/src/App/TopNavbar/ProjectPopup.tsx +++ b/frontend/src/App/TopNavbar/ProjectPopup.tsx @@ -1,17 +1,28 @@ -import React from 'react'; -import ProjectSettings, { DeleteConfirm, DELETE_INFO } from 'shared/components/ProjectSettings'; -import { useDeleteProjectMutation, GetProjectsDocument } from 'shared/generated/graphql'; +import React, { useState } from 'react'; +import ProjectSettings, { DeleteConfirm, DELETE_INFO, PublicConfirm } from 'shared/components/ProjectSettings'; +import { + useDeleteProjectMutation, + GetProjectsDocument, + useToggleProjectVisibilityMutation, +} from 'shared/generated/graphql'; import { usePopup, Popup } from 'shared/components/PopupMenu'; import produce from 'immer'; type ProjectPopupProps = { history: any; name: string; + publicOn: string | null; projectID: string; }; -const ProjectPopup: React.FC = ({ history, name, projectID }) => { +const ProjectPopup: React.FC = ({ history, name, projectID, publicOn: initialPublicOn }) => { const { hidePopup, setTab } = usePopup(); + const [publicOn, setPublicOn] = useState(initialPublicOn); + const [toggleProjectVisibility] = useToggleProjectVisibilityMutation({ + onCompleted: data => { + setPublicOn(data.toggleProjectVisibility.project.publicOn); + }, + }); const [deleteProject] = useDeleteProjectMutation({ update: (client, deleteData) => { const cacheData: any = client.readQuery({ @@ -36,11 +47,28 @@ const ProjectPopup: React.FC = ({ history, name, projectID }) <> { + if (visible) { + setTab(2, { width: 300 }); + } else { + toggleProjectVisibility({ variables: { projectID, isPublic: false } }); + } + }} onDeleteProject={() => { setTab(1, { width: 300 }); }} /> + + { + if (projectID) { + toggleProjectVisibility({ variables: { projectID, isPublic: true } }); + } + }} + /> + void; }; -const GlobalTopNavbar: React.FC = ({ +const LoggedInNavbar: React.FC = ({ currentTab, onSetTab, menuType, @@ -46,9 +49,9 @@ const GlobalTopNavbar: React.FC = ({ onRemoveInvitedFromBoard, onRemoveFromBoard, }) => { - const { user, setUser } = useCurrentUser(); const { data } = useTopNavbarQuery(); const { showPopup, hidePopup } = usePopup(); + const { setUser } = useCurrentUser(); const history = useHistory(); const onLogout = () => { fetch('/auth/logout', { @@ -109,9 +112,6 @@ const GlobalTopNavbar: React.FC = ({ } }; - if (!user) { - return null; - } // TODO: readd permision check // const userIsTeamOrProjectAdmin = user.isAdmin(PermissionLevel.TEAM, PermissionObjectType.TEAM, teamID); const userIsTeamOrProjectAdmin = true; @@ -174,6 +174,8 @@ const GlobalTopNavbar: React.FC = ({ } }; + const user = data ? data.me?.user : null; + return ( <> = ({ ); }} currentTab={currentTab} - user={data ? data.me.user : null} + user={user ?? null} canEditProjectName={userIsTeamOrProjectAdmin} canInviteUser={userIsTeamOrProjectAdmin} onMemberProfile={onMemberProfile} @@ -215,4 +217,45 @@ const GlobalTopNavbar: React.FC = ({ ); }; +const GlobalTopNavbar: React.FC = ({ + currentTab, + onSetTab, + menuType, + teamID, + onChangeProjectOwner, + onChangeRole, + name, + popupContent, + projectMembers, + projectInvitedMembers, + onInviteUser, + onSaveProjectName, + onRemoveInvitedFromBoard, + onRemoveFromBoard, +}) => { + const { user } = useCurrentUser(); + if (user) { + return ( + + ); + } + return ; +}; + export default GlobalTopNavbar; diff --git a/frontend/src/Profile/index.tsx b/frontend/src/Profile/index.tsx index fdec70e..522930b 100644 --- a/frontend/src/Profile/index.tsx +++ b/frontend/src/Profile/index.tsx @@ -62,7 +62,7 @@ const Projects = () => { }} /> - {!loading && data && ( + {!loading && data && data.me && ( { diff --git a/frontend/src/Projects/Project/Board/index.tsx b/frontend/src/Projects/Project/Board/index.tsx index 90d16f8..c41d7ed 100644 --- a/frontend/src/Projects/Project/Board/index.tsx +++ b/frontend/src/Projects/Project/Board/index.tsx @@ -198,6 +198,7 @@ type ProjectBoardProps = { }; export const BoardLoading = () => { + const { user } = useCurrentUser(); return ( <> @@ -215,20 +216,22 @@ export const BoardLoading = () => { Filter - - - - Labels - - - - Fields - - - - Rules - - + {user && ( + + + + Labels + + + + Fields + + + + Rules + + + )} @@ -469,7 +472,7 @@ const ProjectBoard: React.FC = ({ projectID, onCardLabelClick } return 'All Tasks'; }; - if (data && user) { + if (data) { labelsRef.current = data.findProject.labels; membersRef.current = data.findProject.members; const onQuickEditorOpen = ($target: React.RefObject, taskID: string, taskGroupID: string) => { @@ -570,34 +573,37 @@ const ProjectBoard: React.FC = ({ projectID, onCardLabelClick ); })} - - { - showPopup( - $labelsRef, - , - ); - }} - > - - Labels - - - - Fields - - - - Rules - - + {user && ( + + { + showPopup( + $labelsRef, + , + ); + }} + > + + Labels + + + + Fields + + + + Rules + + + )} { history.push(`${match.url}/c/${task.id}`); }} diff --git a/frontend/src/Projects/Project/Details/index.tsx b/frontend/src/Projects/Project/Details/index.tsx index f3962ed..7142190 100644 --- a/frontend/src/Projects/Project/Details/index.tsx +++ b/frontend/src/Projects/Project/Details/index.tsx @@ -424,7 +424,7 @@ const Details: React.FC = ({ updateTaskComment({ variables: { commentID, message } }); }} editableComment={editableComment} - me={data.me.user} + me={data.me ? data.me.user : null} onCommentShowActions={(commentID, $targetRef) => { showPopup( $targetRef, diff --git a/frontend/src/Projects/Project/index.tsx b/frontend/src/Projects/Project/index.tsx index 9cc6bf1..69c3067 100644 --- a/frontend/src/Projects/Project/index.tsx +++ b/frontend/src/Projects/Project/index.tsx @@ -163,10 +163,6 @@ const Project = () => { } }, [data]); - if (error) { - history.push('/projects'); - } - if (data) { labelsRef.current = data.findProject.labels; @@ -204,7 +200,14 @@ const Project = () => { />, ); }} - popupContent={} + popupContent={ + + } menuType={[{ name: 'Board', link: location.pathname }]} currentTab={0} projectMembers={data.findProject.members} diff --git a/frontend/src/shared/components/Card/index.tsx b/frontend/src/shared/components/Card/index.tsx index 4f3f055..a6c352f 100644 --- a/frontend/src/shared/components/Card/index.tsx +++ b/frontend/src/shared/components/Card/index.tsx @@ -58,12 +58,14 @@ type Props = { onCardTitleChange?: (name: string) => void; labelVariant?: CardLabelVariant; toggleLabels?: boolean; + isPublic?: boolean; toggleDirection?: 'shrink' | 'expand'; }; const Card = React.forwardRef( ( { + isPublic = false, wrapperProps, onContextMenu, taskID, @@ -120,9 +122,11 @@ const Card = React.forwardRef( } }; const onTaskContext = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - onOpenComposer(); + if (!isPublic) { + e.preventDefault(); + e.stopPropagation(); + onOpenComposer(); + } }; const onOperationClick = (e: React.MouseEvent) => { e.preventDefault(); @@ -145,7 +149,7 @@ const Card = React.forwardRef( {...wrapperProps} > - {isActive && !editable && ( + {!isPublic && isActive && !editable && ( { e.stopPropagation(); diff --git a/frontend/src/shared/components/List/Styles.ts b/frontend/src/shared/components/List/Styles.ts index 422f6c8..bd45736 100644 --- a/frontend/src/shared/components/List/Styles.ts +++ b/frontend/src/shared/components/List/Styles.ts @@ -72,6 +72,9 @@ export const HeaderName = styled(TextareaAutosize)` box-shadow: none; font-weight: 600; margin: -4px 0; + &:disabled { + opacity: 1; + } letter-spacing: normal; word-spacing: normal; diff --git a/frontend/src/shared/components/List/index.tsx b/frontend/src/shared/components/List/index.tsx index 4ae813f..7d6eff0 100644 --- a/frontend/src/shared/components/List/index.tsx +++ b/frontend/src/shared/components/List/index.tsx @@ -24,6 +24,7 @@ type Props = { onOpenComposer: (id: string) => void; wrapperProps?: any; headerProps?: any; + isPublic: boolean; index?: number; onExtraMenuOpen: (taskGroupID: string, $targetRef: React.RefObject) => void; }; @@ -37,6 +38,7 @@ const List = React.forwardRef( isComposerOpen, onOpenComposer, children, + isPublic, wrapperProps, headerProps, onExtraMenuOpen, @@ -86,39 +88,37 @@ const List = React.forwardRef(
- + {!isPublic && } - - - + {!isPublic && ( + + + + )}
{children && children} - + {!isPublic && ( + + )}
); }, ); -List.defaultProps = { - children: null, - isComposerOpen: false, - wrapperProps: {}, - headerProps: {}, -}; - List.displayName = 'List'; export default List; diff --git a/frontend/src/shared/components/Lists/index.tsx b/frontend/src/shared/components/Lists/index.tsx index f5fa30a..b6c5507 100644 --- a/frontend/src/shared/components/Lists/index.tsx +++ b/frontend/src/shared/components/Lists/index.tsx @@ -151,6 +151,7 @@ interface SimpleProps { onCardMemberClick: OnCardMemberClick; onCardLabelClick: () => void; cardLabelVariant: CardLabelVariant; + isPublic?: boolean; taskStatusFilter?: TaskStatusFilter; taskMetaFilters?: TaskMetaFilters; taskSorting?: TaskSorting; @@ -188,6 +189,7 @@ const SimpleLists: React.FC = ({ onExtraMenuOpen, onCardMemberClick, taskStatusFilter = initTaskStatusFilter, + isPublic = false, taskMetaFilters = initTaskMetaFilters, taskSorting = initTaskSorting, }) => { @@ -300,6 +302,7 @@ const SimpleLists: React.FC = ({ onOpenComposer={id => setCurrentComposer(id)} isComposerOpen={currentComposer === taskGroup.id} onSaveName={name => onChangeTaskGroupName(taskGroup.id, name)} + isPublic={isPublic} ref={columnDragProvided.innerRef} wrapperProps={columnDragProvided.draggableProps} headerProps={columnDragProvided.dragHandleProps} @@ -328,6 +331,7 @@ const SimpleLists: React.FC = ({ = ({ )} - { - onCreateTaskGroup(listName); - }} - /> + {!isPublic && ( + { + onCreateTaskGroup(listName); + }} + /> + )} ); diff --git a/frontend/src/shared/components/ProjectSettings/index.tsx b/frontend/src/shared/components/ProjectSettings/index.tsx index 2b253a9..bdc3987 100644 --- a/frontend/src/shared/components/ProjectSettings/index.tsx +++ b/frontend/src/shared/components/ProjectSettings/index.tsx @@ -38,12 +38,17 @@ export const ListSeparator = styled.hr` `; type Props = { + publicOn: null | string; onDeleteProject: () => void; + onToggleProjectVisible: (visible: boolean) => void; }; -const ProjectSettings: React.FC = ({ onDeleteProject }) => { +const ProjectSettings: React.FC = ({ publicOn, onDeleteProject, onToggleProjectVisible }) => { return ( <> + onToggleProjectVisible(publicOn === null)}> + {`Make ${publicOn === null ? 'public' : 'private'}`} + onDeleteProject()}> Delete Project @@ -127,5 +132,18 @@ const DeleteConfirm: React.FC = ({ description, deletedItems ); }; -export { DeleteConfirm }; +type PublicConfirmProps = { + onConfirm: () => void; +}; + +const PublicConfirm: React.FC = ({ onConfirm }) => { + return ( + + Public projects can be accessed by anyone with a link to the project. + onConfirm()}>Make public + + ); +}; + +export { DeleteConfirm, PublicConfirm }; export default ProjectSettings; diff --git a/frontend/src/shared/components/TaskDetails/CommentCreator.tsx b/frontend/src/shared/components/TaskDetails/CommentCreator.tsx index f568482..df89adb 100644 --- a/frontend/src/shared/components/TaskDetails/CommentCreator.tsx +++ b/frontend/src/shared/components/TaskDetails/CommentCreator.tsx @@ -69,7 +69,7 @@ const CommentCreator: React.FC = ({ )} = () => { + const { user } = useCurrentUser(); return ( TASK GROUP - + DUE DATE - + MEMBERS - + - - ACTIONS - }> - Labels - - }> - Checklist - - Cover - + {user && ( + + ACTIONS + }> + Labels + + }> + Checklist + + Cover + + )} @@ -125,23 +129,25 @@ const TaskDetailsLoading: React.FC = () => { Mark complete - - - - - - - - - - - - - - + {user && ( + + + + + + + + + + + + + + + )} - - + + @@ -151,9 +157,11 @@ const TaskDetailsLoading: React.FC = () => { - - null} onMemberProfile={() => null} /> - + {user && ( + + null} onMemberProfile={() => null} /> + + )} ); diff --git a/frontend/src/shared/components/TaskDetails/Styles.ts b/frontend/src/shared/components/TaskDetails/Styles.ts index 8b6763a..39b47df 100644 --- a/frontend/src/shared/components/TaskDetails/Styles.ts +++ b/frontend/src/shared/components/TaskDetails/Styles.ts @@ -108,7 +108,7 @@ export const skeletonKeyframes = keyframes` } `; -export const SidebarButton = styled.div<{ loading?: boolean }>` +export const SidebarButton = styled.div<{ $loading?: boolean }>` font-size: 14px; color: ${props => props.theme.colors.text.primary}; min-height: 32px; @@ -116,7 +116,7 @@ export const SidebarButton = styled.div<{ loading?: boolean }>` border-radius: 6px; ${props => - props.loading + props.$loading ? css` background: ${props.theme.colors.bg.primary}; ` @@ -178,15 +178,15 @@ export const HeaderLeft = styled.div` justify-content: flex-start; `; -export const TaskDetailsTitleWrapper = styled.div<{ loading?: boolean }>` +export const TaskDetailsTitleWrapper = styled.div<{ $loading?: boolean }>` width: 100%; margin: 8px 0 4px 0; display: flex; border-radius: 6px; - ${props => props.loading && `background: ${props.theme.colors.bg.primary};`} + ${props => props.$loading && `background: ${props.theme.colors.bg.primary};`} `; -export const TaskDetailsTitle = styled(TextareaAutosize)<{ loading?: boolean }>` +export const TaskDetailsTitle = styled(TextareaAutosize)<{ $loading?: boolean }>` padding: 9px 8px 7px 8px; border-color: transparent; border-radius: 6px; @@ -198,8 +198,11 @@ export const TaskDetailsTitle = styled(TextareaAutosize)<{ loading?: boolean }>` font-weight: 700; background: none; + &:disabled { + opacity: 1; + } ${props => - props.loading + props.$loading ? css` background-image: linear-gradient(90deg, ${defaultBaseColor}, ${defaultHighlightColor}, ${defaultBaseColor}); background-size: 200px 100%; @@ -207,7 +210,7 @@ export const TaskDetailsTitle = styled(TextareaAutosize)<{ loading?: boolean }>` animation: ${skeletonKeyframes} 1.2s ease-in-out infinite; ` : css` - &:hover { + &:not(:disabled):hover { border-color: #414561; border-width: 1px; border-style: solid; @@ -534,7 +537,7 @@ export const CommentProfile = styled(TaskAssignee)` align-items: normal; `; -export const CommentTextArea = styled(TextareaAutosize)<{ showCommentActions: boolean }>` +export const CommentTextArea = styled(TextareaAutosize)<{ $showCommentActions: boolean }>` width: 100%; line-height: 28px; padding: 4px 6px; @@ -546,7 +549,7 @@ export const CommentTextArea = styled(TextareaAutosize)<{ showCommentActions: bo min-height: 36px; max-height: 36px; ${props => - props.showCommentActions + props.$showCommentActions ? css` min-height: 80px; max-height: none; diff --git a/frontend/src/shared/components/TaskDetails/index.tsx b/frontend/src/shared/components/TaskDetails/index.tsx index 64f8b12..4e7ca02 100644 --- a/frontend/src/shared/components/TaskDetails/index.tsx +++ b/frontend/src/shared/components/TaskDetails/index.tsx @@ -83,6 +83,7 @@ import Checklist, { ChecklistItem, ChecklistItems } from '../Checklist'; import onDragEnd from './onDragEnd'; import { plugin as em } from './remark'; import ActivityMessage from './ActivityMessage'; +import { useCurrentUser } from 'App/context'; const parseEmojis = (value: string) => { const emojisArray = toArray(value); @@ -277,6 +278,7 @@ const TaskDetails: React.FC = ({ onToggleChecklistItem, onMemberProfile, }) => { + const { user } = useCurrentUser(); const [taskName, setTaskName] = useState(task.name); const [editTaskDescription, setEditTaskDescription] = useState(() => { if (task.description) { @@ -338,7 +340,9 @@ const TaskDetails: React.FC = ({ { - onOpenDueDatePopop(task, $dueDateBtn); + if (user) { + onOpenDueDatePopop(task, $dueDateBtn); + } }} > {task.dueDate ? ( @@ -360,14 +364,18 @@ const TaskDetails: React.FC = ({ member={m} size={32} onMemberProfile={$target => { - onMemberProfile($target, m.id); + if (user) { + onMemberProfile($target, m.id); + } }} /> ))} { - onOpenAddMemberPopup(task, $addMemberBtn); + if (user) { + onOpenAddMemberPopup(task, $addMemberBtn); + } }} > @@ -377,7 +385,9 @@ const TaskDetails: React.FC = ({ { - onOpenAddMemberPopup(task, $noMemberBtn); + if (user) { + onOpenAddMemberPopup(task, $noMemberBtn); + } }} > @@ -387,26 +397,28 @@ const TaskDetails: React.FC = ({ )} - - ACTIONS - { - onOpenAddLabelPopup(task, $target); - }} - icon={} - > - Labels - - { - onOpenAddChecklistPopup(task, $target); - }} - icon={} - > - Checklist - - Cover - + {user && ( + + ACTIONS + { + onOpenAddLabelPopup(task, $target); + }} + icon={} + > + Labels + + { + onOpenAddChecklistPopup(task, $target); + }} + icon={} + > + Checklist + + Cover + + )} @@ -414,34 +426,40 @@ const TaskDetails: React.FC = ({ { - onToggleTaskComplete(task); + if (user) { + onToggleTaskComplete(task); + } }} > {task.complete ? 'Completed' : 'Mark complete'} - - - - - - - - - - - onDeleteTask(task)}> - - - + {user && ( + + + + + + + + + + + onDeleteTask(task)}> + + + + )} { if (e.keyCode === 13) { e.preventDefault(); @@ -496,7 +514,7 @@ const TaskDetails: React.FC = ({ { setSaveTimeout(() => { @@ -612,15 +630,15 @@ const TaskDetails: React.FC = ({ )} - - {me && ( + {me && ( + onCreateComment(task, message)} onMemberProfile={onMemberProfile} /> - )} - + + )} ); diff --git a/frontend/src/shared/components/TopNavbar/LoggedOut.tsx b/frontend/src/shared/components/TopNavbar/LoggedOut.tsx new file mode 100644 index 0000000..5265aa6 --- /dev/null +++ b/frontend/src/shared/components/TopNavbar/LoggedOut.tsx @@ -0,0 +1,63 @@ +import React, { useRef, useState, useEffect } from 'react'; +import { Home, Star, Bell, AngleDown, BarChart, CheckCircle, ListUnordered } from 'shared/icons'; +import { RoleCode } from 'shared/generated/graphql'; +import * as S from './Styles'; +import { Link } from 'react-router-dom'; + +export type MenuItem = { + name: string; + link: string; +}; + +type NavBarProps = { + menuType?: Array | null; + name: string | null; +}; + +const NavBar: React.FC = ({ menuType, name }) => { + return ( + + + + + + + + + + {name && {name}} + {name && ( + + {menuType && + menuType.map((menu, idx) => { + return ( + { + // TODO + }} + > + {menu.name} + + ); + })} + + )} + + + + Taskcafé + + + + Sign In + + + + + ); +}; + +export default NavBar; diff --git a/frontend/src/shared/components/TopNavbar/Styles.ts b/frontend/src/shared/components/TopNavbar/Styles.ts index 49b8035..8277ea2 100644 --- a/frontend/src/shared/components/TopNavbar/Styles.ts +++ b/frontend/src/shared/components/TopNavbar/Styles.ts @@ -297,6 +297,16 @@ export const ProjectFinder = styled(Button)` padding: 6px 12px; `; +export const SignUp = styled(Button)` + margin-right: 8px; + padding: 6px 12px; +`; + +export const SignIn = styled(Button)` + margin-right: 20px; + padding: 6px 12px; +`; + export const NavSeparator = styled.div` width: 1px; background: ${props => props.theme.colors.border}; diff --git a/frontend/src/shared/generated/graphql.tsx b/frontend/src/shared/generated/graphql.tsx index 0851447..5143be3 100644 --- a/frontend/src/shared/generated/graphql.tsx +++ b/frontend/src/shared/generated/graphql.tsx @@ -18,15 +18,180 @@ export type Scalars = { }; -export enum ActionLevel { - Org = 'ORG', - Team = 'TEAM', - Project = 'PROJECT' + + + + +export enum RoleCode { + Owner = 'owner', + Admin = 'admin', + Member = 'member', + Observer = 'observer' } -export enum ActionType { - TaskMemberAdded = 'TASK_MEMBER_ADDED' -} +export type ProjectLabel = { + __typename?: 'ProjectLabel'; + id: Scalars['ID']; + createdDate: Scalars['Time']; + labelColor: LabelColor; + name?: Maybe; +}; + +export type LabelColor = { + __typename?: 'LabelColor'; + id: Scalars['ID']; + name: Scalars['String']; + position: Scalars['Float']; + colorHex: Scalars['String']; +}; + +export type TaskLabel = { + __typename?: 'TaskLabel'; + id: Scalars['ID']; + projectLabel: ProjectLabel; + assignedDate: Scalars['Time']; +}; + +export type ProfileIcon = { + __typename?: 'ProfileIcon'; + url?: Maybe; + initials?: Maybe; + bgColor?: Maybe; +}; + +export type OwnersList = { + __typename?: 'OwnersList'; + projects: Array; + teams: Array; +}; + +export type Member = { + __typename?: 'Member'; + id: Scalars['ID']; + role: Role; + fullName: Scalars['String']; + username: Scalars['String']; + profileIcon: ProfileIcon; + owned: OwnedList; + member: MemberList; +}; + +export type Role = { + __typename?: 'Role'; + code: Scalars['String']; + name: Scalars['String']; +}; + +export type OwnedList = { + __typename?: 'OwnedList'; + teams: Array; + projects: Array; +}; + +export type MemberList = { + __typename?: 'MemberList'; + teams: Array; + projects: Array; +}; + +export type UserAccount = { + __typename?: 'UserAccount'; + id: Scalars['ID']; + email: Scalars['String']; + createdAt: Scalars['Time']; + fullName: Scalars['String']; + initials: Scalars['String']; + bio: Scalars['String']; + role: Role; + username: Scalars['String']; + profileIcon: ProfileIcon; + owned: OwnedList; + member: MemberList; +}; + +export type InvitedUserAccount = { + __typename?: 'InvitedUserAccount'; + id: Scalars['ID']; + email: Scalars['String']; + invitedOn: Scalars['Time']; + member: MemberList; +}; + +export type Team = { + __typename?: 'Team'; + id: Scalars['ID']; + createdAt: Scalars['Time']; + name: Scalars['String']; + permission: TeamPermission; + members: Array; +}; + +export type InvitedMember = { + __typename?: 'InvitedMember'; + email: Scalars['String']; + invitedOn: Scalars['Time']; +}; + +export type TeamPermission = { + __typename?: 'TeamPermission'; + team: RoleCode; + org: RoleCode; +}; + +export type ProjectPermission = { + __typename?: 'ProjectPermission'; + team: RoleCode; + project: RoleCode; + org: RoleCode; +}; + +export type Project = { + __typename?: 'Project'; + id: Scalars['ID']; + createdAt: Scalars['Time']; + name: Scalars['String']; + team?: Maybe; + taskGroups: Array; + members: Array; + invitedMembers: Array; + publicOn?: Maybe; + permission: ProjectPermission; + labels: Array; +}; + +export type TaskGroup = { + __typename?: 'TaskGroup'; + id: Scalars['ID']; + projectID: Scalars['String']; + createdAt: Scalars['Time']; + name: Scalars['String']; + position: Scalars['Float']; + tasks: Array; +}; + +export type ChecklistBadge = { + __typename?: 'ChecklistBadge'; + complete: Scalars['Int']; + total: Scalars['Int']; +}; + +export type TaskBadges = { + __typename?: 'TaskBadges'; + checklist?: Maybe; +}; + +export type CausedBy = { + __typename?: 'CausedBy'; + id: Scalars['ID']; + fullName: Scalars['String']; + profileIcon?: Maybe; +}; + +export type TaskActivityData = { + __typename?: 'TaskActivityData'; + name: Scalars['String']; + value: Scalars['String']; +}; export enum ActivityType { TaskAdded = 'TASK_ADDED', @@ -41,65 +206,33 @@ export enum ActivityType { TaskChecklistRemoved = 'TASK_CHECKLIST_REMOVED' } -export enum ActorType { - User = 'USER' -} - -export type AddTaskLabelInput = { - taskID: Scalars['UUID']; - projectLabelID: Scalars['UUID']; -}; - -export type AssignTaskInput = { - taskID: Scalars['UUID']; - userID: Scalars['UUID']; -}; - -export type CausedBy = { - __typename?: 'CausedBy'; +export type TaskActivity = { + __typename?: 'TaskActivity'; id: Scalars['ID']; - fullName: Scalars['String']; - profileIcon?: Maybe; + type: ActivityType; + data: Array; + causedBy: CausedBy; + createdAt: Scalars['Time']; }; -export type ChecklistBadge = { - __typename?: 'ChecklistBadge'; - complete: Scalars['Int']; - total: Scalars['Int']; -}; - -export type CreateTaskChecklist = { - taskID: Scalars['UUID']; +export type Task = { + __typename?: 'Task'; + id: Scalars['ID']; + taskGroup: TaskGroup; + createdAt: Scalars['Time']; name: Scalars['String']; position: Scalars['Float']; -}; - -export type CreateTaskChecklistItem = { - taskChecklistID: Scalars['UUID']; - name: Scalars['String']; - position: Scalars['Float']; -}; - -export type CreateTaskComment = { - taskID: Scalars['UUID']; - message: Scalars['String']; -}; - -export type CreateTaskCommentPayload = { - __typename?: 'CreateTaskCommentPayload'; - taskID: Scalars['UUID']; - comment: TaskComment; -}; - -export type CreateTeamMember = { - userID: Scalars['UUID']; - teamID: Scalars['UUID']; -}; - -export type CreateTeamMemberPayload = { - __typename?: 'CreateTeamMemberPayload'; - team: Team; - teamMember: Member; + description?: Maybe; + dueDate?: Maybe; + hasTime: Scalars['Boolean']; + complete: Scalars['Boolean']; + completedAt?: Maybe; + assigned: Array; + labels: Array; + checklists: Array; + badges: TaskBadges; + activity: Array; + comments: Array; }; export type CreatedBy = { @@ -109,257 +242,118 @@ export type CreatedBy = { profileIcon: ProfileIcon; }; -export type DeleteInvitedProjectMember = { - projectID: Scalars['UUID']; - email: Scalars['String']; +export type TaskComment = { + __typename?: 'TaskComment'; + id: Scalars['ID']; + createdAt: Scalars['Time']; + updatedAt?: Maybe; + message: Scalars['String']; + createdBy: CreatedBy; + pinned: Scalars['Boolean']; }; -export type DeleteInvitedProjectMemberPayload = { - __typename?: 'DeleteInvitedProjectMemberPayload'; - invitedMember: InvitedMember; +export type Organization = { + __typename?: 'Organization'; + id: Scalars['ID']; + name: Scalars['String']; }; -export type DeleteInvitedUserAccount = { - invitedUserID: Scalars['UUID']; -}; - -export type DeleteInvitedUserAccountPayload = { - __typename?: 'DeleteInvitedUserAccountPayload'; - invitedUser: InvitedUserAccount; -}; - -export type DeleteProject = { - projectID: Scalars['UUID']; -}; - -export type DeleteProjectLabel = { - projectLabelID: Scalars['UUID']; -}; - -export type DeleteProjectMember = { - projectID: Scalars['UUID']; - userID: Scalars['UUID']; -}; - -export type DeleteProjectMemberPayload = { - __typename?: 'DeleteProjectMemberPayload'; - ok: Scalars['Boolean']; - member: Member; - projectID: Scalars['UUID']; -}; - -export type DeleteProjectPayload = { - __typename?: 'DeleteProjectPayload'; - ok: Scalars['Boolean']; - project: Project; -}; - -export type DeleteTaskChecklist = { +export type TaskChecklistItem = { + __typename?: 'TaskChecklistItem'; + id: Scalars['ID']; + name: Scalars['String']; taskChecklistID: Scalars['UUID']; + complete: Scalars['Boolean']; + position: Scalars['Float']; + dueDate: Scalars['Time']; }; -export type DeleteTaskChecklistItem = { - taskChecklistItemID: Scalars['UUID']; -}; - -export type DeleteTaskChecklistItemPayload = { - __typename?: 'DeleteTaskChecklistItemPayload'; - ok: Scalars['Boolean']; - taskChecklistItem: TaskChecklistItem; -}; - -export type DeleteTaskChecklistPayload = { - __typename?: 'DeleteTaskChecklistPayload'; - ok: Scalars['Boolean']; - taskChecklist: TaskChecklist; -}; - -export type DeleteTaskComment = { - commentID: Scalars['UUID']; -}; - -export type DeleteTaskCommentPayload = { - __typename?: 'DeleteTaskCommentPayload'; - taskID: Scalars['UUID']; - commentID: Scalars['UUID']; -}; - -export type DeleteTaskGroupInput = { - taskGroupID: Scalars['UUID']; -}; - -export type DeleteTaskGroupPayload = { - __typename?: 'DeleteTaskGroupPayload'; - ok: Scalars['Boolean']; - affectedRows: Scalars['Int']; - taskGroup: TaskGroup; -}; - -export type DeleteTaskGroupTasks = { - taskGroupID: Scalars['UUID']; -}; - -export type DeleteTaskGroupTasksPayload = { - __typename?: 'DeleteTaskGroupTasksPayload'; - taskGroupID: Scalars['UUID']; - tasks: Array; -}; - -export type DeleteTaskInput = { - taskID: Scalars['UUID']; -}; - -export type DeleteTaskPayload = { - __typename?: 'DeleteTaskPayload'; - taskID: Scalars['UUID']; -}; - -export type DeleteTeam = { - teamID: Scalars['UUID']; -}; - -export type DeleteTeamMember = { - teamID: Scalars['UUID']; - userID: Scalars['UUID']; - newOwnerID?: Maybe; -}; - -export type DeleteTeamMemberPayload = { - __typename?: 'DeleteTeamMemberPayload'; - teamID: Scalars['UUID']; - userID: Scalars['UUID']; - affectedProjects: Array; -}; - -export type DeleteTeamPayload = { - __typename?: 'DeleteTeamPayload'; - ok: Scalars['Boolean']; - team: Team; - projects: Array; -}; - -export type DeleteUserAccount = { - userID: Scalars['UUID']; - newOwnerID?: Maybe; -}; - -export type DeleteUserAccountPayload = { - __typename?: 'DeleteUserAccountPayload'; - ok: Scalars['Boolean']; - userAccount: UserAccount; -}; - -export type DuplicateTaskGroup = { - projectID: Scalars['UUID']; - taskGroupID: Scalars['UUID']; +export type TaskChecklist = { + __typename?: 'TaskChecklist'; + id: Scalars['ID']; name: Scalars['String']; position: Scalars['Float']; + items: Array; }; -export type DuplicateTaskGroupPayload = { - __typename?: 'DuplicateTaskGroupPayload'; - taskGroup: TaskGroup; -}; - -export enum EntityType { - Task = 'TASK' +export enum ShareStatus { + Invited = 'INVITED', + Joined = 'JOINED' } -export type FindProject = { - projectID: Scalars['UUID']; -}; +export enum RoleLevel { + Admin = 'ADMIN', + Member = 'MEMBER' +} -export type FindTask = { - taskID: Scalars['UUID']; -}; +export enum ActionLevel { + Org = 'ORG', + Team = 'TEAM', + Project = 'PROJECT' +} -export type FindTeam = { - teamID: Scalars['UUID']; -}; +export enum ObjectType { + Org = 'ORG', + Team = 'TEAM', + Project = 'PROJECT', + Task = 'TASK', + TaskGroup = 'TASK_GROUP', + TaskChecklist = 'TASK_CHECKLIST', + TaskChecklistItem = 'TASK_CHECKLIST_ITEM' +} -export type FindUser = { - userID: Scalars['UUID']; -}; - -export type InviteProjectMembers = { - projectID: Scalars['UUID']; - members: Array; -}; - -export type InviteProjectMembersPayload = { - __typename?: 'InviteProjectMembersPayload'; - ok: Scalars['Boolean']; - projectID: Scalars['UUID']; - members: Array; - invitedMembers: Array; -}; - -export type InvitedMember = { - __typename?: 'InvitedMember'; - email: Scalars['String']; - invitedOn: Scalars['Time']; -}; - -export type InvitedUserAccount = { - __typename?: 'InvitedUserAccount'; - id: Scalars['ID']; - email: Scalars['String']; - invitedOn: Scalars['Time']; - member: MemberList; -}; - -export type LabelColor = { - __typename?: 'LabelColor'; - id: Scalars['ID']; - name: Scalars['String']; - position: Scalars['Float']; - colorHex: Scalars['String']; -}; - -export type LogoutUser = { - userID: Scalars['UUID']; -}; - -export type MePayload = { - __typename?: 'MePayload'; - user: UserAccount; - teamRoles: Array; - projectRoles: Array; -}; - -export type Member = { - __typename?: 'Member'; - id: Scalars['ID']; - role: Role; - fullName: Scalars['String']; - username: Scalars['String']; - profileIcon: ProfileIcon; - owned: OwnedList; - member: MemberList; -}; - -export type MemberInvite = { - userID?: Maybe; - email?: Maybe; -}; - -export type MemberList = { - __typename?: 'MemberList'; - teams: Array; +export type Query = { + __typename?: 'Query'; + findProject: Project; + findTask: Task; + findTeam: Team; + findUser: UserAccount; + invitedUsers: Array; + labelColors: Array; + me?: Maybe; + myTasks: MyTasksPayload; + notifications: Array; + organizations: Array; projects: Array; + searchMembers: Array; + taskGroups: Array; + teams: Array; + users: Array; }; -export type MemberSearchFilter = { - searchFilter: Scalars['String']; - projectID?: Maybe; + +export type QueryFindProjectArgs = { + input: FindProject; }; -export type MemberSearchResult = { - __typename?: 'MemberSearchResult'; - similarity: Scalars['Int']; - id: Scalars['String']; - user?: Maybe; - status: ShareStatus; + +export type QueryFindTaskArgs = { + input: FindTask; +}; + + +export type QueryFindTeamArgs = { + input: FindTeam; +}; + + +export type QueryFindUserArgs = { + input: FindUser; +}; + + +export type QueryMyTasksArgs = { + input: MyTasks; +}; + + +export type QueryProjectsArgs = { + input?: Maybe; +}; + + +export type QuerySearchMembersArgs = { + input: MemberSearchFilter; }; export type Mutation = { @@ -369,7 +363,6 @@ export type Mutation = { clearProfileAvatar: UserAccount; createProject: Project; createProjectLabel: ProjectLabel; - createRefreshToken: RefreshToken; createTask: Task; createTaskChecklist: TaskChecklist; createTaskChecklistItem: TaskChecklistItem; @@ -399,6 +392,7 @@ export type Mutation = { setTaskChecklistItemComplete: TaskChecklistItem; setTaskComplete: Task; sortTaskGroup: SortTaskGroupPayload; + toggleProjectVisibility: ToggleProjectVisibilityPayload; toggleTaskLabel: ToggleTaskLabelPayload; unassignTask: Task; updateProjectLabel: ProjectLabel; @@ -444,11 +438,6 @@ export type MutationCreateProjectLabelArgs = { }; -export type MutationCreateRefreshTokenArgs = { - input: NewRefreshToken; -}; - - export type MutationCreateTaskArgs = { input: NewTask; }; @@ -594,6 +583,11 @@ export type MutationSortTaskGroupArgs = { }; +export type MutationToggleProjectVisibilityArgs = { + input: ToggleProjectVisibility; +}; + + export type MutationToggleTaskLabelArgs = { input: ToggleTaskLabelInput; }; @@ -703,23 +697,6 @@ export type MutationUpdateUserRoleArgs = { input: UpdateUserRole; }; -export type MyTasks = { - status: MyTasksStatus; - sort: MyTasksSort; -}; - -export type MyTasksPayload = { - __typename?: 'MyTasksPayload'; - tasks: Array; - projects: Array; -}; - -export enum MyTasksSort { - None = 'NONE', - Project = 'PROJECT', - DueDate = 'DUE_DATE' -} - export enum MyTasksStatus { All = 'ALL', Incomplete = 'INCOMPLETE', @@ -731,68 +708,80 @@ export enum MyTasksStatus { CompleteThreeWeek = 'COMPLETE_THREE_WEEK' } -export type NewProject = { - teamID?: Maybe; - name: Scalars['String']; +export enum MyTasksSort { + None = 'NONE', + Project = 'PROJECT', + DueDate = 'DUE_DATE' +} + +export type MyTasks = { + status: MyTasksStatus; + sort: MyTasksSort; }; -export type NewProjectLabel = { +export type ProjectTaskMapping = { + __typename?: 'ProjectTaskMapping'; projectID: Scalars['UUID']; - labelColorID: Scalars['UUID']; - name?: Maybe; + taskID: Scalars['UUID']; }; -export type NewRefreshToken = { +export type MyTasksPayload = { + __typename?: 'MyTasksPayload'; + tasks: Array; + projects: Array; +}; + +export type TeamRole = { + __typename?: 'TeamRole'; + teamID: Scalars['UUID']; + roleCode: RoleCode; +}; + +export type ProjectRole = { + __typename?: 'ProjectRole'; + projectID: Scalars['UUID']; + roleCode: RoleCode; +}; + +export type MePayload = { + __typename?: 'MePayload'; + user: UserAccount; + organization?: Maybe; + teamRoles: Array; + projectRoles: Array; +}; + +export type ProjectsFilter = { + teamID?: Maybe; +}; + +export type FindUser = { userID: Scalars['UUID']; }; -export type NewTask = { - taskGroupID: Scalars['UUID']; - name: Scalars['String']; - position: Scalars['Float']; - assigned?: Maybe>; -}; - -export type NewTaskGroup = { +export type FindProject = { projectID: Scalars['UUID']; - name: Scalars['String']; - position: Scalars['Float']; }; -export type NewTaskGroupLocation = { - taskGroupID: Scalars['UUID']; - position: Scalars['Float']; -}; - -export type NewTaskLocation = { +export type FindTask = { taskID: Scalars['UUID']; - taskGroupID: Scalars['UUID']; - position: Scalars['Float']; }; -export type NewTeam = { - name: Scalars['String']; - organizationID: Scalars['UUID']; +export type FindTeam = { + teamID: Scalars['UUID']; }; -export type NewUserAccount = { - username: Scalars['String']; - email: Scalars['String']; - fullName: Scalars['String']; - initials: Scalars['String']; - password: Scalars['String']; - roleCode: Scalars['String']; -}; +export enum EntityType { + Task = 'TASK' +} -export type Notification = { - __typename?: 'Notification'; - id: Scalars['ID']; - entity: NotificationEntity; - actionType: ActionType; - actor: NotificationActor; - read: Scalars['Boolean']; - createdAt: Scalars['Time']; -}; +export enum ActorType { + User = 'USER' +} + +export enum ActionType { + TaskMemberAdded = 'TASK_MEMBER_ADDED' +} export type NotificationActor = { __typename?: 'NotificationActor'; @@ -808,308 +797,59 @@ export type NotificationEntity = { name: Scalars['String']; }; -export enum ObjectType { - Org = 'ORG', - Team = 'TEAM', - Project = 'PROJECT', - Task = 'TASK', - TaskGroup = 'TASK_GROUP', - TaskChecklist = 'TASK_CHECKLIST', - TaskChecklistItem = 'TASK_CHECKLIST_ITEM' -} - -export type Organization = { - __typename?: 'Organization'; - id: Scalars['ID']; - name: Scalars['String']; -}; - -export type OwnedList = { - __typename?: 'OwnedList'; - teams: Array; - projects: Array; -}; - -export type OwnersList = { - __typename?: 'OwnersList'; - projects: Array; - teams: Array; -}; - -export type ProfileIcon = { - __typename?: 'ProfileIcon'; - url?: Maybe; - initials?: Maybe; - bgColor?: Maybe; -}; - -export type Project = { - __typename?: 'Project'; +export type Notification = { + __typename?: 'Notification'; id: Scalars['ID']; + entity: NotificationEntity; + actionType: ActionType; + actor: NotificationActor; + read: Scalars['Boolean']; createdAt: Scalars['Time']; - name: Scalars['String']; - team?: Maybe; - taskGroups: Array; - members: Array; - invitedMembers: Array; - labels: Array; }; -export type ProjectLabel = { - __typename?: 'ProjectLabel'; - id: Scalars['ID']; - createdDate: Scalars['Time']; - labelColor: LabelColor; +export type ToggleProjectVisibility = { + projectID: Scalars['UUID']; + isPublic: Scalars['Boolean']; +}; + +export type ToggleProjectVisibilityPayload = { + __typename?: 'ToggleProjectVisibilityPayload'; + project: Project; +}; + +export type NewProject = { + teamID?: Maybe; + name: Scalars['String']; +}; + +export type UpdateProjectName = { + projectID: Scalars['UUID']; + name: Scalars['String']; +}; + +export type DeleteProject = { + projectID: Scalars['UUID']; +}; + +export type DeleteProjectPayload = { + __typename?: 'DeleteProjectPayload'; + ok: Scalars['Boolean']; + project: Project; +}; + +export type NewProjectLabel = { + projectID: Scalars['UUID']; + labelColorID: Scalars['UUID']; name?: Maybe; }; -export type ProjectRole = { - __typename?: 'ProjectRole'; - projectID: Scalars['UUID']; - roleCode: RoleCode; -}; - -export type ProjectTaskMapping = { - __typename?: 'ProjectTaskMapping'; - projectID: Scalars['UUID']; - taskID: Scalars['UUID']; -}; - -export type ProjectsFilter = { - teamID?: Maybe; -}; - -export type Query = { - __typename?: 'Query'; - findProject: Project; - findTask: Task; - findTeam: Team; - findUser: UserAccount; - invitedUsers: Array; - labelColors: Array; - me: MePayload; - myTasks: MyTasksPayload; - notifications: Array; - organizations: Array; - projects: Array; - searchMembers: Array; - taskGroups: Array; - teams: Array; - users: Array; -}; - - -export type QueryFindProjectArgs = { - input: FindProject; -}; - - -export type QueryFindTaskArgs = { - input: FindTask; -}; - - -export type QueryFindTeamArgs = { - input: FindTeam; -}; - - -export type QueryFindUserArgs = { - input: FindUser; -}; - - -export type QueryMyTasksArgs = { - input: MyTasks; -}; - - -export type QueryProjectsArgs = { - input?: Maybe; -}; - - -export type QuerySearchMembersArgs = { - input: MemberSearchFilter; -}; - -export type RefreshToken = { - __typename?: 'RefreshToken'; - id: Scalars['ID']; - userId: Scalars['UUID']; - expiresAt: Scalars['Time']; - createdAt: Scalars['Time']; -}; - -export type RemoveTaskLabelInput = { - taskID: Scalars['UUID']; - taskLabelID: Scalars['UUID']; -}; - -export type Role = { - __typename?: 'Role'; - code: Scalars['String']; - name: Scalars['String']; -}; - -export enum RoleCode { - Owner = 'owner', - Admin = 'admin', - Member = 'member', - Observer = 'observer' -} - -export enum RoleLevel { - Admin = 'ADMIN', - Member = 'MEMBER' -} - -export type SetTaskChecklistItemComplete = { - taskChecklistItemID: Scalars['UUID']; - complete: Scalars['Boolean']; -}; - -export type SetTaskComplete = { - taskID: Scalars['UUID']; - complete: Scalars['Boolean']; -}; - -export enum ShareStatus { - Invited = 'INVITED', - Joined = 'JOINED' -} - -export type SortTaskGroup = { - taskGroupID: Scalars['UUID']; - tasks: Array; -}; - -export type SortTaskGroupPayload = { - __typename?: 'SortTaskGroupPayload'; - taskGroupID: Scalars['UUID']; - tasks: Array; -}; - -export type Task = { - __typename?: 'Task'; - id: Scalars['ID']; - taskGroup: TaskGroup; - createdAt: Scalars['Time']; - name: Scalars['String']; - position: Scalars['Float']; - description?: Maybe; - dueDate?: Maybe; - hasTime: Scalars['Boolean']; - complete: Scalars['Boolean']; - completedAt?: Maybe; - assigned: Array; - labels: Array; - checklists: Array; - badges: TaskBadges; - activity: Array; - comments: Array; -}; - -export type TaskActivity = { - __typename?: 'TaskActivity'; - id: Scalars['ID']; - type: ActivityType; - data: Array; - causedBy: CausedBy; - createdAt: Scalars['Time']; -}; - -export type TaskActivityData = { - __typename?: 'TaskActivityData'; - name: Scalars['String']; - value: Scalars['String']; -}; - -export type TaskBadges = { - __typename?: 'TaskBadges'; - checklist?: Maybe; -}; - -export type TaskChecklist = { - __typename?: 'TaskChecklist'; - id: Scalars['ID']; - name: Scalars['String']; - position: Scalars['Float']; - items: Array; -}; - -export type TaskChecklistItem = { - __typename?: 'TaskChecklistItem'; - id: Scalars['ID']; - name: Scalars['String']; - taskChecklistID: Scalars['UUID']; - complete: Scalars['Boolean']; - position: Scalars['Float']; - dueDate: Scalars['Time']; -}; - -export type TaskComment = { - __typename?: 'TaskComment'; - id: Scalars['ID']; - createdAt: Scalars['Time']; - updatedAt?: Maybe; - message: Scalars['String']; - createdBy: CreatedBy; - pinned: Scalars['Boolean']; -}; - -export type TaskGroup = { - __typename?: 'TaskGroup'; - id: Scalars['ID']; - projectID: Scalars['String']; - createdAt: Scalars['Time']; - name: Scalars['String']; - position: Scalars['Float']; - tasks: Array; -}; - -export type TaskLabel = { - __typename?: 'TaskLabel'; - id: Scalars['ID']; - projectLabel: ProjectLabel; - assignedDate: Scalars['Time']; -}; - -export type TaskPositionUpdate = { - taskID: Scalars['UUID']; - position: Scalars['Float']; -}; - -export type Team = { - __typename?: 'Team'; - id: Scalars['ID']; - createdAt: Scalars['Time']; - name: Scalars['String']; - members: Array; -}; - -export type TeamRole = { - __typename?: 'TeamRole'; - teamID: Scalars['UUID']; - roleCode: RoleCode; -}; - - -export type ToggleTaskLabelInput = { - taskID: Scalars['UUID']; +export type DeleteProjectLabel = { projectLabelID: Scalars['UUID']; }; -export type ToggleTaskLabelPayload = { - __typename?: 'ToggleTaskLabelPayload'; - active: Scalars['Boolean']; - task: Task; -}; - - -export type UnassignTaskInput = { - taskID: Scalars['UUID']; - userID: Scalars['UUID']; +export type UpdateProjectLabelName = { + projectLabelID: Scalars['UUID']; + name: Scalars['String']; }; export type UpdateProjectLabel = { @@ -1123,9 +863,44 @@ export type UpdateProjectLabelColor = { labelColorID: Scalars['UUID']; }; -export type UpdateProjectLabelName = { - projectLabelID: Scalars['UUID']; - name: Scalars['String']; +export type DeleteInvitedProjectMember = { + projectID: Scalars['UUID']; + email: Scalars['String']; +}; + +export type DeleteInvitedProjectMemberPayload = { + __typename?: 'DeleteInvitedProjectMemberPayload'; + invitedMember: InvitedMember; +}; + +export type MemberInvite = { + userID?: Maybe; + email?: Maybe; +}; + +export type InviteProjectMembers = { + projectID: Scalars['UUID']; + members: Array; +}; + +export type InviteProjectMembersPayload = { + __typename?: 'InviteProjectMembersPayload'; + ok: Scalars['Boolean']; + projectID: Scalars['UUID']; + members: Array; + invitedMembers: Array; +}; + +export type DeleteProjectMember = { + projectID: Scalars['UUID']; + userID: Scalars['UUID']; +}; + +export type DeleteProjectMemberPayload = { + __typename?: 'DeleteProjectMemberPayload'; + ok: Scalars['Boolean']; + member: Member; + projectID: Scalars['UUID']; }; export type UpdateProjectMemberRole = { @@ -1140,8 +915,62 @@ export type UpdateProjectMemberRolePayload = { member: Member; }; -export type UpdateProjectName = { - projectID: Scalars['UUID']; +export type NewTask = { + taskGroupID: Scalars['UUID']; + name: Scalars['String']; + position: Scalars['Float']; + assigned?: Maybe>; +}; + +export type AssignTaskInput = { + taskID: Scalars['UUID']; + userID: Scalars['UUID']; +}; + +export type UnassignTaskInput = { + taskID: Scalars['UUID']; + userID: Scalars['UUID']; +}; + +export type UpdateTaskDescriptionInput = { + taskID: Scalars['UUID']; + description: Scalars['String']; +}; + +export type UpdateTaskLocationPayload = { + __typename?: 'UpdateTaskLocationPayload'; + previousTaskGroupID: Scalars['UUID']; + task: Task; +}; + +export type UpdateTaskDueDate = { + taskID: Scalars['UUID']; + hasTime: Scalars['Boolean']; + dueDate?: Maybe; +}; + +export type SetTaskComplete = { + taskID: Scalars['UUID']; + complete: Scalars['Boolean']; +}; + +export type NewTaskLocation = { + taskID: Scalars['UUID']; + taskGroupID: Scalars['UUID']; + position: Scalars['Float']; +}; + +export type DeleteTaskInput = { + taskID: Scalars['UUID']; +}; + +export type DeleteTaskPayload = { + __typename?: 'DeleteTaskPayload'; + taskID: Scalars['UUID']; +}; + +export type UpdateTaskName = { + taskID: Scalars['UUID']; name: Scalars['String']; }; @@ -1158,11 +987,6 @@ export type UpdateTaskChecklistItemLocationPayload = { checklistItem: TaskChecklistItem; }; -export type UpdateTaskChecklistItemName = { - taskChecklistItemID: Scalars['UUID']; - name: Scalars['String']; -}; - export type UpdateTaskChecklistLocation = { taskChecklistID: Scalars['UUID']; position: Scalars['Float']; @@ -1173,11 +997,64 @@ export type UpdateTaskChecklistLocationPayload = { checklist: TaskChecklist; }; +export type CreateTaskChecklist = { + taskID: Scalars['UUID']; + name: Scalars['String']; + position: Scalars['Float']; +}; + +export type DeleteTaskChecklistItemPayload = { + __typename?: 'DeleteTaskChecklistItemPayload'; + ok: Scalars['Boolean']; + taskChecklistItem: TaskChecklistItem; +}; + +export type CreateTaskChecklistItem = { + taskChecklistID: Scalars['UUID']; + name: Scalars['String']; + position: Scalars['Float']; +}; + +export type SetTaskChecklistItemComplete = { + taskChecklistItemID: Scalars['UUID']; + complete: Scalars['Boolean']; +}; + +export type DeleteTaskChecklistItem = { + taskChecklistItemID: Scalars['UUID']; +}; + +export type UpdateTaskChecklistItemName = { + taskChecklistItemID: Scalars['UUID']; + name: Scalars['String']; +}; + export type UpdateTaskChecklistName = { taskChecklistID: Scalars['UUID']; name: Scalars['String']; }; +export type DeleteTaskChecklist = { + taskChecklistID: Scalars['UUID']; +}; + +export type DeleteTaskChecklistPayload = { + __typename?: 'DeleteTaskChecklistPayload'; + ok: Scalars['Boolean']; + taskChecklist: TaskChecklist; +}; + +export type CreateTaskComment = { + taskID: Scalars['UUID']; + message: Scalars['String']; +}; + +export type CreateTaskCommentPayload = { + __typename?: 'CreateTaskCommentPayload'; + taskID: Scalars['UUID']; + comment: TaskComment; +}; + export type UpdateTaskComment = { commentID: Scalars['UUID']; message: Scalars['String']; @@ -1189,15 +1066,57 @@ export type UpdateTaskCommentPayload = { comment: TaskComment; }; -export type UpdateTaskDescriptionInput = { - taskID: Scalars['UUID']; - description: Scalars['String']; +export type DeleteTaskComment = { + commentID: Scalars['UUID']; }; -export type UpdateTaskDueDate = { +export type DeleteTaskCommentPayload = { + __typename?: 'DeleteTaskCommentPayload'; taskID: Scalars['UUID']; - hasTime: Scalars['Boolean']; - dueDate?: Maybe; + commentID: Scalars['UUID']; +}; + +export type DeleteTaskGroupTasks = { + taskGroupID: Scalars['UUID']; +}; + +export type DeleteTaskGroupTasksPayload = { + __typename?: 'DeleteTaskGroupTasksPayload'; + taskGroupID: Scalars['UUID']; + tasks: Array; +}; + +export type TaskPositionUpdate = { + taskID: Scalars['UUID']; + position: Scalars['Float']; +}; + +export type SortTaskGroupPayload = { + __typename?: 'SortTaskGroupPayload'; + taskGroupID: Scalars['UUID']; + tasks: Array; +}; + +export type SortTaskGroup = { + taskGroupID: Scalars['UUID']; + tasks: Array; +}; + +export type DuplicateTaskGroup = { + projectID: Scalars['UUID']; + taskGroupID: Scalars['UUID']; + name: Scalars['String']; + position: Scalars['Float']; +}; + +export type DuplicateTaskGroupPayload = { + __typename?: 'DuplicateTaskGroupPayload'; + taskGroup: TaskGroup; +}; + +export type NewTaskGroupLocation = { + taskGroupID: Scalars['UUID']; + position: Scalars['Float']; }; export type UpdateTaskGroupName = { @@ -1205,15 +1124,82 @@ export type UpdateTaskGroupName = { name: Scalars['String']; }; -export type UpdateTaskLocationPayload = { - __typename?: 'UpdateTaskLocationPayload'; - previousTaskGroupID: Scalars['UUID']; +export type DeleteTaskGroupInput = { + taskGroupID: Scalars['UUID']; +}; + +export type DeleteTaskGroupPayload = { + __typename?: 'DeleteTaskGroupPayload'; + ok: Scalars['Boolean']; + affectedRows: Scalars['Int']; + taskGroup: TaskGroup; +}; + +export type NewTaskGroup = { + projectID: Scalars['UUID']; + name: Scalars['String']; + position: Scalars['Float']; +}; + +export type AddTaskLabelInput = { + taskID: Scalars['UUID']; + projectLabelID: Scalars['UUID']; +}; + +export type RemoveTaskLabelInput = { + taskID: Scalars['UUID']; + taskLabelID: Scalars['UUID']; +}; + +export type ToggleTaskLabelInput = { + taskID: Scalars['UUID']; + projectLabelID: Scalars['UUID']; +}; + +export type ToggleTaskLabelPayload = { + __typename?: 'ToggleTaskLabelPayload'; + active: Scalars['Boolean']; task: Task; }; -export type UpdateTaskName = { - taskID: Scalars['UUID']; +export type NewTeam = { name: Scalars['String']; + organizationID: Scalars['UUID']; +}; + +export type DeleteTeam = { + teamID: Scalars['UUID']; +}; + +export type DeleteTeamPayload = { + __typename?: 'DeleteTeamPayload'; + ok: Scalars['Boolean']; + team: Team; + projects: Array; +}; + +export type DeleteTeamMember = { + teamID: Scalars['UUID']; + userID: Scalars['UUID']; + newOwnerID?: Maybe; +}; + +export type DeleteTeamMemberPayload = { + __typename?: 'DeleteTeamMemberPayload'; + teamID: Scalars['UUID']; + userID: Scalars['UUID']; + affectedProjects: Array; +}; + +export type CreateTeamMember = { + userID: Scalars['UUID']; + teamID: Scalars['UUID']; +}; + +export type CreateTeamMemberPayload = { + __typename?: 'CreateTeamMemberPayload'; + team: Team; + teamMember: Member; }; export type UpdateTeamMemberRole = { @@ -1229,11 +1215,26 @@ export type UpdateTeamMemberRolePayload = { member: Member; }; -export type UpdateUserInfo = { - name: Scalars['String']; - initials: Scalars['String']; - email: Scalars['String']; - bio: Scalars['String']; +export type DeleteInvitedUserAccount = { + invitedUserID: Scalars['UUID']; +}; + +export type DeleteInvitedUserAccountPayload = { + __typename?: 'DeleteInvitedUserAccountPayload'; + invitedUser: InvitedUserAccount; +}; + +export type MemberSearchFilter = { + searchFilter: Scalars['String']; + projectID?: Maybe; +}; + +export type MemberSearchResult = { + __typename?: 'MemberSearchResult'; + similarity: Scalars['Int']; + id: Scalars['String']; + user?: Maybe; + status: ShareStatus; }; export type UpdateUserInfoPayload = { @@ -1241,6 +1242,13 @@ export type UpdateUserInfoPayload = { user: UserAccount; }; +export type UpdateUserInfo = { + name: Scalars['String']; + initials: Scalars['String']; + email: Scalars['String']; + bio: Scalars['String']; +}; + export type UpdateUserPassword = { userID: Scalars['UUID']; password: Scalars['String']; @@ -1262,20 +1270,28 @@ export type UpdateUserRolePayload = { user: UserAccount; }; - -export type UserAccount = { - __typename?: 'UserAccount'; - id: Scalars['ID']; +export type NewUserAccount = { + username: Scalars['String']; email: Scalars['String']; - createdAt: Scalars['Time']; fullName: Scalars['String']; initials: Scalars['String']; - bio: Scalars['String']; - role: Role; - username: Scalars['String']; - profileIcon: ProfileIcon; - owned: OwnedList; - member: MemberList; + password: Scalars['String']; + roleCode: Scalars['String']; +}; + +export type LogoutUser = { + userID: Scalars['UUID']; +}; + +export type DeleteUserAccount = { + userID: Scalars['UUID']; + newOwnerID?: Maybe; +}; + +export type DeleteUserAccountPayload = { + __typename?: 'DeleteUserAccountPayload'; + ok: Scalars['Boolean']; + userAccount: UserAccount; }; export type AssignTaskMutationVariables = Exact<{ @@ -1419,7 +1435,7 @@ export type FindProjectQuery = ( { __typename?: 'Query' } & { findProject: ( { __typename?: 'Project' } - & Pick + & Pick & { team?: Maybe<( { __typename?: 'Team' } & Pick @@ -1555,7 +1571,7 @@ export type FindTaskQuery = ( & Pick ) } )> } - ), me: ( + ), me?: Maybe<( { __typename?: 'MePayload' } & { user: ( { __typename?: 'UserAccount' } @@ -1565,7 +1581,7 @@ export type FindTaskQuery = ( & Pick ) } ) } - ) } + )> } ); export type TaskFieldsFragment = ( @@ -1627,7 +1643,7 @@ export type MeQueryVariables = Exact<{ [key: string]: never; }>; export type MeQuery = ( { __typename?: 'Query' } - & { me: ( + & { me?: Maybe<( { __typename?: 'MePayload' } & { user: ( { __typename?: 'UserAccount' } @@ -1643,7 +1659,7 @@ export type MeQuery = ( { __typename?: 'ProjectRole' } & Pick )> } - ) } + )> } ); export type MyTasksQueryVariables = Exact<{ @@ -2253,6 +2269,23 @@ export type UpdateTeamMemberRoleMutation = ( ) } ); +export type ToggleProjectVisibilityMutationVariables = Exact<{ + projectID: Scalars['UUID']; + isPublic: Scalars['Boolean']; +}>; + + +export type ToggleProjectVisibilityMutation = ( + { __typename?: 'Mutation' } + & { toggleProjectVisibility: ( + { __typename?: 'ToggleProjectVisibilityPayload' } + & { project: ( + { __typename?: 'Project' } + & Pick + ) } + ) } +); + export type ToggleTaskLabelMutationVariables = Exact<{ taskID: Scalars['UUID']; projectLabelID: Scalars['UUID']; @@ -2298,7 +2331,7 @@ export type TopNavbarQuery = ( { __typename?: 'NotificationActor' } & Pick ) } - )>, me: ( + )>, me?: Maybe<( { __typename?: 'MePayload' } & { user: ( { __typename?: 'UserAccount' } @@ -2314,7 +2347,7 @@ export type TopNavbarQuery = ( { __typename?: 'ProjectRole' } & Pick )> } - ) } + )> } ); export type UnassignTaskMutationVariables = Exact<{ @@ -2979,6 +3012,7 @@ export const FindProjectDocument = gql` query findProject($projectID: UUID!) { findProject(input: {projectID: $projectID}) { name + publicOn team { id } @@ -4605,6 +4639,43 @@ export function useUpdateTeamMemberRoleMutation(baseOptions?: Apollo.MutationHoo export type UpdateTeamMemberRoleMutationHookResult = ReturnType; export type UpdateTeamMemberRoleMutationResult = Apollo.MutationResult; export type UpdateTeamMemberRoleMutationOptions = Apollo.BaseMutationOptions; +export const ToggleProjectVisibilityDocument = gql` + mutation toggleProjectVisibility($projectID: UUID!, $isPublic: Boolean!) { + toggleProjectVisibility(input: {projectID: $projectID, isPublic: $isPublic}) { + project { + id + publicOn + } + } +} + `; +export type ToggleProjectVisibilityMutationFn = Apollo.MutationFunction; + +/** + * __useToggleProjectVisibilityMutation__ + * + * To run a mutation, you first call `useToggleProjectVisibilityMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useToggleProjectVisibilityMutation` 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 [toggleProjectVisibilityMutation, { data, loading, error }] = useToggleProjectVisibilityMutation({ + * variables: { + * projectID: // value for 'projectID' + * isPublic: // value for 'isPublic' + * }, + * }); + */ +export function useToggleProjectVisibilityMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(ToggleProjectVisibilityDocument, options); + } +export type ToggleProjectVisibilityMutationHookResult = ReturnType; +export type ToggleProjectVisibilityMutationResult = Apollo.MutationResult; +export type ToggleProjectVisibilityMutationOptions = Apollo.BaseMutationOptions; export const ToggleTaskLabelDocument = gql` mutation toggleTaskLabel($taskID: UUID!, $projectLabelID: UUID!) { toggleTaskLabel(input: {taskID: $taskID, projectLabelID: $projectLabelID}) { @@ -5368,4 +5439,4 @@ export function useUsersLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions; export type UsersLazyQueryHookResult = ReturnType; -export type UsersQueryResult = Apollo.QueryResult; +export type UsersQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/frontend/src/shared/graphql/findProject.ts b/frontend/src/shared/graphql/findProject.ts index caf9624..c44de54 100644 --- a/frontend/src/shared/graphql/findProject.ts +++ b/frontend/src/shared/graphql/findProject.ts @@ -5,6 +5,7 @@ const FIND_PROJECT_QUERY = gql` query findProject($projectID: UUID!) { findProject(input: { projectID: $projectID }) { name + publicOn team { id } diff --git a/frontend/src/shared/graphql/toggleProjectVisibility.ts b/frontend/src/shared/graphql/toggleProjectVisibility.ts new file mode 100644 index 0000000..a7a5135 --- /dev/null +++ b/frontend/src/shared/graphql/toggleProjectVisibility.ts @@ -0,0 +1,14 @@ +import gql from 'graphql-tag'; + +export const DELETE_PROJECT_MUTATION = gql` + mutation toggleProjectVisibility($projectID: UUID!, $isPublic: Boolean!) { + toggleProjectVisibility(input: { projectID: $projectID, isPublic: $isPublic }) { + project { + id + publicOn + } + } + } +`; + +export default DELETE_PROJECT_MUTATION; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 6981df2..4b544dc 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -39,11 +39,6 @@ ts-invariant "^0.4.4" tslib "^1.10.0" -"@ardatan/aggregate-error@0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@ardatan/aggregate-error/-/aggregate-error-0.0.1.tgz#1403ac5de10d8ca689fc1f65844c27179ae1d44f" - integrity sha512-UQ9BequOTIavs0pTHLMwQwKQF8tTV1oezY/H2O9chA+JNPFZSua55xpU5dPSjAU9/jLJ1VwU+HJuTVN8u7S6Fg== - "@ardatan/aggregate-error@0.0.6": version "0.0.6" resolved "https://registry.yarnpkg.com/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz#fe6924771ea40fc98dc7a7045c2e872dc8527609" @@ -1349,13 +1344,13 @@ ts-node "^9" tslib "^2" -"@graphql-codegen/cli@^1.13.2": - version "1.19.4" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-1.19.4.tgz#16e5caaa46ee159cbb01e2a2699203f8f25faac2" - integrity sha512-PN1xgaXZaTTEPhBfQkIRK82RzrzY8Pr+tfCqOtibeJMed0mtIJ5gSQfollRbtZ0/t7ZEmYCxojqQiVSbKzXDMw== +"@graphql-codegen/cli@^1.21.4": + version "1.21.4" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-1.21.4.tgz#41ce6abc6b33e369a3ee795621373b8ffa1aadeb" + integrity sha512-fCrPn7DeGnCU6d9xjo04VSzIGioVLj1BAjuZpgRJNNjyzTmv+qZJJVTWfD55J17XxlhFsfKJBprtdmRxZ3V2hw== dependencies: "@graphql-codegen/core" "1.17.9" - "@graphql-codegen/plugin-helpers" "^1.18.2" + "@graphql-codegen/plugin-helpers" "^1.18.5" "@graphql-tools/apollo-engine-loader" "^6" "@graphql-tools/code-file-loader" "^6" "@graphql-tools/git-loader" "^6" @@ -1365,16 +1360,15 @@ "@graphql-tools/load" "^6" "@graphql-tools/prisma-loader" "^6" "@graphql-tools/url-loader" "^6" - "@graphql-tools/utils" "^6" + "@graphql-tools/utils" "^7.0.0" ansi-escapes "^4.3.1" - camel-case "^4.1.1" chalk "^4.1.0" + change-case-all "1.0.14" chokidar "^3.4.3" common-tags "^1.8.0" - constant-case "^3.0.3" cosmiconfig "^7.0.0" debounce "^1.2.0" - dependency-graph "^0.9.0" + dependency-graph "^0.11.0" detect-indent "^6.0.0" glob "^7.1.6" graphql-config "^3.2.0" @@ -1386,17 +1380,14 @@ listr "^0.14.3" listr-update-renderer "^0.5.0" log-symbols "^4.0.0" - lower-case "^2.0.1" minimatch "^3.0.4" mkdirp "^1.0.4" - pascal-case "^3.1.1" - request "^2.88.2" string-env-interpolation "^1.0.1" ts-log "^2.2.3" - tslib "~2.0.1" - upper-case "^2.0.1" + tslib "~2.2.0" valid-url "^1.0.9" wrap-ansi "^7.0.0" + yaml "^1.10.0" yargs "^16.1.1" "@graphql-codegen/core@1.17.9": @@ -1409,23 +1400,6 @@ "@graphql-tools/utils" "^6" tslib "~2.0.1" -"@graphql-codegen/plugin-helpers@1.17.7": - version "1.17.7" - resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.17.7.tgz#5903105fda9470aafefe9da29e3a6fb3a52b8376" - integrity sha512-LsXS0s/ZOACZXa3W29ekcaQLzP8TsYzow6nIjW6rtkWX5T0EDooBQbDn1cdLdlpenqbUU+vtONwR6Qqc6hrq2Q== - dependencies: - "@graphql-tools/utils" "^6.0.0" - camel-case "4.1.1" - common-tags "1.8.0" - constant-case "3.0.3" - import-from "3.0.0" - lodash "~4.17.15" - lower-case "2.0.1" - param-case "3.0.3" - pascal-case "3.1.1" - tslib "~2.0.0" - upper-case "2.0.1" - "@graphql-codegen/plugin-helpers@^1.18.2": version "1.18.2" resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.18.2.tgz#57011076cb8b8f5d04d37d226a5eda300c01be94" @@ -1443,69 +1417,64 @@ tslib "~2.0.1" upper-case "2.0.1" -"@graphql-codegen/typescript-operations@^1.13.2": - version "1.17.12" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-1.17.12.tgz#25e0f0ede1bcd165196f0c161286d745395c7768" - integrity sha512-UXe/O3kNAlLptURuripAhK8n/Gf6FthL7xr6sG4vpuLVwrWjXu/KlS7+TP+UvlI9OH2LICm0KHqA0xoFCjNbMg== +"@graphql-codegen/plugin-helpers@^1.18.5": + version "1.18.5" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.18.5.tgz#e1d875cfb6a2f7bf4b4318135f7fee6e1200f3b0" + integrity sha512-xY8dWdU4+mm+253esLYcKQIgWZEgI3spKPmMRQ+oAxlrCn9oIcdhhiMqNxa9rHS20xpZtlAjozxHulrqjFLuyA== dependencies: - "@graphql-codegen/plugin-helpers" "^1.18.2" - "@graphql-codegen/typescript" "^1.18.1" - "@graphql-codegen/visitor-plugin-common" "^1.17.20" - auto-bind "~4.0.0" - tslib "~2.0.1" + "@graphql-tools/utils" "^7.0.0" + common-tags "1.8.0" + import-from "3.0.0" + lodash "~4.17.20" + tslib "~2.2.0" -"@graphql-codegen/typescript-react-apollo@^1.13.2": - version "1.17.7" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-1.17.7.tgz#b1d1ad1150c07097566f46c208ca54d42a8ad4f5" - integrity sha512-Yoc7XwruETJ1Aoi5UBLF+AVsyReGPxI9jEpsNWv/Xm5zzGGk8iVwKIwos9hBjzkDO0sQooZZa0UXBKXNQa7j0w== +"@graphql-codegen/typescript-operations@^1.17.16": + version "1.17.16" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-1.17.16.tgz#75eb389f268b2dbd2e46b235bcb957be561c31cb" + integrity sha512-DoWIhg/c2XS4IpgRLRXRBJDwmKoIl6KuzL2iO9GElX0rdjYguwbqx6iDV1pgktTTajMy8pXP56oZhmEcZLRD2Q== dependencies: - "@graphql-codegen/plugin-helpers" "1.17.7" - "@graphql-codegen/visitor-plugin-common" "1.17.7" + "@graphql-codegen/plugin-helpers" "^1.18.5" + "@graphql-codegen/typescript" "^1.22.0" + "@graphql-codegen/visitor-plugin-common" "^1.20.0" auto-bind "~4.0.0" - camel-case "4.1.1" - pascal-case "3.1.1" - tslib "~2.0.0" + tslib "~2.2.0" -"@graphql-codegen/typescript@^1.13.2", "@graphql-codegen/typescript@^1.18.1": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-1.19.0.tgz#05b1b4502b91dee53ec4f0ae39e9537eff004c5c" - integrity sha512-ThpMdtb6LmEKzLNxlotpk33eIMfX1i1aAlYDaqctIhFjZ2Rbvkgdb8q/5/my7yeH33BLnuqHDKrdkDsAAvCHcw== +"@graphql-codegen/typescript-react-apollo@^2.2.4": + version "2.2.4" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-2.2.4.tgz#149b8e4f2b684295a2e6be8b9e88285a0f8be0f6" + integrity sha512-2db+KrujuBjQ8GraamEPGEp74Bxcw6Vd2AubhqenoTmHjK/Jw3HLpO8ndetcoND08e3EZCb/CNyKkLcxwYD0eg== dependencies: - "@graphql-codegen/plugin-helpers" "^1.18.2" - "@graphql-codegen/visitor-plugin-common" "^1.17.21" + "@graphql-codegen/plugin-helpers" "^1.18.5" + "@graphql-codegen/visitor-plugin-common" "^1.20.0" auto-bind "~4.0.0" - tslib "~2.0.1" + change-case-all "1.0.14" + tslib "~2.2.0" -"@graphql-codegen/visitor-plugin-common@1.17.7": - version "1.17.7" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.17.7.tgz#3158ca4fc7d45a0f5a6ad0706061015eae481d9e" - integrity sha512-z5WvYqgCgPAAuMJMOE0e0nEicdaQRm59/vP+yYihKQmwrASzPlqa1BUiGDnfrPcCooB9fEUHB+cQb3gt9GRfEg== +"@graphql-codegen/typescript@^1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-1.22.0.tgz#d05be3a971e5d75a076a43e123b6330f4366a6ab" + integrity sha512-YzN/3MBYHrP110m8JgUWQIHt7Ivi3JXiq0RT5XNx/F9mVOSbZz6Ezbaji8YJA3y04Gl2f6ZgtdGazWANUvcOcg== dependencies: - "@graphql-codegen/plugin-helpers" "1.17.7" - "@graphql-tools/relay-operation-optimizer" "6.0.15" - array.prototype.flatmap "1.2.3" + "@graphql-codegen/plugin-helpers" "^1.18.5" + "@graphql-codegen/visitor-plugin-common" "^1.20.0" auto-bind "~4.0.0" - dependency-graph "0.9.0" - graphql-tag "2.11.0" - parse-filepath "1.0.2" - pascal-case "3.1.1" - tslib "~2.0.0" + tslib "~2.2.0" -"@graphql-codegen/visitor-plugin-common@^1.17.20", "@graphql-codegen/visitor-plugin-common@^1.17.21": - version "1.17.21" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.17.21.tgz#e47a5623392bcdb89d5af2484798a2fd9a59df8c" - integrity sha512-rhdJdj+4DAMgMSBlgkvfOMw65L46sAtYb7G/n9ucc9uJGUbiIcgvS/wpoQ/gxz2eUaNfcoFmjVLm+dkTM/XwqA== +"@graphql-codegen/visitor-plugin-common@^1.20.0": + version "1.20.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.20.0.tgz#38d829eab7370c79aa5229190788f94adcae8f76" + integrity sha512-AYrpy8NA3DpvhDLqYGerQRv44S+YAMPKtwT8x9GNVjzP0gVfmqi3gG1bDWbP5sm6kOZKvDC0kTxGePuBSZerxw== dependencies: - "@graphql-codegen/plugin-helpers" "^1.18.2" + "@graphql-codegen/plugin-helpers" "^1.18.5" "@graphql-tools/optimize" "^1.0.1" "@graphql-tools/relay-operation-optimizer" "^6" array.prototype.flatmap "^1.2.4" auto-bind "~4.0.0" - dependency-graph "^0.9.0" + change-case-all "1.0.14" + dependency-graph "^0.11.0" graphql-tag "^2.11.0" parse-filepath "^1.0.2" - pascal-case "^3.1.1" - tslib "~2.0.1" + tslib "~2.2.0" "@graphql-tools/apollo-engine-loader@^6": version "6.2.5" @@ -1665,14 +1634,6 @@ tslib "~2.0.1" yaml-ast-parser "^0.0.43" -"@graphql-tools/relay-operation-optimizer@6.0.15": - version "6.0.15" - resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.0.15.tgz#f991499c54945cb8fa2396bb5292252fbda0a773" - integrity sha512-Y4h2kclKh5HvyvmoxeZiDhqdhMKfLKamOYx6UVpFsbeKb6Tt9RCNPVhpa+YToXxUXl0PvjhxZWeQ4lZY0GE0ug== - dependencies: - "@graphql-tools/utils" "6.0.15" - relay-compiler "10.0.0" - "@graphql-tools/relay-operation-optimizer@^6": version "6.3.0" resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.3.0.tgz#f8c7f6c8aa4a9cf50ab151fbc5db4f4282a79532" @@ -1713,14 +1674,6 @@ valid-url "1.0.9" ws "7.4.1" -"@graphql-tools/utils@6.0.15": - version "6.0.15" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-6.0.15.tgz#6d54d383285bea3c22797531933b62a408e78e49" - integrity sha512-VG5cMLPgh9RDLGHamGpXVnBrNw7bZGT46LrxK7IIqDZI9H0GPsRCo8+p+CfDkw0IlDiEECb624WVCpm9IYNecA== - dependencies: - "@ardatan/aggregate-error" "0.0.1" - camel-case "4.1.1" - "@graphql-tools/utils@^6", "@graphql-tools/utils@^6.0.0": version "6.2.4" resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-6.2.4.tgz#38a2314d2e5e229ad4f78cca44e1199e18d55856" @@ -2413,10 +2366,10 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== -"@types/styled-components@^5.0.0": - version "5.1.7" - resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.7.tgz#3cd10b088c1cb1acde2e4b166b3e8275a3083710" - integrity sha512-BJzPhFygYspyefAGFZTZ/8lCEY4Tk+Iqktvnko3xmJf9LrLqs3+grxPeU3O0zLl6yjbYBopD0/VikbHgXDbJtA== +"@types/styled-components@^5.1.0": + version "5.1.9" + resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.9.tgz#00d3d84b501420521c4db727e3c195459f87a6cf" + integrity sha512-kbEG6YlwK8rucITpKEr6pA4Ho9KSQHUUOzZ9lY3va1mtcjvS3D0wDciFyHEiNHKLL/npZCKDQJqm0x44sPO9oA== dependencies: "@types/hoist-non-react-statics" "*" "@types/react" "*" @@ -3076,15 +3029,6 @@ array.prototype.flat@^1.2.1, array.prototype.flat@^1.2.3: define-properties "^1.1.3" es-abstract "^1.18.0-next.1" -array.prototype.flatmap@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.3.tgz#1c13f84a178566042dd63de4414440db9222e443" - integrity sha512-OOEk+lkePcg+ODXIpvuU9PAryCikCJyo7GlDG1upleEpQRx6mzL9puEBkozQ5iAx20KV0l3DbyQwqciJtqe5Pg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - function-bind "^1.1.1" - array.prototype.flatmap@^1.2.3, array.prototype.flatmap@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9" @@ -3881,7 +3825,7 @@ camel-case@4.1.1: pascal-case "^3.1.1" tslib "^1.10.0" -camel-case@4.1.2, camel-case@^4.1.1: +camel-case@4.1.2, camel-case@^4.1.1, camel-case@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== @@ -3919,6 +3863,15 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001035, can resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001168.tgz#6fcd098c139d003b9bd484cbb9ca26cb89907f9a" integrity sha512-P2zmX7swIXKu+GMMR01TWa4csIKELTNnZKc+f1CjebmZJQtTAEXmpQSoKVJVVcvPGAA0TEYTOUp3VehavZSFPQ== +capital-case@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" + integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -3964,6 +3917,40 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +change-case-all@1.0.14: + version "1.0.14" + resolved "https://registry.yarnpkg.com/change-case-all/-/change-case-all-1.0.14.tgz#bac04da08ad143278d0ac3dda7eccd39280bfba1" + integrity sha512-CWVm2uT7dmSHdO/z1CXT/n47mWonyypzBbuCy5tN7uMg22BsfkhwT6oHmFCAk+gL1LOOxhdbB9SZz3J1KTY3gA== + dependencies: + change-case "^4.1.2" + is-lower-case "^2.0.2" + is-upper-case "^2.0.2" + lower-case "^2.0.2" + lower-case-first "^2.0.2" + sponge-case "^1.0.1" + swap-case "^2.0.2" + title-case "^3.0.3" + upper-case "^2.0.2" + upper-case-first "^2.0.2" + +change-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12" + integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A== + dependencies: + camel-case "^4.1.2" + capital-case "^1.0.4" + constant-case "^3.0.4" + dot-case "^3.0.4" + header-case "^2.0.4" + no-case "^3.0.4" + param-case "^3.0.4" + pascal-case "^3.1.2" + path-case "^3.0.4" + sentence-case "^3.0.4" + snake-case "^3.0.4" + tslib "^2.0.3" + character-entities-legacy@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" @@ -4369,7 +4356,7 @@ constant-case@3.0.3: tslib "^1.10.0" upper-case "^2.0.1" -constant-case@^3.0.3: +constant-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== @@ -4459,7 +4446,7 @@ core-js-pure@^3.0.0: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.8.1.tgz#23f84048f366fdfcf52d3fd1c68fec349177d119" integrity sha512-Se+LaxqXlVXGvmexKGPvnUIYC1jwXu1H6Pkyb3uBM5d8/NELMYCHs/4/roD7721NxrTLyv7e5nXd5/QLBO+10g== -core-js@^2.4.0, core-js@^2.4.1: +core-js@^2.4.0: version "2.6.12" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== @@ -5042,10 +5029,10 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -dependency-graph@0.9.0, dependency-graph@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.9.0.tgz#11aed7e203bc8b00f48356d92db27b265c445318" - integrity sha512-9YLIBURXj4DJMFALxXw9K3Y3rwb5Fk0X5/8ipCzaN84+gKxoHK43tVKRNakCQbiEx07E8Uwhuq21BpUagFhZ8w== +dependency-graph@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27" + integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg== des.js@^1.0.0: version "1.0.1" @@ -5407,13 +5394,6 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -encoding@^0.1.11: - version "0.1.13" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" - integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== - dependencies: - iconv-lite "^0.6.2" - end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -6117,20 +6097,6 @@ fbjs-css-vars@^1.0.0: resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== -fbjs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-1.0.0.tgz#52c215e0883a3c86af2a7a776ed51525ae8e0a5a" - integrity sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA== - dependencies: - core-js "^2.4.1" - fbjs-css-vars "^1.0.0" - isomorphic-fetch "^2.1.1" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.18" - fbjs@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.0.tgz#0907067fb3f57a78f45d95f1eacffcacd623c165" @@ -6711,7 +6677,7 @@ graphql-request@^3.3.0: extract-files "^9.0.0" form-data "^3.0.0" -graphql-tag@2.11.0, graphql-tag@^2.10.3, graphql-tag@^2.11.0: +graphql-tag@^2.10.3, graphql-tag@^2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.11.0.tgz#1deb53a01c46a7eb401d6cb59dec86fa1cccbffd" integrity sha512-VmsD5pJqWJnQZMUeRwrDhfgoyqcfwEkvtpANqcoUG8/tOLkwNgU9mzub/Mc78OJMhHjx7gfAMTxzdG43VGg3bA== @@ -6876,6 +6842,14 @@ he@^1.1.0, he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +header-case@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" + integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q== + dependencies: + capital-case "^1.0.4" + tslib "^2.0.3" + hex-color-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" @@ -7128,13 +7102,6 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" - integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - icss-utils@^4.0.0, icss-utils@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" @@ -7591,6 +7558,13 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== +is-lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-2.0.2.tgz#1c0884d3012c841556243483aa5d522f47396d2a" + integrity sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ== + dependencies: + tslib "^2.0.3" + is-negative-zero@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" @@ -7695,7 +7669,7 @@ is-root@2.1.0: resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== -is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -7731,6 +7705,13 @@ is-unc-path@^1.0.0: dependencies: unc-path-regex "^0.1.2" +is-upper-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-2.0.2.tgz#f1105ced1fe4de906a5f39553e7d3803fd804649" + integrity sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ== + dependencies: + tslib "^2.0.3" + is-whitespace-character@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" @@ -7790,14 +7771,6 @@ isobject@^4.0.0: resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== -isomorphic-fetch@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" - integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk= - dependencies: - node-fetch "^1.0.1" - whatwg-fetch ">=0.10.0" - isomorphic-fetch@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" @@ -8841,7 +8814,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.5, lodash@~4.17.15, lodash@~4.17.20: +"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.5, lodash@~4.17.20: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -8881,6 +8854,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3 dependencies: js-tokens "^3.0.0 || ^4.0.0" +lower-case-first@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-2.0.2.tgz#64c2324a2250bf7c37c5901e76a5b5309301160b" + integrity sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg== + dependencies: + tslib "^2.0.3" + lower-case@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7" @@ -8888,7 +8868,7 @@ lower-case@2.0.1: dependencies: tslib "^1.10.0" -lower-case@^2.0.1, lower-case@^2.0.2: +lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== @@ -9397,14 +9377,6 @@ node-fetch@2.6.1, node-fetch@^2.6.1: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== -node-fetch@^1.0.1: - version "1.7.3" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" - integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== - dependencies: - encoding "^0.1.11" - is-stream "^1.0.1" - node-forge@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" @@ -9919,7 +9891,7 @@ param-case@3.0.3: dot-case "^3.0.3" tslib "^1.10.0" -param-case@^3.0.3: +param-case@^3.0.3, param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== @@ -9969,7 +9941,7 @@ parse-entities@^2.0.0: is-decimal "^1.0.0" is-hexadecimal "^1.0.0" -parse-filepath@1.0.2, parse-filepath@^1.0.2: +parse-filepath@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= @@ -10044,6 +10016,14 @@ path-browserify@0.0.1: resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== +path-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f" + integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -11844,28 +11824,6 @@ relateurl@^0.2.7: resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= -relay-compiler@10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/relay-compiler/-/relay-compiler-10.0.0.tgz#04d50d8ec53e3f683bc379b756cf0542a76105af" - integrity sha512-EVBMcMCiP+waOPR2930cNCCsac1sNhfQayzS+bOEMz2Lls5Bx7grhaadkBZLTEdCHQ1kf7lrsmcMDqj9mxABFw== - dependencies: - "@babel/core" "^7.0.0" - "@babel/generator" "^7.5.0" - "@babel/parser" "^7.0.0" - "@babel/runtime" "^7.0.0" - "@babel/traverse" "^7.0.0" - "@babel/types" "^7.0.0" - babel-preset-fbjs "^3.3.0" - chalk "^4.0.0" - fb-watchman "^2.0.0" - fbjs "^1.0.0" - glob "^7.1.1" - immutable "~3.7.6" - nullthrows "^1.1.1" - relay-runtime "10.0.0" - signedsource "^1.0.0" - yargs "^15.3.1" - relay-compiler@10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/relay-compiler/-/relay-compiler-10.1.0.tgz#fb4672cdbe9b54869a3a79759edd8c2d91609cbe" @@ -11888,14 +11846,6 @@ relay-compiler@10.1.0: signedsource "^1.0.0" yargs "^15.3.1" -relay-runtime@10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-10.0.0.tgz#cfceb0f8453b39a385d63093f2dbf1702ddc02b3" - integrity sha512-QEpFwEjvGgWgQ0MPJyrZKggaCoGMKwxPQx7NwYl4FcMmxZcicc8wk6vI1iTxl0tsPKgW/YG8FgueQR+X7ZtZqw== - dependencies: - "@babel/runtime" "^7.0.0" - fbjs "^1.0.0" - relay-runtime@10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-10.1.0.tgz#4753bf36e95e8d862cef33608e3d98b4ed730d16" @@ -11987,7 +11937,7 @@ request-promise-native@^1.0.5: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.87.0, request@^2.88.0, request@^2.88.2: +request@^2.87.0, request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -12267,7 +12217,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -12411,6 +12361,15 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" +sentence-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f" + integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" + serialize-javascript@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" @@ -12616,6 +12575,14 @@ smooth-scroll-into-view-if-needed@^1.1.27: dependencies: scroll-into-view-if-needed "^2.2.26" +snake-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -12778,6 +12745,13 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +sponge-case@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sponge-case/-/sponge-case-1.0.1.tgz#260833b86453883d974f84854cdb63aecc5aef4c" + integrity sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA== + dependencies: + tslib "^2.0.3" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -13081,7 +13055,7 @@ style-loader@0.23.1: loader-utils "^1.1.0" schema-utils "^1.0.0" -styled-components@^5.0.1, styled-components@^5.1.0: +styled-components@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.2.1.tgz#6ed7fad2dc233825f64c719ffbdedd84ad79101a" integrity sha512-sBdgLWrCFTKtmZm/9x7jkIabjFNVzCUeKfoQsM6R3saImkUnjx0QYdLwJHBjY9ifEcmjDamJDVfknWm1yxZPxQ== @@ -13156,6 +13130,13 @@ svgo@^1.0.0, svgo@^1.2.2: unquote "~1.1.1" util.promisify "~1.0.0" +swap-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-2.0.2.tgz#671aedb3c9c137e2985ef51c51f9e98445bf70d9" + integrity sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw== + dependencies: + tslib "^2.0.3" + symbol-observable@^1.0.2, symbol-observable@^1.1.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -13298,6 +13279,13 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.3: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +title-case@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982" + integrity sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA== + dependencies: + tslib "^2.0.3" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -13462,11 +13450,16 @@ tslib@^1.10.0, tslib@^1.14.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2, tslib@^2.0.0, tslib@^2.0.3, tslib@~2.0.0, tslib@~2.0.1: +tslib@^2, tslib@^2.0.0, tslib@^2.0.3, tslib@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== +tslib@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" + integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== + tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" @@ -13704,6 +13697,13 @@ upath@^1.1.1: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== +upper-case-first@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" + integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== + dependencies: + tslib "^2.0.3" + upper-case@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.1.tgz#6214d05e235dc817822464ccbae85822b3d8665f" @@ -14092,7 +14092,7 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" -whatwg-fetch@>=0.10.0, whatwg-fetch@^3.0.0, whatwg-fetch@^3.4.1: +whatwg-fetch@^3.0.0, whatwg-fetch@^3.4.1: version "3.5.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz#605a2cd0a7146e5db141e29d1c62ab84c0c4c868" integrity sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A== diff --git a/internal/db/models.go b/internal/db/models.go index eb5ce0a..ea729c9 100644 --- a/internal/db/models.go +++ b/internal/db/models.go @@ -53,10 +53,11 @@ type PersonalProject struct { } type Project struct { - ProjectID uuid.UUID `json:"project_id"` - TeamID uuid.UUID `json:"team_id"` - CreatedAt time.Time `json:"created_at"` - Name string `json:"name"` + ProjectID uuid.UUID `json:"project_id"` + TeamID uuid.UUID `json:"team_id"` + CreatedAt time.Time `json:"created_at"` + Name string `json:"name"` + PublicOn sql.NullTime `json:"public_on"` } type ProjectLabel struct { diff --git a/internal/db/project.sql.go b/internal/db/project.sql.go index 1b9e839..05243ba 100644 --- a/internal/db/project.sql.go +++ b/internal/db/project.sql.go @@ -5,13 +5,14 @@ package db import ( "context" + "database/sql" "time" "github.com/google/uuid" ) const createPersonalProject = `-- name: CreatePersonalProject :one -INSERT INTO project(team_id, created_at, name) VALUES (null, $1, $2) RETURNING project_id, team_id, created_at, name +INSERT INTO project(team_id, created_at, name) VALUES (null, $1, $2) RETURNING project_id, team_id, created_at, name, public_on ` type CreatePersonalProjectParams struct { @@ -27,6 +28,7 @@ func (q *Queries) CreatePersonalProject(ctx context.Context, arg CreatePersonalP &i.TeamID, &i.CreatedAt, &i.Name, + &i.PublicOn, ) return i, err } @@ -78,7 +80,7 @@ func (q *Queries) CreateProjectMember(ctx context.Context, arg CreateProjectMemb } const createTeamProject = `-- name: CreateTeamProject :one -INSERT INTO project(team_id, created_at, name) VALUES ($1, $2, $3) RETURNING project_id, team_id, created_at, name +INSERT INTO project(team_id, created_at, name) VALUES ($1, $2, $3) RETURNING project_id, team_id, created_at, name, public_on ` type CreateTeamProjectParams struct { @@ -95,6 +97,7 @@ func (q *Queries) CreateTeamProject(ctx context.Context, arg CreateTeamProjectPa &i.TeamID, &i.CreatedAt, &i.Name, + &i.PublicOn, ) return i, err } @@ -132,7 +135,7 @@ func (q *Queries) DeleteProjectMember(ctx context.Context, arg DeleteProjectMemb } const getAllProjectsForTeam = `-- name: GetAllProjectsForTeam :many -SELECT project_id, team_id, created_at, name FROM project WHERE team_id = $1 +SELECT project_id, team_id, created_at, name, public_on FROM project WHERE team_id = $1 ` func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error) { @@ -149,6 +152,7 @@ func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ( &i.TeamID, &i.CreatedAt, &i.Name, + &i.PublicOn, ); err != nil { return nil, err } @@ -164,7 +168,7 @@ func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ( } const getAllTeamProjects = `-- name: GetAllTeamProjects :many -SELECT project_id, team_id, created_at, name FROM project WHERE team_id IS NOT null +SELECT project_id, team_id, created_at, name, public_on FROM project WHERE team_id IS NOT null ` func (q *Queries) GetAllTeamProjects(ctx context.Context) ([]Project, error) { @@ -181,6 +185,7 @@ func (q *Queries) GetAllTeamProjects(ctx context.Context) ([]Project, error) { &i.TeamID, &i.CreatedAt, &i.Name, + &i.PublicOn, ); err != nil { return nil, err } @@ -196,7 +201,7 @@ func (q *Queries) GetAllTeamProjects(ctx context.Context) ([]Project, error) { } const getAllVisibleProjectsForUserID = `-- name: GetAllVisibleProjectsForUserID :many -SELECT project.project_id, project.team_id, project.created_at, project.name FROM project LEFT JOIN +SELECT project.project_id, project.team_id, project.created_at, project.name, project.public_on FROM project LEFT JOIN project_member ON project_member.project_id = project.project_id WHERE project_member.user_id = $1 ` @@ -214,6 +219,7 @@ func (q *Queries) GetAllVisibleProjectsForUserID(ctx context.Context, userID uui &i.TeamID, &i.CreatedAt, &i.Name, + &i.PublicOn, ); err != nil { return nil, err } @@ -292,7 +298,7 @@ func (q *Queries) GetMemberProjectIDsForUserID(ctx context.Context, userID uuid. } const getPersonalProjectsForUserID = `-- name: GetPersonalProjectsForUserID :many -SELECT project.project_id, project.team_id, project.created_at, project.name FROM project +SELECT project.project_id, project.team_id, project.created_at, project.name, project.public_on FROM project LEFT JOIN personal_project ON personal_project.project_id = project.project_id WHERE personal_project.user_id = $1 ` @@ -311,6 +317,7 @@ func (q *Queries) GetPersonalProjectsForUserID(ctx context.Context, userID uuid. &i.TeamID, &i.CreatedAt, &i.Name, + &i.PublicOn, ); err != nil { return nil, err } @@ -326,7 +333,7 @@ func (q *Queries) GetPersonalProjectsForUserID(ctx context.Context, userID uuid. } const getProjectByID = `-- name: GetProjectByID :one -SELECT project_id, team_id, created_at, name FROM project WHERE project_id = $1 +SELECT project_id, team_id, created_at, name, public_on FROM project WHERE project_id = $1 ` func (q *Queries) GetProjectByID(ctx context.Context, projectID uuid.UUID) (Project, error) { @@ -337,6 +344,7 @@ func (q *Queries) GetProjectByID(ctx context.Context, projectID uuid.UUID) (Proj &i.TeamID, &i.CreatedAt, &i.Name, + &i.PublicOn, ) return i, err } @@ -426,6 +434,17 @@ func (q *Queries) GetProjectRolesForUserID(ctx context.Context, userID uuid.UUID return items, nil } +const getPublicOn = `-- name: GetPublicOn :one +SELECT public_on FROM project WHERE project_id = $1 +` + +func (q *Queries) GetPublicOn(ctx context.Context, projectID uuid.UUID) (sql.NullTime, error) { + row := q.db.QueryRowContext(ctx, getPublicOn, projectID) + var public_on sql.NullTime + err := row.Scan(&public_on) + return public_on, err +} + const getRoleForProjectMemberByUserID = `-- name: GetRoleForProjectMemberByUserID :one SELECT code, role.name FROM project_member INNER JOIN role ON role.code = project_member.role_code WHERE user_id = $1 AND project_id = $2 @@ -469,6 +488,28 @@ func (q *Queries) GetUserRolesForProject(ctx context.Context, arg GetUserRolesFo return i, err } +const setPublicOn = `-- name: SetPublicOn :one +UPDATE project SET public_on = $2 WHERE project_id = $1 RETURNING project_id, team_id, created_at, name, public_on +` + +type SetPublicOnParams struct { + ProjectID uuid.UUID `json:"project_id"` + PublicOn sql.NullTime `json:"public_on"` +} + +func (q *Queries) SetPublicOn(ctx context.Context, arg SetPublicOnParams) (Project, error) { + row := q.db.QueryRowContext(ctx, setPublicOn, arg.ProjectID, arg.PublicOn) + var i Project + err := row.Scan( + &i.ProjectID, + &i.TeamID, + &i.CreatedAt, + &i.Name, + &i.PublicOn, + ) + return i, err +} + const updateProjectMemberRole = `-- name: UpdateProjectMemberRole :one UPDATE project_member SET role_code = $3 WHERE project_id = $1 AND user_id = $2 RETURNING project_member_id, project_id, user_id, added_at, role_code @@ -494,7 +535,7 @@ func (q *Queries) UpdateProjectMemberRole(ctx context.Context, arg UpdateProject } const updateProjectNameByID = `-- name: UpdateProjectNameByID :one -UPDATE project SET name = $2 WHERE project_id = $1 RETURNING project_id, team_id, created_at, name +UPDATE project SET name = $2 WHERE project_id = $1 RETURNING project_id, team_id, created_at, name, public_on ` type UpdateProjectNameByIDParams struct { @@ -510,6 +551,7 @@ func (q *Queries) UpdateProjectNameByID(ctx context.Context, arg UpdateProjectNa &i.TeamID, &i.CreatedAt, &i.Name, + &i.PublicOn, ) return i, err } diff --git a/internal/db/querier.go b/internal/db/querier.go index 284e30b..a60e771 100644 --- a/internal/db/querier.go +++ b/internal/db/querier.go @@ -4,6 +4,7 @@ package db import ( "context" + "database/sql" "github.com/google/uuid" ) @@ -100,6 +101,7 @@ type Querier interface { GetProjectMembersForProjectID(ctx context.Context, projectID uuid.UUID) ([]ProjectMember, error) GetProjectRolesForUserID(ctx context.Context, userID uuid.UUID) ([]GetProjectRolesForUserIDRow, error) GetProjectsForInvitedMember(ctx context.Context, email string) ([]uuid.UUID, error) + GetPublicOn(ctx context.Context, projectID uuid.UUID) (sql.NullTime, error) GetRecentlyAssignedTaskForUserID(ctx context.Context, arg GetRecentlyAssignedTaskForUserIDParams) ([]Task, error) GetRoleForProjectMemberByUserID(ctx context.Context, arg GetRoleForProjectMemberByUserIDParams) (Role, error) GetRoleForTeamMember(ctx context.Context, arg GetRoleForTeamMemberParams) (Role, error) @@ -132,6 +134,7 @@ type Querier interface { HasAnyUser(ctx context.Context) (bool, error) SetFirstUserActive(ctx context.Context) (UserAccount, error) SetInactiveLastMoveForTaskID(ctx context.Context, taskID uuid.UUID) error + SetPublicOn(ctx context.Context, arg SetPublicOnParams) (Project, error) SetTaskChecklistItemComplete(ctx context.Context, arg SetTaskChecklistItemCompleteParams) (TaskChecklistItem, error) SetTaskComplete(ctx context.Context, arg SetTaskCompleteParams) (Task, error) SetTaskGroupName(ctx context.Context, arg SetTaskGroupNameParams) (TaskGroup, error) diff --git a/internal/db/query/project.sql b/internal/db/query/project.sql index 0f4f34e..4662088 100644 --- a/internal/db/query/project.sql +++ b/internal/db/query/project.sql @@ -77,3 +77,9 @@ SELECT p.team_id, COALESCE(tm.role_code, '') AS team_role, COALESCE(pm.role_code -- name: CreatePersonalProjectLink :one INSERT INTO personal_project (project_id, user_id) VALUES ($1, $2) RETURNING *; + +-- name: SetPublicOn :one +UPDATE project SET public_on = $2 WHERE project_id = $1 RETURNING *; + +-- name: GetPublicOn :one +SELECT public_on FROM project WHERE project_id = $1; diff --git a/internal/graph/generated.go b/internal/graph/generated.go index 1d6b081..b07dc5d 100644 --- a/internal/graph/generated.go +++ b/internal/graph/generated.go @@ -57,7 +57,8 @@ type ResolverRoot interface { } type DirectiveRoot struct { - HasRole func(ctx context.Context, obj interface{}, next graphql.Resolver, roles []RoleLevel, level ActionLevel, typeArg ObjectType) (res interface{}, err error) + HasRole func(ctx context.Context, obj interface{}, next graphql.Resolver, roles []RoleLevel, level ActionLevel, typeArg ObjectType) (res interface{}, err error) + RequiresUser func(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) } type ComplexityRoot struct { @@ -248,6 +249,7 @@ type ComplexityRoot struct { SetTaskChecklistItemComplete func(childComplexity int, input SetTaskChecklistItemComplete) int SetTaskComplete func(childComplexity int, input SetTaskComplete) int SortTaskGroup func(childComplexity int, input SortTaskGroup) int + ToggleProjectVisibility func(childComplexity int, input ToggleProjectVisibility) int ToggleTaskLabel func(childComplexity int, input ToggleTaskLabelInput) int UnassignTask func(childComplexity int, input *UnassignTaskInput) int UpdateProjectLabel func(childComplexity int, input UpdateProjectLabel) int @@ -327,6 +329,7 @@ type ComplexityRoot struct { Members func(childComplexity int) int Name func(childComplexity int) int Permission func(childComplexity int) int + PublicOn func(childComplexity int) int TaskGroups func(childComplexity int) int Team func(childComplexity int) int } @@ -476,6 +479,10 @@ type ComplexityRoot struct { TeamID func(childComplexity int) int } + ToggleProjectVisibilityPayload struct { + Project func(childComplexity int) int + } + ToggleTaskLabelPayload struct { Active func(childComplexity int) int Task func(childComplexity int) int @@ -547,6 +554,7 @@ type MutationResolver interface { CreateProject(ctx context.Context, input NewProject) (*db.Project, error) DeleteProject(ctx context.Context, input DeleteProject) (*DeleteProjectPayload, error) UpdateProjectName(ctx context.Context, input *UpdateProjectName) (*db.Project, error) + ToggleProjectVisibility(ctx context.Context, input ToggleProjectVisibility) (*ToggleProjectVisibilityPayload, error) CreateProjectLabel(ctx context.Context, input NewProjectLabel) (*db.ProjectLabel, error) DeleteProjectLabel(ctx context.Context, input DeleteProjectLabel) (*db.ProjectLabel, error) UpdateProjectLabel(ctx context.Context, input UpdateProjectLabel) (*db.ProjectLabel, error) @@ -619,6 +627,7 @@ type ProjectResolver interface { TaskGroups(ctx context.Context, obj *db.Project) ([]db.TaskGroup, error) Members(ctx context.Context, obj *db.Project) ([]Member, error) InvitedMembers(ctx context.Context, obj *db.Project) ([]InvitedMember, error) + PublicOn(ctx context.Context, obj *db.Project) (*time.Time, error) Permission(ctx context.Context, obj *db.Project) (*ProjectPermission, error) Labels(ctx context.Context, obj *db.Project) ([]db.ProjectLabel, error) } @@ -1624,6 +1633,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.SortTaskGroup(childComplexity, args["input"].(SortTaskGroup)), true + case "Mutation.toggleProjectVisibility": + if e.complexity.Mutation.ToggleProjectVisibility == nil { + break + } + + args, err := ec.field_Mutation_toggleProjectVisibility_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.ToggleProjectVisibility(childComplexity, args["input"].(ToggleProjectVisibility)), true + case "Mutation.toggleTaskLabel": if e.complexity.Mutation.ToggleTaskLabel == nil { break @@ -2098,6 +2119,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Project.Permission(childComplexity), true + case "Project.publicOn": + if e.complexity.Project.PublicOn == nil { + break + } + + return e.complexity.Project.PublicOn(childComplexity), true + case "Project.taskGroups": if e.complexity.Project.TaskGroups == nil { break @@ -2763,6 +2791,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.TeamRole.TeamID(childComplexity), true + case "ToggleProjectVisibilityPayload.project": + if e.complexity.ToggleProjectVisibilityPayload.Project == nil { + break + } + + return e.complexity.ToggleProjectVisibilityPayload.Project(childComplexity), true + case "ToggleTaskLabelPayload.active": if e.complexity.ToggleTaskLabelPayload.Active == nil { break @@ -3158,6 +3193,7 @@ type Project { taskGroups: [TaskGroup!]! members: [Member!]! invitedMembers: [InvitedMember!]! + publicOn: Time permission: ProjectPermission! labels: [ProjectLabel!]! } @@ -3294,6 +3330,7 @@ enum ObjectType { } directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION +directive @requiresUser on FIELD_DEFINITION type Query { organizations: [Organization!]! @@ -3301,7 +3338,7 @@ type Query { invitedUsers: [InvitedUserAccount!]! findUser(input: FindUser!): UserAccount! findProject(input: FindProject!): - Project! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) + Project! findTask(input: FindTask!): Task! projects(input: ProjectsFilter): [Project!]! findTeam(input: FindTeam!): Team! @@ -3309,7 +3346,7 @@ type Query { myTasks(input: MyTasks!): MyTasksPayload! labelColors: [LabelColor!]! taskGroups: [TaskGroup!]! - me: MePayload! + me: MePayload } @@ -3427,6 +3464,16 @@ extend type Mutation { DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) updateProjectName(input: UpdateProjectName): Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + toggleProjectVisibility(input: ToggleProjectVisibility!): ToggleProjectVisibilityPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) +} + +input ToggleProjectVisibility { + projectID: UUID! + isPublic: Boolean! +} + +type ToggleProjectVisibilityPayload { + project: Project! } input NewProject { @@ -4558,6 +4605,21 @@ func (ec *executionContext) field_Mutation_sortTaskGroup_args(ctx context.Contex return args, nil } +func (ec *executionContext) field_Mutation_toggleProjectVisibility_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 ToggleProjectVisibility + if tmp, ok := rawArgs["input"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) + arg0, err = ec.unmarshalNToggleProjectVisibility2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleProjectVisibility(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_toggleTaskLabel_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -7744,6 +7806,80 @@ func (ec *executionContext) _Mutation_updateProjectName(ctx context.Context, fie return ec.marshalNProject2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProject(ctx, field.Selections, res) } +func (ec *executionContext) _Mutation_toggleProjectVisibility(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + IsResolver: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_toggleProjectVisibility_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().ToggleProjectVisibility(rctx, args["input"].(ToggleProjectVisibility)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) + if err != nil { + return nil, err + } + level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") + if err != nil { + return nil, err + } + typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT") + if err != nil { + return nil, err + } + if ec.directives.HasRole == nil { + return nil, errors.New("directive hasRole is not implemented") + } + return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*ToggleProjectVisibilityPayload); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.ToggleProjectVisibilityPayload`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*ToggleProjectVisibilityPayload) + fc.Result = res + return ec.marshalNToggleProjectVisibilityPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleProjectVisibilityPayload(ctx, field.Selections, res) +} + func (ec *executionContext) _Mutation_createProjectLabel(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -12601,6 +12737,38 @@ func (ec *executionContext) _Project_invitedMembers(ctx context.Context, field g return ec.marshalNInvitedMember2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInvitedMemberᚄ(ctx, field.Selections, res) } +func (ec *executionContext) _Project_publicOn(ctx context.Context, field graphql.CollectedField, obj *db.Project) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Project", + Field: field, + Args: nil, + IsMethod: true, + IsResolver: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Project().PublicOn(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*time.Time) + fc.Result = res + return ec.marshalOTime2ᚖtimeᚐTime(ctx, field.Selections, res) +} + func (ec *executionContext) _Project_permission(ctx context.Context, field graphql.CollectedField, obj *db.Project) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -13224,40 +13392,8 @@ func (ec *executionContext) _Query_findProject(ctx context.Context, field graphq } fc.Args = args resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - directive0 := func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().FindProject(rctx, args["input"].(FindProject)) - } - directive1 := func(ctx context.Context) (interface{}, error) { - roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"}) - if err != nil { - return nil, err - } - level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT") - if err != nil { - return nil, err - } - typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT") - if err != nil { - return nil, err - } - if ec.directives.HasRole == nil { - return nil, errors.New("directive hasRole is not implemented") - } - return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg) - } - - tmp, err := directive1(rctx) - if err != nil { - return nil, graphql.ErrorOnPath(ctx, err) - } - if tmp == nil { - return nil, nil - } - if data, ok := tmp.(*db.Project); ok { - return data, nil - } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Project`, tmp) + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().FindProject(rctx, args["input"].(FindProject)) }) if err != nil { ec.Error(ctx, err) @@ -13572,14 +13708,11 @@ func (ec *executionContext) _Query_me(ctx context.Context, field graphql.Collect return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } res := resTmp.(*MePayload) fc.Result = res - return ec.marshalNMePayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMePayload(ctx, field.Selections, res) + return ec.marshalOMePayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMePayload(ctx, field.Selections, res) } func (ec *executionContext) _Query_notifications(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { @@ -15885,6 +16018,41 @@ func (ec *executionContext) _TeamRole_roleCode(ctx context.Context, field graphq return ec.marshalNRoleCode2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleCode(ctx, field.Selections, res) } +func (ec *executionContext) _ToggleProjectVisibilityPayload_project(ctx context.Context, field graphql.CollectedField, obj *ToggleProjectVisibilityPayload) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "ToggleProjectVisibilityPayload", + Field: field, + Args: nil, + IsMethod: false, + IsResolver: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Project, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*db.Project) + fc.Result = res + return ec.marshalNProject2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProject(ctx, field.Selections, res) +} + func (ec *executionContext) _ToggleTaskLabelPayload_active(ctx context.Context, field graphql.CollectedField, obj *ToggleTaskLabelPayload) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -19238,6 +19406,34 @@ func (ec *executionContext) unmarshalInputTaskPositionUpdate(ctx context.Context return it, nil } +func (ec *executionContext) unmarshalInputToggleProjectVisibility(ctx context.Context, obj interface{}) (ToggleProjectVisibility, error) { + var it ToggleProjectVisibility + var asMap = obj.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "projectID": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("projectID")) + it.ProjectID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v) + if err != nil { + return it, err + } + case "isPublic": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("isPublic")) + it.IsPublic, err = ec.unmarshalNBoolean2bool(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputToggleTaskLabelInput(ctx context.Context, obj interface{}) (ToggleTaskLabelInput, error) { var it ToggleTaskLabelInput var asMap = obj.(map[string]interface{}) @@ -20841,6 +21037,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { invalids++ } + case "toggleProjectVisibility": + out.Values[i] = ec._Mutation_toggleProjectVisibility(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } case "createProjectLabel": out.Values[i] = ec._Mutation_createProjectLabel(ctx, field) if out.Values[i] == graphql.Null { @@ -21541,6 +21742,17 @@ func (ec *executionContext) _Project(ctx context.Context, sel ast.SelectionSet, } return res }) + case "publicOn": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Project_publicOn(ctx, field, obj) + return res + }) case "permission": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -21939,9 +22151,6 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } }() res = ec._Query_me(ctx, field) - if res == graphql.Null { - atomic.AddUint32(&invalids, 1) - } return res }) case "notifications": @@ -22860,6 +23069,33 @@ func (ec *executionContext) _TeamRole(ctx context.Context, sel ast.SelectionSet, return out } +var toggleProjectVisibilityPayloadImplementors = []string{"ToggleProjectVisibilityPayload"} + +func (ec *executionContext) _ToggleProjectVisibilityPayload(ctx context.Context, sel ast.SelectionSet, obj *ToggleProjectVisibilityPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, toggleProjectVisibilityPayloadImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ToggleProjectVisibilityPayload") + case "project": + out.Values[i] = ec._ToggleProjectVisibilityPayload_project(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var toggleTaskLabelPayloadImplementors = []string{"ToggleTaskLabelPayload"} func (ec *executionContext) _ToggleTaskLabelPayload(ctx context.Context, sel ast.SelectionSet, obj *ToggleTaskLabelPayload) graphql.Marshaler { @@ -24186,20 +24422,6 @@ func (ec *executionContext) unmarshalNLogoutUser2githubᚗcomᚋjordanknottᚋta return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNMePayload2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMePayload(ctx context.Context, sel ast.SelectionSet, v MePayload) graphql.Marshaler { - return ec._MePayload(ctx, sel, &v) -} - -func (ec *executionContext) marshalNMePayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMePayload(ctx context.Context, sel ast.SelectionSet, v *MePayload) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - return ec._MePayload(ctx, sel, v) -} - func (ec *executionContext) marshalNMember2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMember(ctx context.Context, sel ast.SelectionSet, v Member) graphql.Marshaler { return ec._Member(ctx, sel, &v) } @@ -25468,6 +25690,25 @@ func (ec *executionContext) marshalNTime2ᚖtimeᚐTime(ctx context.Context, sel return res } +func (ec *executionContext) unmarshalNToggleProjectVisibility2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleProjectVisibility(ctx context.Context, v interface{}) (ToggleProjectVisibility, error) { + res, err := ec.unmarshalInputToggleProjectVisibility(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNToggleProjectVisibilityPayload2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleProjectVisibilityPayload(ctx context.Context, sel ast.SelectionSet, v ToggleProjectVisibilityPayload) graphql.Marshaler { + return ec._ToggleProjectVisibilityPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNToggleProjectVisibilityPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleProjectVisibilityPayload(ctx context.Context, sel ast.SelectionSet, v *ToggleProjectVisibilityPayload) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._ToggleProjectVisibilityPayload(ctx, sel, v) +} + func (ec *executionContext) unmarshalNToggleTaskLabelInput2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleTaskLabelInput(ctx context.Context, v interface{}) (ToggleTaskLabelInput, error) { res, err := ec.unmarshalInputToggleTaskLabelInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) @@ -26081,6 +26322,13 @@ func (ec *executionContext) unmarshalODeleteTaskComment2ᚖgithubᚗcomᚋjordan return &res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalOMePayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMePayload(ctx context.Context, sel ast.SelectionSet, v *MePayload) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._MePayload(ctx, sel, v) +} + func (ec *executionContext) marshalOProfileIcon2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐProfileIcon(ctx context.Context, sel ast.SelectionSet, v *ProfileIcon) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/internal/graph/graph.go b/internal/graph/graph.go index af00bc2..ee2a71c 100644 --- a/internal/graph/graph.go +++ b/internal/graph/graph.go @@ -46,6 +46,12 @@ func NewHandler(repo db.Repository, emailConfig utils.EmailConfig) http.Handler } */ + userID, ok := GetUserID(ctx) + if !ok { + return nil, errors.New("user must be logged in") + } + log.Info("has role") + var subjectID uuid.UUID in := graphql.GetFieldContext(ctx).Args["input"] val := reflect.ValueOf(in) // could be any underlying type @@ -78,7 +84,7 @@ func NewHandler(repo db.Repository, emailConfig utils.EmailConfig) http.Handler // TODO: add config setting to disable personal projects return next(ctx) } - subjectID, ok := subjectField.Interface().(uuid.UUID) + subjectID, ok = subjectField.Interface().(uuid.UUID) if !ok { logger.New(ctx).Error("error while casting subject UUID") return nil, errors.New("error while casting subject uuid") @@ -130,10 +136,6 @@ func NewHandler(repo db.Repository, emailConfig utils.EmailConfig) http.Handler }, } } else if level == ActionLevelTeam { - userID, ok := GetUserID(ctx) - if !ok { - return nil, errors.New("user id is missing") - } role, err := repo.GetTeamRoleForUserID(ctx, db.GetTeamRoleForUserIDParams{UserID: userID, TeamID: subjectID}) if err != nil { logger.New(ctx).WithError(err).Error("error while getting team roles for user ID") @@ -270,3 +272,23 @@ const ( TASK_CHECKLIST_ADDED int32 = 9 TASK_CHECKLIST_REMOVED int32 = 10 ) + +func NotAuthorized() error { + return &gqlerror.Error{ + Message: "Not authorized", + Extensions: map[string]interface{}{ + "code": "UNAUTHENTICATED", + }, + } +} + +func IsProjectPublic(ctx context.Context, repo db.Repository, projectID uuid.UUID) (bool, error) { + publicOn, err := repo.GetPublicOn(ctx, projectID) + if err != nil { + return false, err + } + if !publicOn.Valid { + return false, nil + } + return true, nil +} diff --git a/internal/graph/models_gen.go b/internal/graph/models_gen.go index c2ef349..5c2c98d 100644 --- a/internal/graph/models_gen.go +++ b/internal/graph/models_gen.go @@ -448,6 +448,15 @@ type TeamRole struct { RoleCode RoleCode `json:"roleCode"` } +type ToggleProjectVisibility struct { + ProjectID uuid.UUID `json:"projectID"` + IsPublic bool `json:"isPublic"` +} + +type ToggleProjectVisibilityPayload struct { + Project *db.Project `json:"project"` +} + type ToggleTaskLabelInput struct { TaskID uuid.UUID `json:"taskID"` ProjectLabelID uuid.UUID `json:"projectLabelID"` diff --git a/internal/graph/schema.graphqls b/internal/graph/schema.graphqls index 4ccf57c..2533a59 100644 --- a/internal/graph/schema.graphqls +++ b/internal/graph/schema.graphqls @@ -119,6 +119,7 @@ type Project { taskGroups: [TaskGroup!]! members: [Member!]! invitedMembers: [InvitedMember!]! + publicOn: Time permission: ProjectPermission! labels: [ProjectLabel!]! } @@ -255,6 +256,7 @@ enum ObjectType { } directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION +directive @requiresUser on FIELD_DEFINITION type Query { organizations: [Organization!]! @@ -262,7 +264,7 @@ type Query { invitedUsers: [InvitedUserAccount!]! findUser(input: FindUser!): UserAccount! findProject(input: FindProject!): - Project! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) + Project! findTask(input: FindTask!): Task! projects(input: ProjectsFilter): [Project!]! findTeam(input: FindTeam!): Team! @@ -270,7 +272,7 @@ type Query { myTasks(input: MyTasks!): MyTasksPayload! labelColors: [LabelColor!]! taskGroups: [TaskGroup!]! - me: MePayload! + me: MePayload } @@ -388,6 +390,16 @@ extend type Mutation { DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) updateProjectName(input: UpdateProjectName): Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + toggleProjectVisibility(input: ToggleProjectVisibility!): ToggleProjectVisibilityPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) +} + +input ToggleProjectVisibility { + projectID: UUID! + isPublic: Boolean! +} + +type ToggleProjectVisibilityPayload { + project: Project! } input NewProject { @@ -982,3 +994,4 @@ type DeleteUserAccountPayload { ok: Boolean! userAccount: UserAccount! } + diff --git a/internal/graph/schema.resolvers.go b/internal/graph/schema.resolvers.go index d3a8c8d..c230655 100644 --- a/internal/graph/schema.resolvers.go +++ b/internal/graph/schema.resolvers.go @@ -84,6 +84,15 @@ func (r *mutationResolver) UpdateProjectName(ctx context.Context, input *UpdateP return &project, nil } +func (r *mutationResolver) ToggleProjectVisibility(ctx context.Context, input ToggleProjectVisibility) (*ToggleProjectVisibilityPayload, error) { + if input.IsPublic { + project, err := r.Repository.SetPublicOn(ctx, db.SetPublicOnParams{ProjectID: input.ProjectID, PublicOn: sql.NullTime{Valid: true, Time: time.Now().UTC()}}) + return &ToggleProjectVisibilityPayload{Project: &project}, err + } + project, err := r.Repository.SetPublicOn(ctx, db.SetPublicOnParams{ProjectID: input.ProjectID, PublicOn: sql.NullTime{Valid: false, Time: time.Time{}}}) + return &ToggleProjectVisibilityPayload{Project: &project}, err +} + func (r *mutationResolver) CreateProjectLabel(ctx context.Context, input NewProjectLabel) (*db.ProjectLabel, error) { createdAt := time.Now().UTC() @@ -1206,6 +1215,13 @@ func (r *projectResolver) InvitedMembers(ctx context.Context, obj *db.Project) ( return invited, err } +func (r *projectResolver) PublicOn(ctx context.Context, obj *db.Project) (*time.Time, error) { + if obj.PublicOn.Valid { + return &obj.PublicOn.Time, nil + } + return nil, nil +} + func (r *projectResolver) Permission(ctx context.Context, obj *db.Project) (*ProjectPermission, error) { panic(fmt.Errorf("not implemented")) } @@ -1277,12 +1293,19 @@ func (r *queryResolver) FindUser(ctx context.Context, input FindUser) (*db.UserA func (r *queryResolver) FindProject(ctx context.Context, input FindProject) (*db.Project, error) { logger.New(ctx).Info("finding project user") + _, isLoggedIn := GetUser(ctx) + if !isLoggedIn { + isPublic, _ := IsProjectPublic(ctx, r.Repository, input.ProjectID) + if !isPublic { + return &db.Project{}, NotAuthorized() + } + } project, err := r.Repository.GetProjectByID(ctx, input.ProjectID) if err == sql.ErrNoRows { return &db.Project{}, &gqlerror.Error{ Message: "Project not found", Extensions: map[string]interface{}{ - "code": "11-404", + "code": "NOT_FOUND", }, } } @@ -1497,7 +1520,7 @@ func (r *queryResolver) TaskGroups(ctx context.Context) ([]db.TaskGroup, error) func (r *queryResolver) Me(ctx context.Context) (*MePayload, error) { userID, ok := GetUserID(ctx) if !ok { - return &MePayload{}, fmt.Errorf("internal server error") + return nil, nil } user, err := r.Repository.GetUserAccountByID(ctx, userID) if err == sql.ErrNoRows { diff --git a/internal/graph/schema/_models.gql b/internal/graph/schema/_models.gql index 7f9b009..e41931b 100644 --- a/internal/graph/schema/_models.gql +++ b/internal/graph/schema/_models.gql @@ -119,6 +119,7 @@ type Project { taskGroups: [TaskGroup!]! members: [Member!]! invitedMembers: [InvitedMember!]! + publicOn: Time permission: ProjectPermission! labels: [ProjectLabel!]! } diff --git a/internal/graph/schema/_root.gql b/internal/graph/schema/_root.gql index ab197ee..01fc379 100644 --- a/internal/graph/schema/_root.gql +++ b/internal/graph/schema/_root.gql @@ -25,6 +25,7 @@ enum ObjectType { } directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION +directive @requiresUser on FIELD_DEFINITION type Query { organizations: [Organization!]! @@ -32,7 +33,7 @@ type Query { invitedUsers: [InvitedUserAccount!]! findUser(input: FindUser!): UserAccount! findProject(input: FindProject!): - Project! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) + Project! findTask(input: FindTask!): Task! projects(input: ProjectsFilter): [Project!]! findTeam(input: FindTeam!): Team! @@ -40,7 +41,7 @@ type Query { myTasks(input: MyTasks!): MyTasksPayload! labelColors: [LabelColor!]! taskGroups: [TaskGroup!]! - me: MePayload! + me: MePayload } diff --git a/internal/graph/schema/project.gql b/internal/graph/schema/project.gql index 90afc43..9135fae 100644 --- a/internal/graph/schema/project.gql +++ b/internal/graph/schema/project.gql @@ -4,6 +4,16 @@ extend type Mutation { DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) updateProjectName(input: UpdateProjectName): Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) + toggleProjectVisibility(input: ToggleProjectVisibility!): ToggleProjectVisibilityPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) +} + +input ToggleProjectVisibility { + projectID: UUID! + isPublic: Boolean! +} + +type ToggleProjectVisibilityPayload { + project: Project! } input NewProject { diff --git a/internal/route/middleware.go b/internal/route/middleware.go index dcc86fa..d63033e 100644 --- a/internal/route/middleware.go +++ b/internal/route/middleware.go @@ -18,6 +18,7 @@ type AuthenticationMiddleware struct { // Middleware returns the middleware handler func (m *AuthenticationMiddleware) Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log.Info("middleware") requestID := uuid.New() foundToken := true tokenRaw := "" @@ -25,42 +26,26 @@ func (m *AuthenticationMiddleware) Middleware(next http.Handler) http.Handler { if err != nil { if err == http.ErrNoCookie { foundToken = false - } else { - log.WithError(err).Error("unknown error") - w.WriteHeader(http.StatusBadRequest) - return } } if !foundToken { token := r.Header.Get("Authorization") - if token == "" { - log.WithError(err).Error("no auth token found in cookie or authorization header") - w.WriteHeader(http.StatusBadRequest) - return + if token != "" { + tokenRaw = token } - tokenRaw = token } else { tokenRaw = c.Value } - authTokenID := uuid.MustParse(tokenRaw) - token, err := m.repo.GetAuthTokenByID(r.Context(), authTokenID) - - if err != nil { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(`{ - "data": {}, - "errors": [ - { - "extensions": { - "code": "UNAUTHENTICATED" - } - } - ] - }`)) - return + authTokenID, err := uuid.Parse(tokenRaw) + log.Info("checking if logged in") + ctx := r.Context() + if err == nil { + token, err := m.repo.GetAuthTokenByID(r.Context(), authTokenID) + if err == nil { + ctx = context.WithValue(ctx, utils.UserIDKey, token.UserID) + } } - ctx := context.WithValue(r.Context(), utils.UserIDKey, token.UserID) // ctx = context.WithValue(ctx, utils.RestrictedModeKey, accessClaims.Restricted) // ctx = context.WithValue(ctx, utils.OrgRoleKey, accessClaims.OrgRole) ctx = context.WithValue(ctx, utils.ReqIDKey, requestID) diff --git a/migrations/0065_add-public_on-to-project.up.sql b/migrations/0065_add-public_on-to-project.up.sql new file mode 100644 index 0000000..0337f52 --- /dev/null +++ b/migrations/0065_add-public_on-to-project.up.sql @@ -0,0 +1 @@ +ALTER TABLE project ADD COLUMN public_on timestamptz;