Ajout de la modification des user avec photo de profil etc et des informations de profil dans les messages
This commit is contained in:
parent
2d4cea3c4e
commit
ab3e81d020
7 changed files with 186 additions and 97 deletions
|
@ -6,19 +6,19 @@
|
|||
|
||||
export let myMessage: boolean; // Si c'est le message de l'utilisateur courant
|
||||
|
||||
export let user = null; // Infos utilisateur
|
||||
export let messageContent = ""; // Contenu du message
|
||||
export let createdAt = new Date(); // Date de création du message
|
||||
export let message = null; // Contenu du message
|
||||
|
||||
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)
|
||||
let timeElapsed: string;
|
||||
|
||||
// Fonction pour mettre à jour le temps écoulé
|
||||
const updateElapsed = () => {
|
||||
timeElapsed = formatDistanceToNow(createdAt);
|
||||
timeElapsed = formatDistanceToNow(message.createdAt);
|
||||
};
|
||||
|
||||
// Initialisation de l'intervalle
|
||||
|
@ -30,6 +30,17 @@
|
|||
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>
|
||||
|
||||
<Card.Root class="relative">
|
||||
|
@ -45,25 +56,24 @@
|
|||
<div class="flex items-center gap-3 {myMessage ? 'flex-row-reverse' : 'flex-row'}">
|
||||
<div
|
||||
class="relative"
|
||||
on:mouseenter={() => (showProfileInfo = true)}
|
||||
on:mouseleave={() => (showProfileInfo = false)}
|
||||
on:click={toggleProfileInfo}
|
||||
>
|
||||
<!-- Image de profil -->
|
||||
<img
|
||||
src={user.profilePicture ? `http://localhost:5173/${user.profilePicture}` : defaultProfilePicture}
|
||||
src={message.user.profilePicture ? `http://localhost:5173/${message.user.profilePicture}` : defaultProfilePicture}
|
||||
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) -->
|
||||
<ProfileInfo user={user} show={showProfileInfo} position={myMessage ? "right" : "left"} />
|
||||
<ProfileInfo user={message.user} show={activeProfileId === message.id} position={myMessage} />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col text-right {myMessage ? 'text-right' : 'text-left'}">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -71,7 +81,7 @@
|
|||
|
||||
<!-- Contenu du message -->
|
||||
<Card.Content class="text-sm sm:text-base md:text-lg text-gray-700">
|
||||
<p>{messageContent}</p>
|
||||
<p>{message.text}</p>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
|
|
|
@ -1,21 +1,62 @@
|
|||
<script lang="ts">
|
||||
export let profilePicture: File | null = null;
|
||||
const defaultImage = '/profile-default.svg'; // Remplacez par votre image par défaut
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
// 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 input = event.target as HTMLInputElement;
|
||||
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 = () => {
|
||||
profilePicture = null;
|
||||
};
|
||||
</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>
|
||||
.container {
|
||||
display: flex;
|
||||
|
@ -69,37 +110,3 @@
|
|||
cursor: not-allowed;
|
||||
}
|
||||
</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>
|
||||
|
|
|
@ -23,18 +23,29 @@
|
|||
console.error('Erreur lors de la déconnexion:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const editProfile = () => {
|
||||
window.location.href = '/user/edit';
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if show}
|
||||
<div class="overlay" role="dialog" aria-labelledby="profile-card-title" on:click={onClose}>
|
||||
<div class="profile-card" on:click|stopPropagation>
|
||||
<div class="profile-header">
|
||||
<!-- Image de profil -->
|
||||
<img src={user.profilePicture} alt="Profile" class="profile-image" />
|
||||
<h2 id="profile-card-title" class="profile-name">{user.username}</h2>
|
||||
<div class="profile-card flex flex-col gap-5" on:click|stopPropagation>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="profile-header">
|
||||
<!-- Image de profil -->
|
||||
<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>
|
||||
<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>
|
||||
{/if}
|
||||
|
@ -65,7 +76,7 @@
|
|||
|
||||
.profile-header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
justify-content: left;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
</script>
|
||||
|
||||
{#if show}
|
||||
<div class="user-info" style="left: {position === 'left' ? 'auto' : '0'}; right: {position === 'right' ? '0' : 'auto'};">
|
||||
<h3>{user.pseudo}</h3>
|
||||
<p>{user.name} {user.surname}</p>
|
||||
<p>{user.description}</p>
|
||||
<div class="user-info {position ? 'left' : 'right' }">
|
||||
<h2 class="text-lg font-semibold">{user.username}</h2>
|
||||
<p class="text-sm text-gray-500">{user.email}</p>
|
||||
<p class="text-sm text-gray-500">{user.name} {user.surname}</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
@ -18,12 +18,13 @@
|
|||
top: 0; /* Aligner en haut */
|
||||
left: auto;
|
||||
right: auto;
|
||||
width: 200px; /* Largeur appropriée */
|
||||
width: 200px; /* Largeur par défaut */
|
||||
background: rgba(255, 255, 255, 0.95); /* Fond semi-transparent */
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
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 {
|
||||
|
@ -33,5 +34,32 @@
|
|||
.user-info.right {
|
||||
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>
|
||||
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
let scrollContainer: HTMLElement;
|
||||
let messageText = '';
|
||||
|
||||
let activeProfileId = null;
|
||||
|
||||
function setActiveProfile(id) {
|
||||
activeProfileId = id;
|
||||
}
|
||||
|
||||
let socket = initSocket(); // Initialiser le socket
|
||||
|
||||
async function sendMessage() {
|
||||
|
@ -144,7 +150,12 @@
|
|||
<!-- Afficher les messages (mock d'un utilisateur sélectionné ou aucun message par défaut) -->
|
||||
{#if messages !== undefined && messages.length > 0}
|
||||
{#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}
|
||||
{:else}
|
||||
<div class="text-center text-gray-500 mt-10">Sélectionnez un message le chat est vide.</div>
|
||||
|
|
23
src/routes/user/edit/+page.server.ts
Normal file
23
src/routes/user/edit/+page.server.ts
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
|
@ -2,11 +2,16 @@
|
|||
import { onMount } from 'svelte';
|
||||
import ChoosePicture from "$lib/components/ui/ChoosePicture.svelte"; // Import du composant
|
||||
|
||||
let pseudo = '';
|
||||
let firstName = '';
|
||||
let lastName = '';
|
||||
let email = '';
|
||||
let profilePicture: File | null = null;
|
||||
export let data;
|
||||
const user = data.user;
|
||||
console.log(user);
|
||||
|
||||
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 showMessage = false;
|
||||
|
@ -26,28 +31,31 @@
|
|||
message = 'L\'email est invalide.';
|
||||
showMessage = true;
|
||||
} else {
|
||||
// Vous pouvez ici envoyer les données à un serveur via une API
|
||||
updateUser();
|
||||
message = 'Informations mises à jour avec succès!';
|
||||
showMessage = true;
|
||||
}
|
||||
};
|
||||
|
||||
// Fonction pour gérer le téléchargement de l'image de profil
|
||||
const handleFileChange = (event: Event) => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (input.files?.length) {
|
||||
profilePicture = input.files[0];
|
||||
}
|
||||
};
|
||||
async function updateUser() {
|
||||
const formData = new FormData();
|
||||
formData.append('username', pseudo);
|
||||
formData.append('name', firstName);
|
||||
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>
|
||||
|
||||
<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 -->
|
||||
<div class="mb-4">
|
||||
<ChoosePicture profilePicture={profilePicture} onFileChange={handleFileChange} />
|
||||
{#if profilePicture}
|
||||
<div class="mt-2 text-sm text-gray-600">Image sélectionnée : {profilePicture.name}</div>
|
||||
{/if}
|
||||
<ChoosePicture bind:profilePicture={profilePicture} />
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-center">
|
||||
|
@ -128,12 +133,6 @@
|
|||
</div>
|
||||
|
||||
<style>
|
||||
/* Supprimez le sélecteur body si non utilisé */
|
||||
/* body {
|
||||
background-color: #f8fafc;
|
||||
font-family: sans-serif;
|
||||
} */
|
||||
|
||||
input, button {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue