feature: add user project count to Admin component

This commit is contained in:
Jordan Knott 2020-07-17 19:40:05 -05:00
parent ccaa97e2bb
commit 68fa7aef94
23 changed files with 1140 additions and 140 deletions

View File

@ -12,7 +12,7 @@ import {
import Input from 'shared/components/Input';
import styled from 'styled-components';
import Button from 'shared/components/Button';
import { useForm } from 'react-hook-form';
import { useForm, Controller } from 'react-hook-form';
import { usePopup, Popup } from 'shared/components/PopupMenu';
import produce from 'immer';
import updateApolloCache from 'shared/utils/cache';
@ -45,14 +45,19 @@ const DeleteUserPopup: React.FC<DeleteUserPopupProps> = ({ onDeleteUser }) => {
</DeleteUserWrapper>
);
};
type RoleCodeOption = {
label: string;
value: string;
};
type CreateUserData = {
email: string;
username: string;
fullName: string;
initials: string;
password: string;
roleCode: string;
roleCode: RoleCodeOption;
};
const CreateUserForm = styled.form`
display: flex;
flex-direction: column;
@ -78,11 +83,11 @@ type AddUserPopupProps = {
};
const AddUserPopup: React.FC<AddUserPopupProps> = ({ onAddUser }) => {
const { register, handleSubmit, errors, setValue } = useForm<CreateUserData>();
const [role, setRole] = useState<string | null>(null);
register({ name: 'roleCode' }, { required: true });
const { register, handleSubmit, errors, setValue, control } = useForm<CreateUserData>();
console.log(errors);
const createUser = (data: CreateUserData) => {
console.log(data);
onAddUser(data);
};
return (
@ -106,19 +111,23 @@ const AddUserPopup: React.FC<AddUserPopupProps> = ({ onAddUser }) => {
variant="alternate"
ref={register({ required: 'Email is required' })}
/>
<Select
label="Role"
value={role}
options={[
{ label: 'Admin', value: 'admin' },
{ label: 'Member', value: 'member' },
]}
onChange={newRole => {
setRole(newRole);
setValue('roleCode', newRole.value);
}}
<Controller
control={control}
name="roleCode"
rules={{ required: 'Role is required' }}
render={({ onChange, onBlur, value }) => (
<Select
label="Role"
value={value}
onChange={onChange}
options={[
{ label: 'Admin', value: 'admin' },
{ label: 'Member', value: 'member' },
]}
/>
)}
/>
{errors.email && <InputError>{errors.email.message}</InputError>}
{errors.roleCode && errors.roleCode.value && <InputError>{errors.roleCode.value.message}</InputError>}
<AddUserInput
floatingLabel
width="100%"
@ -146,6 +155,7 @@ const AddUserPopup: React.FC<AddUserPopupProps> = ({ onAddUser }) => {
id="password"
name="password"
variant="alternate"
type="password"
ref={register({ required: 'Password is required' })}
/>
{errors.password && <InputError>{errors.password.message}</InputError>}
@ -196,7 +206,7 @@ const AdminRoute = () => {
<>
<GlobalTopNavbar projectID={null} onSaveProjectName={() => {}} name={null} />
<Admin
initialTab={1}
initialTab={0}
users={data.users}
onInviteUser={() => {}}
onUpdateUserPassword={(user, password) => {
@ -223,7 +233,8 @@ const AdminRoute = () => {
<Popup tab={0} title="Add member" onClose={() => hidePopup()}>
<AddUserPopup
onAddUser={user => {
createUser({ variables: { ...user } });
const { roleCode, ...userData } = user;
createUser({ variables: { ...userData, roleCode: roleCode.value } });
hidePopup();
}}
/>

View File

@ -87,11 +87,12 @@ const CreateChecklistPopup: React.FC<CreateChecklistPopupProps> = ({ onCreateChe
const createUser = (data: CreateChecklistData) => {
onCreateChecklist(data);
};
console.log(errors);
return (
<CreateChecklistForm onSubmit={handleSubmit(createUser)}>
<CreateChecklistInput
floatingLabel
autoFocus
autoSelect
defaultValue="Checklist"
width="100%"
label="Name"
@ -437,7 +438,7 @@ const Details: React.FC<DetailsProps> = ({
showPopup(
$target,
<Popup
title={'Add checklist'}
title="Add checklist"
tab={0}
onClose={() => {
hidePopup();

View File

@ -178,6 +178,8 @@ const Project = () => {
FindProjectDocument,
cache =>
produce(cache, draftCache => {
console.log(cache);
console.log(response);
draftCache.findProject.members = cache.findProject.members.filter(
m => m.id !== response.data.deleteProjectMember.member.id,
);

View File

@ -159,8 +159,8 @@ export const RemoveMemberButton = styled(Button)`
`;
type TeamRoleManagerPopupProps = {
currentUserID: string;
subject: TaskUser;
members: Array<TaskUser>;
subject: User;
members: Array<User>;
warning?: string | null;
canChangeRole: boolean;
onChangeRole: (roleCode: RoleCode) => void;

View File

@ -22,13 +22,19 @@ type Role = {
name: string;
};
type User = {
type UserProject = {
id: string;
fullName: string;
username: string;
email: string;
role: Role;
profileIcon: ProfileIcon;
name: string;
};
type UserTeam = {
id: string;
name: string;
};
type RelatedList = {
teams: Array<UserTeam>;
projects: Array<UserProject>;
};
type OwnedList = {
@ -42,7 +48,12 @@ type TaskUser = {
profileIcon: ProfileIcon;
username?: string;
role?: Role;
owned?: OwnedList | null;
};
type User = TaskUser & {
email?: string;
member: RelatedList;
owned: RelatedList;
};
type RefreshTokenResponse = {

View File

@ -1,18 +1,18 @@
import React, {useRef} from 'react';
import React, { useRef } from 'react';
import Admin from '.';
import {theme} from 'App/ThemeStyles';
import { theme } from 'App/ThemeStyles';
import NormalizeStyles from 'App/NormalizeStyles';
import BaseStyles from 'App/BaseStyles';
import {ThemeProvider} from 'styled-components';
import {action} from '@storybook/addon-actions';
import { ThemeProvider } from 'styled-components';
import { action } from '@storybook/addon-actions';
export default {
component: Admin,
title: 'Admin',
parameters: {
backgrounds: [
{name: 'gray', value: '#f8f8f8', default: true},
{name: 'white', value: '#ffffff'},
{ name: 'gray', value: '#f8f8f8', default: true },
{ name: 'white', value: '#ffffff' },
],
},
};
@ -33,13 +33,21 @@ export const Default = () => {
id: '1',
username: 'jordanthedev',
email: 'jordan@jordanthedev.com',
role: {code: 'admin', name: 'Admin'},
role: { code: 'admin', name: 'Admin' },
fullName: 'Jordan Knott',
profileIcon: {
bgColor: '#fff',
initials: 'JK',
url: null,
},
owned: {
teams: [{ id: '1', name: 'Team' }],
projects: [{ id: '2', name: 'Project' }],
},
member: {
teams: [],
projects: [],
},
},
]}
onAddUser={action('add user')}

View File

@ -509,7 +509,7 @@ const ListTable: React.FC<ListTableProps> = ({ users, onDeleteUser }) => {
rowSelection="multiple"
defaultColDef={data.defaultColDef}
columnDefs={data.columnDefs}
rowData={users.map(u => ({ ...u, roleName: u.role.name }))}
rowData={users.map(u => ({ ...u, roleName: 'member' }))}
frameworkComponents={data.frameworkComponents}
onFirstDataRendered={params => {
params.api.sizeColumnsToFit();
@ -717,46 +717,49 @@ const Admin: React.FC<AdminProps> = ({
</ListActions>
</MemberListHeader>
<MemberList>
{users.map(member => (
<MemberListItem>
<MemberProfile showRoleIcons size={32} onMemberProfile={() => {}} member={member} />
<MemberListItemDetails>
<MemberItemName>{member.fullName}</MemberItemName>
<MemberItemUsername>{`@${member.username}`}</MemberItemUsername>
</MemberListItemDetails>
<MemberItemOptions>
<MemberItemOption variant="flat">On 6 projects</MemberItemOption>
<MemberItemOption
variant="outline"
onClick={$target => {
showPopup(
$target,
<TeamRoleManagerPopup
user={member}
warning={member.role && member.role.code === 'owner' ? warning : null}
updateUserPassword={(user, password) => {
onUpdateUserPassword(user, password);
}}
canChangeRole={member.role && member.role.code !== 'owner'}
onChangeRole={roleCode => {
updateUserRole({ variables: { userID: member.id, roleCode } });
}}
onRemoveFromTeam={
member.role && member.role.code === 'owner'
? undefined
: () => {
hidePopup();
}
}
/>,
);
}}
>
Manage
</MemberItemOption>
</MemberItemOptions>
</MemberListItem>
))}
{users.map(member => {
const projectTotal = member.owned.projects.length + member.member.projects.length;
return (
<MemberListItem>
<MemberProfile showRoleIcons size={32} onMemberProfile={() => {}} member={member} />
<MemberListItemDetails>
<MemberItemName>{member.fullName}</MemberItemName>
<MemberItemUsername>{`@${member.username}`}</MemberItemUsername>
</MemberListItemDetails>
<MemberItemOptions>
<MemberItemOption variant="flat">{`On ${projectTotal} projects`}</MemberItemOption>
<MemberItemOption
variant="outline"
onClick={$target => {
showPopup(
$target,
<TeamRoleManagerPopup
user={member}
warning={member.role && member.role.code === 'owner' ? warning : null}
updateUserPassword={(user, password) => {
onUpdateUserPassword(user, password);
}}
canChangeRole={(member.role && member.role.code !== 'owner') ?? false}
onChangeRole={roleCode => {
updateUserRole({ variables: { userID: member.id, roleCode } });
}}
onRemoveFromTeam={
member.role && member.role.code === 'owner'
? undefined
: () => {
hidePopup();
}
}
/>,
);
}}
>
Manage
</MemberItemOption>
</MemberItemOptions>
</MemberListItem>
);
})}
</MemberList>
</MemberListWrapper>
</TabContent>

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import styled, { css } from 'styled-components/macro';
const InputWrapper = styled.div<{ width: string }>`
@ -85,6 +85,8 @@ type InputProps = {
icon?: JSX.Element;
type?: string;
autocomplete?: boolean;
autoFocus?: boolean;
autoSelect?: boolean;
id?: string;
name?: string;
className?: string;
@ -92,12 +94,32 @@ type InputProps = {
onClick?: (e: React.MouseEvent<HTMLInputElement>) => void;
};
function useCombinedRefs(...refs: any) {
const targetRef = React.useRef();
React.useEffect(() => {
refs.forEach((ref: any) => {
if (!ref) return;
if (typeof ref === 'function') {
ref(targetRef.current);
} else {
ref.current = targetRef.current;
}
});
}, [refs]);
return targetRef;
}
const Input = React.forwardRef(
(
{
width = 'auto',
variant = 'normal',
type = 'text',
autoFocus = false,
autoSelect = false,
autocomplete,
label,
placeholder,
@ -111,9 +133,25 @@ const Input = React.forwardRef(
}: InputProps,
$ref: any,
) => {
const [hasValue, setHasValue] = useState(false);
const [hasValue, setHasValue] = useState(defaultValue !== '');
const borderColor = variant === 'normal' ? 'rgba(0, 0, 0, 0.2)' : '#414561';
const focusBg = variant === 'normal' ? 'rgba(38, 44, 73, )' : 'rgba(16, 22, 58, 1)';
// Merge forwarded ref and internal ref in order to be able to access the ref in the useEffect
// The forwarded ref is not accessible by itself, which is what the innerRef & combined ref is for
// TODO(jordanknott): This is super ugly, find a better approach?
const $innerRef = React.useRef<HTMLInputElement>(null);
const combinedRef: any = useCombinedRefs($ref, $innerRef);
useEffect(() => {
if (combinedRef && combinedRef.current) {
if (autoFocus) {
combinedRef.current.focus();
}
if (autoSelect) {
combinedRef.current.select();
}
}
}, []);
return (
<InputWrapper className={className} width={width}>
<InputInput
@ -121,7 +159,7 @@ const Input = React.forwardRef(
setHasValue((e.currentTarget.value !== '' || floatingLabel) ?? false);
}}
hasValue={hasValue}
ref={$ref}
ref={combinedRef}
id={id}
type={type}
name={name}

View File

@ -67,7 +67,8 @@ export type Member = {
fullName: Scalars['String'];
username: Scalars['String'];
profileIcon: ProfileIcon;
owned?: Maybe<OwnersList>;
owned: OwnedList;
member: MemberList;
};
export type RefreshToken = {
@ -84,6 +85,18 @@ export type Role = {
name: Scalars['String'];
};
export type OwnedList = {
__typename?: 'OwnedList';
teams: Array<Team>;
projects: Array<Project>;
};
export type MemberList = {
__typename?: 'MemberList';
teams: Array<Team>;
projects: Array<Project>;
};
export type UserAccount = {
__typename?: 'UserAccount';
id: Scalars['ID'];
@ -94,6 +107,8 @@ export type UserAccount = {
role: Role;
username: Scalars['String'];
profileIcon: ProfileIcon;
owned: OwnedList;
member: MemberList;
};
export type Team = {
@ -1096,6 +1111,24 @@ export type FindProjectQuery = (
), profileIcon: (
{ __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
), owned: (
{ __typename?: 'OwnedList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
), member: (
{ __typename?: 'MemberList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
) }
)> }
);
@ -1611,12 +1644,27 @@ export type GetTeamQuery = (
& { role: (
{ __typename?: 'Role' }
& Pick<Role, 'code' | 'name'>
), owned?: Maybe<(
{ __typename?: 'OwnersList' }
& Pick<OwnersList, 'projects' | 'teams'>
)>, profileIcon: (
), profileIcon: (
{ __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
), owned: (
{ __typename?: 'OwnedList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
), member: (
{ __typename?: 'MemberList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
) }
)> }
), projects: Array<(
@ -1635,6 +1683,24 @@ export type GetTeamQuery = (
), profileIcon: (
{ __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
), owned: (
{ __typename?: 'OwnedList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
), member: (
{ __typename?: 'MemberList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
) }
)> }
);
@ -1876,6 +1942,24 @@ export type UsersQuery = (
), profileIcon: (
{ __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
), owned: (
{ __typename?: 'OwnedList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
), member: (
{ __typename?: 'MemberList' }
& { teams: Array<(
{ __typename?: 'Team' }
& Pick<Team, 'id' | 'name'>
)>, projects: Array<(
{ __typename?: 'Project' }
& Pick<Project, 'id' | 'name'>
)> }
) }
)> }
);
@ -2278,6 +2362,26 @@ export const FindProjectDocument = gql`
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}
${TaskFieldsFragmentDoc}`;
@ -3288,15 +3392,31 @@ export const GetTeamDocument = gql`
code
name
}
owned {
projects
teams
}
profileIcon {
url
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}
projects(input: {teamID: $teamID}) {
@ -3321,6 +3441,26 @@ export const GetTeamDocument = gql`
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}
`;
@ -3834,6 +3974,26 @@ export const UsersDocument = gql`
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}
`;

View File

@ -59,6 +59,26 @@ query findProject($projectId: String!) {
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
${TASK_FRAGMENT}
}

View File

@ -14,15 +14,31 @@ export const GET_TEAM_QUERY = gql`
code
name
}
owned {
projects
teams
}
profileIcon {
url
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}
projects(input: { teamID: $teamID }) {
@ -47,6 +63,26 @@ export const GET_TEAM_QUERY = gql`
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}
`;

View File

@ -13,6 +13,26 @@ query users {
initials
bgColor
}
owned {
teams {
id
name
}
projects {
id
name
}
}
member {
teams {
id
name
}
projects {
id
name
}
}
}
}

View File

@ -158,6 +158,66 @@ func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) (
return items, nil
}
const getMemberProjectIDsForUserID = `-- name: GetMemberProjectIDsForUserID :many
SELECT project_id FROM project_member WHERE user_id = $1
`
func (q *Queries) GetMemberProjectIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error) {
rows, err := q.db.QueryContext(ctx, getMemberProjectIDsForUserID, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []uuid.UUID
for rows.Next() {
var project_id uuid.UUID
if err := rows.Scan(&project_id); err != nil {
return nil, err
}
items = append(items, project_id)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getOwnedProjectsForUserID = `-- name: GetOwnedProjectsForUserID :many
SELECT project_id, team_id, created_at, name, owner FROM project WHERE owner = $1
`
func (q *Queries) GetOwnedProjectsForUserID(ctx context.Context, owner uuid.UUID) ([]Project, error) {
rows, err := q.db.QueryContext(ctx, getOwnedProjectsForUserID, owner)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Project
for rows.Next() {
var i Project
if err := rows.Scan(
&i.ProjectID,
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.Owner,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getOwnedTeamProjectsForUserID = `-- name: GetOwnedTeamProjectsForUserID :many
SELECT project_id FROM project WHERE owner = $1 AND team_id = $2
`

View File

@ -52,7 +52,11 @@ type Querier interface {
GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error)
GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error)
GetLabelColors(ctx context.Context) ([]LabelColor, error)
GetMemberProjectIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error)
GetMemberTeamIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error)
GetOwnedProjectsForUserID(ctx context.Context, owner uuid.UUID) ([]Project, error)
GetOwnedTeamProjectsForUserID(ctx context.Context, arg GetOwnedTeamProjectsForUserIDParams) ([]uuid.UUID, error)
GetOwnedTeamsForUserID(ctx context.Context, owner uuid.UUID) ([]Team, error)
GetProjectByID(ctx context.Context, projectID uuid.UUID) (Project, error)
GetProjectIDForTask(ctx context.Context, taskID uuid.UUID) (uuid.UUID, error)
GetProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) (ProjectLabel, error)

View File

@ -39,3 +39,9 @@ UPDATE project_member SET role_code = $3 WHERE project_id = $1 AND user_id = $2
-- name: GetOwnedTeamProjectsForUserID :many
SELECT project_id FROM project WHERE owner = $1 AND team_id = $2;
-- name: GetOwnedProjectsForUserID :many
SELECT * FROM project WHERE owner = $1;
-- name: GetMemberProjectIDsForUserID :many
SELECT project_id FROM project_member WHERE user_id = $1;

View File

@ -15,3 +15,9 @@ SELECT * FROM team WHERE organization_id = $1;
-- name: SetTeamOwner :one
UPDATE team SET owner = $2 WHERE team_id = $1 RETURNING *;
-- name: GetOwnedTeamsForUserID :many
SELECT * FROM team WHERE owner = $1;
-- name: GetMemberTeamIDsForUserID :many
SELECT team_id FROM team_member WHERE user_id = $1;

View File

@ -81,6 +81,66 @@ func (q *Queries) GetAllTeams(ctx context.Context) ([]Team, error) {
return items, nil
}
const getMemberTeamIDsForUserID = `-- name: GetMemberTeamIDsForUserID :many
SELECT team_id FROM team_member WHERE user_id = $1
`
func (q *Queries) GetMemberTeamIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error) {
rows, err := q.db.QueryContext(ctx, getMemberTeamIDsForUserID, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []uuid.UUID
for rows.Next() {
var team_id uuid.UUID
if err := rows.Scan(&team_id); err != nil {
return nil, err
}
items = append(items, team_id)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getOwnedTeamsForUserID = `-- name: GetOwnedTeamsForUserID :many
SELECT team_id, created_at, name, organization_id, owner FROM team WHERE owner = $1
`
func (q *Queries) GetOwnedTeamsForUserID(ctx context.Context, owner uuid.UUID) ([]Team, error) {
rows, err := q.db.QueryContext(ctx, getOwnedTeamsForUserID, owner)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Team
for rows.Next() {
var i Team
if err := rows.Scan(
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.OrganizationID,
&i.Owner,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTeamByID = `-- name: GetTeamByID :one
SELECT team_id, created_at, name, organization_id, owner FROM team WHERE team_id = $1
`

View File

@ -130,12 +130,18 @@ type ComplexityRoot struct {
Member struct {
FullName func(childComplexity int) int
ID func(childComplexity int) int
Member func(childComplexity int) int
Owned func(childComplexity int) int
ProfileIcon func(childComplexity int) int
Role func(childComplexity int) int
Username func(childComplexity int) int
}
MemberList struct {
Projects func(childComplexity int) int
Teams func(childComplexity int) int
}
Mutation struct {
AddTaskLabel func(childComplexity int, input *AddTaskLabelInput) int
AssignTask func(childComplexity int, input *AssignTaskInput) int
@ -194,6 +200,11 @@ type ComplexityRoot struct {
Name func(childComplexity int) int
}
OwnedList struct {
Projects func(childComplexity int) int
Teams func(childComplexity int) int
}
OwnersList struct {
Projects func(childComplexity int) int
Teams func(childComplexity int) int
@ -363,6 +374,8 @@ type ComplexityRoot struct {
FullName func(childComplexity int) int
ID func(childComplexity int) int
Initials func(childComplexity int) int
Member func(childComplexity int) int
Owned func(childComplexity int) int
ProfileIcon func(childComplexity int) int
Role func(childComplexity int) int
Username func(childComplexity int) int
@ -501,6 +514,8 @@ type UserAccountResolver interface {
Role(ctx context.Context, obj *db.UserAccount) (*db.Role, error)
ProfileIcon(ctx context.Context, obj *db.UserAccount) (*ProfileIcon, error)
Owned(ctx context.Context, obj *db.UserAccount) (*OwnedList, error)
Member(ctx context.Context, obj *db.UserAccount) (*MemberList, error)
}
type executableSchema struct {
@ -749,6 +764,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Member.ID(childComplexity), true
case "Member.member":
if e.complexity.Member.Member == nil {
break
}
return e.complexity.Member.Member(childComplexity), true
case "Member.owned":
if e.complexity.Member.Owned == nil {
break
@ -777,6 +799,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Member.Username(childComplexity), true
case "MemberList.projects":
if e.complexity.MemberList.Projects == nil {
break
}
return e.complexity.MemberList.Projects(childComplexity), true
case "MemberList.teams":
if e.complexity.MemberList.Teams == nil {
break
}
return e.complexity.MemberList.Teams(childComplexity), true
case "Mutation.addTaskLabel":
if e.complexity.Mutation.AddTaskLabel == nil {
break
@ -1386,6 +1422,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Organization.Name(childComplexity), true
case "OwnedList.projects":
if e.complexity.OwnedList.Projects == nil {
break
}
return e.complexity.OwnedList.Projects(childComplexity), true
case "OwnedList.teams":
if e.complexity.OwnedList.Teams == nil {
break
}
return e.complexity.OwnedList.Teams(childComplexity), true
case "OwnersList.projects":
if e.complexity.OwnersList.Projects == nil {
break
@ -2083,6 +2133,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.UserAccount.Initials(childComplexity), true
case "UserAccount.member":
if e.complexity.UserAccount.Member == nil {
break
}
return e.complexity.UserAccount.Member(childComplexity), true
case "UserAccount.owned":
if e.complexity.UserAccount.Owned == nil {
break
}
return e.complexity.UserAccount.Owned(childComplexity), true
case "UserAccount.profileIcon":
if e.complexity.UserAccount.ProfileIcon == nil {
break
@ -2216,7 +2280,8 @@ type Member {
fullName: String!
username: String!
profileIcon: ProfileIcon!
owned: OwnersList
owned: OwnedList!
member: MemberList!
}
type RefreshToken {
@ -2231,6 +2296,16 @@ type Role {
name: String!
}
type OwnedList {
teams: [Team!]!
projects: [Project!]!
}
type MemberList {
teams: [Team!]!
projects: [Project!]!
}
type UserAccount {
id: ID!
email: String!
@ -2240,6 +2315,8 @@ type UserAccount {
role: Role!
username: String!
profileIcon: ProfileIcon!
owned: OwnedList!
member: MemberList!
}
type Team {
@ -4851,11 +4928,116 @@ func (ec *executionContext) _Member_owned(ctx context.Context, field graphql.Col
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(*OwnersList)
res := resTmp.(*OwnedList)
fc.Result = res
return ec.marshalOOwnersList2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐOwnersList(ctx, field.Selections, res)
return ec.marshalNOwnedList2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐOwnedList(ctx, field.Selections, res)
}
func (ec *executionContext) _Member_member(ctx context.Context, field graphql.CollectedField, obj *Member) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Member",
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.(*MemberList)
fc.Result = res
return ec.marshalNMemberList2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐMemberList(ctx, field.Selections, res)
}
func (ec *executionContext) _MemberList_teams(ctx context.Context, field graphql.CollectedField, obj *MemberList) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "MemberList",
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.Teams, 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.Team)
fc.Result = res
return ec.marshalNTeam2ᚕgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋdbᚐTeamᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) _MemberList_projects(ctx context.Context, field graphql.CollectedField, obj *MemberList) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "MemberList",
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.Projects, 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ᚋprojectᚑcitadelᚋapiᚋinternalᚋdbᚐProjectᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation_createProject(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
@ -6969,6 +7151,74 @@ func (ec *executionContext) _Organization_name(ctx context.Context, field graphq
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _OwnedList_teams(ctx context.Context, field graphql.CollectedField, obj *OwnedList) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "OwnedList",
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.Teams, 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.Team)
fc.Result = res
return ec.marshalNTeam2ᚕgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋdbᚐTeamᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) _OwnedList_projects(ctx context.Context, field graphql.CollectedField, obj *OwnedList) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "OwnedList",
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.Projects, 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ᚋprojectᚑcitadelᚋapiᚋinternalᚋdbᚐProjectᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) _OwnersList_projects(ctx context.Context, field graphql.CollectedField, obj *OwnersList) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@ -10418,6 +10668,74 @@ func (ec *executionContext) _UserAccount_profileIcon(ctx context.Context, field
return ec.marshalNProfileIcon2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐProfileIcon(ctx, field.Selections, res)
}
func (ec *executionContext) _UserAccount_owned(ctx context.Context, field graphql.CollectedField, obj *db.UserAccount) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "UserAccount",
Field: field,
Args: nil,
IsMethod: 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.UserAccount().Owned(rctx, obj)
})
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.(*OwnedList)
fc.Result = res
return ec.marshalNOwnedList2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐOwnedList(ctx, field.Selections, res)
}
func (ec *executionContext) _UserAccount_member(ctx context.Context, field graphql.CollectedField, obj *db.UserAccount) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "UserAccount",
Field: field,
Args: nil,
IsMethod: 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.UserAccount().Member(rctx, obj)
})
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.(*MemberList)
fc.Result = res
return ec.marshalNMemberList2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐMemberList(ctx, field.Selections, res)
}
func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@ -13265,6 +13583,46 @@ func (ec *executionContext) _Member(ctx context.Context, sel ast.SelectionSet, o
}
case "owned":
out.Values[i] = ec._Member_owned(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "member":
out.Values[i] = ec._Member_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 memberListImplementors = []string{"MemberList"}
func (ec *executionContext) _MemberList(ctx context.Context, sel ast.SelectionSet, obj *MemberList) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, memberListImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("MemberList")
case "teams":
out.Values[i] = ec._MemberList_teams(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "projects":
out.Values[i] = ec._MemberList_projects(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
@ -13593,6 +13951,38 @@ func (ec *executionContext) _Organization(ctx context.Context, sel ast.Selection
return out
}
var ownedListImplementors = []string{"OwnedList"}
func (ec *executionContext) _OwnedList(ctx context.Context, sel ast.SelectionSet, obj *OwnedList) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, ownedListImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("OwnedList")
case "teams":
out.Values[i] = ec._OwnedList_teams(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "projects":
out.Values[i] = ec._OwnedList_projects(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 ownersListImplementors = []string{"OwnersList"}
func (ec *executionContext) _OwnersList(ctx context.Context, sel ast.SelectionSet, obj *OwnersList) graphql.Marshaler {
@ -15001,6 +15391,34 @@ func (ec *executionContext) _UserAccount(ctx context.Context, sel ast.SelectionS
}
return res
})
case "owned":
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._UserAccount_owned(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "member":
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._UserAccount_member(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
default:
panic("unknown field " + strconv.Quote(field.Name))
}
@ -15645,6 +16063,20 @@ func (ec *executionContext) marshalNMember2ᚖgithubᚗcomᚋjordanknottᚋproje
return ec._Member(ctx, sel, v)
}
func (ec *executionContext) marshalNMemberList2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐMemberList(ctx context.Context, sel ast.SelectionSet, v MemberList) graphql.Marshaler {
return ec._MemberList(ctx, sel, &v)
}
func (ec *executionContext) marshalNMemberList2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐMemberList(ctx context.Context, sel ast.SelectionSet, v *MemberList) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
return ec._MemberList(ctx, sel, v)
}
func (ec *executionContext) unmarshalNNewProject2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐNewProject(ctx context.Context, v interface{}) (NewProject, error) {
return ec.unmarshalInputNewProject(ctx, v)
}
@ -15722,6 +16154,20 @@ func (ec *executionContext) marshalNOrganization2ᚕgithubᚗcomᚋjordanknott
return ret
}
func (ec *executionContext) marshalNOwnedList2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐOwnedList(ctx context.Context, sel ast.SelectionSet, v OwnedList) graphql.Marshaler {
return ec._OwnedList(ctx, sel, &v)
}
func (ec *executionContext) marshalNOwnedList2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐOwnedList(ctx context.Context, sel ast.SelectionSet, v *OwnedList) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
return ec._OwnedList(ctx, sel, v)
}
func (ec *executionContext) marshalNProfileIcon2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐProfileIcon(ctx context.Context, sel ast.SelectionSet, v ProfileIcon) graphql.Marshaler {
return ec._ProfileIcon(ctx, sel, &v)
}
@ -16829,17 +17275,6 @@ func (ec *executionContext) marshalOChecklistBadge2ᚖgithubᚗcomᚋjordanknott
return ec._ChecklistBadge(ctx, sel, v)
}
func (ec *executionContext) marshalOOwnersList2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐOwnersList(ctx context.Context, sel ast.SelectionSet, v OwnersList) graphql.Marshaler {
return ec._OwnersList(ctx, sel, &v)
}
func (ec *executionContext) marshalOOwnersList2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐOwnersList(ctx context.Context, sel ast.SelectionSet, v *OwnersList) graphql.Marshaler {
if v == nil {
return graphql.Null
}
return ec._OwnersList(ctx, sel, v)
}
func (ec *executionContext) unmarshalOProjectsFilter2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋinternalᚋgraphᚐProjectsFilter(ctx context.Context, v interface{}) (ProjectsFilter, error) {
return ec.unmarshalInputProjectsFilter(ctx, v)
}

48
internal/graph/helpers.go Normal file
View File

@ -0,0 +1,48 @@
package graph
import (
"context"
"database/sql"
"github.com/jordanknott/project-citadel/api/internal/db"
)
func GetOwnedList(ctx context.Context, r db.Repository, user db.UserAccount) (*OwnedList, error) {
ownedTeams, err := r.GetOwnedTeamsForUserID(ctx, user.UserID)
if err != sql.ErrNoRows && err != nil {
return &OwnedList{}, err
}
ownedProjects, err := r.GetOwnedProjectsForUserID(ctx, user.UserID)
if err != sql.ErrNoRows && err != nil {
return &OwnedList{}, err
}
return &OwnedList{Teams: ownedTeams, Projects: ownedProjects}, nil
}
func GetMemberList(ctx context.Context, r db.Repository, user db.UserAccount) (*MemberList, error) {
projectMemberIDs, err := r.GetMemberProjectIDsForUserID(ctx, user.UserID)
if err != sql.ErrNoRows && err != nil {
return &MemberList{}, err
}
var projects []db.Project
for _, projectID := range projectMemberIDs {
project, err := r.GetProjectByID(ctx, projectID)
if err != nil {
return &MemberList{}, err
}
projects = append(projects, project)
}
teamMemberIDs, err := r.GetMemberTeamIDsForUserID(ctx, user.UserID)
if err != sql.ErrNoRows && err != nil {
return &MemberList{}, err
}
var teams []db.Team
for _, teamID := range teamMemberIDs {
team, err := r.GetTeamByID(ctx, teamID)
if err != nil {
return &MemberList{}, err
}
teams = append(teams, team)
}
return &MemberList{Teams: teams, Projects: projects}, nil
}

View File

@ -176,7 +176,13 @@ type Member struct {
FullName string `json:"fullName"`
Username string `json:"username"`
ProfileIcon *ProfileIcon `json:"profileIcon"`
Owned *OwnersList `json:"owned"`
Owned *OwnedList `json:"owned"`
Member *MemberList `json:"member"`
}
type MemberList struct {
Teams []db.Team `json:"teams"`
Projects []db.Project `json:"projects"`
}
type NewProject struct {
@ -232,6 +238,11 @@ type NewUserAccount struct {
RoleCode string `json:"roleCode"`
}
type OwnedList struct {
Teams []db.Team `json:"teams"`
Projects []db.Project `json:"projects"`
}
type OwnersList struct {
Projects []uuid.UUID `json:"projects"`
Teams []uuid.UUID `json:"teams"`

View File

@ -46,7 +46,8 @@ type Member {
fullName: String!
username: String!
profileIcon: ProfileIcon!
owned: OwnersList
owned: OwnedList!
member: MemberList!
}
type RefreshToken {
@ -61,6 +62,16 @@ type Role {
name: String!
}
type OwnedList {
teams: [Team!]!
projects: [Project!]!
}
type MemberList {
teams: [Team!]!
projects: [Project!]!
}
type UserAccount {
id: ID!
email: String!
@ -70,6 +81,8 @@ type UserAccount {
role: Role!
username: String!
profileIcon: ProfileIcon!
owned: OwnedList!
member: MemberList!
}
type Team {

View File

@ -113,16 +113,13 @@ func (r *mutationResolver) CreateProjectMember(ctx context.Context, input Create
return &CreateProjectMemberPayload{Ok: true, Member: &Member{
ID: input.UserID,
FullName: user.FullName,
Username: user.Username,
ProfileIcon: profileIcon,
Role: &db.Role{Code: role.Code, Name: role.Name},
}}, nil
}
func (r *mutationResolver) DeleteProjectMember(ctx context.Context, input DeleteProjectMember) (*DeleteProjectMemberPayload, error) {
err := r.Repository.DeleteProjectMember(ctx, db.DeleteProjectMemberParams{UserID: input.UserID, ProjectID: input.ProjectID})
if err != nil {
return &DeleteProjectMemberPayload{Ok: false}, err
}
user, err := r.Repository.GetUserAccountByID(ctx, input.UserID)
if err != nil {
return &DeleteProjectMemberPayload{Ok: false}, err
@ -136,6 +133,10 @@ func (r *mutationResolver) DeleteProjectMember(ctx context.Context, input Delete
if err != nil {
return &DeleteProjectMemberPayload{Ok: false}, err
}
err = r.Repository.DeleteProjectMember(ctx, db.DeleteProjectMemberParams{UserID: input.UserID, ProjectID: input.ProjectID})
if err != nil {
return &DeleteProjectMemberPayload{Ok: false}, err
}
return &DeleteProjectMemberPayload{Ok: true, Member: &Member{
ID: input.UserID,
FullName: user.FullName,
@ -1156,20 +1157,15 @@ func (r *teamResolver) Members(ctx context.Context, obj *db.Team) ([]Member, err
log.WithError(err).Error("get user account by ID")
return members, err
}
ownedProjects, err := r.Repository.GetOwnedTeamProjectsForUserID(ctx, db.GetOwnedTeamProjectsForUserIDParams{TeamID: obj.TeamID, Owner: user.UserID})
log.WithFields(log.Fields{"projects": ownedProjects}).Info("retrieved owned project list")
if err == sql.ErrNoRows {
ownedProjects = []uuid.UUID{}
} else if err != nil {
log.WithError(err).Error("get owned team projects for user id")
ownedList, err := GetOwnedList(ctx, r.Repository, user)
if err != nil {
return members, err
}
ownedTeams := []uuid.UUID{}
var ownerList *OwnersList
if len(ownedTeams) != 0 || len(ownedProjects) != 0 {
log.Info("owned list is not empty")
ownerList = &OwnersList{Projects: ownedProjects, Teams: ownedTeams}
memberList, err := GetMemberList(ctx, r.Repository, user)
if err != nil {
return members, err
}
var url *string
if user.ProfileAvatarUrl.Valid {
url = &user.ProfileAvatarUrl.String
@ -1177,7 +1173,7 @@ func (r *teamResolver) Members(ctx context.Context, obj *db.Team) ([]Member, err
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
members = append(members, Member{
ID: obj.Owner, FullName: user.FullName, ProfileIcon: profileIcon, Username: user.Username,
Owned: ownerList, Role: &db.Role{Code: "owner", Name: "Owner"},
Owned: ownedList, Member: memberList, Role: &db.Role{Code: "owner", Name: "Owner"},
})
teamMembers, err := r.Repository.GetTeamMembersForTeamID(ctx, obj.TeamID)
if err != nil {
@ -1200,24 +1196,19 @@ func (r *teamResolver) Members(ctx context.Context, obj *db.Team) ([]Member, err
log.WithError(err).Error("get role for projet member by user ID")
return members, err
}
ownedProjects, err := r.Repository.GetOwnedTeamProjectsForUserID(ctx, db.GetOwnedTeamProjectsForUserIDParams{TeamID: obj.TeamID, Owner: user.UserID})
log.WithFields(log.Fields{"projects": ownedProjects}).Info("retrieved owned project list")
if err == sql.ErrNoRows {
ownedProjects = []uuid.UUID{}
} else if err != nil {
log.WithError(err).Error("get owned team projects for user id")
ownedList, err := GetOwnedList(ctx, r.Repository, user)
if err != nil {
return members, err
}
ownedTeams := []uuid.UUID{}
var ownerList *OwnersList
if len(ownedTeams) != 0 || len(ownedProjects) != 0 {
log.Info("owned list is not empty")
ownerList = &OwnersList{Projects: ownedProjects, Teams: ownedTeams}
memberList, err := GetMemberList(ctx, r.Repository, user)
if err != nil {
return members, err
}
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
members = append(members, Member{ID: user.UserID, FullName: user.FullName, ProfileIcon: profileIcon,
Username: user.Username, Owned: ownerList, Role: &db.Role{Code: role.Code, Name: role.Name},
Username: user.Username, Owned: ownedList, Member: memberList, Role: &db.Role{Code: role.Code, Name: role.Name},
})
}
return members, nil
@ -1246,6 +1237,47 @@ func (r *userAccountResolver) ProfileIcon(ctx context.Context, obj *db.UserAccou
return profileIcon, nil
}
func (r *userAccountResolver) Owned(ctx context.Context, obj *db.UserAccount) (*OwnedList, error) {
ownedTeams, err := r.Repository.GetOwnedTeamsForUserID(ctx, obj.UserID)
if err != sql.ErrNoRows && err != nil {
return &OwnedList{}, err
}
ownedProjects, err := r.Repository.GetOwnedProjectsForUserID(ctx, obj.UserID)
if err != sql.ErrNoRows && err != nil {
return &OwnedList{}, err
}
return &OwnedList{Teams: ownedTeams, Projects: ownedProjects}, nil
}
func (r *userAccountResolver) Member(ctx context.Context, obj *db.UserAccount) (*MemberList, error) {
projectMemberIDs, err := r.Repository.GetMemberProjectIDsForUserID(ctx, obj.UserID)
if err != sql.ErrNoRows && err != nil {
return &MemberList{}, err
}
var projects []db.Project
for _, projectID := range projectMemberIDs {
project, err := r.Repository.GetProjectByID(ctx, projectID)
if err != nil {
return &MemberList{}, err
}
projects = append(projects, project)
}
teamMemberIDs, err := r.Repository.GetMemberTeamIDsForUserID(ctx, obj.UserID)
if err != sql.ErrNoRows && err != nil {
return &MemberList{}, err
}
var teams []db.Team
for _, teamID := range teamMemberIDs {
team, err := r.Repository.GetTeamByID(ctx, teamID)
if err != nil {
return &MemberList{}, err
}
teams = append(teams, team)
}
return &MemberList{Teams: teams, Projects: projects}, err
}
// LabelColor returns LabelColorResolver implementation.
func (r *Resolver) LabelColor() LabelColorResolver { return &labelColorResolver{r} }
@ -1274,7 +1306,9 @@ func (r *Resolver) Task() TaskResolver { return &taskResolver{r} }
func (r *Resolver) TaskChecklist() TaskChecklistResolver { return &taskChecklistResolver{r} }
// TaskChecklistItem returns TaskChecklistItemResolver implementation.
func (r *Resolver) TaskChecklistItem() TaskChecklistItemResolver { return &taskChecklistItemResolver{r} }
func (r *Resolver) TaskChecklistItem() TaskChecklistItemResolver {
return &taskChecklistItemResolver{r}
}
// TaskGroup returns TaskGroupResolver implementation.
func (r *Resolver) TaskGroup() TaskGroupResolver { return &taskGroupResolver{r} }

View File

@ -46,7 +46,8 @@ type Member {
fullName: String!
username: String!
profileIcon: ProfileIcon!
owned: OwnersList
owned: OwnedList!
member: MemberList!
}
type RefreshToken {
@ -61,6 +62,16 @@ type Role {
name: String!
}
type OwnedList {
teams: [Team!]!
projects: [Project!]!
}
type MemberList {
teams: [Team!]!
projects: [Project!]!
}
type UserAccount {
id: ID!
email: String!
@ -70,6 +81,8 @@ type UserAccount {
role: Role!
username: String!
profileIcon: ProfileIcon!
owned: OwnedList!
member: MemberList!
}
type Team {