feat: redesign project sharing

This commit is contained in:
Jordan Knott 2020-09-29 16:01:52 -05:00
parent 36f25391b4
commit 737d2b640f
17 changed files with 708 additions and 530 deletions

View File

@ -3,32 +3,11 @@ import updateApolloCache from 'shared/utils/cache';
import { usePopup, Popup } from 'shared/components/PopupMenu'; import { usePopup, Popup } from 'shared/components/PopupMenu';
import produce from 'immer'; import produce from 'immer';
import { import {
useUpdateProjectMemberRoleMutation,
useCreateProjectMemberMutation,
useDeleteProjectMemberMutation,
useSetTaskCompleteMutation,
useToggleTaskLabelMutation,
useUpdateProjectNameMutation,
useFindProjectQuery,
useUpdateTaskGroupNameMutation,
useUpdateTaskNameMutation,
useUpdateProjectLabelMutation, useUpdateProjectLabelMutation,
useCreateTaskMutation,
useDeleteProjectLabelMutation, useDeleteProjectLabelMutation,
useDeleteTaskMutation,
useUpdateTaskLocationMutation,
useUpdateTaskGroupLocationMutation,
useCreateTaskGroupMutation,
useDeleteTaskGroupMutation,
useUpdateTaskDescriptionMutation,
useAssignTaskMutation,
DeleteTaskDocument,
FindProjectDocument, FindProjectDocument,
useCreateProjectLabelMutation, useCreateProjectLabelMutation,
useUnassignTaskMutation,
useUpdateTaskDueDateMutation,
FindProjectQuery, FindProjectQuery,
useUsersQuery,
} from 'shared/generated/graphql'; } from 'shared/generated/graphql';
import LabelManager from 'shared/components/PopupMenu/LabelManager'; import LabelManager from 'shared/components/PopupMenu/LabelManager';
import LabelEditor from 'shared/components/PopupMenu/LabelEditor'; import LabelEditor from 'shared/components/PopupMenu/LabelEditor';

View File

