Skip to content

Commit

Permalink
feat(front): add profile deletion (#73)
Browse files Browse the repository at this point in the history
* feat(front): add logout button to the profile page

* feat(front): add profile deletion request button

* feat(front): decode the JWT to get admin status

* feat(front): add admin user deletion page

* feat(front): allow admin to delete users
  • Loading branch information
Kuruyia authored Feb 20, 2024
1 parent 6f1ceb5 commit 04707ea
Show file tree
Hide file tree
Showing 18 changed files with 429 additions and 11 deletions.
18 changes: 18 additions & 0 deletions front/assets/translations/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,17 @@
"radiusRange": "Radius range: %{range} km",
"delete": "Delete availability",
"deleteConfirm": "Are you sure you want to delete this availability?"
},
"requestDeletion": {
"button": "Delete my profile",
"buttonDisabled": "Profile deletion requested",
"title": "Request profile deletion",
"message": "Are you sure you want to request the deletion of your profile?\n\nAn administrator will manually review your request before deleting your profile.",
"confirm": "Request deletion"
},
"admin": {
"buttonTitle": "Administration panel",
"buttonSubtitle": "Administration of the platform"
}
},
"login": {
Expand All @@ -149,5 +160,12 @@
},
"applications": {
"applications": "Applications"
},
"admin": {
"userDeletion": {
"headerTitle": "User deletion",
"dialogTitle": "Delete user",
"dialogMessage": "Are you sure you want to delete %{name}?"
}
}
}
18 changes: 18 additions & 0 deletions front/assets/translations/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,17 @@
"radiusRange": "Rayon de recherche : %{range} km",
"delete": "Supprimer la disponibilité",
"deleteConfirm": "Voulez-vous vraiment supprimer cette disponibilité?"
},
"requestDeletion": {
"button": "Supprimer mon profil",
"buttonDisabled": "Suppression du profil demandée",
"title": "Demande de suppression de profil",
"message": "Êtes-vous sûr de vouloir supprimer votre profil ?\n\nUn administrateur devra valider votre demande avant que votre profil ne soit supprimé.",
"confirm": "Confirmer la demande"
},
"admin": {
"buttonTitle": "Panel d'administration",
"buttonSubtitle": "Administration de la plateforme"
}
},
"login": {
Expand All @@ -149,5 +160,12 @@
},
"applications": {
"applications": "Candidatures"
},
"admin": {
"userDeletion": {
"headerTitle": "Suppression d'utilisateurs",
"dialogTitle": "Supprimer un utilisateur",
"dialogMessage": "Êtes-vous sûr de vouloir supprimer %{name} ?"
}
}
}
21 changes: 21 additions & 0 deletions front/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,28 @@
"@react-navigation/native": "^6.1.9",
"@react-navigation/native-stack": "^6.9.17",
"@reduxjs/toolkit": "^2.1.0",
"@types/base-64": "^1.0.2",
"@types/color": "^3.0.6",
"@types/react": "~18.2.55",
"base-64": "^1.0.0",
"color": "^4.2.3",
"deepmerge": "^4.3.1",
"expo": "~50.0.6",
"expo-auth-session": "~5.4.0",
"expo-crypto": "~12.8.0",
"expo-document-picker": "~11.10.1",
"expo-file-system": "~16.0.6",
"expo-font": "~11.10.2",
"expo-image-picker": "~14.7.1",
"expo-localization": "~14.8.3",
"expo-secure-store": "~12.8.1",
"expo-sharing": "~11.10.0",
"expo-splash-screen": "~0.26.4",
"expo-status-bar": "~1.11.1",
"expo-system-ui": "~2.9.3",
"expo-web-browser": "~12.8.2",
"i18n-js": "^4.3.2",
"jwt-decode": "^4.0.0",
"react": "^18.2.0",
"react-native": "0.73.4",
"react-native-pager-view": "6.2.3",
Expand All @@ -45,10 +51,7 @@
"react-native-screens": "~3.29.0",
"react-native-tab-view": "^3.5.2",
"react-redux": "^9.1.0",
"typescript": "^5.3.3",
"expo-document-picker": "~11.10.1",
"expo-sharing": "~11.10.0",
"expo-file-system": "~16.0.6"
"typescript": "^5.3.3"
},
"devDependencies": {
"@babel/core": "^7.23.9",
Expand Down
67 changes: 67 additions & 0 deletions front/src/components/administration/UserRequestingDeletionItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { FC } from 'react';
import { StyleSheet, View } from 'react-native';
import { Icon, Text, TouchableRipple } from 'react-native-paper';

import ProfileHeaderDescription from '@/components/profile/header/ProfileHeaderDescription';
import { Profile } from '@/models/entities/profile';

/**
* The styles for the UserRequestingDeletionItem component.
*/
const styles = StyleSheet.create({
container: {
alignItems: 'center',
flexDirection: 'row',
gap: 4,
},
iconContainer: {
paddingHorizontal: 8,
},
nameContainer: {
flex: 1,
flexDirection: 'column',
gap: 8,
},
});

/**
* The props for the UserRequestingDeletionList component.
*/
type UserRequestingDeletionItemProps = {
/**
* The profile to display.
*/
profile: Profile;

/**
* The function to call when an item is pressed.
*/
onItemPress?: (profile: Profile) => void;
};

/**
* Displays a user requesting their deletion.
* @constructor
*/
export const UserRequestingDeletionItem: FC<
UserRequestingDeletionItemProps
> = ({ profile, onItemPress }) => {
return (
<TouchableRipple onPress={() => onItemPress?.(profile)}>
<View style={styles.container}>
<View style={styles.nameContainer}>
<Text variant='headlineSmall'>
{profile.firstName} {profile.lastName}
</Text>
<ProfileHeaderDescription shortBiography={profile.shortBiography} />
</View>

<View style={styles.iconContainer}>
<Icon size={30} source='trash-can-outline' />
</View>
</View>
</TouchableRipple>
);
};

export default UserRequestingDeletionItem;
58 changes: 58 additions & 0 deletions front/src/components/administration/UserRequestingDeletionList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { FC } from 'react';
import { StyleSheet, View } from 'react-native';
import { Divider } from 'react-native-paper';

import UserRequestingDeletionItem from '@/components/administration/UserRequestingDeletionItem';
import { Profile } from '@/models/entities/profile';

/**
* The styles for the ExperienceList component.
*/
const styles = StyleSheet.create({
divider: {
height: 1,
marginVertical: 8,
width: '100%',
},
});

/**
* The props for the UserRequestingDeletionList component.
*/
type UserRequestingDeletionListProps = {
/**
* The list of profiles to display.
*/
profiles: Profile[];

/**
* The function to call when an item is pressed.
*/
onItemPress?: (profile: Profile) => void;
};

/**
* Displays a list of users requesting their deletion.
* @constructor
*/
const UserRequestingDeletionList: FC<UserRequestingDeletionListProps> = ({
profiles,
onItemPress,
}) => {
return (
<View>
{profiles?.map((profile) => (
<View key={profile.id}>
<UserRequestingDeletionItem
profile={profile}
onItemPress={onItemPress}
/>

<Divider style={styles.divider} />
</View>
))}
</View>
);
};

export default UserRequestingDeletionList;
1 change: 0 additions & 1 deletion front/src/components/availabilities/AvailabilityList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ type AvailabilityListProps = {
/**
* The function to call when the delete button of an item is pressed.
*/

onItemDeletePress?: (availability: Availability) => void;
};

Expand Down
14 changes: 12 additions & 2 deletions front/src/components/login/LogoutButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as AuthSession from 'expo-auth-session';
import { TokenTypeHint, useAutoDiscovery } from 'expo-auth-session';
import { FC, useCallback } from 'react';
import { ViewStyle } from 'react-native';
import { Button } from 'react-native-paper';

import { logout } from '@/store/features/authenticationSlice';
Expand All @@ -15,13 +16,18 @@ type LogoutButtonProps = {
* The mode of the button.
*/
mode?: 'text' | 'outlined' | 'contained' | 'elevated' | 'contained-tonal';

/**
* The style of the container.
*/
style?: ViewStyle;
};

/**
* The button for logging out of the app.
* @constructor
*/
const LogoutButton: FC<LogoutButtonProps> = ({ mode }) => {
const LogoutButton: FC<LogoutButtonProps> = ({ mode, style }) => {
// Store hooks
const authState = useAppSelector((state) => state.auth);
const dispatch = useAppDispatch();
Expand Down Expand Up @@ -61,7 +67,11 @@ const LogoutButton: FC<LogoutButtonProps> = ({ mode }) => {
}, [authState, discovery, dispatch, oidcClientId]);

return (
<Button mode={mode ?? 'contained'} onPress={handleLogoutPress}>
<Button
mode={mode ?? 'contained'}
onPress={handleLogoutPress}
style={style}
>
{i18n.t('login.logout')}
</Button>
);
Expand Down
22 changes: 22 additions & 0 deletions front/src/components/profile/ProfileContents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import AvailabilityList from '@/components/availabilities/AvailabilityList';
import ProfileContactInfos from '@/components/profile/ProfileContactInfos';
import ProfileFooterButtons from '@/components/profile/ProfileFooterButtons';
import ProfileHeader from '@/components/profile/header/ProfileHeader';
import BigButton from '@/components/utils/BigButton';
import { Availability } from '@/models/entities/availability';
import { Profile } from '@/models/entities/profile';
import i18n from '@/utils/i18n';

/**
* The styles for the ProfilePage component.
Expand Down Expand Up @@ -45,6 +47,11 @@ type ProfileProps = {
*/
availabilities: Availability[];

/**
* Whether to show access to the administration panel.
*/
showAdmin: boolean;

/**
* The function to call when the experiences button is pressed.
*/
Expand All @@ -54,6 +61,11 @@ type ProfileProps = {
* The function to call when the references button is pressed.
*/
onReferencesPress?: () => void;

/**
* The function to call when the admin button is pressed.
*/
onAdminPress?: () => void;
};

/**
Expand All @@ -64,8 +76,10 @@ const ProfileContents: FC<ProfileProps> = ({
profile,
profilePictureUrl,
availabilities,
showAdmin,
onExperiencesPress,
onReferencesPress,
onAdminPress,
}) => {
return (
<ScrollView
Expand All @@ -90,6 +104,14 @@ const ProfileContents: FC<ProfileProps> = ({
onReferencesPress={onReferencesPress}
style={styles.footerButtons}
/>

{showAdmin && (
<BigButton
title={i18n.t('profile.admin.buttonTitle')}
subtitle={i18n.t('profile.admin.buttonSubtitle')}
onPress={onAdminPress}
/>
)}
</ScrollView>
);
};
Expand Down
Loading

0 comments on commit 04707ea

Please sign in to comment.