Notifs + Logout + delete Avatar
This commit is contained in:
@@ -26,6 +26,17 @@ router.post('/login', async(req, res) =>
|
|||||||
res.status(result.status).json(result.data);
|
res.status(result.status).json(result.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.post('/logout', async(req, res) =>
|
||||||
|
{
|
||||||
|
const authHeader = req.headers['authorization'];
|
||||||
|
const token = authHeader && authHeader.split(' ')[1];
|
||||||
|
if (!token)
|
||||||
|
return (res.status(401).json({error: 'Missing token'}));
|
||||||
|
|
||||||
|
const result = await authService.logout(token);
|
||||||
|
res.status(result.status).json(result.data);
|
||||||
|
});
|
||||||
|
|
||||||
router.get('/github', (req, res) => {
|
router.get('/github', (req, res) => {
|
||||||
const githubAuthUrl = `https://github.com/login/oauth/authorize?` +
|
const githubAuthUrl = `https://github.com/login/oauth/authorize?` +
|
||||||
`client_id=${process.env.GITHUB_CLIENT_ID}&` +
|
`client_id=${process.env.GITHUB_CLIENT_ID}&` +
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ router.post('/upload', authenticateToken, upload.single('avatar'), async(req, re
|
|||||||
res.status(result.status).json(result.data);
|
res.status(result.status).json(result.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/', authenticateToken, async(req, res) =>
|
router.delete('/delete', authenticateToken, async(req, res) =>
|
||||||
{
|
{
|
||||||
const result = await avatarService.deleteAvatar(req.user.userId);
|
const result = await avatarService.deleteAvatar(req.user.userId);
|
||||||
res.status(result.status).json(result.data);
|
res.status(result.status).json(result.data);
|
||||||
|
|||||||
@@ -2,6 +2,30 @@ import bcrypt from 'bcrypt';
|
|||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import {query} from '../db.js';
|
import {query} from '../db.js';
|
||||||
|
|
||||||
|
async function logout(token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!token)
|
||||||
|
return ({status: 400, data: {error: 'Missing token'}});
|
||||||
|
try
|
||||||
|
{
|
||||||
|
jwt.verify(token, process.env.JWT_SECRET);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return ({status: 401, data: {error: 'Invalid token'}});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ({status: 200, data: {message: 'Logged out'}});
|
||||||
|
}
|
||||||
|
catch (err)
|
||||||
|
{
|
||||||
|
console.error(err);
|
||||||
|
return ({status: 500, data: {error: 'Server error'}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function login(username, password)
|
async function login(username, password)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -60,4 +84,4 @@ async function register(username, password)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {register, login};
|
export default {register, login, logout};
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ async function deleteAvatar(userId) {
|
|||||||
if (currentAvatar === null)
|
if (currentAvatar === null)
|
||||||
return ({status: 404, data: {error: 'User not found'}});
|
return ({status: 404, data: {error: 'User not found'}});
|
||||||
|
|
||||||
|
if (currentAvatar === DEFAULT_AVATAR)
|
||||||
|
return ({status: 400, data: {error: 'Cannot delete default avatar'}});
|
||||||
|
|
||||||
// Reset the avatar to the default one
|
// Reset the avatar to the default one
|
||||||
await setAvatar(DEFAULT_AVATAR, userId);
|
await setAvatar(DEFAULT_AVATAR, userId);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { windowRegistry } from './core/windows.js';
|
import { windowRegistry } from './core/windows.js';
|
||||||
import { LoginWindow } from './windows/login.js';
|
import { LoginWindow } from './windows/login.js';
|
||||||
|
import { LogoutWindow } from './windows/logout.js';
|
||||||
import { GlobalChat } from './windows/global_chat.js';
|
import { GlobalChat } from './windows/global_chat.js';
|
||||||
import { AvatarWindow } from './windows/avatar.js';
|
import { AvatarWindow } from './windows/avatar.js';
|
||||||
import { FriendsWindow } from './windows/friends.js';
|
import { FriendsWindow } from './windows/friends.js';
|
||||||
@@ -32,6 +33,7 @@ class App {
|
|||||||
new FriendsWindow();
|
new FriendsWindow();
|
||||||
new GameRoomWindow();
|
new GameRoomWindow();
|
||||||
new StatsWindow();
|
new StatsWindow();
|
||||||
|
new LogoutWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,7 +51,8 @@ class App {
|
|||||||
'login': 'login',
|
'login': 'login',
|
||||||
'chat': 'chat',
|
'chat': 'chat',
|
||||||
'avatar': 'avatar',
|
'avatar': 'avatar',
|
||||||
'friends': 'friends'
|
'friends': 'friends',
|
||||||
|
'logout': 'logout'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Event delegation on the menu
|
// Event delegation on the menu
|
||||||
|
|||||||
@@ -6,12 +6,14 @@
|
|||||||
export const API = {
|
export const API = {
|
||||||
AUTH: {
|
AUTH: {
|
||||||
LOGIN: '/api/auth/login',
|
LOGIN: '/api/auth/login',
|
||||||
|
LOGOUT: '/api/auth/logout',
|
||||||
REGISTER: '/api/auth/register',
|
REGISTER: '/api/auth/register',
|
||||||
GITHUB: '/api/auth/github'
|
GITHUB: '/api/auth/github'
|
||||||
},
|
},
|
||||||
AVATAR: {
|
AVATAR: {
|
||||||
GET: '/api/avatar/me',
|
GET: '/api/avatar/me',
|
||||||
UPLOAD: '/api/avatar/upload'
|
UPLOAD: '/api/avatar/upload',
|
||||||
|
DELETE: '/api/avatar/delete'
|
||||||
},
|
},
|
||||||
FRIENDS: {
|
FRIENDS: {
|
||||||
LIST: '/api/friends',
|
LIST: '/api/friends',
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ export const Events = {
|
|||||||
|
|
||||||
// Avatar
|
// Avatar
|
||||||
AVATAR_UPDATED: 'avatar:updated',
|
AVATAR_UPDATED: 'avatar:updated',
|
||||||
|
AVATAR_DELETED: 'avatar:deleted',
|
||||||
|
|
||||||
// Chat
|
// Chat
|
||||||
CHAT_CONNECTED: 'chat:connected',
|
CHAT_CONNECTED: 'chat:connected',
|
||||||
|
|||||||
@@ -228,6 +228,56 @@ export class Window {
|
|||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NotficationContainer()
|
||||||
|
{
|
||||||
|
if (document.getElementById('notification-container')) return;
|
||||||
|
|
||||||
|
const container = this.createElement('div');
|
||||||
|
container.id = 'notification-container';
|
||||||
|
Object.assign(container.style, {
|
||||||
|
position: 'fixed',
|
||||||
|
top: '20px',
|
||||||
|
right: '20px',
|
||||||
|
zIndex: 1000,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '10px'
|
||||||
|
});
|
||||||
|
document.body.appendChild(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
showNotification(message, color) {
|
||||||
|
this.NotficationContainer();
|
||||||
|
const container = document.getElementById('notification-container');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.textContent = message;
|
||||||
|
Object.assign(notification.style, {
|
||||||
|
backgroundColor: color,
|
||||||
|
color: 'white',
|
||||||
|
padding: '10px 20px',
|
||||||
|
borderRadius: '5px',
|
||||||
|
boxShadow: '0 2px 6px rgba(0,0,0,0.3)',
|
||||||
|
opacity: '0',
|
||||||
|
transform: 'translateY(-8px)',
|
||||||
|
transition: 'opacity 0.5s ease, transform 0.5s ease'
|
||||||
|
});
|
||||||
|
|
||||||
|
container.appendChild(notification);
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
notification.style.opacity = '1';
|
||||||
|
notification.style.transform = 'translateY(0)';
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.style.opacity = '0';
|
||||||
|
notification.style.transform = 'translateY(-8px)';
|
||||||
|
setTimeout(() => notification.remove(), 500);
|
||||||
|
}, 2200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export old class name for compatibility (alias)
|
// Export old class name for compatibility (alias)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<button class="menu__item" data-action="chat" aria-label="Global chat">Global chat</button>
|
<button class="menu__item" data-action="chat" aria-label="Global chat">Global chat</button>
|
||||||
<button class="menu__item" data-action="avatar" aria-label="Avatar">Avatar</button>
|
<button class="menu__item" data-action="avatar" aria-label="Avatar">Avatar</button>
|
||||||
<button class="menu__item" data-action="friends" aria-label="Amis">Amis</button>
|
<button class="menu__item" data-action="friends" aria-label="Amis">Amis</button>
|
||||||
|
<button class="menu__item" data-action="logout" aria-label="Logout">Logout</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<nav class="game" aria-label="Game">
|
<nav class="game" aria-label="Game">
|
||||||
|
|||||||
@@ -67,8 +67,12 @@ export class AvatarWindow extends Window {
|
|||||||
this.refreshBtn = this.createElement('button', [CSS.BTN, CSS.BTN_SECONDARY], {
|
this.refreshBtn = this.createElement('button', [CSS.BTN, CSS.BTN_SECONDARY], {
|
||||||
text: 'Refresh'
|
text: 'Refresh'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.deleteBtn = this.createElement('button', [CSS.BTN, CSS.BTN_SECONDARY], {
|
||||||
|
text: 'Delete avatar'
|
||||||
|
});
|
||||||
|
|
||||||
this.controls.append(this.statsBtn, this.chooseBtn, this.saveBtn, this.refreshBtn);
|
this.controls.append(this.statsBtn, this.chooseBtn, this.saveBtn, this.refreshBtn, this.deleteBtn);
|
||||||
|
|
||||||
// Feedback message
|
// Feedback message
|
||||||
this.message = this.createElement('div', CSS.MESSAGE);
|
this.message = this.createElement('div', CSS.MESSAGE);
|
||||||
@@ -93,6 +97,7 @@ export class AvatarWindow extends Window {
|
|||||||
this.chooseBtn.addEventListener('click', () => this.fileInput.click());
|
this.chooseBtn.addEventListener('click', () => this.fileInput.click());
|
||||||
this.saveBtn.addEventListener('click', () => this.uploadAvatar());
|
this.saveBtn.addEventListener('click', () => this.uploadAvatar());
|
||||||
this.refreshBtn.addEventListener('click', () => this.loadAvatar());
|
this.refreshBtn.addEventListener('click', () => this.loadAvatar());
|
||||||
|
this.deleteBtn.addEventListener('click', () => this.deleteAvatar());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -212,12 +217,14 @@ export class AvatarWindow extends Window {
|
|||||||
const token = localStorage.getItem(STORAGE_KEYS.AUTH_TOKEN);
|
const token = localStorage.getItem(STORAGE_KEYS.AUTH_TOKEN);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
this.showMessage('You must be logged in', 'error');
|
this.showMessage('You must be logged in', 'error');
|
||||||
|
this.showNotification('You must be logged in to change your avatar', 'red');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = this.fileInput.files?.[0];
|
const file = this.fileInput.files?.[0];
|
||||||
if (!file) {
|
if (!file) {
|
||||||
this.showMessage('Select an image first', 'error');
|
this.showMessage('Select an image first', 'error');
|
||||||
|
this.showNotification('Please select an image to upload', 'red');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +247,7 @@ export class AvatarWindow extends Window {
|
|||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorMsg = data?.error || data?.message || 'Upload failed';
|
const errorMsg = data?.error || data?.message || 'Upload failed';
|
||||||
this.showMessage(errorMsg, 'error');
|
this.showMessage(errorMsg, 'error');
|
||||||
|
this.showNotification('Failed to upload avatar.', 'red');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,11 +256,47 @@ export class AvatarWindow extends Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.showMessage('Avatar saved!', 'success');
|
this.showMessage('Avatar saved!', 'success');
|
||||||
|
this.showNotification('Avatar updated successfully!', 'green');
|
||||||
eventBus.emit(Events.AVATAR_UPDATED, { url: data?.avatar_url });
|
eventBus.emit(Events.AVATAR_UPDATED, { url: data?.avatar_url });
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Avatar upload error:', error);
|
console.error('Avatar upload error:', error);
|
||||||
this.showMessage('Upload error', 'error');
|
this.showMessage('Upload error', 'error');
|
||||||
|
this.showNotification('Failed to upload avatar.', 'red');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteAvatar() {
|
||||||
|
const token = localStorage.getItem(STORAGE_KEYS.AUTH_TOKEN);
|
||||||
|
if (!token) {
|
||||||
|
this.showMessage('You must be logged in', 'error');
|
||||||
|
this.showNotification('You must be logged in to delete your avatar', 'red');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(API.AVATAR.DELETE, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
this.showMessage('Failed to delete avatar', 'error');
|
||||||
|
this.showNotification('Failed to delete avatar.', 'red');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.preview.src = '';
|
||||||
|
this.showMessage('Avatar deleted!', 'success');
|
||||||
|
this.showNotification('Avatar deleted successfully!', 'green');
|
||||||
|
eventBus.emit(Events.AVATAR_DELETED);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Avatar delete error:', error);
|
||||||
|
this.showMessage('Delete error', 'error');
|
||||||
|
this.showNotification('Failed to delete avatar.', 'red');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -840,17 +840,20 @@ export class GameRoomWindow extends Window {
|
|||||||
const name = this.roomNameInput.value.trim();
|
const name = this.roomNameInput.value.trim();
|
||||||
if (!name) {
|
if (!name) {
|
||||||
this.showMessage('Entrez un nom pour le salon', 'error');
|
this.showMessage('Entrez un nom pour le salon', 'error');
|
||||||
|
this.showNotification('Entrez un nom pour le salon', 'red');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = localStorage.getItem(STORAGE_KEYS.AUTH_TOKEN);
|
const token = localStorage.getItem(STORAGE_KEYS.AUTH_TOKEN);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
this.showMessage('Connectez-vous pour creer un salon', 'info');
|
this.showMessage('Connectez-vous pour creer un salon', 'info');
|
||||||
|
this.showNotification('Connectez-vous pour créer un salon', 'red');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.currentRoom) {
|
if (this.currentRoom) {
|
||||||
this.showMessage('Vous etes deja dans un salon. Quittez-le d\'abord.', 'error');
|
this.showMessage('Vous etes deja dans un salon. Quittez-le d\'abord.', 'error');
|
||||||
|
this.showNotification('Vous êtes déjà dans un salon. Quittez-le d\'abord.', 'red');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -864,6 +867,7 @@ export class GameRoomWindow extends Window {
|
|||||||
this.currentRoom = currentData;
|
this.currentRoom = currentData;
|
||||||
this.enterLobby(currentData);
|
this.enterLobby(currentData);
|
||||||
this.showMessage('Vous etes deja dans un salon', 'error');
|
this.showMessage('Vous etes deja dans un salon', 'error');
|
||||||
|
this.showNotification('Vous êtes déjà dans un salon. Quittez-le d\'abord.', 'red');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -884,6 +888,7 @@ export class GameRoomWindow extends Window {
|
|||||||
|
|
||||||
if (this.roomNameExists(name)) {
|
if (this.roomNameExists(name)) {
|
||||||
this.showMessage('Un salon avec ce nom existe deja', 'error');
|
this.showMessage('Un salon avec ce nom existe deja', 'error');
|
||||||
|
this.showNotification('Un salon avec ce nom existe deja', 'red');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -905,6 +910,7 @@ export class GameRoomWindow extends Window {
|
|||||||
this.showMessage('Salon cree', 'success');
|
this.showMessage('Salon cree', 'success');
|
||||||
eventBus.emit(Events.ROOM_CREATED, data);
|
eventBus.emit(Events.ROOM_CREATED, data);
|
||||||
this.enterLobby(data);
|
this.enterLobby(data);
|
||||||
|
this.showNotification(`Vous avez créé le salon "${data.name}"`, 'green');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Create room error:', error);
|
console.error('Create room error:', error);
|
||||||
this.showMessage('Erreur de connexion', 'error');
|
this.showMessage('Erreur de connexion', 'error');
|
||||||
@@ -1037,6 +1043,7 @@ export class GameRoomWindow extends Window {
|
|||||||
|
|
||||||
if (this.currentRoom) {
|
if (this.currentRoom) {
|
||||||
this.showMessage('Vous etes deja dans un salon. Quittez-le d\'abord.', 'error');
|
this.showMessage('Vous etes deja dans un salon. Quittez-le d\'abord.', 'error');
|
||||||
|
this.showNotification('Vous êtes déjà dans un salon. Quittez-le d\'abord.', 'red');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1050,6 +1057,7 @@ export class GameRoomWindow extends Window {
|
|||||||
this.currentRoom = currentData;
|
this.currentRoom = currentData;
|
||||||
this.enterLobby(currentData);
|
this.enterLobby(currentData);
|
||||||
this.showMessage('Vous etes deja dans un salon', 'error');
|
this.showMessage('Vous etes deja dans un salon', 'error');
|
||||||
|
this.showNotification('Vous êtes déjà dans un salon. Quittez-le d\'abord.', 'red');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export class LoginWindow extends Window {
|
|||||||
this.buildUI();
|
this.buildUI();
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
this.checkIfAlreadyLoggedIn();
|
this.checkIfAlreadyLoggedIn();
|
||||||
this.NotficationContainer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -221,55 +220,6 @@ export class LoginWindow extends Window {
|
|||||||
window.addEventListener('message', handleMessage, { once: true });
|
window.addEventListener('message', handleMessage, { once: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
NotficationContainer()
|
|
||||||
{
|
|
||||||
if (document.getElementById('notification-container')) return;
|
|
||||||
|
|
||||||
const container = this.createElement('div');
|
|
||||||
container.id = 'notification-container';
|
|
||||||
Object.assign(container.style, {
|
|
||||||
position: 'fixed',
|
|
||||||
top: '20px',
|
|
||||||
right: '20px',
|
|
||||||
zIndex: 1000,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
gap: '10px'
|
|
||||||
});
|
|
||||||
document.body.appendChild(container);
|
|
||||||
}
|
|
||||||
|
|
||||||
showNotification(message, color) {
|
|
||||||
const container = document.getElementById('notification-container');
|
|
||||||
if (!container) return;
|
|
||||||
|
|
||||||
const notification = document.createElement('div');
|
|
||||||
notification.textContent = message;
|
|
||||||
Object.assign(notification.style, {
|
|
||||||
backgroundColor: color,
|
|
||||||
color: 'white',
|
|
||||||
padding: '10px 20px',
|
|
||||||
borderRadius: '5px',
|
|
||||||
boxShadow: '0 2px 6px rgba(0,0,0,0.3)',
|
|
||||||
opacity: '0',
|
|
||||||
transform: 'translateY(-8px)',
|
|
||||||
transition: 'opacity 0.5s ease, transform 0.5s ease'
|
|
||||||
});
|
|
||||||
|
|
||||||
container.appendChild(notification);
|
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
notification.style.opacity = '1';
|
|
||||||
notification.style.transform = 'translateY(0)';
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
notification.style.opacity = '0';
|
|
||||||
notification.style.transform = 'translateY(-8px)';
|
|
||||||
setTimeout(() => notification.remove(), 500);
|
|
||||||
}, 2200);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a feedback message
|
* Displays a feedback message
|
||||||
* @param {string} text - Message text
|
* @param {string} text - Message text
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { Window } from '../core/windows.js';
|
||||||
|
import { API, STORAGE_KEYS, CSS } from '../core/config.js';
|
||||||
|
import { eventBus, Events } from '../core/events.js';
|
||||||
|
|
||||||
|
export class LogoutWindow extends Window {
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
name: 'logout',
|
||||||
|
title: 'Logout',
|
||||||
|
cssClasses: ['logout-window']
|
||||||
|
});
|
||||||
|
|
||||||
|
this.buildUI();
|
||||||
|
this.bindEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
buildUI() {
|
||||||
|
this.text = this.createElement('div', 'logout__text', {
|
||||||
|
text: 'Are you sure you want to log out?'
|
||||||
|
});
|
||||||
|
this.actions = this.createElement('div', 'logout__actions');
|
||||||
|
|
||||||
|
this.yesBtn = this.createElement('button', [CSS.BTN, CSS.BTN_PRIMARY], {
|
||||||
|
text: 'Yes'
|
||||||
|
});
|
||||||
|
this.noBtn = this.createElement('button', [CSS.BTN, CSS.BTN_SECONDARY], {
|
||||||
|
text: 'No'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.actions.append(this.yesBtn, this.noBtn);
|
||||||
|
this.body.append(this.text, this.actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEvents() {
|
||||||
|
this.yesBtn.addEventListener('click', () => this.confirmLogout());
|
||||||
|
this.noBtn.addEventListener('click', () => this.hide());
|
||||||
|
}
|
||||||
|
|
||||||
|
show () {
|
||||||
|
const token = localStorage.getItem(STORAGE_KEYS.AUTH_TOKEN);
|
||||||
|
if (!token) {
|
||||||
|
this.text.textContent = 'You need to login first';
|
||||||
|
this.yesBtn.style.display = 'none';
|
||||||
|
this.noBtn.textContent = 'OK';
|
||||||
|
} else {
|
||||||
|
this.text.textContent = 'Are you sure you want to log out?';
|
||||||
|
this.yesBtn.style.display = 'inline-flex';
|
||||||
|
this.noBtn.textContent = 'No';
|
||||||
|
}
|
||||||
|
super.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
async confirmLogout() {
|
||||||
|
const token = localStorage.getItem(STORAGE_KEYS.AUTH_TOKEN);
|
||||||
|
if (token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await fetch(API.AUTH.LOGOUT, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Authorization': `Bearer ${token}` }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (err)
|
||||||
|
{
|
||||||
|
console.warn('Logout failed:', err);
|
||||||
|
this.showNotification('Logout failed. Please try again.', 'red');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
localStorage.removeItem(STORAGE_KEYS.AUTH_TOKEN);
|
||||||
|
eventBus.emit(Events.USER_LOGGED_OUT);
|
||||||
|
setTimeout(() => window.location.reload(), 500);
|
||||||
|
this.showNotification('You have been logged out successfully.', 'green');
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user