Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions backend/src/controllers/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ export const getCurrentUser = async (req: Request, res: Response) => {
}
};

export const getUserContactInformation = async (req: Request, res: Response) => {
const { userId } = req.params;

try {
const userContactInfo = await user_service.getUserContactInformation(parseInt(userId));
Ok(res, { data: userContactInfo });
} catch {
Error(res, { msg: 'Erreur lors de la récupération des informations de contact de l\'utilisateur.' });
}
};

export const updateProfile = async (req: Request, res: Response) => {
const userId = req.user?.userId;
const { branch, contact } = req.body;
Expand Down
1 change: 1 addition & 0 deletions backend/src/routes/user.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const userRouter = express.Router();
userRouter.get('/admin/getusersbypermission', checkRole("Admin", []), userController.getUsersByPermission);
userRouter.patch('/admin/user/:userId', checkRole("Admin", []), userController.adminUpdateUser);
userRouter.delete('/admin/user/:userId', checkRole("Admin", []), userController.adminDeleteUser);
userRouter.get('/admin/getusercontactinformation/:userId', checkRole("Admin", []), userController.getUserContactInformation);
userRouter.get('/admin/getusers', checkRole("Admin", ["Respo CE"]), userController.getUsersAdmin);
userRouter.post('/admin/syncnewstudent', checkRole("Admin", []), userController.syncNewstudent);

Expand Down
10 changes: 10 additions & 0 deletions backend/src/schemas/Relational/userinformation.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { pgTable, integer, text } from "drizzle-orm/pg-core";
import { userSchema } from "../Basic/user.schema";

export const userInformationSchema = pgTable("user_informations", {
user_id: integer("user_id").primaryKey().references(() => userSchema.id, { onDelete: "cascade" }),
urgency_contact_name: text("urgency_contact_name"),
urgency_contact_phone: integer("urgency_contact_phone"),
});

export type UserInformation = typeof userInformationSchema.$inferSelect;
17 changes: 17 additions & 0 deletions backend/src/services/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { registrationSchema } from '../schemas/Relational/registration.schema';
import { getFaction } from './faction.service';
import { getUserRoles } from './role.service';
import { getTeam, getTeamFaction, getUserTeam } from './team.service';
import { userInformationSchema } from '../schemas/Relational/userinformation.schema';

// Fonction pour récupérer un utilisateur par email
export const getUserByEmail = async (email: string) => {
Expand Down Expand Up @@ -133,6 +134,22 @@ export const getUsers = async () => {
}
};

export const getUserContactInformation = async (userId: number) => {
try {
const user = await db.select(
{
userId: userInformationSchema.user_id,
urgency_contact_name: userInformationSchema.urgency_contact_name,
urgency_contact_phone: userInformationSchema.urgency_contact_phone
}
).from(userInformationSchema).where(eq(userInformationSchema.user_id, userId));
return user[0];
} catch (err) {
console.error('Erreur lors de la récupération des informations de contact de l\'utilisateur ', err);
throw new Error('Erreur de base de données');
}
};

export const getUsersAll = async () => {
try {
const users = await db.select().from(userSchema);
Expand Down
229 changes: 130 additions & 99 deletions frontend/src/components/Admin/adminUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { useEffect, useState } from 'react';
import Select from 'react-select';
import Swal from 'sweetalert2';

import { type User } from '../../interfaces/user.interface';
import { type User,type UserContactInformation } from '../../interfaces/user.interface';
import { renewTokenUser, requestPasswordUser } from '../../services/requests/auth.service';
import {
deleteUserByAdmin,
getUserContactInformation,
getUsersAdmin,
syncnewStudent,
updateUserByAdmin,
Expand Down Expand Up @@ -46,6 +47,7 @@ export const AdminUser = () => {
const [users, setUsers] = useState<User[]>([]);
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const [formData, setFormData] = useState<Partial<User>>({});
const [contactInformation, setContactInformation] = useState<Partial<UserContactInformation>>({});

useEffect(() => {
const fetchUsers = async () => {
Expand All @@ -55,11 +57,12 @@ export const AdminUser = () => {
fetchUsers();
}, []);

const handleUserSelect = (option: any) => {
const handleUserSelect = async (option: any) => {
const user = users.find((u) => u.userId === option.value);
if (user) {
setSelectedUser(user);
setFormData({ ...user });
setContactInformation({ ...(await getUserContactInformation(user.userId)) });
}
};

Expand Down Expand Up @@ -165,104 +168,132 @@ export const AdminUser = () => {
};

return (
<Card className="w-full max-w-3xl mx-auto">
<CardHeader>
<CardTitle className="text-2xl font-semibold text-gray-800 text-center">
👤 Gérer un utilisateur
</CardTitle>
</CardHeader>

<CardContent className="space-y-4">
<Select
placeholder="Sélectionner un utilisateur"
options={users.map((u) => ({
value: u.userId,
label: `${u.firstName} ${u.lastName} (${u.email})`,
}))}
onChange={handleUserSelect}
/>

{selectedUser && (
<form className="space-y-3 mt-4">
<Input
name="firstName"
value={formData.firstName || ''}
onChange={handleInputChange}
placeholder="Prénom"
/>
<Input
name="lastName"
value={formData.lastName || ''}
onChange={handleInputChange}
placeholder="Nom"
/>
<Input name="email" value={formData.email || ''} disabled placeholder="Email" />

<p className="text-sm text-red-500 underline mt-2">
<strong>Attention : la donnée récupérée dépend de la date de synchro choisie</strong>
</p>

<Select
placeholder="Majeur ?"
options={majeurOptions}
value={majeurOptions.find((opt) => opt.value === formData.majeur) || null}
onChange={handleSelectChange('majeur')}
isClearable
/>

<Select
value={branchOptions.find((b) => b.value === formData.branch) || null}
onChange={handleSelectChange('branch')}
options={branchOptions}
placeholder="Choisir une filière"
isClearable
/>

<Input
name="contact"
value={formData.contact || ''}
onChange={handleInputChange}
placeholder="Contact"
/>

<Select
placeholder="Permission"
options={permissionOptions}
value={permissionOptions.find((opt) => opt.value === formData.permission) || null}
onChange={handleSelectChange('permission')}
isClearable
/>

<div className="flex gap-4 mt-4">
<Button
type="button"
onClick={handleSave}
className="bg-green-600 hover:bg-green-700 text-white">
💾 Sauvegarder
</Button>
<Button
type="button"
onClick={handleDelete}
className="bg-red-600 hover:bg-red-700 text-white">
🗑 Supprimer
</Button>
<Button
type="button"
onClick={handleRenewToken}
className="bg-blue-500 hover:bg-blue-600 text-white">
🔄 Reset Token
</Button>
<Button
type="button"
onClick={handleRequestPassword}
className="bg-purple-500 hover:bg-purple-600 text-white">
📧 Reset Password (mail)
</Button>
<div className="flex flex-col gap-6">
<Card className="w-full max-w-3xl mx-auto">
<CardHeader>
<CardTitle className="text-2xl font-semibold text-gray-800 text-center">
👤 Gérer un utilisateur
</CardTitle>
</CardHeader>

<CardContent className="space-y-4">
<Select
placeholder="Sélectionner un utilisateur"
options={users.map((u) => ({
value: u.userId,
label: `${u.firstName} ${u.lastName} (${u.email})`,
}))}
onChange={handleUserSelect}
/>

{selectedUser && (
<form className="space-y-3 mt-4">
<Input
name="firstName"
value={formData.firstName || ''}
onChange={handleInputChange}
placeholder="Prénom"
/>
<Input
name="lastName"
value={formData.lastName || ''}
onChange={handleInputChange}
placeholder="Nom"
/>
<Input name="email" value={formData.email || ''} disabled placeholder="Email" />

<p className="text-sm text-red-500 underline mt-2">
<strong>Attention : la donnée récupérée dépend de la date de synchro choisie</strong>
</p>

<Select
placeholder="Majeur ?"
options={majeurOptions}
value={majeurOptions.find((opt) => opt.value === formData.majeur) || null}
onChange={handleSelectChange('majeur')}
isClearable
/>

<Select
value={branchOptions.find((b) => b.value === formData.branch) || null}
onChange={handleSelectChange('branch')}
options={branchOptions}
placeholder="Choisir une filière"
isClearable
/>

<Input
name="contact"
value={formData.contact || ''}
onChange={handleInputChange}
placeholder="Contact"
/>

<Select
placeholder="Permission"
options={permissionOptions}
value={permissionOptions.find((opt) => opt.value === formData.permission) || null}
onChange={handleSelectChange('permission')}
isClearable
/>

<div className="flex gap-4 mt-4">
<Button
type="button"
onClick={handleSave}
className="bg-green-600 hover:bg-green-700 text-white">
💾 Sauvegarder
</Button>
<Button
type="button"
onClick={handleDelete}
className="bg-red-600 hover:bg-red-700 text-white">
🗑 Supprimer
</Button>
<Button
type="button"
onClick={handleRenewToken}
className="bg-blue-500 hover:bg-blue-600 text-white">
🔄 Reset Token
</Button>
<Button
type="button"
onClick={handleRequestPassword}
className="bg-purple-500 hover:bg-purple-600 text-white">
📧 Reset Password (mail)
</Button>
</div>
</form>
)}
</CardContent>
</Card>

{selectedUser && (
<Card className="w-full max-w-3xl mx-auto">
<CardHeader>
<CardTitle className="text-2xl font-semibold text-gray-800 text-center">
📞 Information De Contact
</CardTitle>

<div className="space-y-3 mt-4">
<Input
name="Urgency Contact Name"
value={contactInformation.urgency_contact_name || ''}
disabled
placeholder="Nom du contact d'urgence"
/>
<Input
name="Urgency Contact Phone"
value={contactInformation.urgency_contact_phone || ''}
disabled
placeholder="Téléphone du contact d'urgence"
/>
</div>
</form>
)}
</CardContent>
</Card>

</CardHeader>
</Card>
)}
</div>
);
};

Expand Down
Loading
Loading