@@ -0,0 +1,10 @@
|
||||
<%- include('partials/page-start') %>
|
||||
|
||||
<section class="panel coming-soon-page">
|
||||
<div class="coming-soon-icon">⏳</div>
|
||||
<h1><%= heading %></h1>
|
||||
<p><%= description %></p>
|
||||
<a class="btn" href="/">Retour au dashboard</a>
|
||||
</section>
|
||||
|
||||
<%- include('partials/page-end') %>
|
||||
@@ -0,0 +1,9 @@
|
||||
<%- include('partials/page-start') %>
|
||||
|
||||
<section class="panel error-page">
|
||||
<h1><%= title %></h1>
|
||||
<p><%= message %></p>
|
||||
<a class="btn" href="/">Retour au dashboard</a>
|
||||
</section>
|
||||
|
||||
<%- include('partials/page-end') %>
|
||||
@@ -0,0 +1,106 @@
|
||||
<%- include('partials/page-start') %>
|
||||
|
||||
<%- include('partials/page-header', { title, subtitle: 'Vue d\'ensemble du serveur en temps réel' }) %>
|
||||
|
||||
<section class="stats-grid">
|
||||
<article class="stat-card">
|
||||
<span class="stat-label">Joueurs en ligne</span>
|
||||
<strong class="stat-value accent"><%= fmt.formatNumber(stats.onlinePlayers) %></strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span class="stat-label">Joueurs inscrits</span>
|
||||
<strong class="stat-value"><%= fmt.formatNumber(stats.totalPlayers) %></strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span class="stat-label">Niveau moyen</span>
|
||||
<strong class="stat-value"><%= stats.averageLevel %></strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span class="stat-label">Money en circulation</span>
|
||||
<strong class="stat-value"><%= fmt.formatMoney(stats.totalMoney) %></strong>
|
||||
</article>
|
||||
<article class="stat-card">
|
||||
<span class="stat-label">Groupes actifs</span>
|
||||
<strong class="stat-value"><%= fmt.formatNumber(stats.totalGroups) %></strong>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-header">
|
||||
<h2>Joueurs connectés</h2>
|
||||
<span class="badge"><%= onlinePlayers.length %></span>
|
||||
</div>
|
||||
<% if (onlinePlayers.length === 0) { %>
|
||||
<p class="empty">Aucun joueur connecté pour le moment.</p>
|
||||
<% } else { %>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Joueur</th>
|
||||
<th>Niveau</th>
|
||||
<th>Classe</th>
|
||||
<th>Race</th>
|
||||
<th>Money</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% onlinePlayers.forEach(function(p) { %>
|
||||
<tr>
|
||||
<td>
|
||||
<span class="player-name">
|
||||
<span class="status-dot online inline"></span>
|
||||
<%= p.display_name %>
|
||||
</span>
|
||||
</td>
|
||||
<td><%= p.level %></td>
|
||||
<td><%= p.classLabel %></td>
|
||||
<td><%= p.raceLabel %></td>
|
||||
<td><%= fmt.formatMoney(p.money) %></td>
|
||||
<td><a class="link" href="/players/<%= p.uuid %>">Profil</a></td>
|
||||
</tr>
|
||||
<% }); %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% } %>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-header">
|
||||
<h2>Dernières connexions</h2>
|
||||
</div>
|
||||
<% if (recentPlayers.length === 0) { %>
|
||||
<p class="empty">Aucun joueur enregistré.</p>
|
||||
<% } else { %>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Joueur</th>
|
||||
<th>Niveau</th>
|
||||
<th>Statut</th>
|
||||
<th>Dernière connexion</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% recentPlayers.forEach(function(p) { %>
|
||||
<tr>
|
||||
<td><%= p.display_name %></td>
|
||||
<td><%= p.level %></td>
|
||||
<td>
|
||||
<% if (p.isOnline) { %>
|
||||
<span class="tag tag-online">En ligne</span>
|
||||
<% } else { %>
|
||||
<span class="tag tag-offline">Hors ligne</span>
|
||||
<% } %>
|
||||
</td>
|
||||
<td><%= fmt.formatTimestamp(p.last_date_connected) %></td>
|
||||
<td><a class="link" href="/players/<%= p.uuid %>">Profil</a></td>
|
||||
</tr>
|
||||
<% }); %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% } %>
|
||||
</section>
|
||||
|
||||
<%- include('partials/page-end') %>
|
||||
@@ -0,0 +1,4 @@
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,8 @@
|
||||
<header class="page-header">
|
||||
<div>
|
||||
<h1><%= title %></h1>
|
||||
<% if (typeof subtitle !== 'undefined' && subtitle) { %>
|
||||
<p class="subtitle"><%= subtitle %></p>
|
||||
<% } %>
|
||||
</div>
|
||||
</header>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><%= title %> · Orion.ovh</title>
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="layout">
|
||||
<aside class="sidebar">
|
||||
<div class="brand">
|
||||
<span class="brand-mark">O</span>
|
||||
<div>
|
||||
<strong>Orion.ovh</strong>
|
||||
<small>Serveur MMORPG</small>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="nav">
|
||||
<% nav.forEach(function(item) { %>
|
||||
<a href="<%= item.href %>" class="nav-link<%= page === item.key ? ' active' : '' %>">
|
||||
<%= item.label %>
|
||||
</a>
|
||||
<% }); %>
|
||||
</nav>
|
||||
<div class="sidebar-footer">
|
||||
<% if (sessionUser) { %>
|
||||
<span class="status-dot online"></span>
|
||||
<span><%= sessionUser.displayName %></span>
|
||||
<% } else if (dbStatus.exists) { %>
|
||||
<span class="status-dot online"></span> Base connectée
|
||||
<% } else { %>
|
||||
<span class="status-dot offline"></span> Base indisponible
|
||||
<% } %>
|
||||
</div>
|
||||
</aside>
|
||||
<main class="main">
|
||||
<% if (!dbStatus.exists) { %>
|
||||
<div class="alert alert-warning">
|
||||
Fichier <code>mmorpg.db</code> introuvable. Démarrez le serveur Hytale pour initialiser la base à la racine du monorepo.
|
||||
</div>
|
||||
<% } %>
|
||||
@@ -0,0 +1,80 @@
|
||||
<div class="profile-layout">
|
||||
<section class="panel profile-card">
|
||||
<div class="profile-head">
|
||||
<div class="avatar"><%= player.display_name.charAt(0).toUpperCase() %></div>
|
||||
<div>
|
||||
<h2><%= player.display_name %></h2>
|
||||
<p class="muted"><%= player.uuid %></p>
|
||||
<% if (player.isOnline) { %>
|
||||
<span class="tag tag-online">En ligne</span>
|
||||
<% } else { %>
|
||||
<span class="tag tag-offline">Hors ligne</span>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dl class="detail-grid">
|
||||
<div>
|
||||
<dt>Niveau</dt>
|
||||
<dd><%= player.level %></dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Expérience</dt>
|
||||
<dd><%= fmt.formatNumber(player.experience) %></dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Classe</dt>
|
||||
<dd><%= player.classLabel %></dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Race</dt>
|
||||
<dd><%= player.raceLabel %></dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Money</dt>
|
||||
<dd><%= fmt.formatMoney(player.money) %></dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Temps de jeu</dt>
|
||||
<dd><%= fmt.formatDuration(player.total_time_play) %></dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Création du compte</dt>
|
||||
<dd><%= fmt.formatTimestamp(player.date_creation || player.created_at) %></dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Dernière connexion</dt>
|
||||
<dd><%= fmt.formatTimestamp(player.last_date_connected) %></dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<% if (player.jobs.length > 0) { %>
|
||||
<div class="chip-list">
|
||||
<span class="chip-label">Métiers</span>
|
||||
<% player.jobs.forEach(function(job) { %>
|
||||
<span class="chip"><%= job %></span>
|
||||
<% }); %>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<% if (player.powers.length > 0) { %>
|
||||
<div class="chip-list">
|
||||
<span class="chip-label">Pouvoirs</span>
|
||||
<% player.powers.forEach(function(power) { %>
|
||||
<span class="chip"><%= power %></span>
|
||||
<% }); %>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<% if (group) { %>
|
||||
<p class="group-info">Membre du groupe <code><%= group.id %></code></p>
|
||||
<% } %>
|
||||
</section>
|
||||
|
||||
<% if (typeof comingSoon !== 'undefined' && comingSoon) { %>
|
||||
<section class="panel coming-soon">
|
||||
<h2>Fonctionnalités à venir</h2>
|
||||
<p>Inventaire, compétences détaillées, historique de combat et statistiques avancées seront disponibles prochainement sur cette page.</p>
|
||||
</section>
|
||||
<% } %>
|
||||
</div>
|
||||
@@ -0,0 +1,49 @@
|
||||
<%- include('../partials/page-start') %>
|
||||
|
||||
<%- include('../partials/page-header', { title, subtitle: players.length + ' joueur(s) enregistré(s)' }) %>
|
||||
|
||||
<section class="panel">
|
||||
<% if (players.length === 0) { %>
|
||||
<p class="empty">Aucun joueur trouvé dans la base de données.</p>
|
||||
<% } else { %>
|
||||
<table class="table table-clickable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Joueur</th>
|
||||
<th>Niveau</th>
|
||||
<th>XP</th>
|
||||
<th>Classe</th>
|
||||
<th>Race</th>
|
||||
<th>Money</th>
|
||||
<th>Statut</th>
|
||||
<th>Temps de jeu</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% players.forEach(function(p) { %>
|
||||
<tr class="row-link" onclick="window.location='/players/<%= p.uuid %>'">
|
||||
<td>
|
||||
<span class="player-name"><%= p.display_name %></span>
|
||||
<small class="muted"><%= p.uuid %></small>
|
||||
</td>
|
||||
<td><%= p.level %></td>
|
||||
<td><%= fmt.formatNumber(p.experience) %></td>
|
||||
<td><%= p.classLabel %></td>
|
||||
<td><%= p.raceLabel %></td>
|
||||
<td><%= fmt.formatMoney(p.money) %></td>
|
||||
<td>
|
||||
<% if (p.isOnline) { %>
|
||||
<span class="tag tag-online">En ligne</span>
|
||||
<% } else { %>
|
||||
<span class="tag tag-offline">Hors ligne</span>
|
||||
<% } %>
|
||||
</td>
|
||||
<td><%= fmt.formatDuration(p.total_time_play) %></td>
|
||||
</tr>
|
||||
<% }); %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% } %>
|
||||
</section>
|
||||
|
||||
<%- include('../partials/page-end') %>
|
||||
@@ -0,0 +1,9 @@
|
||||
<%- include('../partials/page-start') %>
|
||||
|
||||
<%- include('../partials/page-header', { title: player.display_name, subtitle: 'Profil joueur' }) %>
|
||||
|
||||
<%- include('../partials/player-card', { player, group, comingSoon }) %>
|
||||
|
||||
<p class="back-link"><a class="link" href="/players">← Retour à la liste</a></p>
|
||||
|
||||
<%- include('../partials/page-end') %>
|
||||
@@ -0,0 +1,19 @@
|
||||
<%- include('../partials/page-start') %>
|
||||
|
||||
<%- include('../partials/page-header', { title: 'Mon profil', subtitle: 'Connecté en tant que ' + player.display_name }) %>
|
||||
|
||||
<div class="profile-actions">
|
||||
<form method="post" action="/profile/logout">
|
||||
<button type="submit" class="btn btn-secondary">Se déconnecter</button>
|
||||
</form>
|
||||
<a class="link" href="/players/<%= player.uuid %>">Voir le profil public</a>
|
||||
</div>
|
||||
|
||||
<%- include('../partials/player-card', { player, group, comingSoon: false }) %>
|
||||
|
||||
<section class="panel coming-soon">
|
||||
<h2>À venir</h2>
|
||||
<p>Inventaire, historique de combat et gestion avancée du compte seront ajoutés prochainement.</p>
|
||||
</section>
|
||||
|
||||
<%- include('../partials/page-end') %>
|
||||
@@ -0,0 +1,38 @@
|
||||
<%- include('../partials/page-start') %>
|
||||
|
||||
<%- include('../partials/page-header', { title: 'Mon profil', subtitle: 'Connexion à Orion.ovh' }) %>
|
||||
|
||||
<section class="panel auth-panel">
|
||||
<% if (needsMigration) { %>
|
||||
<div class="alert alert-warning">
|
||||
La colonne <code>password_hash</code> est absente. Redémarrez le serveur Hytale pour appliquer la migration.
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<% if (error) { %>
|
||||
<div class="alert alert-error"><%= error %></div>
|
||||
<% } %>
|
||||
|
||||
<form class="auth-form" method="post" action="/profile/login">
|
||||
<label>
|
||||
Pseudo
|
||||
<input type="text" name="pseudo" value="<%= pseudo %>" autocomplete="username" required autofocus>
|
||||
</label>
|
||||
<label>
|
||||
Mot de passe
|
||||
<input type="password" name="password" autocomplete="current-password" required>
|
||||
</label>
|
||||
<button type="submit" class="btn btn-block">Se connecter</button>
|
||||
</form>
|
||||
|
||||
<div class="auth-help">
|
||||
<h2>Première connexion ?</h2>
|
||||
<p>
|
||||
Rejoignez le serveur Hytale, puis définissez votre mot de passe web en jeu :
|
||||
</p>
|
||||
<pre><code>/mmorpg password MonMotDePasse</code></pre>
|
||||
<p class="muted">Minimum <%= auth.MIN_PASSWORD_LENGTH %> caractères. Vous pourrez le modifier à tout moment avec la même commande.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%- include('../partials/page-end') %>
|
||||
Reference in New Issue
Block a user