diff --git a/packages/api/schema.gql b/packages/api/schema.gql index 28ceeaa0..187d1ee1 100644 --- a/packages/api/schema.gql +++ b/packages/api/schema.gql @@ -244,12 +244,14 @@ type Mutation { """ Marks User to be deleted """ - markUserForDeletion(id: ID!): UserInterface + adminMarkUserForDeletion(id: ID!): UserInterface + trainerMarkUserForDeletion(id: ID!): UserInterface """ Unmarks User from deletion """ - unmarkUserForDeletion(id: ID!): UserInterface + adminUnMarkUserForDeletion(id: ID!): UserInterface + trainerUnMarkUserForDeletion(id: ID!): UserInterface """ Updates Trainee. diff --git a/packages/api/src/graphql.ts b/packages/api/src/graphql.ts index 29c79f59..23bb31c3 100644 --- a/packages/api/src/graphql.ts +++ b/packages/api/src/graphql.ts @@ -164,6 +164,10 @@ export type GqlMutation = { _devloginuser?: Maybe; /** [DEV] Sets the users type. */ _devsetusertype: GqlDevSetUserPayload; + /** Marks User to be deleted */ + adminMarkUserForDeletion?: Maybe; + /** Unmarks User from deletion */ + adminUnMarkUserForDeletion?: Maybe; /** Claims a Trainee by the current Trainer */ claimTrainee?: Maybe; /** Creates Admin. */ @@ -196,16 +200,14 @@ export type GqlMutation = { linkAlexa?: Maybe; /** Login via microsoft */ login?: Maybe; - /** Marks User to be deleted */ - markUserForDeletion?: Maybe; /** Publishes all comments on a report which is identified by the id argument. */ publishAllComments: GqlPublishCommentsPayload; + trainerMarkUserForDeletion?: Maybe; + trainerUnMarkUserForDeletion?: Maybe; /** Unclaims a Trainee by the current Trainer */ unclaimTrainee?: Maybe; /** Unlink Alexa account */ unlinkAlexa?: Maybe; - /** Unmarks User from deletion */ - unmarkUserForDeletion?: Maybe; /** Updates Admin. */ updateAdmin?: Maybe; /** Updates a comment on a Day which is identified by the id argument. */ @@ -241,6 +243,16 @@ export type GqlMutation_DevsetusertypeArgs = { }; +export type GqlMutationAdminMarkUserForDeletionArgs = { + id: Scalars['ID']['input']; +}; + + +export type GqlMutationAdminUnMarkUserForDeletionArgs = { + id: Scalars['ID']['input']; +}; + + export type GqlMutationClaimTraineeArgs = { id: Scalars['ID']['input']; }; @@ -330,23 +342,23 @@ export type GqlMutationLoginArgs = { }; -export type GqlMutationMarkUserForDeletionArgs = { +export type GqlMutationPublishAllCommentsArgs = { id: Scalars['ID']['input']; + traineeId: Scalars['ID']['input']; }; -export type GqlMutationPublishAllCommentsArgs = { +export type GqlMutationTrainerMarkUserForDeletionArgs = { id: Scalars['ID']['input']; - traineeId: Scalars['ID']['input']; }; -export type GqlMutationUnclaimTraineeArgs = { +export type GqlMutationTrainerUnMarkUserForDeletionArgs = { id: Scalars['ID']['input']; }; -export type GqlMutationUnmarkUserForDeletionArgs = { +export type GqlMutationUnclaimTraineeArgs = { id: Scalars['ID']['input']; }; @@ -909,6 +921,8 @@ export type GqlMutateEntryPayloadResolvers = ResolversObject<{ _devloginuser?: Resolver, ParentType, ContextType, RequireFields>; _devsetusertype?: Resolver>; + adminMarkUserForDeletion?: Resolver, ParentType, ContextType, RequireFields>; + adminUnMarkUserForDeletion?: Resolver, ParentType, ContextType, RequireFields>; claimTrainee?: Resolver, ParentType, ContextType, RequireFields>; createAdmin?: Resolver, ParentType, ContextType, RequireFields>; createCommentOnDay?: Resolver>; @@ -925,11 +939,11 @@ export type GqlMutationResolvers, ParentType, ContextType, RequireFields>; linkAlexa?: Resolver, ParentType, ContextType, RequireFields>; login?: Resolver, ParentType, ContextType, RequireFields>; - markUserForDeletion?: Resolver, ParentType, ContextType, RequireFields>; publishAllComments?: Resolver>; + trainerMarkUserForDeletion?: Resolver, ParentType, ContextType, RequireFields>; + trainerUnMarkUserForDeletion?: Resolver, ParentType, ContextType, RequireFields>; unclaimTrainee?: Resolver, ParentType, ContextType, RequireFields>; unlinkAlexa?: Resolver, ParentType, ContextType>; - unmarkUserForDeletion?: Resolver, ParentType, ContextType, RequireFields>; updateAdmin?: Resolver, ParentType, ContextType, RequireFields>; updateCommentOnDay?: Resolver>; updateCommentOnEntry?: Resolver>; diff --git a/packages/backend/src/permissions.ts b/packages/backend/src/permissions.ts index 392c3f8e..8babb58c 100644 --- a/packages/backend/src/permissions.ts +++ b/packages/backend/src/permissions.ts @@ -86,6 +86,8 @@ export const permissions = shield( // Trainer mutations claimTrainee: and(authenticated, trainer), unclaimTrainee: and(authenticated, trainer), + trainerMarkUserForDeletion: and(authenticated, trainer), + trainerUnMarkUserForDeletion: and(authenticated, trainer), // Trainer and Admin mutations createTrainee: and(authenticated, or(admin, trainer)), @@ -94,8 +96,8 @@ export const permissions = shield( updateTrainee: and(authenticated, admin), createTrainer: and(authenticated, admin), updateTrainer: and(authenticated, admin), - markUserForDeletion: and(authenticated, admin), - unmarkUserForDeletion: and(authenticated, admin), + adminMarkUserForDeletion: and(authenticated, admin), + adminUnMarkUserForDeletion: and(authenticated, admin), }, }, { allowExternalErrors: true } diff --git a/packages/backend/src/resolvers/admin.resolver.ts b/packages/backend/src/resolvers/admin.resolver.ts index 7ede0a0f..d70d555b 100644 --- a/packages/backend/src/resolvers/admin.resolver.ts +++ b/packages/backend/src/resolvers/admin.resolver.ts @@ -66,7 +66,7 @@ export const adminResolver: GqlResolvers = { }, }, Mutation: { - markUserForDeletion: async (_parent, { id }, { currentUser }) => { + adminMarkUserForDeletion: async (_parent, { id }, { currentUser }) => { const user = await userById(id) if (!user) { @@ -85,7 +85,7 @@ export const adminResolver: GqlResolvers = { return user }, - unmarkUserForDeletion: async (_parent, { id }, { currentUser }) => { + adminUnMarkUserForDeletion: async (_parent, { id }, { currentUser }) => { const user = await userById(id) if (!user) { diff --git a/packages/backend/src/resolvers/trainer.resolver.ts b/packages/backend/src/resolvers/trainer.resolver.ts index 97089cfa..4870fe26 100644 --- a/packages/backend/src/resolvers/trainer.resolver.ts +++ b/packages/backend/src/resolvers/trainer.resolver.ts @@ -4,9 +4,12 @@ import { GqlResolvers, TrainerContext } from '@lara/api' import { reportByYearAndWeek } from '../repositories/report.repo' import { allTrainees, traineeById, traineesByTrainerId } from '../repositories/trainee.repo' -import { updateUser } from '../repositories/user.repo' +import { updateUser, userById } from '../repositories/user.repo' import { alexaSkillLinked } from '../services/alexa.service' import { createT } from '../i18n' +import { addMonths } from 'date-fns' +import { isTrainee } from '../permissions' +import { sendDeletionMail } from '../services/email.service' export const trainerResolver: GqlResolvers = { Trainer: { @@ -78,7 +81,7 @@ export const trainerResolver: GqlResolvers = { throw new GraphQLError(t('errors.missingReport')) } - return reportCleaned + return report }, }, Mutation: { @@ -120,5 +123,55 @@ export const trainerResolver: GqlResolvers = { trainer: currentUser, } }, + + trainerMarkUserForDeletion: async (_parent, { id }, { currentUser }) => { + const user = await userById(id) + const t = createT(currentUser.language) + + if (!user) { + throw new GraphQLError(t('errors.missingUser')) + } + + if (user.id === currentUser.id) { + throw new GraphQLError(t('errors.cantDeleteYourself')) + } + + if (!isTrainee(user)) { + throw new GraphQLError(t('errors.insufficientPermissions')) + } + + // // Prüfe ob der Trainer der Trainer des Trainees ist + // if (user.trainerId !== currentUser.id) { + // throw new GraphQLError(t('errors.cantDeleteOtherTrainersTrainee')) + // } + + user.deleteAt = addMonths(new Date(), 3).toISOString() + + await updateUser(user, { updateKeys: ['deleteAt'] }) + + await sendDeletionMail(user) + + return user + }, + + trainerUnMarkUserForDeletion: async (_parent, { id }, { currentUser }) => { + const user = await userById(id) + const t = createT(currentUser.language) + + if (!user) { + throw new GraphQLError(t('errors.missingUser')) + } + + if (!isTrainee(user)) { + throw new GraphQLError(t('errors.insufficientPermissions')) + } + + // // Prüfe ob der Trainer der Trainer des Trainees ist + // if (user.trainerId !== currentUser.id) { + // throw new GraphQLError(t('errors.cantUnmarkOtherTrainersTrainee')) + // } + + return updateUser(user, { removeKeys: ['deleteAt'] }) + }, }, } diff --git a/packages/components/src/edit-user.tsx b/packages/components/src/edit-user.tsx index 209636be..50ad96cc 100644 --- a/packages/components/src/edit-user.tsx +++ b/packages/components/src/edit-user.tsx @@ -1,4 +1,4 @@ -import React, { JSX } from 'react' +import React, { JSX, ReactNode } from 'react' import styled from 'styled-components' import { Container } from './container' @@ -7,7 +7,7 @@ import { UnstyledLink } from './unstyled-link' type EditUserLayoutProps = { backButton: JSX.Element - content: JSX.Element + content?: ReactNode actions: JSX.Element } diff --git a/packages/frontend/src/components/DeletionModal.tsx b/packages/frontend/src/components/DeletionModal.tsx new file mode 100644 index 00000000..e53bdf59 --- /dev/null +++ b/packages/frontend/src/components/DeletionModal.tsx @@ -0,0 +1,66 @@ +import React from 'react' +import { H1, Paragraph, Spacings, Flex, Box } from '@lara/components' +import { PrimaryButton, SecondaryButton } from './button' +import Modal from './modal' +import { useToastContext } from '../hooks/use-toast-context' +import { GraphQLError } from 'graphql' +import strings from '../locales/localization' + +interface DeletionModalProps { + show: boolean + onClose: () => void + onConfirm: () => Promise + userName: string + loading?: boolean +} + +export const DeletionModal: React.FC = ({ + show, + onClose, + onConfirm, + userName, + loading = false, +}) => { + const { addToast } = useToastContext() + + const handleConfirm = () => { + onConfirm() + .then(() => { + onClose() + addToast({ + icon: 'PersonAttention', + title: strings.userDelete.title, + text: strings.userDelete.description, + type: 'error', + }) + }) + .catch((exception: GraphQLError) => { + addToast({ + title: strings.errors.error, + text: exception.message, + type: 'error', + }) + }) + } + + return ( + +

{strings.formatString(strings.deleteTrainer.title, userName)}

+ + {strings.deleteTrainer.description} + + + + + {strings.cancel} + + + + + {strings.deactivate} + + + +
+ ) +} diff --git a/packages/frontend/src/components/renderDeleteAction.tsx b/packages/frontend/src/components/renderDeleteAction.tsx new file mode 100644 index 00000000..fa98a3d1 --- /dev/null +++ b/packages/frontend/src/components/renderDeleteAction.tsx @@ -0,0 +1,75 @@ +import { SecondaryButton } from './button' +import strings from '../locales/localization' +import React from 'react' +import { + useAdminMarkUserForDeleteMutation, + useAdminUnmarkUserForDeleteMutation, + useTrainerMarkUserForDeleteMutation, + useTrainerUnmarkUserForDeleteMutation, +} from '../graphql' +interface UseDeleteActionsProps { + //id = routesId + id: string | undefined + currentUserId: string | undefined +} + +interface MutationVariables { + variables: { + id: string + } +} + +export const useDeleteActions = ({ currentUserId, id }: UseDeleteActionsProps) => { + const vars = { variables: { id: id ?? '' } } + + const [markForDeleteAdmin, { loading: deleteLoadingAdmin }] = useTrainerMarkUserForDeleteMutation() + const [unmarkDeleteAdmin, { loading: undeleteLoadingAdmin }] = useTrainerUnmarkUserForDeleteMutation() + const [markForDeleteTrainer, { loading: deleteLoadingTrainer }] = useAdminMarkUserForDeleteMutation() + const [unmarkDeleteTrainer, { loading: undeleteLoadingTrainer }] = useAdminUnmarkUserForDeleteMutation() + + const isTrainer = currentUserId === '456' + const isAdmin = currentUserId === '789' + + const deleteActionLoading = isTrainer + ? deleteLoadingTrainer || undeleteLoadingTrainer + : deleteLoadingAdmin || undeleteLoadingAdmin + + const [showDeletionModal, setShowDeletionModal] = React.useState(false) + + const toggleDeletionModal = () => { + setShowDeletionModal(!showDeletionModal) + } + + const selectQueryForType = (vars: MutationVariables) => { + if (isTrainer) { + unmarkDeleteTrainer(vars) + } else if (isAdmin) { + unmarkDeleteAdmin(vars) + } + } + + const renderDeleteAction = (deleteAt?: string) => { + if (currentUserId === id) return <> + if (deleteAt) { + return ( + selectQueryForType(vars)}> + {strings.unmarkDelete} + + ) + } + + return ( + toggleDeletionModal()}> + {strings.markDelete} + + ) + } + + return { + renderDeleteAction, + toggleDeletionModal, + showDeletionModal, + markForDeleteTrainer, + markForDeleteAdmin, + } +} diff --git a/packages/frontend/src/components/trainee-row.tsx b/packages/frontend/src/components/trainee-row.tsx index 20deacf3..45eb4d91 100644 --- a/packages/frontend/src/components/trainee-row.tsx +++ b/packages/frontend/src/components/trainee-row.tsx @@ -23,7 +23,6 @@ import strings from '../locales/localization' import Avatar from './avatar' import Badge from './badge' import Loader from './loader' - interface TraineeRowProps { active?: boolean trainee: Pick & { @@ -52,7 +51,6 @@ const getTraineeshipYear = (startDateString: string) => { const TraineeRow: React.FunctionComponent = (props) => { const [claimTrainee] = useClaimTraineeMutation() const [unclaimTrainee] = useUnclaimTraineeMutation() - const [loading, setLoading] = React.useState(false) const claim = async () => { @@ -80,6 +78,7 @@ const TraineeRow: React.FunctionComponent = (props) => { } const { active, trainee, trainerId } = props + const headerDestination = active ? '/trainees/' : `/trainees/${trainee.id}` return ( @@ -125,6 +124,7 @@ const TraineeRow: React.FunctionComponent = (props) => { + ; /** [DEV] Sets the users type. */ _devsetusertype: DevSetUserPayload; + /** Marks User to be deleted */ + adminMarkUserForDeletion?: Maybe; + /** Unmarks User from deletion */ + adminUnMarkUserForDeletion?: Maybe; /** Claims a Trainee by the current Trainer */ claimTrainee?: Maybe; /** Creates Admin. */ @@ -195,16 +199,14 @@ export type Mutation = { linkAlexa?: Maybe; /** Login via microsoft */ login?: Maybe; - /** Marks User to be deleted */ - markUserForDeletion?: Maybe; /** Publishes all comments on a report which is identified by the id argument. */ publishAllComments: PublishCommentsPayload; + trainerMarkUserForDeletion?: Maybe; + trainerUnMarkUserForDeletion?: Maybe; /** Unclaims a Trainee by the current Trainer */ unclaimTrainee?: Maybe; /** Unlink Alexa account */ unlinkAlexa?: Maybe; - /** Unmarks User from deletion */ - unmarkUserForDeletion?: Maybe; /** Updates Admin. */ updateAdmin?: Maybe; /** Updates a comment on a Day which is identified by the id argument. */ @@ -240,6 +242,16 @@ export type Mutation_DevsetusertypeArgs = { }; +export type MutationAdminMarkUserForDeletionArgs = { + id: Scalars['ID']['input']; +}; + + +export type MutationAdminUnMarkUserForDeletionArgs = { + id: Scalars['ID']['input']; +}; + + export type MutationClaimTraineeArgs = { id: Scalars['ID']['input']; }; @@ -329,23 +341,23 @@ export type MutationLoginArgs = { }; -export type MutationMarkUserForDeletionArgs = { +export type MutationPublishAllCommentsArgs = { id: Scalars['ID']['input']; + traineeId: Scalars['ID']['input']; }; -export type MutationPublishAllCommentsArgs = { +export type MutationTrainerMarkUserForDeletionArgs = { id: Scalars['ID']['input']; - traineeId: Scalars['ID']['input']; }; -export type MutationUnclaimTraineeArgs = { +export type MutationTrainerUnMarkUserForDeletionArgs = { id: Scalars['ID']['input']; }; -export type MutationUnmarkUserForDeletionArgs = { +export type MutationUnclaimTraineeArgs = { id: Scalars['ID']['input']; }; @@ -655,6 +667,20 @@ export enum UserTypeEnum { Trainer = 'Trainer' } +export type AdminMarkUserForDeleteMutationVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type AdminMarkUserForDeleteMutation = { __typename?: 'Mutation', adminMarkUserForDeletion?: { __typename?: 'Admin', deleteAt?: string | undefined, id: string } | { __typename?: 'Trainee', deleteAt?: string | undefined, id: string } | { __typename?: 'Trainer', deleteAt?: string | undefined, id: string } | undefined }; + +export type AdminUnmarkUserForDeleteMutationVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type AdminUnmarkUserForDeleteMutation = { __typename?: 'Mutation', adminUnMarkUserForDeletion?: { __typename?: 'Admin', deleteAt?: string | undefined, id: string } | { __typename?: 'Trainee', deleteAt?: string | undefined, id: string } | { __typename?: 'Trainer', deleteAt?: string | undefined, id: string } | undefined }; + export type ApplicationSettingsUpdateUserMutationVariables = Exact<{ language?: InputMaybe; theme?: InputMaybe; @@ -811,13 +837,6 @@ export type LoginPageLoginMutationVariables = Exact<{ export type LoginPageLoginMutation = { __typename?: 'Mutation', login?: { __typename?: 'OAuthPayload', accessToken: string, refreshToken?: string | undefined, expiresIn: number } | undefined }; -export type MarkUserForDeleteMutationVariables = Exact<{ - id: Scalars['ID']['input']; -}>; - - -export type MarkUserForDeleteMutation = { __typename?: 'Mutation', markUserForDeletion?: { __typename?: 'Admin', deleteAt?: string | undefined, id: string } | { __typename?: 'Trainee', deleteAt?: string | undefined, id: string } | { __typename?: 'Trainer', deleteAt?: string | undefined, id: string } | undefined }; - export type PublishAllCommentsMutationVariables = Exact<{ id: Scalars['ID']['input']; traineeId: Scalars['ID']['input']; @@ -840,24 +859,31 @@ export type TraineeSettingsUpdateTraineeMutationVariables = Exact<{ export type TraineeSettingsUpdateTraineeMutation = { __typename?: 'Mutation', updateCurrentTrainee?: { __typename?: 'Trainee', id: string, course?: string | undefined } | undefined }; -export type UnclaimTraineeMutationVariables = Exact<{ +export type TrainerMarkUserForDeleteMutationVariables = Exact<{ id: Scalars['ID']['input']; }>; -export type UnclaimTraineeMutation = { __typename?: 'Mutation', unclaimTrainee?: { __typename?: 'TrainerTraineePayload', trainee: { __typename?: 'Trainee', id: string, trainer?: { __typename?: 'Trainer', id: string } | undefined }, trainer: { __typename?: 'Trainer', id: string, trainees: Array<{ __typename?: 'Trainee', id: string }> } } | undefined }; +export type TrainerMarkUserForDeleteMutation = { __typename?: 'Mutation', trainerMarkUserForDeletion?: { __typename?: 'Admin', id: string } | { __typename?: 'Trainee', deleteAt?: string | undefined, id: string } | { __typename?: 'Trainer', id: string } | undefined }; -export type UnlinkAlexaMutationVariables = Exact<{ [key: string]: never; }>; +export type TrainerUnmarkUserForDeleteMutationVariables = Exact<{ + id: Scalars['ID']['input']; +}>; -export type UnlinkAlexaMutation = { __typename?: 'Mutation', unlinkAlexa?: { __typename?: 'Admin', id: string, alexaSkillLinked?: boolean | undefined } | { __typename?: 'Trainee', id: string, alexaSkillLinked?: boolean | undefined } | { __typename?: 'Trainer', id: string, alexaSkillLinked?: boolean | undefined } | undefined }; +export type TrainerUnmarkUserForDeleteMutation = { __typename?: 'Mutation', trainerUnMarkUserForDeletion?: { __typename?: 'Admin', id: string } | { __typename?: 'Trainee', deleteAt?: string | undefined, id: string } | { __typename?: 'Trainer', id: string } | undefined }; -export type UnmarkUserForDeleteMutationVariables = Exact<{ +export type UnclaimTraineeMutationVariables = Exact<{ id: Scalars['ID']['input']; }>; -export type UnmarkUserForDeleteMutation = { __typename?: 'Mutation', unmarkUserForDeletion?: { __typename?: 'Admin', deleteAt?: string | undefined, id: string } | { __typename?: 'Trainee', deleteAt?: string | undefined, id: string } | { __typename?: 'Trainer', deleteAt?: string | undefined, id: string } | undefined }; +export type UnclaimTraineeMutation = { __typename?: 'Mutation', unclaimTrainee?: { __typename?: 'TrainerTraineePayload', trainee: { __typename?: 'Trainee', id: string, trainer?: { __typename?: 'Trainer', id: string } | undefined }, trainer: { __typename?: 'Trainer', id: string, trainees: Array<{ __typename?: 'Trainee', id: string }> } } | undefined }; + +export type UnlinkAlexaMutationVariables = Exact<{ [key: string]: never; }>; + + +export type UnlinkAlexaMutation = { __typename?: 'Mutation', unlinkAlexa?: { __typename?: 'Admin', id: string, alexaSkillLinked?: boolean | undefined } | { __typename?: 'Trainee', id: string, alexaSkillLinked?: boolean | undefined } | { __typename?: 'Trainer', id: string, alexaSkillLinked?: boolean | undefined } | undefined }; export type UpdateAdminMutationVariables = Exact<{ input: UpdateAdminInput; @@ -1090,6 +1116,48 @@ export type TrainersPageQueryVariables = Exact<{ [key: string]: never; }>; export type TrainersPageQuery = { __typename?: 'Query', trainers: Array<{ __typename?: 'Trainer', id: string, firstName: string, lastName: string }> }; +export const AdminMarkUserForDeleteDocument = gql` + mutation AdminMarkUserForDelete($id: ID!) { + adminMarkUserForDeletion(id: $id) { + id + ... on Trainee { + deleteAt + } + ... on Trainer { + deleteAt + } + ... on Admin { + deleteAt + } + } +} + `; +export function useAdminMarkUserForDeleteMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(AdminMarkUserForDeleteDocument, options); + } +export type AdminMarkUserForDeleteMutationHookResult = ReturnType; +export const AdminUnmarkUserForDeleteDocument = gql` + mutation AdminUnmarkUserForDelete($id: ID!) { + adminUnMarkUserForDeletion(id: $id) { + id + ... on Trainee { + deleteAt + } + ... on Trainer { + deleteAt + } + ... on Admin { + deleteAt + } + } +} + `; +export function useAdminUnmarkUserForDeleteMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(AdminUnmarkUserForDeleteDocument, options); + } +export type AdminUnmarkUserForDeleteMutationHookResult = ReturnType; export const ApplicationSettingsUpdateUserDocument = gql` mutation ApplicationSettingsUpdateUser($language: String, $theme: String, $notification: Boolean) { updateCurrentUser( @@ -1476,27 +1544,6 @@ export function useLoginPageLoginMutation(baseOptions?: Apollo.MutationHookOptio return Apollo.useMutation(LoginPageLoginDocument, options); } export type LoginPageLoginMutationHookResult = ReturnType; -export const MarkUserForDeleteDocument = gql` - mutation MarkUserForDelete($id: ID!) { - markUserForDeletion(id: $id) { - id - ... on Trainee { - deleteAt - } - ... on Trainer { - deleteAt - } - ... on Admin { - deleteAt - } - } -} - `; -export function useMarkUserForDeleteMutation(baseOptions?: Apollo.MutationHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useMutation(MarkUserForDeleteDocument, options); - } -export type MarkUserForDeleteMutationHookResult = ReturnType; export const PublishAllCommentsDocument = gql` mutation publishAllComments($id: ID!, $traineeId: ID!) { publishAllComments(id: $id, traineeId: $traineeId) { @@ -1547,6 +1594,36 @@ export function useTraineeSettingsUpdateTraineeMutation(baseOptions?: Apollo.Mut return Apollo.useMutation(TraineeSettingsUpdateTraineeDocument, options); } export type TraineeSettingsUpdateTraineeMutationHookResult = ReturnType; +export const TrainerMarkUserForDeleteDocument = gql` + mutation TrainerMarkUserForDelete($id: ID!) { + trainerMarkUserForDeletion(id: $id) { + id + ... on Trainee { + deleteAt + } + } +} + `; +export function useTrainerMarkUserForDeleteMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(TrainerMarkUserForDeleteDocument, options); + } +export type TrainerMarkUserForDeleteMutationHookResult = ReturnType; +export const TrainerUnmarkUserForDeleteDocument = gql` + mutation TrainerUnmarkUserForDelete($id: ID!) { + trainerUnMarkUserForDeletion(id: $id) { + id + ... on Trainee { + deleteAt + } + } +} + `; +export function useTrainerUnmarkUserForDeleteMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(TrainerUnmarkUserForDeleteDocument, options); + } +export type TrainerUnmarkUserForDeleteMutationHookResult = ReturnType; export const UnclaimTraineeDocument = gql` mutation unclaimTrainee($id: ID!) { unclaimTrainee(id: $id) { @@ -1583,27 +1660,6 @@ export function useUnlinkAlexaMutation(baseOptions?: Apollo.MutationHookOptions< return Apollo.useMutation(UnlinkAlexaDocument, options); } export type UnlinkAlexaMutationHookResult = ReturnType; -export const UnmarkUserForDeleteDocument = gql` - mutation UnmarkUserForDelete($id: ID!) { - unmarkUserForDeletion(id: $id) { - id - ... on Trainee { - deleteAt - } - ... on Trainer { - deleteAt - } - ... on Admin { - deleteAt - } - } -} - `; -export function useUnmarkUserForDeleteMutation(baseOptions?: Apollo.MutationHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useMutation(UnmarkUserForDeleteDocument, options); - } -export type UnmarkUserForDeleteMutationHookResult = ReturnType; export const UpdateAdminDocument = gql` mutation UpdateAdmin($input: UpdateAdminInput!, $id: ID!) { updateAdmin(input: $input, id: $id) { diff --git a/packages/frontend/src/graphql/mutations/mark-delete.gql b/packages/frontend/src/graphql/mutations/admin-mark-delete.gql similarity index 63% rename from packages/frontend/src/graphql/mutations/mark-delete.gql rename to packages/frontend/src/graphql/mutations/admin-mark-delete.gql index 255eaa6f..37079bd8 100644 --- a/packages/frontend/src/graphql/mutations/mark-delete.gql +++ b/packages/frontend/src/graphql/mutations/admin-mark-delete.gql @@ -1,5 +1,5 @@ -mutation MarkUserForDelete($id: ID!) { - markUserForDeletion(id: $id) { +mutation AdminMarkUserForDelete($id: ID!) { + adminMarkUserForDeletion(id: $id) { id ... on Trainee { diff --git a/packages/frontend/src/graphql/mutations/unmark-delete.gql b/packages/frontend/src/graphql/mutations/admin-unmark-delete.gql similarity index 61% rename from packages/frontend/src/graphql/mutations/unmark-delete.gql rename to packages/frontend/src/graphql/mutations/admin-unmark-delete.gql index a81f76e9..b54000d9 100644 --- a/packages/frontend/src/graphql/mutations/unmark-delete.gql +++ b/packages/frontend/src/graphql/mutations/admin-unmark-delete.gql @@ -1,5 +1,5 @@ -mutation UnmarkUserForDelete($id: ID!) { - unmarkUserForDeletion(id: $id) { +mutation AdminUnmarkUserForDelete($id: ID!) { + adminUnMarkUserForDeletion(id: $id) { id ... on Trainee { diff --git a/packages/frontend/src/graphql/mutations/trainer-mark-delete.gql b/packages/frontend/src/graphql/mutations/trainer-mark-delete.gql new file mode 100644 index 00000000..9deb97fd --- /dev/null +++ b/packages/frontend/src/graphql/mutations/trainer-mark-delete.gql @@ -0,0 +1,8 @@ +mutation TrainerMarkUserForDelete($id: ID!) { + trainerMarkUserForDeletion(id: $id) { + id + ... on Trainee { + deleteAt + } + } +} diff --git a/packages/frontend/src/graphql/mutations/trainer-unmark-delete.gql b/packages/frontend/src/graphql/mutations/trainer-unmark-delete.gql new file mode 100644 index 00000000..87ee7a3f --- /dev/null +++ b/packages/frontend/src/graphql/mutations/trainer-unmark-delete.gql @@ -0,0 +1,9 @@ +mutation TrainerUnmarkUserForDelete($id: ID!) { + trainerUnMarkUserForDeletion(id: $id) { + id + + ... on Trainee { + deleteAt + } + } +} diff --git a/packages/frontend/src/pages/admin-edit-user-page.tsx b/packages/frontend/src/pages/admin-edit-user-page.tsx index 11a43a9d..6dce746c 100644 --- a/packages/frontend/src/pages/admin-edit-user-page.tsx +++ b/packages/frontend/src/pages/admin-edit-user-page.tsx @@ -1,20 +1,21 @@ import React from 'react' import { useParams } from 'react-router' -import { EditUserLayout, H1, Paragraph, Spacings, Flex, Box } from '@lara/components' +import { EditUserLayout } from '@lara/components' -import { PrimaryButton, SecondaryButton } from '../components/button' import { EditTraineeContent } from '../components/edit-trainee-content' -import { EditTrainer } from '../components/edit-trainer-content' +//import { EditTrainer } from '../components/edit-trainer-content' import Loader from '../components/loader' import NavigationButtonLink from '../components/navigation-button-link' -import { useMarkUserForDeleteMutation, useUnmarkUserForDeleteMutation, useUserPageQuery } from '../graphql' +import { useUserPageQuery } from '../graphql' import strings from '../locales/localization' import { Template } from '../templates/template' -import Modal from '../components/modal' -import { useToastContext } from '../hooks/use-toast-context' +//import { EditAdmin } from '../components/edit-admin-content' +import { useDeleteActions } from '../components/renderDeleteAction' import { EditAdmin } from '../components/edit-admin-content' -import { GraphQLError } from 'graphql' +import { EditTrainer } from '../components/edit-trainer-content' +import { DeletionModal } from '../components/DeletionModal' +//import { DeletionModal } from '../components/DeletionModal' type AdminEditUserPageParams = { id: string @@ -25,40 +26,17 @@ export const AdminEditUserPage: React.FunctionComponent = () => { const vars = { variables: { id: id ?? '' } } const { data, loading } = useUserPageQuery(vars) - const [markForDelete, { loading: deleteLoading }] = useMarkUserForDeleteMutation() - const [unmarkDelete, { loading: undeleteLoading }] = useUnmarkUserForDeleteMutation() + const { renderDeleteAction, showDeletionModal, toggleDeletionModal, markForDeleteAdmin } = useDeleteActions({ + currentUserId: data?.currentUser?.id ?? '', + id: id ?? '', + }) - const deleteActionLoading = deleteLoading || undeleteLoading - const [showDeletionModal, setShowDeletionModal] = React.useState(false) - const { addToast } = useToastContext() - - const toggleDeletionModal = () => { - setShowDeletionModal(!showDeletionModal) - } const currentUser = data?.currentUser if (!currentUser) return null - const renderDeleteAction = (deleteAt?: string) => { - if (currentUser.id === id) return <> - if (deleteAt) { - return ( - unmarkDelete(vars)}> - {strings.unmarkDelete} - - ) - } - - return ( - toggleDeletionModal()}> - {strings.markDelete} - - ) - } - return ( ) diff --git a/packages/frontend/src/pages/trainee-page.tsx b/packages/frontend/src/pages/trainee-page.tsx index 33f627b1..4bf75cdb 100644 --- a/packages/frontend/src/pages/trainee-page.tsx +++ b/packages/frontend/src/pages/trainee-page.tsx @@ -1,11 +1,11 @@ import React from 'react' import { useParams } from 'react-router' -import { AdminCreateUserLayout, H1, Paragraph } from '@lara/components' +import { AdminCreateUserLayout, EditUserLayout, H1, Paragraph } from '@lara/components' import Loader from '../components/loader' import TraineeRow from '../components/trainee-row' -import { useCreateTraineeMutation, useTraineePageDataQuery } from '../graphql' +import { useCreateTraineeMutation, useTraineePageDataQuery, useUserPageQuery } from '../graphql' import strings from '../locales/localization' import { Template } from '../templates/template' import { Fab } from '../components/fab' @@ -13,20 +13,28 @@ import Modal from '../components/modal' import { EditTraineeFormData, TraineeForm } from '../components/trainee-form' import { useToastContext } from '../hooks/use-toast-context' import { GraphQLError } from 'graphql' +import { DeletionModal } from '../components/DeletionModal' +import NavigationButtonLink from '../components/navigation-button-link' +import { useDeleteActions } from '../components/renderDeleteAction' const TraineePage: React.FunctionComponent = () => { const { trainee } = useParams() const { loading, data } = useTraineePageDataQuery() + const vars = { variables: { id: trainee ?? '' } } + const { data: dataPageQuery, loading: pagequeryloading } = useUserPageQuery(vars) const [mutate] = useCreateTraineeMutation() - const { addToast } = useToastContext() - const [showModal, setShowModal] = React.useState(false) const isActive = (id: string): boolean => { return id === trainee } + const { renderDeleteAction, showDeletionModal, toggleDeletionModal, markForDeleteTrainer } = useDeleteActions({ + currentUserId: dataPageQuery?.currentUser?.id, + id: trainee, + }) + const createTrainee = async (data: EditTraineeFormData) => { await mutate({ variables: { input: data }, @@ -57,16 +65,41 @@ const TraineePage: React.FunctionComponent = () => { }) }) } - + const activeTrainee = data?.trainees.find((t) => isActive(t.id)) return (