@ -16,7 +16,7 @@ import {
} from 'react-router-dom'; } from 'react-router-dom';
import { import {
useUpdateProjectMemberRoleMutation, useUpdateProjectMemberRoleMutation,
useCreateProjectMemberMutation, useInviteProjectMemberMutation,
useDeleteProjectMemberMutation, useDeleteProjectMemberMutation,
useToggleTaskLabelMutation, useToggleTaskLabelMutation,
useUpdateProjectNameMutation, useUpdateProjectNameMutation,
@ -38,10 +38,12 @@ import Input from 'shared/components/Input';
import Member from 'shared/components/Member'; import Member from 'shared/components/Member';
import EmptyBoard from 'shared/components/EmptyBoard'; import EmptyBoard from 'shared/components/EmptyBoard';
import NOOP from 'shared/utils/noop'; import NOOP from 'shared/utils/noop';
import { Lock } from 'shared/icons'; import { Lock, Cross } from 'shared/icons';
import Button from 'shared/components/Button'; import Button from 'shared/components/Button';
import { useApolloClient } from '@apollo/react-hooks'; import { useApolloClient } from '@apollo/react-hooks';
import TaskAssignee from 'shared/components/TaskAssignee';
import gql from 'graphql-tag'; import gql from 'graphql-tag';
import { colourStyles } from 'shared/components/Select';
import Board, { BoardLoading } from './Board'; import Board, { BoardLoading } from './Board';
import Details from './Details'; import Details from './Details';
import LabelManagerEditor from './LabelManagerEditor'; import LabelManagerEditor from './LabelManagerEditor';
@ -77,10 +79,14 @@ const MemberList = styled.div`
margin: 8px 0; margin: 8px 0;
`; `;
type InviteUserData = {
email?: string;
suerID?: string;
};
type UserManagementPopupProps = { type UserManagementPopupProps = {
users: Array<User>; users: Array<User>;
projectMembers: Array<TaskUser>; projectMembers: Array<TaskUser>;
onAddProjectMember: (userID: string) => void; onInviteProjectMember: (data: InviteUserData) => void;
}; };
const VisibiltyPrivateIcon = styled(Lock)` const VisibiltyPrivateIcon = styled(Lock)`
@ -133,40 +139,182 @@ const fetchMembers = async (client: any, options: MemberFilterOptions, input: st
query: gql` query: gql`
query { query {
searchMembers(input: {SearchFilter:"${input}"}) { searchMembers(input: {SearchFilter:"${input}"}) {
id
similarity similarity
username
fullName
confirmed confirmed
joined joined
user {
id
fullName
email
profileIcon {
url
initials
bgColor
}
}
} }
} }
`, `,
}); });
let results: any = []; let results: any = [];
const emails: Array<string> = [];
if (res.data && res.data.searchMembers) { if (res.data && res.data.searchMembers) {
results = [...res.data.searchMembers.map((m: any) => ({ label: m.fullName, value: m.id }))]; results = [
...res.data.searchMembers.map((m: any) => {
emails.push(m.user.email);
return {
label: m.user.fullName,
value: { id: m.id, type: 0, profileIcon: m.user.profileIcon },
};
}),
];
} }
if (RFC2822_EMAIL.test(input)) { if (RFC2822_EMAIL.test(input) && !emails.find(e => e === input)) {
results = [...results, { label: input, value: input }]; results = [
...results,
{
label: input,
value: {
id: input,
type: 1,
profileIcon: {
bgColor: '#ccc',
initials: input.charAt(0),
},
},
},
];
} }
return results; return results;
}; };
const UserManagementPopup: React.FC<UserManagementPopupProps> = ({ users, projectMembers, onAddProjectMember }) => { type UserOptionProps = {
innerProps: any;
isDisabled: boolean;
isFocused: boolean;
label: string;
data: any;
getValue: any;
};
const OptionWrapper = styled.div<{ isFocused: boolean }>`
cursor: pointer;
padding: 4px 8px;
${props => props.isFocused && `background: rgba(${props.theme.colors.primary});`}
display: flex;
align-items: center;
`;
const OptionContent = styled.div`
display: flex;
flex-direction: column;
margin-left: 12px;
`;
const UserOption: React.FC<UserOptionProps> = ({ isDisabled, isFocused, innerProps, label, data }) => {
return !isDisabled ? (
<OptionWrapper {...innerProps} isFocused={isFocused}>
<TaskAssignee
size={32}
member={{
id: '',
fullName: 'Jordan Knott',
profileIcon: data.value.profileIcon,
}}
/>
<OptionContent>{label}</OptionContent>
</OptionWrapper>
) : null;
};
const OptionValueWrapper = styled.div`
background: rgba(${props => props.theme.colors.bg.primary});
border-radius: 4px;
margin: 2px;
padding: 3px 6px 3px 4px;
display: flex;
align-items: center;
`;
const OptionValueLabel = styled.span`
font-size: 12px;
color: rgba(${props => props.theme.colors.text.secondary});
`;
const OptionValueRemove = styled.button`
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
background: none;
border: none;
outline: none;
padding: 0;
margin: 0;
margin-left: 4px;
`;
const OptionValue = ({ data, removeProps }: any) => {
return (
<OptionValueWrapper>
<OptionValueLabel>{data.label}</OptionValueLabel>
<OptionValueRemove {...removeProps}>
<Cross width={14} height={14} />
</OptionValueRemove>
</OptionValueWrapper>
);
};
const InviteButton = styled(Button)`
margin-top: 12px;
height: 32px;
padding: 4px 12px;
width: 100%;
justify-content: center;
`;
const InviteContainer = styled.div`
min-height: 300px;
display: flex;
flex-direction: column;
`;
const UserManagementPopup: React.FC<UserManagementPopupProps> = ({ users, projectMembers, onInviteProjectMember }) => {
const client = useApolloClient(); const client = useApolloClient();
const [invitedUsers, setInvitedUsers] = useState<Array<any> | null>(null);
return ( return (
<Popup tab={0} title="Invite a user"> <Popup tab={0} title="Invite a user">
<AsyncSelect isMulti cacheOptions defaultOption loadOptions={(i, cb) => fetchMembers(client, {}, i, cb)} /> <InviteContainer>
<ShareActions> <AsyncSelect
<VisibiltyButton> placeholder="Email address or username"
<VisibiltyPrivateIcon width={12} height={12} /> noOptionsMessage={() => null}
<VisibiltyButtonText>Private</VisibiltyButtonText> onChange={(e: any) => setInvitedUsers(e ? e.value : null)}
</VisibiltyButton> isMulti
</ShareActions> autoFocus
cacheOptions
styles={colourStyles}
defaultOption
components={{
MultiValue: OptionValue,
Option: UserOption,
IndicatorSeparator: null,
DropdownIndicator: null,
}}
loadOptions={(i, cb) => fetchMembers(client, {}, i, cb)}
/>
</InviteContainer>
<InviteButton
onClick={() => {
// FUCK, gotta rewrite invite member to be MULTIPLE. SHIT!
// onInviteProjectMember();
}}
disabled={invitedUsers === null}
hoverVariant="none"
fontSize="16px"
>
Send Invite
</InviteButton>
</Popup> </Popup>
); );
}; };
@ -250,14 +398,14 @@ const Project = () => {
}, },
}); });
const [createProjectMember] = useCreateProjectMemberMutation({ const [inviteProjectMember] = useInviteProjectMemberMutation({
update: (client, response) => { update: (client, response) => {
updateApolloCache<FindProjectQuery>( updateApolloCache<FindProjectQuery>(
client, client,
FindProjectDocument, FindProjectDocument,
cache => cache =>
produce(cache, draftCache => { produce(cache, draftCache => {
draftCache.findProject.members.push({ ...response.data.createProjectMember.member }); draftCache.findProject.members.push({ ...response.data.inviteProjectMember.member });
}), }),
{ projectID }, { projectID },
); );
@ -324,8 +472,8 @@ const Project = () => {
showPopup( showPopup(
$target, $target,
<UserManagementPopup <UserManagementPopup
onAddProjectMember={userID => { onInviteProjectMember={userID => {
createProjectMember({ variables: { userID, projectID } }); // /inviteProjectMember({ variables: { userID, projectID } });
}} }}
users={data.users} users={data.users}
projectMembers={data.findProject.members} projectMembers={data.findProject.members}

View File

@ -35,11 +35,15 @@ const Base = styled.button<{ color: string; disabled: boolean }>`
`} `}
`; `;
const Filled = styled(Base)` const Filled = styled(Base)<{ hoverVariant: HoverVariant }>`
background: rgba(${props => props.theme.colors[props.color]}); background: rgba(${props => props.theme.colors[props.color]});
${props =>
props.hoverVariant === 'boxShadow' &&
css`
&:hover { &:hover {
box-shadow: 0 8px 25px -8px rgba(${props => props.theme.colors[props.color]}); box-shadow: 0 8px 25px -8px rgba(${props.theme.colors[props.color]});
} }
`}
`; `;
const Outline = styled(Base)<{ invert: boolean }>` const Outline = styled(Base)<{ invert: boolean }>`
border: 1px solid rgba(${props => props.theme.colors[props.color]}); border: 1px solid rgba(${props => props.theme.colors[props.color]});
@ -123,9 +127,11 @@ const Relief = styled(Base)`
} }
`; `;
type HoverVariant = 'boxShadow' | 'none';
type ButtonProps = { type ButtonProps = {
fontSize?: string; fontSize?: string;
variant?: 'filled' | 'outline' | 'flat' | 'lineDown' | 'gradient' | 'relief'; variant?: 'filled' | 'outline' | 'flat' | 'lineDown' | 'gradient' | 'relief';
hoverVariant?: HoverVariant;
color?: 'primary' | 'danger' | 'success' | 'warning' | 'dark'; color?: 'primary' | 'danger' | 'success' | 'warning' | 'dark';
disabled?: boolean; disabled?: boolean;
type?: 'button' | 'submit'; type?: 'button' | 'submit';
@ -142,6 +148,7 @@ const Button: React.FC<ButtonProps> = ({
invert = false, invert = false,
color = 'primary', color = 'primary',
variant = 'filled', variant = 'filled',
hoverVariant = 'boxShadow',
type = 'button', type = 'button',
justifyTextContent = 'center', justifyTextContent = 'center',
icon, icon,
@ -158,7 +165,15 @@ const Button: React.FC<ButtonProps> = ({
switch (variant) { switch (variant) {
case 'filled': case 'filled':
return ( return (
<Filled ref={$button} type={type} onClick={handleClick} className={className} disabled={disabled} color={color}> <Filled
ref={$button}
hoverVariant={hoverVariant}
type={type}
onClick={handleClick}
className={className}
disabled={disabled}
color={color}
>
{icon && icon} {icon && icon}
<Text hasIcon={typeof icon !== 'undefined'} justifyTextContent={justifyTextContent} fontSize={fontSize}> <Text hasIcon={typeof icon !== 'undefined'} justifyTextContent={justifyTextContent} fontSize={fontSize}>
{children} {children}

View File

@ -16,7 +16,7 @@ function getBackgroundColor(isDisabled: boolean, isSelected: boolean, isFocused:
return null; return null;
} }
const colourStyles = { export const colourStyles = {
control: (styles: any, data: any) => { control: (styles: any, data: any) => {
return { return {
...styles, ...styles,

View File

@ -1,5 +1,5 @@
import React, { useRef } from 'react'; import React, { useRef } from 'react';
import styled from 'styled-components'; import styled, { css } from 'styled-components';
import { DoubleChevronUp, Crown } from 'shared/icons'; import { DoubleChevronUp, Crown } from 'shared/icons';
export const AdminIcon = styled(DoubleChevronUp)` export const AdminIcon = styled(DoubleChevronUp)`
@ -24,7 +24,12 @@ const TaskDetailAssignee = styled.div`
position: relative; position: relative;
`; `;
export const Wrapper = styled.div<{ size: number | string; bgColor: string | null; backgroundURL: string | null }>` export const Wrapper = styled.div<{
size: number | string;
bgColor: string | null;
backgroundURL: string | null;
hasClick: boolean;
}>`
width: ${props => props.size}px; width: ${props => props.size}px;
height: ${props => props.size}px; height: ${props => props.size}px;
border-radius: 9999px; border-radius: 9999px;
@ -37,33 +42,52 @@ export const Wrapper = styled.div<{ size: number | string; bgColor: string | nul
background-size: contain; background-size: contain;
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
${props =>
props.hasClick &&
css`
&:hover { &:hover {
opacity: 0.8; opacity: 0.8;
} }
`}
`; `;
type TaskAssigneeProps = { type TaskAssigneeProps = {
size: number | string; size: number | string;
showRoleIcons?: boolean; showRoleIcons?: boolean;
member: TaskUser; member: TaskUser;
onMemberProfile: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void; onMemberProfile?: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void;
className?: string; className?: string;
}; };
const TaskAssignee: React.FC<TaskAssigneeProps> = ({ showRoleIcons, member, onMemberProfile, size, className }) => { const TaskAssignee: React.FC<TaskAssigneeProps> = ({ showRoleIcons, member, onMemberProfile, size, className }) => {
const $memberRef = useRef<HTMLDivElement>(null); const $memberRef = useRef<HTMLDivElement>(null);
let profileIcon: ProfileIcon = {
url: null,
bgColor: null,
initials: null,
};
if (member.profileIcon) {
profileIcon = member.profileIcon;
}
return ( return (
<TaskDetailAssignee <TaskDetailAssignee
className={className} className={className}
ref={$memberRef} ref={$memberRef}
onClick={e => { onClick={e => {
e.stopPropagation(); e.stopPropagation();
if (onMemberProfile) {
onMemberProfile($memberRef, member.id); onMemberProfile($memberRef, member.id);
}
}} }}
key={member.id} key={member.id}
> >
<Wrapper backgroundURL={member.profileIcon.url ?? null} bgColor={member.profileIcon.bgColor ?? null} size={size}> <Wrapper
{(!member.profileIcon.url && member.profileIcon.initials) ?? ''} hasClick={typeof onMemberProfile !== undefined}
backgroundURL={profileIcon.url ?? null}
bgColor={profileIcon.bgColor ?? null}
size={size}
>
{(!profileIcon.url && profileIcon.initials) ?? ''}
</Wrapper> </Wrapper>
{showRoleIcons && member.role && member.role.code === 'admin' && <AdminIcon width={10} height={10} />} {showRoleIcons && member.role && member.role.code === 'admin' && <AdminIcon width={10} height={10} />}
{showRoleIcons && member.role && member.role.code === 'owner' && <OwnerIcon width={10} height={10} />} {showRoleIcons && member.role && member.role.code === 'owner' && <OwnerIcon width={10} height={10} />}

View File

@ -226,6 +226,7 @@ export type Query = {
notifications: Array<Notification>; notifications: Array<Notification>;
organizations: Array<Organization>; organizations: Array<Organization>;
projects: Array<Project>; projects: Array<Project>;
searchMembers: Array<MemberSearchResult>;
taskGroups: Array<TaskGroup>; taskGroups: Array<TaskGroup>;
teams: Array<Team>; teams: Array<Team>;
users: Array<UserAccount>; users: Array<UserAccount>;
@ -256,6 +257,11 @@ export type QueryProjectsArgs = {
input?: Maybe<ProjectsFilter>; input?: Maybe<ProjectsFilter>;
}; };
export type QuerySearchMembersArgs = {
input: MemberSearchFilter;
};
export type Mutation = { export type Mutation = {
__typename?: 'Mutation'; __typename?: 'Mutation';
addTaskLabel: Task; addTaskLabel: Task;
@ -263,7 +269,6 @@ export type Mutation = {
clearProfileAvatar: UserAccount; clearProfileAvatar: UserAccount;
createProject: Project; createProject: Project;
createProjectLabel: ProjectLabel; createProjectLabel: ProjectLabel;
createProjectMember: CreateProjectMemberPayload;
createRefreshToken: RefreshToken; createRefreshToken: RefreshToken;
createTask: Task; createTask: Task;
createTaskChecklist: TaskChecklist; createTaskChecklist: TaskChecklist;
@ -284,6 +289,7 @@ export type Mutation = {
deleteTeamMember: DeleteTeamMemberPayload; deleteTeamMember: DeleteTeamMemberPayload;
deleteUserAccount: DeleteUserAccountPayload; deleteUserAccount: DeleteUserAccountPayload;
duplicateTaskGroup: DuplicateTaskGroupPayload; duplicateTaskGroup: DuplicateTaskGroupPayload;
inviteProjectMember: InviteProjectMemberPayload;
logoutUser: Scalars['Boolean']; logoutUser: Scalars['Boolean'];
removeTaskLabel: Task; removeTaskLabel: Task;
setTaskChecklistItemComplete: TaskChecklistItem; setTaskChecklistItemComplete: TaskChecklistItem;
@ -333,11 +339,6 @@ export type MutationCreateProjectLabelArgs = {
}; };
export type MutationCreateProjectMemberArgs = {
input: CreateProjectMember;
};
export type MutationCreateRefreshTokenArgs = { export type MutationCreateRefreshTokenArgs = {
input: NewRefreshToken; input: NewRefreshToken;
}; };
@ -438,6 +439,11 @@ export type MutationDuplicateTaskGroupArgs = {
}; };
export type MutationInviteProjectMemberArgs = {
input: InviteProjectMember;
};
export type MutationLogoutUserArgs = { export type MutationLogoutUserArgs = {
input: LogoutUser; input: LogoutUser;
}; };
@ -688,13 +694,14 @@ export type UpdateProjectLabelColor = {
labelColorID: Scalars['UUID']; labelColorID: Scalars['UUID'];
}; };
export type CreateProjectMember = { export type InviteProjectMember = {
projectID: Scalars['UUID']; projectID: Scalars['UUID'];
userID: Scalars['UUID']; userID?: Maybe<Scalars['UUID']>;
email?: Maybe<Scalars['String']>;
}; };
export type CreateProjectMemberPayload = { export type InviteProjectMemberPayload = {
__typename?: 'CreateProjectMemberPayload'; __typename?: 'InviteProjectMemberPayload';
ok: Scalars['Boolean']; ok: Scalars['Boolean'];
member: Member; member: Member;
}; };
@ -989,6 +996,20 @@ export type UpdateTeamMemberRolePayload = {
member: Member; member: Member;
}; };
export type MemberSearchFilter = {
SearchFilter: Scalars['String'];
projectID?: Maybe<Scalars['UUID']>;
};
export type MemberSearchResult = {
__typename?: 'MemberSearchResult';
similarity: Scalars['Int'];
user: UserAccount;
confirmed: Scalars['Boolean'];
invited: Scalars['Boolean'];
joined: Scalars['Boolean'];
};
export type UpdateUserInfoPayload = { export type UpdateUserInfoPayload = {
__typename?: 'UpdateUserInfoPayload'; __typename?: 'UpdateUserInfoPayload';
user: UserAccount; user: UserAccount;
@ -1390,31 +1411,6 @@ export type MeQuery = (
) } ) }
); );
export type CreateProjectMemberMutationVariables = {
projectID: Scalars['UUID'];
userID: Scalars['UUID'];
};
export type CreateProjectMemberMutation = (
{ __typename?: 'Mutation' }
& { createProjectMember: (
{ __typename?: 'CreateProjectMemberPayload' }
& Pick<CreateProjectMemberPayload, 'ok'>
& { member: (
{ __typename?: 'Member' }
& Pick<Member, 'id' | 'fullName' | 'username'>
& { profileIcon: (
{ __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
), role: (
{ __typename?: 'Role' }
& Pick<Role, 'code' | 'name'>
) }
) }
) }
);
export type DeleteProjectMutationVariables = { export type DeleteProjectMutationVariables = {
projectID: Scalars['UUID']; projectID: Scalars['UUID'];
}; };
@ -1450,6 +1446,32 @@ export type DeleteProjectMemberMutation = (
) } ) }
); );
export type InviteProjectMemberMutationVariables = {
projectID: Scalars['UUID'];
userID?: Maybe<Scalars['UUID']>;
email?: Maybe<Scalars['String']>;
};
export type InviteProjectMemberMutation = (
{ __typename?: 'Mutation' }
& { inviteProjectMember: (
{ __typename?: 'InviteProjectMemberPayload' }
& Pick<InviteProjectMemberPayload, 'ok'>
& { member: (
{ __typename?: 'Member' }
& Pick<Member, 'id' | 'fullName' | 'username'>
& { profileIcon: (
{ __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
), role: (
{ __typename?: 'Role' }
& Pick<Role, 'code' | 'name'>
) }
) }
) }
);
export type UpdateProjectMemberRoleMutationVariables = { export type UpdateProjectMemberRoleMutationVariables = {
projectID: Scalars['UUID']; projectID: Scalars['UUID'];
userID: Scalars['UUID']; userID: Scalars['UUID'];
@ -2884,53 +2906,6 @@ export function useMeLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptio
export type MeQueryHookResult = ReturnType<typeof useMeQuery>; export type MeQueryHookResult = ReturnType<typeof useMeQuery>;
export type MeLazyQueryHookResult = ReturnType<typeof useMeLazyQuery>; export type MeLazyQueryHookResult = ReturnType<typeof useMeLazyQuery>;
export type MeQueryResult = ApolloReactCommon.QueryResult<MeQuery, MeQueryVariables>; export type MeQueryResult = ApolloReactCommon.QueryResult<MeQuery, MeQueryVariables>;
export const CreateProjectMemberDocument = gql`
mutation createProjectMember($projectID: UUID!, $userID: UUID!) {
createProjectMember(input: {projectID: $projectID, userID: $userID}) {
ok
member {
id
fullName
profileIcon {
url
initials
bgColor
}
username
role {
code
name
}
}
}
}
`;
export type CreateProjectMemberMutationFn = ApolloReactCommon.MutationFunction<CreateProjectMemberMutation, CreateProjectMemberMutationVariables>;
/**
* __useCreateProjectMemberMutation__
*
* To run a mutation, you first call `useCreateProjectMemberMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useCreateProjectMemberMutation` 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 [createProjectMemberMutation, { data, loading, error }] = useCreateProjectMemberMutation({
* variables: {
* projectID: // value for 'projectID'
* userID: // value for 'userID'
* },
* });
*/
export function useCreateProjectMemberMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<CreateProjectMemberMutation, CreateProjectMemberMutationVariables>) {
return ApolloReactHooks.useMutation<CreateProjectMemberMutation, CreateProjectMemberMutationVariables>(CreateProjectMemberDocument, baseOptions);
}
export type CreateProjectMemberMutationHookResult = ReturnType<typeof useCreateProjectMemberMutation>;
export type CreateProjectMemberMutationResult = ApolloReactCommon.MutationResult<CreateProjectMemberMutation>;
export type CreateProjectMemberMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateProjectMemberMutation, CreateProjectMemberMutationVariables>;
export const DeleteProjectDocument = gql` export const DeleteProjectDocument = gql`
mutation deleteProject($projectID: UUID!) { mutation deleteProject($projectID: UUID!) {
deleteProject(input: {projectID: $projectID}) { deleteProject(input: {projectID: $projectID}) {
@ -3003,6 +2978,54 @@ export function useDeleteProjectMemberMutation(baseOptions?: ApolloReactHooks.Mu
export type DeleteProjectMemberMutationHookResult = ReturnType<typeof useDeleteProjectMemberMutation>; export type DeleteProjectMemberMutationHookResult = ReturnType<typeof useDeleteProjectMemberMutation>;
export type DeleteProjectMemberMutationResult = ApolloReactCommon.MutationResult<DeleteProjectMemberMutation>; export type DeleteProjectMemberMutationResult = ApolloReactCommon.MutationResult<DeleteProjectMemberMutation>;
export type DeleteProjectMemberMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteProjectMemberMutation, DeleteProjectMemberMutationVariables>; export type DeleteProjectMemberMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteProjectMemberMutation, DeleteProjectMemberMutationVariables>;
export const InviteProjectMemberDocument = gql`
mutation inviteProjectMember($projectID: UUID!, $userID: UUID, $email: String) {
inviteProjectMember(input: {projectID: $projectID, userID: $userID, email: $email}) {
ok
member {
id
fullName
profileIcon {
url
initials
bgColor
}
username
role {
code
name
}
}
}
}
`;
export type InviteProjectMemberMutationFn = ApolloReactCommon.MutationFunction<InviteProjectMemberMutation, InviteProjectMemberMutationVariables>;
/**
* __useInviteProjectMemberMutation__
*
* To run a mutation, you first call `useInviteProjectMemberMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useInviteProjectMemberMutation` 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 [inviteProjectMemberMutation, { data, loading, error }] = useInviteProjectMemberMutation({
* variables: {
* projectID: // value for 'projectID'
* userID: // value for 'userID'
* email: // value for 'email'
* },
* });
*/
export function useInviteProjectMemberMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<InviteProjectMemberMutation, InviteProjectMemberMutationVariables>) {
return ApolloReactHooks.useMutation<InviteProjectMemberMutation, InviteProjectMemberMutationVariables>(InviteProjectMemberDocument, baseOptions);
}
export type InviteProjectMemberMutationHookResult = ReturnType<typeof useInviteProjectMemberMutation>;
export type InviteProjectMemberMutationResult = ApolloReactCommon.MutationResult<InviteProjectMemberMutation>;
export type InviteProjectMemberMutationOptions = ApolloReactCommon.BaseMutationOptions<InviteProjectMemberMutation, InviteProjectMemberMutationVariables>;
export const UpdateProjectMemberRoleDocument = gql` export const UpdateProjectMemberRoleDocument = gql`
mutation updateProjectMemberRole($projectID: UUID!, $userID: UUID!, $roleCode: RoleCode!) { mutation updateProjectMemberRole($projectID: UUID!, $userID: UUID!, $roleCode: RoleCode!) {
updateProjectMemberRole(input: {projectID: $projectID, userID: $userID, roleCode: $roleCode}) { updateProjectMemberRole(input: {projectID: $projectID, userID: $userID, roleCode: $roleCode}) {

View File

@ -1,25 +0,0 @@
import gql from 'graphql-tag';
export const CREATE_PROJECT_MEMBER_MUTATION = gql`
mutation createProjectMember($projectID: UUID!, $userID: UUID!) {
createProjectMember(input: { projectID: $projectID, userID: $userID }) {
ok
member {
id
fullName
profileIcon {
url
initials
bgColor
}
username
role {
code
name
}
}
}
}
`;
export default CREATE_PROJECT_MEMBER_MUTATION;

View File

@ -0,0 +1,25 @@
import gql from 'graphql-tag';
export const INVITE_PROJECT_MEMBER_MUTATION = gql`
mutation inviteProjectMember($projectID: UUID!, $userID: UUID, $email: String) {
inviteProjectMember(input: { projectID: $projectID, userID: $userID, email: $email }) {
ok
member {
id
fullName
profileIcon {
url
initials
bgColor
}
username
role {
code
name
}
}
}
}
`;
export default INVITE_PROJECT_MEMBER_MUTATION;

1
go.sum
View File

@ -109,6 +109,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSY
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369 h1:XNT/Zf5l++1Pyg08/HV04ppB0gKxAqtZQBRYiYrUuYk=
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=

View File

@ -16,7 +16,7 @@ UPDATE user_account SET profile_avatar_url = $2 WHERE user_id = $1
RETURNING *; RETURNING *;
-- name: GetMemberData :many -- name: GetMemberData :many
SELECT username, email, user_id FROM user_account; SELECT username, full_name, email, user_id FROM user_account;
-- name: UpdateUserAccountInfo :one -- name: UpdateUserAccountInfo :one
UPDATE user_account SET bio = $2, full_name = $3, initials = $4, email = $5 UPDATE user_account SET bio = $2, full_name = $3, initials = $4, email = $5

View File

@ -102,11 +102,12 @@ func (q *Queries) GetAllUserAccounts(ctx context.Context) ([]UserAccount, error)
} }
const getMemberData = `-- name: GetMemberData :many const getMemberData = `-- name: GetMemberData :many
SELECT username, email, user_id FROM user_account SELECT username, full_name, email, user_id FROM user_account
` `
type GetMemberDataRow struct { type GetMemberDataRow struct {
Username string `json:"username"` Username string `json:"username"`
FullName string `json:"full_name"`
Email string `json:"email"` Email string `json:"email"`
UserID uuid.UUID `json:"user_id"` UserID uuid.UUID `json:"user_id"`
} }
@ -120,7 +121,12 @@ func (q *Queries) GetMemberData(ctx context.Context) ([]GetMemberDataRow, error)
var items []GetMemberDataRow var items []GetMemberDataRow
for rows.Next() { for rows.Next() {
var i GetMemberDataRow var i GetMemberDataRow
if err := rows.Scan(&i.Username, &i.Email, &i.UserID); err != nil { if err := rows.Scan(
&i.Username,
&i.FullName,
&i.Email,
&i.UserID,
); err != nil {
return nil, err return nil, err
} }
items = append(items, i) items = append(items, i)

View File

@ -65,11 +65,6 @@ type ComplexityRoot struct {
Total func(childComplexity int) int Total func(childComplexity int) int
} }
CreateProjectMemberPayload struct {
Member func(childComplexity int) int
Ok func(childComplexity int) int
}
CreateTeamMemberPayload struct { CreateTeamMemberPayload struct {
Team func(childComplexity int) int Team func(childComplexity int) int
TeamMember func(childComplexity int) int TeamMember func(childComplexity int) int
@ -132,6 +127,11 @@ type ComplexityRoot struct {
TaskGroup func(childComplexity int) int TaskGroup func(childComplexity int) int
} }
InviteProjectMemberPayload struct {
Member func(childComplexity int) int
Ok func(childComplexity int) int
}
LabelColor struct { LabelColor struct {
ColorHex func(childComplexity int) int ColorHex func(childComplexity int) int
ID func(childComplexity int) int ID func(childComplexity int) int
@ -162,11 +162,10 @@ type ComplexityRoot struct {
MemberSearchResult struct { MemberSearchResult struct {
Confirmed func(childComplexity int) int Confirmed func(childComplexity int) int
FullName func(childComplexity int) int Invited func(childComplexity int) int
ID func(childComplexity int) int
Joined func(childComplexity int) int Joined func(childComplexity int) int
Similarity func(childComplexity int) int Similarity func(childComplexity int) int
Username func(childComplexity int) int User func(childComplexity int) int
} }
Mutation struct { Mutation struct {
@ -175,7 +174,6 @@ type ComplexityRoot struct {
ClearProfileAvatar func(childComplexity int) int ClearProfileAvatar func(childComplexity int) int
CreateProject func(childComplexity int, input NewProject) int CreateProject func(childComplexity int, input NewProject) int
CreateProjectLabel func(childComplexity int, input NewProjectLabel) int CreateProjectLabel func(childComplexity int, input NewProjectLabel) int
CreateProjectMember func(childComplexity int, input CreateProjectMember) int
CreateRefreshToken func(childComplexity int, input NewRefreshToken) int CreateRefreshToken func(childComplexity int, input NewRefreshToken) int
CreateTask func(childComplexity int, input NewTask) int CreateTask func(childComplexity int, input NewTask) int
CreateTaskChecklist func(childComplexity int, input CreateTaskChecklist) int CreateTaskChecklist func(childComplexity int, input CreateTaskChecklist) int
@ -196,6 +194,7 @@ type ComplexityRoot struct {
DeleteTeamMember func(childComplexity int, input DeleteTeamMember) int DeleteTeamMember func(childComplexity int, input DeleteTeamMember) int
DeleteUserAccount func(childComplexity int, input DeleteUserAccount) int DeleteUserAccount func(childComplexity int, input DeleteUserAccount) int
DuplicateTaskGroup func(childComplexity int, input DuplicateTaskGroup) int DuplicateTaskGroup func(childComplexity int, input DuplicateTaskGroup) int
InviteProjectMember func(childComplexity int, input InviteProjectMember) int
LogoutUser func(childComplexity int, input LogoutUser) int LogoutUser func(childComplexity int, input LogoutUser) int
RemoveTaskLabel func(childComplexity int, input *RemoveTaskLabelInput) int RemoveTaskLabel func(childComplexity int, input *RemoveTaskLabelInput) int
SetTaskChecklistItemComplete func(childComplexity int, input SetTaskChecklistItemComplete) int SetTaskChecklistItemComplete func(childComplexity int, input SetTaskChecklistItemComplete) int
@ -455,7 +454,7 @@ type MutationResolver interface {
UpdateProjectLabel(ctx context.Context, input UpdateProjectLabel) (*db.ProjectLabel, error) UpdateProjectLabel(ctx context.Context, input UpdateProjectLabel) (*db.ProjectLabel, error)
UpdateProjectLabelName(ctx context.Context, input UpdateProjectLabelName) (*db.ProjectLabel, error) UpdateProjectLabelName(ctx context.Context, input UpdateProjectLabelName) (*db.ProjectLabel, error)
UpdateProjectLabelColor(ctx context.Context, input UpdateProjectLabelColor) (*db.ProjectLabel, error) UpdateProjectLabelColor(ctx context.Context, input UpdateProjectLabelColor) (*db.ProjectLabel, error)
CreateProjectMember(ctx context.Context, input CreateProjectMember) (*CreateProjectMemberPayload, error) InviteProjectMember(ctx context.Context, input InviteProjectMember) (*InviteProjectMemberPayload, error)
DeleteProjectMember(ctx context.Context, input DeleteProjectMember) (*DeleteProjectMemberPayload, error) DeleteProjectMember(ctx context.Context, input DeleteProjectMember) (*DeleteProjectMemberPayload, error)
UpdateProjectMemberRole(ctx context.Context, input UpdateProjectMemberRole) (*UpdateProjectMemberRolePayload, error) UpdateProjectMemberRole(ctx context.Context, input UpdateProjectMemberRole) (*UpdateProjectMemberRolePayload, error)
CreateTask(ctx context.Context, input NewTask) (*db.Task, error) CreateTask(ctx context.Context, input NewTask) (*db.Task, error)
@ -620,20 +619,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.ChecklistBadge.Total(childComplexity), true return e.complexity.ChecklistBadge.Total(childComplexity), true
case "CreateProjectMemberPayload.member":
if e.complexity.CreateProjectMemberPayload.Member == nil {
break
}
return e.complexity.CreateProjectMemberPayload.Member(childComplexity), true
case "CreateProjectMemberPayload.ok":
if e.complexity.CreateProjectMemberPayload.Ok == nil {
break
}
return e.complexity.CreateProjectMemberPayload.Ok(childComplexity), true
case "CreateTeamMemberPayload.team": case "CreateTeamMemberPayload.team":
if e.complexity.CreateTeamMemberPayload.Team == nil { if e.complexity.CreateTeamMemberPayload.Team == nil {
break break
@ -816,6 +801,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.DuplicateTaskGroupPayload.TaskGroup(childComplexity), true return e.complexity.DuplicateTaskGroupPayload.TaskGroup(childComplexity), true
case "InviteProjectMemberPayload.member":
if e.complexity.InviteProjectMemberPayload.Member == nil {
break
}
return e.complexity.InviteProjectMemberPayload.Member(childComplexity), true
case "InviteProjectMemberPayload.ok":
if e.complexity.InviteProjectMemberPayload.Ok == nil {
break
}
return e.complexity.InviteProjectMemberPayload.Ok(childComplexity), true
case "LabelColor.colorHex": case "LabelColor.colorHex":
if e.complexity.LabelColor.ColorHex == nil { if e.complexity.LabelColor.ColorHex == nil {
break break
@ -935,19 +934,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.MemberSearchResult.Confirmed(childComplexity), true return e.complexity.MemberSearchResult.Confirmed(childComplexity), true
case "MemberSearchResult.fullName": case "MemberSearchResult.invited":
if e.complexity.MemberSearchResult.FullName == nil { if e.complexity.MemberSearchResult.Invited == nil {
break break
} }
return e.complexity.MemberSearchResult.FullName(childComplexity), true return e.complexity.MemberSearchResult.Invited(childComplexity), true
case "MemberSearchResult.id":
if e.complexity.MemberSearchResult.ID == nil {
break
}
return e.complexity.MemberSearchResult.ID(childComplexity), true
case "MemberSearchResult.joined": case "MemberSearchResult.joined":
if e.complexity.MemberSearchResult.Joined == nil { if e.complexity.MemberSearchResult.Joined == nil {
@ -963,12 +955,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.MemberSearchResult.Similarity(childComplexity), true return e.complexity.MemberSearchResult.Similarity(childComplexity), true
case "MemberSearchResult.username": case "MemberSearchResult.user":
if e.complexity.MemberSearchResult.Username == nil { if e.complexity.MemberSearchResult.User == nil {
break break
} }
return e.complexity.MemberSearchResult.Username(childComplexity), true return e.complexity.MemberSearchResult.User(childComplexity), true
case "Mutation.addTaskLabel": case "Mutation.addTaskLabel":
if e.complexity.Mutation.AddTaskLabel == nil { if e.complexity.Mutation.AddTaskLabel == nil {
@ -1025,18 +1017,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.CreateProjectLabel(childComplexity, args["input"].(NewProjectLabel)), true return e.complexity.Mutation.CreateProjectLabel(childComplexity, args["input"].(NewProjectLabel)), true
case "Mutation.createProjectMember":
if e.complexity.Mutation.CreateProjectMember == nil {
break
}
args, err := ec.field_Mutation_createProjectMember_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.CreateProjectMember(childComplexity, args["input"].(CreateProjectMember)), true
case "Mutation.createRefreshToken": case "Mutation.createRefreshToken":
if e.complexity.Mutation.CreateRefreshToken == nil { if e.complexity.Mutation.CreateRefreshToken == nil {
break break
@ -1277,6 +1257,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.DuplicateTaskGroup(childComplexity, args["input"].(DuplicateTaskGroup)), true return e.complexity.Mutation.DuplicateTaskGroup(childComplexity, args["input"].(DuplicateTaskGroup)), true
case "Mutation.inviteProjectMember":
if e.complexity.Mutation.InviteProjectMember == nil {
break
}
args, err := ec.field_Mutation_inviteProjectMember_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.InviteProjectMember(childComplexity, args["input"].(InviteProjectMember)), true
case "Mutation.logoutUser": case "Mutation.logoutUser":
if e.complexity.Mutation.LogoutUser == nil { if e.complexity.Mutation.LogoutUser == nil {
break break
@ -2877,20 +2869,22 @@ input UpdateProjectLabelColor {
} }
extend type Mutation { extend type Mutation {
createProjectMember(input: CreateProjectMember!): # TODO: rename to inviteProjectMember
CreateProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) inviteProjectMember(input: InviteProjectMember!):
InviteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
deleteProjectMember(input: DeleteProjectMember!): deleteProjectMember(input: DeleteProjectMember!):
DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
updateProjectMemberRole(input: UpdateProjectMemberRole!): updateProjectMemberRole(input: UpdateProjectMemberRole!):
UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
} }
input CreateProjectMember { input InviteProjectMember {
projectID: UUID! projectID: UUID!
userID: UUID! userID: UUID
email: String
} }
type CreateProjectMemberPayload { type InviteProjectMemberPayload {
ok: Boolean! ok: Boolean!
member: Member! member: Member!
} }
@ -3283,11 +3277,10 @@ input MemberSearchFilter {
} }
type MemberSearchResult { type MemberSearchResult {
id: UUID!
similarity: Int! similarity: Int!
username: String! user: UserAccount!
fullName: String!
confirmed: Boolean! confirmed: Boolean!
invited: Boolean!
joined: Boolean! joined: Boolean!
} }
@ -3428,20 +3421,6 @@ func (ec *executionContext) field_Mutation_createProjectLabel_args(ctx context.C
return args, nil return args, nil
} }
func (ec *executionContext) field_Mutation_createProjectMember_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 CreateProjectMember
if tmp, ok := rawArgs["input"]; ok {
arg0, err = ec.unmarshalNCreateProjectMember2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐCreateProjectMember(ctx, tmp)
if err != nil {
return nil, err
}
}
args["input"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation_createProject_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) field_Mutation_createProject_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error var err error
args := map[string]interface{}{} args := map[string]interface{}{}
@ -3736,6 +3715,20 @@ func (ec *executionContext) field_Mutation_duplicateTaskGroup_args(ctx context.C
return args, nil return args, nil
} }
func (ec *executionContext) field_Mutation_inviteProjectMember_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 InviteProjectMember
if tmp, ok := rawArgs["input"]; ok {
arg0, err = ec.unmarshalNInviteProjectMember2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMember(ctx, tmp)
if err != nil {
return nil, err
}
}
args["input"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation_logoutUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) field_Mutation_logoutUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error var err error
args := map[string]interface{}{} args := map[string]interface{}{}
@ -4302,74 +4295,6 @@ func (ec *executionContext) _ChecklistBadge_total(ctx context.Context, field gra
return ec.marshalNInt2int(ctx, field.Selections, res) return ec.marshalNInt2int(ctx, field.Selections, res)
} }
func (ec *executionContext) _CreateProjectMemberPayload_ok(ctx context.Context, field graphql.CollectedField, obj *CreateProjectMemberPayload) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "CreateProjectMemberPayload",
Field: field,
Args: nil,
IsMethod: 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.Ok, 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.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) _CreateProjectMemberPayload_member(ctx context.Context, field graphql.CollectedField, obj *CreateProjectMemberPayload) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "CreateProjectMemberPayload",
Field: field,
Args: nil,
IsMethod: 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.Member, 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.(*Member)
fc.Result = res
return ec.marshalNMember2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMember(ctx, field.Selections, res)
}
func (ec *executionContext) _CreateTeamMemberPayload_team(ctx context.Context, field graphql.CollectedField, obj *CreateTeamMemberPayload) (ret graphql.Marshaler) { func (ec *executionContext) _CreateTeamMemberPayload_team(ctx context.Context, field graphql.CollectedField, obj *CreateTeamMemberPayload) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -5254,6 +5179,74 @@ func (ec *executionContext) _DuplicateTaskGroupPayload_taskGroup(ctx context.Con
return ec.marshalNTaskGroup2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTaskGroup(ctx, field.Selections, res) return ec.marshalNTaskGroup2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐTaskGroup(ctx, field.Selections, res)
} }
func (ec *executionContext) _InviteProjectMemberPayload_ok(ctx context.Context, field graphql.CollectedField, obj *InviteProjectMemberPayload) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "InviteProjectMemberPayload",
Field: field,
Args: nil,
IsMethod: 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.Ok, 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.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) _InviteProjectMemberPayload_member(ctx context.Context, field graphql.CollectedField, obj *InviteProjectMemberPayload) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "InviteProjectMemberPayload",
Field: field,
Args: nil,
IsMethod: 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.Member, 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.(*Member)
fc.Result = res
return ec.marshalNMember2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMember(ctx, field.Selections, res)
}
func (ec *executionContext) _LabelColor_id(ctx context.Context, field graphql.CollectedField, obj *db.LabelColor) (ret graphql.Marshaler) { func (ec *executionContext) _LabelColor_id(ctx context.Context, field graphql.CollectedField, obj *db.LabelColor) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -5798,40 +5791,6 @@ func (ec *executionContext) _MemberList_projects(ctx context.Context, field grap
return ec.marshalNProject2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProjectᚄ(ctx, field.Selections, res) return ec.marshalNProject2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProjectᚄ(ctx, field.Selections, res)
} }
func (ec *executionContext) _MemberSearchResult_id(ctx context.Context, field graphql.CollectedField, obj *MemberSearchResult) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "MemberSearchResult",
Field: field,
Args: nil,
IsMethod: 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.ID, 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.(uuid.UUID)
fc.Result = res
return ec.marshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, field.Selections, res)
}
func (ec *executionContext) _MemberSearchResult_similarity(ctx context.Context, field graphql.CollectedField, obj *MemberSearchResult) (ret graphql.Marshaler) { func (ec *executionContext) _MemberSearchResult_similarity(ctx context.Context, field graphql.CollectedField, obj *MemberSearchResult) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -5866,7 +5825,7 @@ func (ec *executionContext) _MemberSearchResult_similarity(ctx context.Context,
return ec.marshalNInt2int(ctx, field.Selections, res) return ec.marshalNInt2int(ctx, field.Selections, res)
} }
func (ec *executionContext) _MemberSearchResult_username(ctx context.Context, field graphql.CollectedField, obj *MemberSearchResult) (ret graphql.Marshaler) { func (ec *executionContext) _MemberSearchResult_user(ctx context.Context, field graphql.CollectedField, obj *MemberSearchResult) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -5883,7 +5842,7 @@ func (ec *executionContext) _MemberSearchResult_username(ctx context.Context, fi
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.Username, nil return obj.User, nil
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -5895,43 +5854,9 @@ func (ec *executionContext) _MemberSearchResult_username(ctx context.Context, fi
} }
return graphql.Null return graphql.Null
} }
res := resTmp.(string) res := resTmp.(*db.UserAccount)
fc.Result = res fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res) return ec.marshalNUserAccount2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐUserAccount(ctx, field.Selections, res)
}
func (ec *executionContext) _MemberSearchResult_fullName(ctx context.Context, field graphql.CollectedField, obj *MemberSearchResult) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "MemberSearchResult",
Field: field,
Args: nil,
IsMethod: 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.FullName, 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.(string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
} }
func (ec *executionContext) _MemberSearchResult_confirmed(ctx context.Context, field graphql.CollectedField, obj *MemberSearchResult) (ret graphql.Marshaler) { func (ec *executionContext) _MemberSearchResult_confirmed(ctx context.Context, field graphql.CollectedField, obj *MemberSearchResult) (ret graphql.Marshaler) {
@ -5968,6 +5893,40 @@ func (ec *executionContext) _MemberSearchResult_confirmed(ctx context.Context, f
return ec.marshalNBoolean2bool(ctx, field.Selections, res) return ec.marshalNBoolean2bool(ctx, field.Selections, res)
} }
func (ec *executionContext) _MemberSearchResult_invited(ctx context.Context, field graphql.CollectedField, obj *MemberSearchResult) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "MemberSearchResult",
Field: field,
Args: nil,
IsMethod: 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.Invited, 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.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) _MemberSearchResult_joined(ctx context.Context, field graphql.CollectedField, obj *MemberSearchResult) (ret graphql.Marshaler) { func (ec *executionContext) _MemberSearchResult_joined(ctx context.Context, field graphql.CollectedField, obj *MemberSearchResult) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -6586,7 +6545,7 @@ func (ec *executionContext) _Mutation_updateProjectLabelColor(ctx context.Contex
return ec.marshalNProjectLabel2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProjectLabel(ctx, field.Selections, res) return ec.marshalNProjectLabel2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProjectLabel(ctx, field.Selections, res)
} }
func (ec *executionContext) _Mutation_createProjectMember(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Mutation_inviteProjectMember(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -6602,7 +6561,7 @@ func (ec *executionContext) _Mutation_createProjectMember(ctx context.Context, f
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
rawArgs := field.ArgumentMap(ec.Variables) rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Mutation_createProjectMember_args(ctx, rawArgs) args, err := ec.field_Mutation_inviteProjectMember_args(ctx, rawArgs)
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
return graphql.Null return graphql.Null
@ -6611,7 +6570,7 @@ func (ec *executionContext) _Mutation_createProjectMember(ctx context.Context, f
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) { directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return ec.resolvers.Mutation().CreateProjectMember(rctx, args["input"].(CreateProjectMember)) return ec.resolvers.Mutation().InviteProjectMember(rctx, args["input"].(InviteProjectMember))
} }
directive1 := func(ctx context.Context) (interface{}, error) { directive1 := func(ctx context.Context) (interface{}, error) {
roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"}) roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"})
@ -6639,10 +6598,10 @@ func (ec *executionContext) _Mutation_createProjectMember(ctx context.Context, f
if tmp == nil { if tmp == nil {
return nil, nil return nil, nil
} }
if data, ok := tmp.(*CreateProjectMemberPayload); ok { if data, ok := tmp.(*InviteProjectMemberPayload); ok {
return data, nil return data, nil
} }
return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.CreateProjectMemberPayload`, tmp) return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.InviteProjectMemberPayload`, tmp)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -6654,9 +6613,9 @@ func (ec *executionContext) _Mutation_createProjectMember(ctx context.Context, f
} }
return graphql.Null return graphql.Null
} }
res := resTmp.(*CreateProjectMemberPayload) res := resTmp.(*InviteProjectMemberPayload)
fc.Result = res fc.Result = res
return ec.marshalNCreateProjectMemberPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐCreateProjectMemberPayload(ctx, field.Selections, res) return ec.marshalNInviteProjectMemberPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMemberPayload(ctx, field.Selections, res)
} }
func (ec *executionContext) _Mutation_deleteProjectMember(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Mutation_deleteProjectMember(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
@ -15030,30 +14989,6 @@ func (ec *executionContext) unmarshalInputAssignTaskInput(ctx context.Context, o
return it, nil return it, nil
} }
func (ec *executionContext) unmarshalInputCreateProjectMember(ctx context.Context, obj interface{}) (CreateProjectMember, error) {
var it CreateProjectMember
var asMap = obj.(map[string]interface{})
for k, v := range asMap {
switch k {
case "projectID":
var err error
it.ProjectID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v)
if err != nil {
return it, err
}
case "userID":
var err error
it.UserID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v)
if err != nil {
return it, err
}
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputCreateTaskChecklist(ctx context.Context, obj interface{}) (CreateTaskChecklist, error) { func (ec *executionContext) unmarshalInputCreateTaskChecklist(ctx context.Context, obj interface{}) (CreateTaskChecklist, error) {
var it CreateTaskChecklist var it CreateTaskChecklist
var asMap = obj.(map[string]interface{}) var asMap = obj.(map[string]interface{})
@ -15468,6 +15403,36 @@ func (ec *executionContext) unmarshalInputFindUser(ctx context.Context, obj inte
return it, nil return it, nil
} }
func (ec *executionContext) unmarshalInputInviteProjectMember(ctx context.Context, obj interface{}) (InviteProjectMember, error) {
var it InviteProjectMember
var asMap = obj.(map[string]interface{})
for k, v := range asMap {
switch k {
case "projectID":
var err error
it.ProjectID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v)
if err != nil {
return it, err
}
case "userID":
var err error
it.UserID, err = ec.unmarshalOUUID2ᚖgithubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v)
if err != nil {
return it, err
}
case "email":
var err error
it.Email, err = ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputLogoutUser(ctx context.Context, obj interface{}) (LogoutUser, error) { func (ec *executionContext) unmarshalInputLogoutUser(ctx context.Context, obj interface{}) (LogoutUser, error) {
var it LogoutUser var it LogoutUser
var asMap = obj.(map[string]interface{}) var asMap = obj.(map[string]interface{})
@ -16438,38 +16403,6 @@ func (ec *executionContext) _ChecklistBadge(ctx context.Context, sel ast.Selecti
return out return out
} }
var createProjectMemberPayloadImplementors = []string{"CreateProjectMemberPayload"}
func (ec *executionContext) _CreateProjectMemberPayload(ctx context.Context, sel ast.SelectionSet, obj *CreateProjectMemberPayload) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, createProjectMemberPayloadImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("CreateProjectMemberPayload")
case "ok":
out.Values[i] = ec._CreateProjectMemberPayload_ok(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "member":
out.Values[i] = ec._CreateProjectMemberPayload_member(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 createTeamMemberPayloadImplementors = []string{"CreateTeamMemberPayload"} var createTeamMemberPayloadImplementors = []string{"CreateTeamMemberPayload"}
func (ec *executionContext) _CreateTeamMemberPayload(ctx context.Context, sel ast.SelectionSet, obj *CreateTeamMemberPayload) graphql.Marshaler { func (ec *executionContext) _CreateTeamMemberPayload(ctx context.Context, sel ast.SelectionSet, obj *CreateTeamMemberPayload) graphql.Marshaler {
@ -16864,6 +16797,38 @@ func (ec *executionContext) _DuplicateTaskGroupPayload(ctx context.Context, sel
return out return out
} }
var inviteProjectMemberPayloadImplementors = []string{"InviteProjectMemberPayload"}
func (ec *executionContext) _InviteProjectMemberPayload(ctx context.Context, sel ast.SelectionSet, obj *InviteProjectMemberPayload) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, inviteProjectMemberPayloadImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("InviteProjectMemberPayload")
case "ok":
out.Values[i] = ec._InviteProjectMemberPayload_ok(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "member":
out.Values[i] = ec._InviteProjectMemberPayload_member(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 labelColorImplementors = []string{"LabelColor"} var labelColorImplementors = []string{"LabelColor"}
func (ec *executionContext) _LabelColor(ctx context.Context, sel ast.SelectionSet, obj *db.LabelColor) graphql.Marshaler { func (ec *executionContext) _LabelColor(ctx context.Context, sel ast.SelectionSet, obj *db.LabelColor) graphql.Marshaler {
@ -17052,23 +17017,13 @@ func (ec *executionContext) _MemberSearchResult(ctx context.Context, sel ast.Sel
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("MemberSearchResult") out.Values[i] = graphql.MarshalString("MemberSearchResult")
case "id":
out.Values[i] = ec._MemberSearchResult_id(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "similarity": case "similarity":
out.Values[i] = ec._MemberSearchResult_similarity(ctx, field, obj) out.Values[i] = ec._MemberSearchResult_similarity(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "username": case "user":
out.Values[i] = ec._MemberSearchResult_username(ctx, field, obj) out.Values[i] = ec._MemberSearchResult_user(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "fullName":
out.Values[i] = ec._MemberSearchResult_fullName(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
@ -17077,6 +17032,11 @@ func (ec *executionContext) _MemberSearchResult(ctx context.Context, sel ast.Sel
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "invited":
out.Values[i] = ec._MemberSearchResult_invited(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "joined": case "joined":
out.Values[i] = ec._MemberSearchResult_joined(ctx, field, obj) out.Values[i] = ec._MemberSearchResult_joined(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
@ -17148,8 +17108,8 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "createProjectMember": case "inviteProjectMember":
out.Values[i] = ec._Mutation_createProjectMember(ctx, field) out.Values[i] = ec._Mutation_inviteProjectMember(ctx, field)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
@ -19437,24 +19397,6 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se
return res return res
} }
func (ec *executionContext) unmarshalNCreateProjectMember2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐCreateProjectMember(ctx context.Context, v interface{}) (CreateProjectMember, error) {
return ec.unmarshalInputCreateProjectMember(ctx, v)
}
func (ec *executionContext) marshalNCreateProjectMemberPayload2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐCreateProjectMemberPayload(ctx context.Context, sel ast.SelectionSet, v CreateProjectMemberPayload) graphql.Marshaler {
return ec._CreateProjectMemberPayload(ctx, sel, &v)
}
func (ec *executionContext) marshalNCreateProjectMemberPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐCreateProjectMemberPayload(ctx context.Context, sel ast.SelectionSet, v *CreateProjectMemberPayload) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
return ec._CreateProjectMemberPayload(ctx, sel, v)
}
func (ec *executionContext) unmarshalNCreateTaskChecklist2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐCreateTaskChecklist(ctx context.Context, v interface{}) (CreateTaskChecklist, error) { func (ec *executionContext) unmarshalNCreateTaskChecklist2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐCreateTaskChecklist(ctx context.Context, v interface{}) (CreateTaskChecklist, error) {
return ec.unmarshalInputCreateTaskChecklist(ctx, v) return ec.unmarshalInputCreateTaskChecklist(ctx, v)
} }
@ -19750,6 +19692,24 @@ func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.Selecti
return res return res
} }
func (ec *executionContext) unmarshalNInviteProjectMember2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMember(ctx context.Context, v interface{}) (InviteProjectMember, error) {
return ec.unmarshalInputInviteProjectMember(ctx, v)
}
func (ec *executionContext) marshalNInviteProjectMemberPayload2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMemberPayload(ctx context.Context, sel ast.SelectionSet, v InviteProjectMemberPayload) graphql.Marshaler {
return ec._InviteProjectMemberPayload(ctx, sel, &v)
}
func (ec *executionContext) marshalNInviteProjectMemberPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInviteProjectMemberPayload(ctx context.Context, sel ast.SelectionSet, v *InviteProjectMemberPayload) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
return ec._InviteProjectMemberPayload(ctx, sel, v)
}
func (ec *executionContext) marshalNLabelColor2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐLabelColor(ctx context.Context, sel ast.SelectionSet, v db.LabelColor) graphql.Marshaler { func (ec *executionContext) marshalNLabelColor2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐLabelColor(ctx context.Context, sel ast.SelectionSet, v db.LabelColor) graphql.Marshaler {
return ec._LabelColor(ctx, sel, &v) return ec._LabelColor(ctx, sel, &v)
} }

View File

@ -27,16 +27,6 @@ type ChecklistBadge struct {
Total int `json:"total"` Total int `json:"total"`
} }
type CreateProjectMember struct {
ProjectID uuid.UUID `json:"projectID"`
UserID uuid.UUID `json:"userID"`
}
type CreateProjectMemberPayload struct {
Ok bool `json:"ok"`
Member *Member `json:"member"`
}
type CreateTaskChecklist struct { type CreateTaskChecklist struct {
TaskID uuid.UUID `json:"taskID"` TaskID uuid.UUID `json:"taskID"`
Name string `json:"name"` Name string `json:"name"`
@ -187,6 +177,17 @@ type FindUser struct {
UserID uuid.UUID `json:"userID"` UserID uuid.UUID `json:"userID"`
} }
type InviteProjectMember struct {
ProjectID uuid.UUID `json:"projectID"`
UserID *uuid.UUID `json:"userID"`
Email *string `json:"email"`
}
type InviteProjectMemberPayload struct {
Ok bool `json:"ok"`
Member *Member `json:"member"`
}
type LogoutUser struct { type LogoutUser struct {
UserID uuid.UUID `json:"userID"` UserID uuid.UUID `json:"userID"`
} }
@ -218,11 +219,10 @@ type MemberSearchFilter struct {
} }
type MemberSearchResult struct { type MemberSearchResult struct {
ID uuid.UUID `json:"id"`
Similarity int `json:"similarity"` Similarity int `json:"similarity"`
Username string `json:"username"` User *db.UserAccount `json:"user"`
FullName string `json:"fullName"`
Confirmed bool `json:"confirmed"` Confirmed bool `json:"confirmed"`
Invited bool `json:"invited"`
Joined bool `json:"joined"` Joined bool `json:"joined"`
} }

View File

@ -338,20 +338,22 @@ input UpdateProjectLabelColor {
} }
extend type Mutation { extend type Mutation {
createProjectMember(input: CreateProjectMember!): # TODO: rename to inviteProjectMember
CreateProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) inviteProjectMember(input: InviteProjectMember!):
InviteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
deleteProjectMember(input: DeleteProjectMember!): deleteProjectMember(input: DeleteProjectMember!):
DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
updateProjectMemberRole(input: UpdateProjectMemberRole!): updateProjectMemberRole(input: UpdateProjectMemberRole!):
UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
} }
input CreateProjectMember { input InviteProjectMember {
projectID: UUID! projectID: UUID!
userID: UUID! userID: UUID
email: String
} }
type CreateProjectMemberPayload { type InviteProjectMemberPayload {
ok: Boolean! ok: Boolean!
member: Member! member: Member!
} }
@ -744,11 +746,10 @@ input MemberSearchFilter {
} }
type MemberSearchResult { type MemberSearchResult {
id: UUID!
similarity: Int! similarity: Int!
username: String! user: UserAccount!
fullName: String!
confirmed: Boolean! confirmed: Boolean!
invited: Boolean!
joined: Boolean! joined: Boolean!
} }

View File

@ -124,15 +124,32 @@ func (r *mutationResolver) UpdateProjectLabelColor(ctx context.Context, input Up
return &label, err return &label, err
} }
func (r *mutationResolver) CreateProjectMember(ctx context.Context, input CreateProjectMember) (*CreateProjectMemberPayload, error) { func (r *mutationResolver) InviteProjectMember(ctx context.Context, input InviteProjectMember) (*InviteProjectMemberPayload, error) {
addedAt := time.Now().UTC() if input.Email != nil && input.UserID != nil {
_, err := r.Repository.CreateProjectMember(ctx, db.CreateProjectMemberParams{ProjectID: input.ProjectID, UserID: input.UserID, AddedAt: addedAt, RoleCode: "member"}) return &InviteProjectMemberPayload{Ok: false}, &gqlerror.Error{
if err != nil { Message: "Both email and userID can not be used to invite a project member",
return &CreateProjectMemberPayload{Ok: false}, err Extensions: map[string]interface{}{
"code": "403",
},
} }
user, err := r.Repository.GetUserAccountByID(ctx, input.UserID) } else if input.Email == nil && input.UserID == nil {
return &InviteProjectMemberPayload{Ok: false}, &gqlerror.Error{
Message: "Either email or userID must be set to invite a project member",
Extensions: map[string]interface{}{
"code": "403",
},
}
}
if input.UserID != nil {
addedAt := time.Now().UTC()
_, err := r.Repository.CreateProjectMember(ctx, db.CreateProjectMemberParams{ProjectID: input.ProjectID, UserID: *input.UserID, AddedAt: addedAt, RoleCode: "member"})
if err != nil { if err != nil {
return &CreateProjectMemberPayload{Ok: false}, err return &InviteProjectMemberPayload{Ok: false}, err
}
user, err := r.Repository.GetUserAccountByID(ctx, *input.UserID)
if err != nil && err != sql.ErrNoRows {
return &InviteProjectMemberPayload{Ok: false}, err
} }
var url *string var url *string
if user.ProfileAvatarUrl.Valid { if user.ProfileAvatarUrl.Valid {
@ -140,17 +157,20 @@ func (r *mutationResolver) CreateProjectMember(ctx context.Context, input Create
} }
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor} profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: input.UserID, ProjectID: input.ProjectID}) role, err := r.Repository.GetRoleForProjectMemberByUserID(ctx, db.GetRoleForProjectMemberByUserIDParams{UserID: *input.UserID, ProjectID: input.ProjectID})
if err != nil { if err != nil {
return &CreateProjectMemberPayload{Ok: false}, err return &InviteProjectMemberPayload{Ok: false}, err
} }
return &CreateProjectMemberPayload{Ok: true, Member: &Member{ return &InviteProjectMemberPayload{Ok: true, Member: &Member{
ID: input.UserID, ID: *input.UserID,
FullName: user.FullName, FullName: user.FullName,
Username: user.Username, Username: user.Username,
ProfileIcon: profileIcon, ProfileIcon: profileIcon,
Role: &db.Role{Code: role.Code, Name: role.Name}, Role: &db.Role{Code: role.Code, Name: role.Name},
}}, nil }}, nil
}
// invite user
return &InviteProjectMemberPayload{Ok: false}, errors.New("not implemented")
} }
func (r *mutationResolver) DeleteProjectMember(ctx context.Context, input DeleteProjectMember) (*DeleteProjectMemberPayload, error) { func (r *mutationResolver) DeleteProjectMember(ctx context.Context, input DeleteProjectMember) (*DeleteProjectMemberPayload, error) {
@ -1222,7 +1242,7 @@ func (r *queryResolver) SearchMembers(ctx context.Context, input MemberSearchFil
} }
return []MemberSearchResult{}, err return []MemberSearchResult{}, err
} }
results = append(results, MemberSearchResult{FullName: user.FullName, Username: user.Username, Joined: false, Confirmed: false, Similarity: rank.Distance, ID: user.UserID}) results = append(results, MemberSearchResult{User: &user, Joined: false, Confirmed: false, Similarity: rank.Distance})
memberList[masterList[rank.Target]] = true memberList[masterList[rank.Target]] = true
} }
} }

View File

@ -1,18 +1,20 @@
extend type Mutation { extend type Mutation {
createProjectMember(input: CreateProjectMember!): # TODO: rename to inviteProjectMember
CreateProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) inviteProjectMember(input: InviteProjectMember!):
InviteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
deleteProjectMember(input: DeleteProjectMember!): deleteProjectMember(input: DeleteProjectMember!):
DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) DeleteProjectMemberPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
updateProjectMemberRole(input: UpdateProjectMemberRole!): updateProjectMemberRole(input: UpdateProjectMemberRole!):
UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) UpdateProjectMemberRolePayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
} }
input CreateProjectMember { input InviteProjectMember {
projectID: UUID! projectID: UUID!
userID: UUID! userID: UUID
email: String
} }
type CreateProjectMemberPayload { type InviteProjectMemberPayload {
ok: Boolean! ok: Boolean!
member: Member! member: Member!
} }

View File

@ -25,11 +25,10 @@ input MemberSearchFilter {
} }
type MemberSearchResult { type MemberSearchResult {
id: UUID!
similarity: Int! similarity: Int!
username: String! user: UserAccount!
fullName: String!
confirmed: Boolean! confirmed: Boolean!
invited: Boolean!
joined: Boolean! joined: Boolean!
} }