Skip to content

Commit

Permalink
feat(Project): user can transfer ownership
Browse files Browse the repository at this point in the history
  • Loading branch information
awinogradov committed Apr 3, 2023
1 parent d74e9cc commit 56591bb
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 13 deletions.
30 changes: 30 additions & 0 deletions graphql/resolvers/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
ProjectUpdateInput,
ProjectCreateInput,
GoalsMetaOutput,
TransferOwnershipInput,
} from '../types';

export const query = (t: ObjectDefinitionBlock<'Query'>) => {
Expand Down Expand Up @@ -352,4 +353,33 @@ export const mutation = (t: ObjectDefinitionBlock<'Mutation'>) => {
}
},
});

t.field('transferProjectOwnership', {
type: Project,
args: {
data: nonNull(arg({ type: TransferOwnershipInput })),
},
resolve: async (_, { data: { id, activityId } }, { db, activity }) => {
if (!activity) return null;

try {
return db.project.update({
where: { id },
data: {
activityId,
},
});

// await mailServer.sendMail({
// from: `"Fred Foo 👻" <${process.env.MAIL_USER}>`,
// to: '[email protected], [email protected]',
// subject: 'Hello ✔',
// text: `new post '${title}'`,
// html: `new post <b>${title}</b>`,
// });
} catch (error) {
throw Error(`${error}`);
}
},
});
};
7 changes: 7 additions & 0 deletions graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ type Mutation {
toggleReaction(data: ReactionToggleInput!): Reaction
toggleTeamStargizer(data: SubscriptionToggleInput!): Activity
toggleTeamWatcher(data: SubscriptionToggleInput!): Activity
transferProjectOwnership(data: TransferOwnershipInput!): Project
transferTeamOwnership(data: TransferOwnershipInput!): Team
updateComment(data: CommentUpdateInput!): Comment
updateGoal(data: GoalUpdateInput!): Goal
updateProject(data: ProjectUpdateInput!): Project
Expand Down Expand Up @@ -434,6 +436,11 @@ input TeamsInput {
title: String
}

input TransferOwnershipInput {
activityId: String!
id: Int!
}

type User {
activity: Activity
activityId: String
Expand Down
8 changes: 8 additions & 0 deletions graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -558,3 +558,11 @@ export const GoalsMetaOutput = objectType({
t.nonNull.int('count');
},
});

export const TransferOwnershipInput = inputObjectType({
name: 'TransferOwnershipInput',
definition(t) {
t.field(ProjectModel.id);
t.nonNull.string('activityId');
},
});
12 changes: 7 additions & 5 deletions src/components/UserComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,13 @@ export const UserComboBox = React.forwardRef<HTMLDivElement, UserComboBoxProps>(
disabled={props.disabled}
onClick={props.onClick}
iconLeft={
<UserPic
src={value?.user?.image}
email={value?.user?.email || value?.ghost?.email}
size={16}
/>
value ? (
<UserPic
src={value?.user?.image}
email={value?.user?.email || value?.ghost?.email}
size={16}
/>
) : undefined
}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@
"Description": "Description",
"Teams": "Teams",
"Save": "Save",
"Be careful! All data will be lost": "Be careful! All data will be lost.",
"Be careful — all data will be lost": "Be careful — all data will be lost",
"Delete project": "Delete project",
"You are trying to delete project": "",
"To confirm deleting project {project} please type project key below.": "",
"Cancel": "Cancel",
"Yes, delete it": "Yes, delete it",
"Danger zone": "Danger zone"
"Danger zone": "Danger zone",
"Transfer project to other person": "Transfer project to other person",
"Transfer ownership": "Transfer ownership",
"You are trying to transfer project ownership": "You are trying to transfer project ownership",
"To confirm transfering {project} ownership please select new owner below.": "To confirm transfering {project} ownership please select new owner below.",
"New project owner": "New project owner",
"Enter name or email": "Enter name or email"
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@
"Description": "Описание",
"Teams": "Команды",
"Save": "Сохранить",
"Be careful! All data will be lost": "Будьте осторожны! Все данные о проекте будут утеряны.",
"Be careful — all data will be lost": "Будьте осторожны — все данные о проекте будут утеряны",
"Delete project": "Удалить проект",
"You are trying to delete project": "Вы собираетесь удалить проект",
"To confirm deleting project {project} please type project key below.": "Чтобы подтвердить удаление проекта {project} введите ключ проекта в поле ниже.",
"Cancel": "Отменить",
"Yes, delete it": "Удалить проект",
"Danger zone": "Опасная зона"
"Danger zone": "Опасная зона",
"Transfer project to other person": "Передать проект другому владельцу",
"Transfer ownership": "Передать проект",
"You are trying to transfer project ownership": "Вы собираетесь передать проект другому владельцу",
"To confirm transfering {project} ownership please select new owner below.": "Чтобы подтвердить передачу прав на проект {project} введите ключ проекта в поле ниже.",
"New project owner": "Новый владелец",
"Enter name or email": "Имя или почта"
}
72 changes: 68 additions & 4 deletions src/components/pages/ProjectSettingsPage/ProjectSettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import dynamic from 'next/dynamic';
import { useRouter as useNextRouter } from 'next/router';

import { createFetcher, refreshInterval } from '../../../utils/createFetcher';
import { Project } from '../../../../graphql/@generated/genql';
import { Activity, Project } from '../../../../graphql/@generated/genql';
import { Button } from '../../Button';
import { declareSsrProps, ExternalPageProps } from '../../../utils/declareSsrProps';
import { PageSep } from '../../PageSep';
Expand All @@ -32,6 +32,7 @@ import {
import { errorsProvider } from '../../../utils/forms';
import { useLocalStorage } from '../../../hooks/useLocalStorage';
import { FormMultiInput } from '../../FormMultiInput';
import { UserComboBox } from '../../UserComboBox';

import { tr } from './ProjectSettingsPage.i18n';

Expand Down Expand Up @@ -147,7 +148,7 @@ export const ProjectSettingsPage = ({

if (!project) return nextRouter.push('/404');

const { updateProject, deleteProject } = useProjectResource(project.id);
const { updateProject, deleteProject, transferOwnership } = useProjectResource(project.id);
const schema = updateProjectSchemaProvider();

const [actualFields, setActualFields] = useState<Pick<Project, 'title' | 'description' | 'teams'>>({
Expand Down Expand Up @@ -235,6 +236,14 @@ export const ProjectSettingsPage = ({
setLastProjectCache,
]);

const [transferTo, setTransferTo] = useState<Activity | undefined>();
const onTransferToChange = useCallback((a: Activity) => {
setTransferTo(a);
}, []);
const onProjectTransferOwnership = useCallback(() => {
router.project(project.key);
}, [router, project]);

const projectTeamsIds = formValues.teams?.map((team) => team!.id) ?? [];
const [teamsQuery, setTeamsQuery] = useState('');
const { data: teams } = useSWR(teamsQuery, (q) => teamsFetcher(user, q));
Expand All @@ -246,7 +255,7 @@ export const ProjectSettingsPage = ({

return (
<Page user={user} locale={locale} ssrTime={ssrTime} title={pageTitle}>
<ProjectPageLayout actions project={project}>
<ProjectPageLayout project={project}>
<PageSep />

<SettingsContent>
Expand Down Expand Up @@ -314,7 +323,7 @@ export const ProjectSettingsPage = ({
<FormActions flat="top">
<FormAction left>
<Text color={gray9} style={{ paddingLeft: gapS }}>
{tr('Be careful! All data will be lost')}
{tr('Be careful — all data will be lost')}
</Text>
</FormAction>
<FormAction right inline>
Expand All @@ -326,6 +335,22 @@ export const ProjectSettingsPage = ({
/>
</FormAction>
</FormActions>

<FormActions flat="top">
<FormAction left>
<Text color={gray9} style={{ paddingLeft: gapS }}>
{tr('Transfer project to other person')}
</Text>
</FormAction>
<FormAction right inline>
<Button
onClick={dispatchModalEvent(ModalEvent.ProjectTransferModal)}
size="m"
view="warning"
text={tr('Transfer ownership')}
/>
</FormAction>
</FormActions>
</Fieldset>
</Form>
</SettingsCard>
Expand Down Expand Up @@ -369,6 +394,45 @@ export const ProjectSettingsPage = ({
</Form>
</ModalContent>
</ModalOnEvent>

<ModalOnEvent view="warn" event={ModalEvent.ProjectTransferModal}>
<ModalHeader>
<FormTitle color={warn0}>{tr('You are trying to transfer project ownership')}</FormTitle>
</ModalHeader>

<ModalContent>
<Text>
{tr.raw('To confirm transfering {project} ownership please select new owner below.', {
project: <b key={project.title}>{project.title}</b>,
})}
</Text>

<br />

<Form>
<FormActions flat="top">
<FormAction left>
<UserComboBox
text={tr('New project owner')}
placeholder={tr('Enter name or email')}
value={transferTo}
onChange={onTransferToChange}
/>
</FormAction>
<FormAction right inline>
<Button size="m" text={tr('Cancel')} onClick={onDeleteCancel} />
<Button
size="m"
view="warning"
disabled={!transferTo}
onClick={transferOwnership(onProjectTransferOwnership, transferTo?.id)}
text={tr('Transfer ownership')}
/>
</FormAction>
</FormActions>
</Form>
</ModalContent>
</ModalOnEvent>
</ProjectPageLayout>
</Page>
);
Expand Down
32 changes: 32 additions & 0 deletions src/hooks/useProjectResource/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,43 @@ export const useProjectResource = (id: number) => {
[id],
);

const transferOwnership = useCallback(
(cb: Callback, activityId?: string) => async () => {
if (!activityId) return;

const promise = gql.mutation({
transferProjectOwnership: [
{
data: {
id,
activityId,
},
},
{
id: true,
},
],
});

toast.promise(promise, {
error: tr('Something went wrong 😿'),
loading: tr('We are calling owner'),
success: tr('So sad! Project will miss you'),
});

const res = await promise;

res.transferProjectOwnership && cb();
},
[id],
);

return {
createProject,
updateProject,
deleteProject,
toggleProjectWatching,
toggleProjectStar,
transferOwnership,
};
};
1 change: 1 addition & 0 deletions src/utils/dispatchModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export enum ModalEvent {
TeamCreateModal = 'TeamCreateModal',
TeamDeleteModal = 'TeamDeleteModal',
ProjectCreateModal = 'ProjectCreateModal',
ProjectTransferModal = 'ProjectTransferModal',
ProjectDeleteModal = 'ProjectDeleteModal',
UserInviteModal = 'UserInviteModal',
}
Expand Down

0 comments on commit 56591bb

Please sign in to comment.