mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 16:03:21 +00:00
feat: Messagerie WIP [#17]
This commit is contained in:
@ -151,7 +151,7 @@ export default function Layout({ children }) {
|
||||
return (
|
||||
<ProtectedRoute requiredRight={[RIGHTS.ADMIN, RIGHTS.TEACHER]}>
|
||||
{/* Topbar */}
|
||||
<header className="absolute top-0 left-0 right-0 h-16 bg-white border-b border-gray-200 px-4 md:px-8 flex items-center justify-between z-10 box-border">
|
||||
<header className="absolute top-0 left-64 right-0 h-16 bg-white border-b border-gray-200 px-4 md:px-8 flex items-center justify-between z-10 box-border">
|
||||
<div className="flex items-center">
|
||||
<button
|
||||
className="mr-4 md:hidden text-gray-600 hover:text-gray-900"
|
||||
@ -180,7 +180,7 @@ export default function Layout({ children }) {
|
||||
|
||||
{/* Sidebar */}
|
||||
<div
|
||||
className={`absolute top-16 bottom-16 left-0 z-30 w-64 bg-white border-r border-gray-200 box-border ${
|
||||
className={`absolute top-0 bottom-0 left-0 z-30 w-64 bg-white border-r border-gray-200 box-border ${
|
||||
isSidebarOpen ? 'block' : 'hidden md:block'
|
||||
}`}
|
||||
>
|
||||
|
||||
@ -1,10 +1,35 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import SidebarTabs from '@/components/SidebarTabs';
|
||||
import EmailSender from '@/components/Admin/EmailSender';
|
||||
import InstantMessaging from '@/components/Admin/InstantMessaging';
|
||||
import AnnouncementScheduler from '@/components/Admin/AnnouncementScheduler';
|
||||
|
||||
export default function MessageriePage({ csrfToken }) {
|
||||
const tabs = [
|
||||
{
|
||||
id: 'email',
|
||||
label: 'Envoyer un Mail',
|
||||
content: <EmailSender csrfToken={csrfToken} />,
|
||||
},
|
||||
{
|
||||
id: 'instant',
|
||||
label: 'Messagerie Instantanée',
|
||||
content: <InstantMessaging csrfToken={csrfToken} />,
|
||||
},
|
||||
{
|
||||
id: 'announcement',
|
||||
label: 'Planifier une Annonce',
|
||||
content: <AnnouncementScheduler csrfToken={csrfToken} />,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-6">Messagerie Admin</h1>
|
||||
<EmailSender csrfToken={csrfToken} />
|
||||
<div className="flex h-full w-full">
|
||||
<SidebarTabs
|
||||
tabs={tabs}
|
||||
onTabChange={(tabId) => console.log(`Onglet actif : ${tabId}`)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import Tab from '@/components/Tab';
|
||||
import TabContent from '@/components/TabContent';
|
||||
import Button from '@/components/Button';
|
||||
import InputText from '@/components/InputText';
|
||||
import CheckBox from '@/components/CheckBox'; // Import du composant CheckBox
|
||||
import logger from '@/utils/logger';
|
||||
import {
|
||||
fetchSmtpSettings,
|
||||
@ -24,7 +25,6 @@ export default function SettingsPage() {
|
||||
const [smtpPassword, setSmtpPassword] = useState('');
|
||||
const [useTls, setUseTls] = useState(true);
|
||||
const [useSsl, setUseSsl] = useState(false);
|
||||
const [statusMessage, setStatusMessage] = useState('');
|
||||
const { selectedEstablishmentId } = useEstablishment();
|
||||
const csrfToken = useCsrfToken(); // Récupération du csrfToken
|
||||
const { showNotification } = useNotification();
|
||||
@ -35,7 +35,7 @@ export default function SettingsPage() {
|
||||
// Charger les paramètres SMTP existants
|
||||
useEffect(() => {
|
||||
if (activeTab === 'smtp') {
|
||||
fetchSmtpSettings(csrfToken) // Passer le csrfToken ici
|
||||
fetchSmtpSettings(csrfToken, selectedEstablishmentId) // Passer le csrfToken ici
|
||||
.then((data) => {
|
||||
setSmtpServer(data.smtp_server || '');
|
||||
setSmtpPort(data.smtp_port || '');
|
||||
@ -46,7 +46,11 @@ export default function SettingsPage() {
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Erreur lors du chargement des paramètres SMTP:', error);
|
||||
setStatusMessage('Erreur lors du chargement des paramètres SMTP.');
|
||||
showNotification(
|
||||
'Erreur lors du chargement des paramètres SMTP.',
|
||||
'error',
|
||||
'Erreur'
|
||||
);
|
||||
});
|
||||
}
|
||||
}, [activeTab, csrfToken]); // Ajouter csrfToken comme dépendance
|
||||
@ -113,7 +117,11 @@ export default function SettingsPage() {
|
||||
|
||||
editSmtpSettings(smtpData, csrfToken) // Passer le csrfToken ici
|
||||
.then(() => {
|
||||
setStatusMessage('Paramètres SMTP mis à jour avec succès.');
|
||||
showNotification(
|
||||
'Paramètres SMTP mis à jour avec succès.',
|
||||
'success',
|
||||
'Succès'
|
||||
);
|
||||
logger.debug('SMTP Settings Updated:', smtpData);
|
||||
})
|
||||
.catch((error) => {
|
||||
@ -121,7 +129,11 @@ export default function SettingsPage() {
|
||||
'Erreur lors de la mise à jour des paramètres SMTP:',
|
||||
error
|
||||
);
|
||||
setStatusMessage('Erreur lors de la mise à jour des paramètres SMTP.');
|
||||
showNotification(
|
||||
'Erreur lors de la mise à jour des paramètres SMTP.',
|
||||
'error',
|
||||
'Erreur'
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@ -164,48 +176,54 @@ export default function SettingsPage() {
|
||||
</TabContent>
|
||||
<TabContent isActive={activeTab === 'smtp'}>
|
||||
<form onSubmit={handleSmtpSubmit}>
|
||||
<InputText
|
||||
label="Serveur SMTP"
|
||||
value={smtpServer}
|
||||
onChange={handleSmtpServerChange}
|
||||
/>
|
||||
<InputText
|
||||
label="Port SMTP"
|
||||
value={smtpPort}
|
||||
onChange={handleSmtpPortChange}
|
||||
/>
|
||||
<InputText
|
||||
label="Utilisateur SMTP"
|
||||
value={smtpUser}
|
||||
onChange={handleSmtpUserChange}
|
||||
/>
|
||||
<InputText
|
||||
label="Mot de passe SMTP"
|
||||
type="password"
|
||||
value={smtpPassword}
|
||||
onChange={handleSmtpPasswordChange}
|
||||
/>
|
||||
<div className="flex items-center space-x-4">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={useTls}
|
||||
onChange={handleUseTlsChange}
|
||||
/>
|
||||
Utiliser TLS
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={useSsl}
|
||||
onChange={handleUseSslChange}
|
||||
/>
|
||||
Utiliser SSL
|
||||
</label>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<InputText
|
||||
label="Serveur SMTP"
|
||||
value={smtpServer}
|
||||
onChange={handleSmtpServerChange}
|
||||
/>
|
||||
<InputText
|
||||
label="Port SMTP"
|
||||
value={smtpPort}
|
||||
onChange={handleSmtpPortChange}
|
||||
/>
|
||||
<InputText
|
||||
label="Utilisateur SMTP"
|
||||
value={smtpUser}
|
||||
onChange={handleSmtpUserChange}
|
||||
/>
|
||||
<InputText
|
||||
label="Mot de passe SMTP"
|
||||
type="password"
|
||||
value={smtpPassword}
|
||||
onChange={handleSmtpPasswordChange}
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" primary text="Mettre à jour"></Button>
|
||||
<div className="mt-6 border-t pt-4">
|
||||
<div className="flex items-center space-x-4">
|
||||
<CheckBox
|
||||
item={{ id: 'useTls' }}
|
||||
formData={{ useTls }}
|
||||
handleChange={() => setUseTls((prev) => !prev)} // Inverser la valeur booléenne
|
||||
fieldName="useTls"
|
||||
itemLabelFunc={() => 'Utiliser TLS'}
|
||||
/>
|
||||
<CheckBox
|
||||
item={{ id: 'useSsl' }}
|
||||
formData={{ useSsl }}
|
||||
handleChange={() => setUseSsl((prev) => !prev)} // Inverser la valeur booléenne
|
||||
fieldName="useSsl"
|
||||
itemLabelFunc={() => 'Utiliser SSL'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="submit"
|
||||
primary
|
||||
text="Mettre à jour"
|
||||
className="mt-6"
|
||||
></Button>
|
||||
</form>
|
||||
{statusMessage && <p className="mt-4 text-sm">{statusMessage}</p>}
|
||||
</TabContent>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
'use client';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { SendHorizontal } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import React from 'react';
|
||||
import Chat from '@/components/Chat';
|
||||
import { getGravatarUrl } from '@/utils/gravatar';
|
||||
|
||||
const contacts = [
|
||||
@ -23,45 +22,7 @@ const contacts = [
|
||||
];
|
||||
|
||||
export default function MessageriePage() {
|
||||
const [selectedContact, setSelectedContact] = useState(null);
|
||||
const [messages, setMessages] = useState({});
|
||||
const [newMessage, setNewMessage] = useState('');
|
||||
const messagesEndRef = useRef(null);
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom();
|
||||
}, [messages]);
|
||||
|
||||
const handleSendMessage = () => {
|
||||
if (newMessage.trim() && selectedContact) {
|
||||
const contactMessages = messages[selectedContact.id] || [];
|
||||
setMessages({
|
||||
...messages,
|
||||
[selectedContact.id]: [
|
||||
...contactMessages,
|
||||
{
|
||||
id: contactMessages.length + 1,
|
||||
text: newMessage,
|
||||
date: new Date(),
|
||||
},
|
||||
],
|
||||
});
|
||||
setNewMessage('');
|
||||
simulateContactResponse(selectedContact.id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyPress = (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleSendMessage();
|
||||
}
|
||||
};
|
||||
|
||||
const simulateContactResponse = (contactId) => {
|
||||
const simulateResponse = (contactId, setMessages) => {
|
||||
setTimeout(() => {
|
||||
setMessages((prevMessages) => {
|
||||
const contactMessages = prevMessages[contactId] || [];
|
||||
@ -81,79 +42,5 @@ export default function MessageriePage() {
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex" style={{ height: 'calc(100vh - 128px )' }}>
|
||||
{' '}
|
||||
{/* Utilisation de calc pour soustraire la hauteur de l'entête */}
|
||||
<div className="w-1/4 border-r border-gray-200 p-4 overflow-y-auto h-full ">
|
||||
{contacts.map((contact) => (
|
||||
<div
|
||||
key={contact.id}
|
||||
className={`p-2 cursor-pointer ${selectedContact?.id === contact.id ? 'bg-gray-200' : ''}`}
|
||||
onClick={() => setSelectedContact(contact)}
|
||||
>
|
||||
<Image
|
||||
src={contact.profilePic}
|
||||
alt={`${contact.name}'s profile`}
|
||||
className="w-8 h-8 rounded-full inline-block mr-2"
|
||||
width={150}
|
||||
height={150}
|
||||
/>
|
||||
{contact.name}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col h-full">
|
||||
<div className="flex-1 overflow-y-auto p-4 h-full">
|
||||
{selectedContact &&
|
||||
(messages[selectedContact.id] || []).map((message) => (
|
||||
<div
|
||||
key={message.id}
|
||||
className={`mb-2 p-2 rounded max-w-xs ${message.isResponse ? 'bg-gray-200 justify-self-end' : 'bg-emerald-200 justify-self-start'}`}
|
||||
style={{
|
||||
borderRadius: message.isResponse
|
||||
? '20px 20px 0 20px'
|
||||
: '20px 20px 20px 0',
|
||||
minWidth: '25%',
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center mb-1">
|
||||
<img
|
||||
src={selectedContact.profilePic}
|
||||
alt={`${selectedContact.name}'s profile`}
|
||||
className="w-8 h-8 rounded-full inline-block mr-2"
|
||||
width={150}
|
||||
height={150}
|
||||
/>
|
||||
<span className="text-xs text-gray-600">
|
||||
{selectedContact.name}
|
||||
</span>
|
||||
<span className="text-xs text-gray-400 ml-2">
|
||||
{new Date(message.date).toLocaleTimeString()}
|
||||
</span>
|
||||
</div>
|
||||
{message.text}
|
||||
</div>
|
||||
))}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
<div className="p-4 border-t border-gray-200 flex">
|
||||
<input
|
||||
type="text"
|
||||
value={newMessage}
|
||||
onChange={(e) => setNewMessage(e.target.value)}
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
placeholder="Écrire un message..."
|
||||
onKeyDown={handleKeyPress}
|
||||
/>
|
||||
<button
|
||||
onClick={handleSendMessage}
|
||||
className="p-2 bg-emerald-500 text-white rounded mr-2"
|
||||
>
|
||||
<SendHorizontal />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return <Chat contacts={contacts} simulateResponse={simulateResponse} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user