Ajout de la modification des user avec photo de profil etc et des informations de profil dans les messages

This commit is contained in:
Bilal Dieumegard 2024-12-09 21:51:56 +01:00
parent 2d4cea3c4e
commit ab3e81d020
7 changed files with 186 additions and 97 deletions

View file

@ -6,19 +6,19 @@
export let myMessage: boolean; // Si c'est le message de l'utilisateur courant export let myMessage: boolean; // Si c'est le message de l'utilisateur courant
export let user = null; // Infos utilisateur export let message = null; // Contenu du message
export let messageContent = ""; // Contenu du message
export let createdAt = new Date(); // Date de création du message
let defaultProfilePicture = "/images/default-profile.png"; let defaultProfilePicture = "/images/default-profile.png";
let showProfileInfo = false; // Contrôle la visibilité des informations de profil
export let setActiveProfile;
export let activeProfileId = null;
// Temps écoulé (calculé périodiquement) // Temps écoulé (calculé périodiquement)
let timeElapsed: string; let timeElapsed: string;
// Fonction pour mettre à jour le temps écoulé // Fonction pour mettre à jour le temps écoulé
const updateElapsed = () => { const updateElapsed = () => {
timeElapsed = formatDistanceToNow(createdAt); timeElapsed = formatDistanceToNow(message.createdAt);
}; };
// Initialisation de l'intervalle // Initialisation de l'intervalle
@ -30,6 +30,17 @@
clearInterval(interval); // Nettoyage lors du démontage clearInterval(interval); // Nettoyage lors du démontage
}; };
}); });
function toggleProfileInfo() {
if (activeProfileId === message.id) {
// Si le profil cliqué est déjà actif, le fermer
setActiveProfile(null);
} else {
// Sinon, afficher ce profil et masquer les autres
setActiveProfile(message.id);
}
}
</script> </script>
<Card.Root class="relative"> <Card.Root class="relative">
@ -45,25 +56,24 @@
<div class="flex items-center gap-3 {myMessage ? 'flex-row-reverse' : 'flex-row'}"> <div class="flex items-center gap-3 {myMessage ? 'flex-row-reverse' : 'flex-row'}">
<div <div
class="relative" class="relative"
on:mouseenter={() => (showProfileInfo = true)} on:click={toggleProfileInfo}
on:mouseleave={() => (showProfileInfo = false)}
> >
<!-- Image de profil --> <!-- Image de profil -->
<img <img
src={user.profilePicture ? `http://localhost:5173/${user.profilePicture}` : defaultProfilePicture} src={message.user.profilePicture ? `http://localhost:5173/${message.user.profilePicture}` : defaultProfilePicture}
alt="Profile Picture" alt="Profile Picture"
class="h-10 w-10 rounded-full border border-gray-300" class="h-10 w-10 rounded-full border border-gray-300 cursor-pointer"
/> />
<!-- Infos du profil (affichées au survol) --> <!-- Infos du profil (affichées au survol) -->
<ProfileInfo user={user} show={showProfileInfo} position={myMessage ? "right" : "left"} /> <ProfileInfo user={message.user} show={activeProfileId === message.id} position={myMessage} />
</div> </div>
<div class="flex flex-col text-right {myMessage ? 'text-right' : 'text-left'}"> <div class="flex flex-col text-right {myMessage ? 'text-right' : 'text-left'}">
<Card.Title <Card.Title
class="text-gray-800 text-sm sm:text-base md:text-lg truncate {myMessage ? 'font-black' : ''}" class="text-gray-800 text-sm sm:text-base md:text-lg truncate {myMessage ? 'font-bold' : ''}"
> >
{myMessage ? "(Moi)" : ""} {user.username} {myMessage ? "(Moi)" : ""} {message.user.username}
</Card.Title> </Card.Title>
</div> </div>
</div> </div>
@ -71,7 +81,7 @@
<!-- Contenu du message --> <!-- Contenu du message -->
<Card.Content class="text-sm sm:text-base md:text-lg text-gray-700"> <Card.Content class="text-sm sm:text-base md:text-lg text-gray-700">
<p>{messageContent}</p> <p>{message.text}</p>
</Card.Content> </Card.Content>
</Card.Root> </Card.Root>

View file

@ -1,21 +1,62 @@
<script lang="ts"> <script lang="ts">
export let profilePicture: File | null = null; import { onMount } from 'svelte';
const defaultImage = '/profile-default.svg'; // Remplacez par votre image par défaut
// Gérer le changement de fichier sélectionné export let profilePicture: File | null = null;
export let defaultImage = '/profile-default.svg'; // Image par défaut si aucune image n'est sélectionnée
let clientPicture: string | null = profilePicture ? `/${profilePicture}` : defaultImage;
// Fonction exécutée lorsque l'utilisateur sélectionne une image
const handleFileChange = (event: Event) => { const handleFileChange = (event: Event) => {
const input = event.target as HTMLInputElement; const input = event.target as HTMLInputElement;
if (input.files?.length) { if (input.files?.length) {
profilePicture = input.files[0]; profilePicture = input.files[0]; // Affectation du fichier sélectionné
clientPicture = URL.createObjectURL(profilePicture); // Prévisualisation de l'image
} else {
clientPicture = null;
profilePicture = null;
} }
}; };
// Supprimer l'image
const handleDelete = () => { const handleDelete = () => {
profilePicture = null; profilePicture = null;
}; };
</script> </script>
<!-- Conteneur principal -->
<div class="container">
<!-- Image de profil ou image par défaut -->
<img
src={clientPicture}
alt="Image de profil"
class="image-preview mb-10"
/>
<!-- Sélectionner une image -->
<label for="profilePicture" class="file-upload-btn">
Sélectionner une image
<input
type="file"
id="profilePicture"
class="file-input"
accept="image/*"
on:change={handleFileChange}
/>
</label>
<div class="action-buttons">
<!-- Bouton Supprimer l'image -->
<button
type="button"
class="bg-red-500 text-white px-4 py-2 rounded-lg hover:bg-red-600"
on:click={handleDelete}
disabled={!profilePicture}
>
Supprimer l'image
</button>
</div>
</div>
<style> <style>
.container { .container {
display: flex; display: flex;
@ -69,37 +110,3 @@
cursor: not-allowed; cursor: not-allowed;
} }
</style> </style>
<!-- Conteneur principal -->
<div class="container">
<!-- Image de profil ou image par défaut -->
<img
src={profilePicture ? URL.createObjectURL(profilePicture) : defaultImage}
alt="Image de profil"
class="image-preview mb-10"
/>
<!-- Sélectionner une image -->
<label for="profilePicture" class="file-upload-btn">
Sélectionner une image
<input
type="file"
id="profilePicture"
class="file-input"
accept="image/*"
on:change={handleFileChange}
/>
</label>
<div class="action-buttons">
<!-- Bouton Supprimer l'image -->
<button
type="button"
class="bg-red-500 text-white px-4 py-2 rounded-lg hover:bg-red-600"
on:click={handleDelete}
disabled={!profilePicture}
>
Supprimer l'image
</button>
</div>
</div>

View file

@ -23,18 +23,29 @@
console.error('Erreur lors de la déconnexion:', error); console.error('Erreur lors de la déconnexion:', error);
} }
}; };
const editProfile = () => {
window.location.href = '/user/edit';
};
</script> </script>
{#if show} {#if show}
<div class="overlay" role="dialog" aria-labelledby="profile-card-title" on:click={onClose}> <div class="overlay" role="dialog" aria-labelledby="profile-card-title" on:click={onClose}>
<div class="profile-card" on:click|stopPropagation> <div class="profile-card flex flex-col gap-5" on:click|stopPropagation>
<div class="profile-header"> <div class="flex flex-col gap-2">
<!-- Image de profil --> <div class="profile-header">
<img src={user.profilePicture} alt="Profile" class="profile-image" /> <!-- Image de profil -->
<h2 id="profile-card-title" class="profile-name">{user.username}</h2> <img src={user.profilePicture} alt="Profile" class="profile-image" />
<h2 id="profile-card-title" class="profile-name">{user.username}</h2>
</div>
<p>{user.name} {user.surname}</p>
</div> </div>
<p>{user.name} {user.surname}</p>
<Button on:click={disconnect}>Deconnecter</Button> <div class="flex flex-col gap-3">
<Button on:click={editProfile}>Editer</Button>
<Button on:click={disconnect} variant="destructive">Déconnexion</Button>
</div>
</div> </div>
</div> </div>
{/if} {/if}
@ -65,7 +76,7 @@
.profile-header { .profile-header {
display: flex; display: flex;
justify-content: center; justify-content: left;
align-items: center; align-items: center;
margin-bottom: 20px; margin-bottom: 20px;
} }

View file

@ -5,10 +5,10 @@
</script> </script>
{#if show} {#if show}
<div class="user-info" style="left: {position === 'left' ? 'auto' : '0'}; right: {position === 'right' ? '0' : 'auto'};"> <div class="user-info {position ? 'left' : 'right' }">
<h3>{user.pseudo}</h3> <h2 class="text-lg font-semibold">{user.username}</h2>
<p>{user.name} {user.surname}</p> <p class="text-sm text-gray-500">{user.email}</p>
<p>{user.description}</p> <p class="text-sm text-gray-500">{user.name} {user.surname}</p>
</div> </div>
{/if} {/if}
@ -18,12 +18,13 @@
top: 0; /* Aligner en haut */ top: 0; /* Aligner en haut */
left: auto; left: auto;
right: auto; right: auto;
width: 200px; /* Largeur appropriée */ width: 200px; /* Largeur par défaut */
background: rgba(255, 255, 255, 0.95); /* Fond semi-transparent */ background: rgba(255, 255, 255, 0.95); /* Fond semi-transparent */
padding: 15px; padding: 15px;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
z-index: 10; /* Gérer les priorités d'affichage */ z-index: 10; /* Gérer les priorités d'affichage */
transition: all 0.3s ease-in-out; /* Animation fluide pour les changements */
} }
.user-info.left { .user-info.left {
@ -33,5 +34,32 @@
.user-info.right { .user-info.right {
left: 110%; /* Position à droite avec un espace */ left: 110%; /* Position à droite avec un espace */
} }
/* Media Queries pour adapter le style */
@media (max-width: 768px) {
.user-info {
width: 150px; /* Réduire la largeur sur les écrans plus petits */
padding: 10px; /* Réduire le padding */
font-size: 0.9rem; /* Réduire la taille de la police */
}
}
@media (max-width: 480px) {
.user-info {
position: fixed; /* Position fixe pour éviter les débordements */
left: 10px; /* Centrer avec un padding interne */
right: 10px;
width: auto; /* Utiliser toute la largeur disponible */
max-width: 90%; /* Limiter la largeur pour éviter les débordements */
padding: 8px; /* Réduire le padding davantage */
font-size: 0.8rem; /* Police encore plus petite */
}
.user-info.left,
.user-info.right {
left: 10px; /* Ignorer les positionnements relatifs */
right: 10px;
}
}
</style> </style>

View file

@ -15,6 +15,12 @@
let scrollContainer: HTMLElement; let scrollContainer: HTMLElement;
let messageText = ''; let messageText = '';
let activeProfileId = null;
function setActiveProfile(id) {
activeProfileId = id;
}
let socket = initSocket(); // Initialiser le socket let socket = initSocket(); // Initialiser le socket
async function sendMessage() { async function sendMessage() {
@ -144,7 +150,12 @@
<!-- Afficher les messages (mock d'un utilisateur sélectionné ou aucun message par défaut) --> <!-- Afficher les messages (mock d'un utilisateur sélectionné ou aucun message par défaut) -->
{#if messages !== undefined && messages.length > 0} {#if messages !== undefined && messages.length > 0}
{#each messages as message} {#each messages as message}
<Message myMessage={data.userId == message.user.id} user={message.user} messageContent={message.text} createdAt={message.createdAt} /> <Message
myMessage={data.userId == message.user.id}
message={message}
activeProfileId={activeProfileId}
setActiveProfile={setActiveProfile}
/>
{/each} {/each}
{:else} {:else}
<div class="text-center text-gray-500 mt-10">Sélectionnez un message le chat est vide.</div> <div class="text-center text-gray-500 mt-10">Sélectionnez un message le chat est vide.</div>

View file

@ -0,0 +1,23 @@
export async function load({ fetch, locals }) {
try {
// Appel API ou récupération de données
const res = await fetch(`/api/users/${locals.userId}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
const user = await res.json();
// Retourner les données à la page sous forme de props
return {
user
};
} catch (error) {
console.error('Erreur lors du chargement des canaux:', error);
return {
user : null
};
}
}

View file

@ -2,11 +2,16 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import ChoosePicture from "$lib/components/ui/ChoosePicture.svelte"; // Import du composant import ChoosePicture from "$lib/components/ui/ChoosePicture.svelte"; // Import du composant
let pseudo = ''; export let data;
let firstName = ''; const user = data.user;
let lastName = ''; console.log(user);
let email = '';
let profilePicture: File | null = null; let pseudo = user.username;
let firstName = user.name;
let lastName = user.surname;
let email = user.email;
let profilePicture = user.profilePicture; // Chemin initial ou valeur null
let message = ''; let message = '';
let showMessage = false; let showMessage = false;
@ -26,28 +31,31 @@
message = 'L\'email est invalide.'; message = 'L\'email est invalide.';
showMessage = true; showMessage = true;
} else { } else {
// Vous pouvez ici envoyer les données à un serveur via une API updateUser();
message = 'Informations mises à jour avec succès!'; message = 'Informations mises à jour avec succès!';
showMessage = true; showMessage = true;
} }
}; };
// Fonction pour gérer le téléchargement de l'image de profil async function updateUser() {
const handleFileChange = (event: Event) => { const formData = new FormData();
const input = event.target as HTMLInputElement; formData.append('username', pseudo);
if (input.files?.length) { formData.append('name', firstName);
profilePicture = input.files[0]; formData.append('surname', lastName);
} formData.append('email', email);
};
if (profilePicture) {
formData.append('profilePicture', profilePicture);
}
const res = await fetch(`/api/users/${user.id}`, {
method: 'PUT',
body: formData, // Transmet les données comme multipart/form-data
});
const result = await res.json();
console.log(result);
}
// Simulation de données au chargement
onMount(() => {
pseudo = '';
firstName = '';
lastName = '';
email = '';
profilePicture = null; // ou une valeur par défaut si vous en avez une
});
</script> </script>
<div class="flex items-center justify-center min-h-screen bg-gray-100 mg-10"> <div class="flex items-center justify-center min-h-screen bg-gray-100 mg-10">
@ -109,10 +117,7 @@
<!-- Intégration du composant de photo de profil --> <!-- Intégration du composant de photo de profil -->
<div class="mb-4"> <div class="mb-4">
<ChoosePicture profilePicture={profilePicture} onFileChange={handleFileChange} /> <ChoosePicture bind:profilePicture={profilePicture} />
{#if profilePicture}
<div class="mt-2 text-sm text-gray-600">Image sélectionnée : {profilePicture.name}</div>
{/if}
</div> </div>
<div class="mt-6 flex justify-center"> <div class="mt-6 flex justify-center">
@ -128,12 +133,6 @@
</div> </div>
<style> <style>
/* Supprimez le sélecteur body si non utilisé */
/* body {
background-color: #f8fafc;
font-family: sans-serif;
} */
input, button { input, button {
font-family: inherit; font-family: inherit;
} }