chore(renamed): removed froxy prefix on folders
This commit is contained in:
parent
e57bb34a35
commit
6960959794
29 changed files with 32 additions and 12 deletions
30
templates/src/friends.rs
Normal file
30
templates/src/friends.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
pub use crate::templates::FRIENDS;
|
||||
|
||||
pub fn add_to_context(env: &mut minijinja::Environment) {
|
||||
if env.get_template("template.html").is_err() {
|
||||
crate::meta_template::add_to_context(env);
|
||||
}
|
||||
if env.get_template("open_modal.html").is_err() {
|
||||
crate::open_modal::add_to_context(env);
|
||||
}
|
||||
|
||||
env.add_template("friends.html", FRIENDS).unwrap();
|
||||
}
|
||||
|
||||
pub fn render(env: &minijinja::Environment, data: FriendsData) -> Result<String, minijinja::Error> {
|
||||
let template = env.get_template("friends.html")?;
|
||||
template.render(data)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Friend {
|
||||
pub name: smol_str::SmolStr,
|
||||
pub image: String,
|
||||
pub position: Option<smol_str::SmolStr>,
|
||||
pub last_active: Option<smol_str::SmolStr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FriendsData {
|
||||
pub friends: Vec<Friend>,
|
||||
}
|
||||
121
templates/src/index.rs
Normal file
121
templates/src/index.rs
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use smol_str::SmolStr;
|
||||
|
||||
pub use crate::templates::INDEX;
|
||||
|
||||
pub fn add_to_context(env: &mut minijinja::Environment) {
|
||||
if env.get_template("template.html").is_err() {
|
||||
crate::meta_template::add_to_context(env);
|
||||
}
|
||||
if env.get_template("open_modal.html").is_err() {
|
||||
crate::open_modal::add_to_context(env);
|
||||
}
|
||||
env.add_template("index.html", INDEX).unwrap();
|
||||
env.add_function("max", |a: i32, b: i32| -> i32 { a.max(b) });
|
||||
}
|
||||
|
||||
pub fn render(env: &minijinja::Environment, data: IndexData) -> Result<String, minijinja::Error> {
|
||||
let template = env.get_template("index.html")?;
|
||||
template.render(data)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LocationData {
|
||||
pub login: SmolStr,
|
||||
pub image: String,
|
||||
pub me: bool,
|
||||
pub friend: bool,
|
||||
pub close_friend: bool,
|
||||
pub pooled: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ClusterData {
|
||||
pub users: u32,
|
||||
pub dead_pc: u32,
|
||||
pub max_pc: u32,
|
||||
pub silent: bool,
|
||||
pub piscine: bool,
|
||||
pub map: Map,
|
||||
pub issues: HashMap<SmolStr, PcIssue>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(try_from = "SmolStr", into = "SmolStr")]
|
||||
pub enum PcIssue {
|
||||
#[default]
|
||||
Dead,
|
||||
Attention,
|
||||
}
|
||||
|
||||
impl TryFrom<SmolStr> for PcIssue {
|
||||
type Error = &'static str;
|
||||
fn try_from(value: SmolStr) -> Result<Self, &'static str> {
|
||||
Ok(match value.as_str() {
|
||||
"dead" => Self::Dead,
|
||||
"attention" => Self::Attention,
|
||||
_ => return Err("Invalid string"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PcIssue> for SmolStr {
|
||||
fn from(val: PcIssue) -> Self {
|
||||
match val {
|
||||
PcIssue::Dead => SmolStr::new_static("dead"),
|
||||
PcIssue::Attention => SmolStr::new_static("attention"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(from = "SmolStr", into = "SmolStr")]
|
||||
pub enum MapPos {
|
||||
Wall,
|
||||
Flex,
|
||||
Empty,
|
||||
Door,
|
||||
Pc(SmolStr),
|
||||
}
|
||||
|
||||
impl From<SmolStr> for MapPos {
|
||||
fn from(value: SmolStr) -> Self {
|
||||
match value.as_str() {
|
||||
"x" => Self::Wall,
|
||||
"f" => Self::Flex,
|
||||
"" => Self::Empty,
|
||||
"d" => Self::Door,
|
||||
_ => Self::Pc(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<MapPos> for SmolStr {
|
||||
fn from(val: MapPos) -> Self {
|
||||
match val {
|
||||
MapPos::Wall => SmolStr::new_static("x"),
|
||||
MapPos::Flex => SmolStr::new_static("f"),
|
||||
MapPos::Door => SmolStr::new_static("d"),
|
||||
MapPos::Empty => SmolStr::new_static(""),
|
||||
MapPos::Pc(s) => s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct MapRow {
|
||||
pub range: SmolStr,
|
||||
pub col: Vec<MapPos>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Map {
|
||||
pub rows: Vec<MapRow>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct IndexData {
|
||||
pub locations: HashMap<SmolStr, LocationData>,
|
||||
pub clusters: HashMap<SmolStr, ClusterData>,
|
||||
pub current_cluster: SmolStr,
|
||||
}
|
||||
9
templates/src/lib.rs
Normal file
9
templates/src/lib.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
mod templates;
|
||||
|
||||
pub mod friends;
|
||||
pub mod index;
|
||||
pub mod meta_template;
|
||||
pub mod open_modal;
|
||||
pub mod profile;
|
||||
5
templates/src/meta_template.rs
Normal file
5
templates/src/meta_template.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub use crate::templates::TEMPLATE;
|
||||
|
||||
pub fn add_to_context(env: &mut minijinja::Environment) {
|
||||
env.add_template("template.html", TEMPLATE).unwrap();
|
||||
}
|
||||
5
templates/src/open_modal.rs
Normal file
5
templates/src/open_modal.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub use crate::templates::OPEN_MODAL as TEMPLATE;
|
||||
|
||||
pub fn add_to_context(env: &mut minijinja::Environment) {
|
||||
env.add_template("open_modal.html", TEMPLATE).unwrap();
|
||||
}
|
||||
41
templates/src/profile.rs
Normal file
41
templates/src/profile.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
use smol_str::SmolStr;
|
||||
|
||||
pub use crate::templates::PROFILE as TEMPLATE;
|
||||
|
||||
pub fn add_to_context(env: &mut minijinja::Environment) {
|
||||
if env.get_template("template.html").is_err() {
|
||||
crate::meta_template::add_to_context(env);
|
||||
}
|
||||
if env.get_template("open_modal.html").is_err() {
|
||||
crate::open_modal::add_to_context(env);
|
||||
}
|
||||
|
||||
env.add_template("profile.html", TEMPLATE).unwrap();
|
||||
}
|
||||
|
||||
pub fn render(env: &minijinja::Environment, data: ProfileData) -> Result<String, minijinja::Error> {
|
||||
let template = env.get_template("profile.html")?;
|
||||
template.render(data)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ProfileUser {
|
||||
pub position: Option<SmolStr>,
|
||||
pub last_active: Option<SmolStr>,
|
||||
pub image: String,
|
||||
pub name: SmolStr,
|
||||
pub pool: Option<SmolStr>,
|
||||
pub is_friend: bool,
|
||||
|
||||
pub github: Option<String>,
|
||||
pub website: Option<String>,
|
||||
pub discord: Option<String>,
|
||||
|
||||
/// profile description
|
||||
pub recit: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ProfileData {
|
||||
pub user: ProfileUser,
|
||||
}
|
||||
145
templates/src/templates/friends.html
Normal file
145
templates/src/templates/friends.html
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
{% extends 'template.html' %}
|
||||
{% block css %}
|
||||
<link href="/static/css/friends.css?v={{ version }}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="modal fade" id="addFriendModal" tabindex="-1" aria-labelledby="addFriendLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="addFriendLabel">Ajouter un ami</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text" id="loginAddon">Login</span>
|
||||
<input autofocus list="suggestions" type="text" class="form-control" id="addFriendInput"
|
||||
aria-label="Login42" aria-describedby="loginAddon" placeholder="ami1, ami2, ami3">
|
||||
<datalist id="suggestions"></datalist>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
|
||||
<button type="button" class="btn btn-primary" id="addFriendButton">
|
||||
<i class="fa-solid fa-user-plus"></i> <i hidden class="spinner-border spinner-border-sm"></i>
|
||||
Ajouter en ami
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'open_modal.html' %}
|
||||
|
||||
<div class="container">
|
||||
<div class="row text-center justify-content-center">
|
||||
{% for friend in friends %}
|
||||
<div id="case-{{ friend.name }}" class="card shadow m-2 pl-fix card-size grow"
|
||||
onclick="openFriend('{{ friend.name }}', true)">
|
||||
<img src="{{ friend.image | safe }}" class="m-1 card-img-top card-img-size"
|
||||
alt="{{ friend.name }}'s image">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="fa-solid {{ "fa-2xs fa-circle online" if friend.position else "fa-xs fa-person-walking offline" }}"></i> {{ friend.name }}
|
||||
</h5>
|
||||
{%- set position = "Absent" -%}
|
||||
{% if friend.position %}
|
||||
{%- set position = friend.position -%}
|
||||
{% endif%}
|
||||
{%- set last_active = "" -%}
|
||||
{% if friend.last_active %}
|
||||
{%- set last_active = friend.last_active -%}
|
||||
{% endif%}
|
||||
<p class="card-text">{{ position }} {{ last_active }}
|
||||
{% if friend.position %}
|
||||
<a class="fa-solid fa-users-viewfinder" href="/goto/{{ friend.position }}"></a>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="card p-0 m-2 shadow card-size grow" onclick="openAddFriend();">
|
||||
<div class="m-1 w-100 card-img-top text-center card-not-img-size">
|
||||
<i class="fa-solid fa-plus fa-5x"></i>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Ajouter un ami</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
let openAddFriendModal;
|
||||
let openFriendModal;
|
||||
let addFriendInput = document.getElementById('addFriendInput');
|
||||
let addFriendButton = document.getElementById('addFriendButton');
|
||||
|
||||
function fillSuggestions(element, suggestions) {
|
||||
let list = document.querySelector(element);
|
||||
|
||||
list.innerHTML = ''
|
||||
suggestions.forEach(function (item) {
|
||||
if (item['type'] !== "user") return;
|
||||
let option = document.createElement('option');
|
||||
option.value = item['v'];
|
||||
list.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
function openAddFriend() {
|
||||
const modal = new bootstrap.Modal('#addFriendModal', {});
|
||||
openAddFriendModal = modal;
|
||||
modal.show();
|
||||
setTimeout(() => {
|
||||
addFriendInput.focus();
|
||||
addFriendInput.select();
|
||||
}, 500)
|
||||
}
|
||||
|
||||
async function deleteLocalFriend() {
|
||||
let friend_name = document.getElementById('openFriendLabel').innerText.trim();
|
||||
let resp = await deleteFriend(friend_name, "#deleteFriend")
|
||||
if (resp === 200) {
|
||||
openFriendModal.hide();
|
||||
document.getElementById('case-' + friend_name).remove();
|
||||
}
|
||||
}
|
||||
|
||||
addFriendInput.addEventListener('keyup', function (key) {
|
||||
let name = addFriendInput.value.trim().toLowerCase();
|
||||
|
||||
if (key.key === 'Enter') {
|
||||
if (name.length <= 3) {
|
||||
addFriendInput.focus();
|
||||
return;
|
||||
}
|
||||
addFriend(name, '#addFriendButton', true);
|
||||
fillSuggestions('#suggestions', []);
|
||||
}
|
||||
if (name.trim() === '') return fillSuggestions('#suggestions', []);
|
||||
if (name.length < 3) return;
|
||||
setTimeout(() => {
|
||||
if (name !== addFriendInput.value) return;
|
||||
fetch('/search/' + encodeURIComponent(name) + "/1").then((response) => {
|
||||
response.json().then((json) => {
|
||||
fillSuggestions('#suggestions', json)
|
||||
})
|
||||
})
|
||||
}, 200)
|
||||
})
|
||||
|
||||
addFriendButton.addEventListener('click', function () {
|
||||
let val = addFriendInput.value.trim().toLowerCase();
|
||||
if (val.length <= 3) {
|
||||
addFriendInput.focus();
|
||||
return;
|
||||
}
|
||||
addFriend(val, '#addFriendButton', true)
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
238
templates/src/templates/index.html
Normal file
238
templates/src/templates/index.html
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
{% extends 'template.html' %}
|
||||
{% block css %}
|
||||
<link href="/static/css/friends.css?v={{ version }}" rel="stylesheet">
|
||||
<link href="/static/css/index.css?v={{ version }}" rel="stylesheet">
|
||||
<style>
|
||||
@media (max-width: 800px) {
|
||||
.btn-width {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-width {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.range-no-color {
|
||||
min-width: 0 !important;
|
||||
}
|
||||
|
||||
.btn-check:checked+.btn-outline-warning > .text-muted {
|
||||
color: black!important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% include 'open_modal.html' %}
|
||||
<div class="modal fade" id="issueModal" tabindex="-1" aria-labelledby="issueModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="issueModalLabel">Signalement dumps</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close report"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Le dump <span id="dumpIdIssueModal"></span> ne fonctionne pas comme prévu ? Signalez le problème
|
||||
pour avertir que la place n'est pas
|
||||
libre
|
||||
<div class="form-check" id="issueForm">
|
||||
<input class="form-check-input" type="radio" data-id="1" name="issueRadios" id="issueRadios1">
|
||||
<label class="form-check-label" for="issueRadios1">
|
||||
Le dump est inutilisable
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" data-id="2" name="issueRadios" id="issueRadios2">
|
||||
<label class="form-check-label" for="issueRadios2">
|
||||
Le dump fonctionne, mais l'écran ou le clavier est dégradé
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
|
||||
<button type="button" class="btn btn-primary" id="submitIssue">Valider</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<div class="btn-group mb-1 btn-width" role="group" aria-label="Change cluster">
|
||||
{% for name in clusters %}
|
||||
<input type="radio" class="btn-check cluster_radios" name="btn-radio"
|
||||
data-cluster="{{ name }}" id="radio{{ name }}"
|
||||
autocomplete="off">
|
||||
{% set cluster = clusters[name] %}
|
||||
{% set piscine = "" %}
|
||||
{% set silent = "" %}
|
||||
{% set spacer = "" %}
|
||||
|
||||
{%- if cluster.piscine -%}
|
||||
{% set piscine = '<i class="fa-solid fa-person-swimming text-info"></i>' %}
|
||||
{%- endif -%}
|
||||
{%- if cluster.silent -%}
|
||||
{% set piscine = '<i class="fa-solid fa-volume-xmark text-secondary"></i>' %}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if cluster.piscine and cluster.silent -%}
|
||||
{% set spacer = " " %}
|
||||
{%- endif -%}
|
||||
|
||||
{% set u = cluster.users %}
|
||||
{% set m = cluster.max_pc - cluster.dead_pc %}
|
||||
|
||||
{%- if m < 0 -%}
|
||||
{% set m = 0 %}
|
||||
{%- endif -%}
|
||||
|
||||
{% set percent = 0 %}
|
||||
{%- if m > 0 -%}
|
||||
{% set percent = (u * 100) / m %}
|
||||
{%- endif -%}
|
||||
|
||||
{% set fill_level = 'primary' %}
|
||||
{%- if percent > 75 -%}
|
||||
{% set fill_level = 'danger' %}
|
||||
{%- elif percent > 65 -%}
|
||||
{% set fill_level = 'warning' %}
|
||||
{% endif%}
|
||||
|
||||
<label title="{{ percent | int }}%" class="btn btn-outline-{{ fill_level }} m-t" for="radio{{ name }}">
|
||||
{{ piscine | safe }}{{ spacer | safe }}{{ silent | safe }} {{ name }}
|
||||
<span class="text-muted">{{ u }}/{{ m }}</span></label>
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-fluid mt-2 scroll p-0">
|
||||
<table class="grid text-center">
|
||||
<tbody>
|
||||
|
||||
{% set map = clusters[current_cluster].map %}
|
||||
{% set cluster = clusters[current_cluster] %}
|
||||
{% for row in map.rows %}
|
||||
<tr>
|
||||
{%- set countB = 0 -%}
|
||||
<td class="range">{{ row.range }}</td>
|
||||
{%- for col in row.col -%}
|
||||
{%- if col == 'x' -%}
|
||||
<td class="range"> </td>
|
||||
{%- elif col == '|' -%}
|
||||
<td class="range-no-color"></td>
|
||||
{%- elif col == 'h' -%}
|
||||
<td></td>
|
||||
{%- elif col == 'd' -%}
|
||||
<td><i class="fa-solid fa-person-walking fa-2xl"></i></td>
|
||||
{%- elif col == 'f' -%}
|
||||
<td><i class="fa-solid fa-laptop fa-xl"></i></td>
|
||||
{%- elif col == '' -%}
|
||||
<td class="small-colors"></td>
|
||||
{%- else -%}
|
||||
{%- set text_color = '' -%}
|
||||
{%- set img = '<i class="fa-solid fa-user"></i>' -%}
|
||||
{%- if col in locations -%}
|
||||
{%- set img = '<img loading="lazy" class="profile-pic2" alt="" src="' + locations[col].image + '">' -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- set relation = '' -%}
|
||||
{%- if focus and focus == col %}
|
||||
{%- set relation = 'focus' -%}
|
||||
{%- elif col in locations and locations[col].me -%}
|
||||
{%- set relation = 'me' -%}
|
||||
{%- elif col in locations and locations[col].close_friend -%}
|
||||
{%- set relation = 'close_friend' -%}
|
||||
{%- elif col in locations and locations[col].friend -%}
|
||||
{%- set relation = 'friend' -%}
|
||||
{%- elif col in cluster.issues -%}
|
||||
{%- set relation = cluster.issues[col] -%}
|
||||
{%- elif col in locations and locations[col]['pool'] and not cluster.piscine -%}
|
||||
{%- set relation = 'pooled' -%}
|
||||
{%- endif -%}
|
||||
|
||||
{% set loc_name = col|replace(current_cluster, "") %}
|
||||
<td data-pos="{{ col }}"
|
||||
data-login="{{ locations[col].login if col in locations else '' }}"
|
||||
class="sm-t case {{ relation }}">
|
||||
{%- if countB % 2 == 0 -%}
|
||||
{{ img|safe }}<br>{{ loc_name }}
|
||||
{%- else -%}
|
||||
{{ loc_name }}<br>{{ img|safe }}
|
||||
{%- endif -%}
|
||||
</td>
|
||||
{%- endif -%}
|
||||
{%- set countB = (countB + 1) % 2 -%}
|
||||
{%- endfor -%}
|
||||
<td class="range">{{ row.range }}</td>
|
||||
</tr>
|
||||
{%- endfor -%}
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="tooltip" hidden class="rounded bg-white zindex9999">
|
||||
<img class="profile-pic rounded no-shadow" alt="pp">
|
||||
<div class="fluid-container text-center text-black name">name</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/js/popper.min.js?v={{ version }}" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
let tooltip = document.getElementById('tooltip')
|
||||
let cases = document.querySelectorAll('.case');
|
||||
cases.forEach(e => {
|
||||
e.addEventListener('click', () => {
|
||||
let login = e.dataset.login
|
||||
let dump = e.dataset.pos
|
||||
if (login.length === 0) {
|
||||
const modal = new bootstrap.Modal('#issueModal', {});
|
||||
document.getElementById('submitIssue').onclick = async function () {
|
||||
let checked = document.querySelector('input[name="issueRadios"]:checked');
|
||||
if (!checked) {
|
||||
document.getElementById('issueRadios2').focus()
|
||||
return;
|
||||
}
|
||||
let resp = checked.dataset.id;
|
||||
fetch(`/addissue/${dump}/${resp}`).then(resp => {
|
||||
if (resp.status === 200) {
|
||||
triggerToast('Merci de votre signalement ! :)', true, false);
|
||||
modal.hide();
|
||||
} else {
|
||||
triggerToast("Une erreur s'est produite lors du signalement", false, false);
|
||||
}
|
||||
})
|
||||
}
|
||||
document.getElementById('dumpIdIssueModal').innerText = dump;
|
||||
modal.show();
|
||||
return;
|
||||
}
|
||||
openFriend(login, true)
|
||||
})
|
||||
|
||||
if (!e.dataset.login)
|
||||
return;
|
||||
e.addEventListener('mouseover', () => {
|
||||
if (window.isTouchDevice && isTouchDevice()) return;
|
||||
if (!window.Popper) return;
|
||||
tooltip.hidden = false;
|
||||
tooltip.querySelector('img').src = e.querySelector('img').src
|
||||
tooltip.querySelector('.name').innerText = e.dataset.login
|
||||
Popper.createPopper(e, tooltip, {
|
||||
placement: 'right'
|
||||
})
|
||||
})
|
||||
|
||||
e.addEventListener('mouseleave', () => {
|
||||
tooltip.hidden = true;
|
||||
});
|
||||
});
|
||||
|
||||
// Change cluster buttons
|
||||
let radio_clusters = document.querySelectorAll('.cluster_radios')
|
||||
radio_clusters.forEach(e => {
|
||||
e.addEventListener('click', () => {
|
||||
let cluster = e.dataset.cluster
|
||||
location.href = "/?cluster=" + cluster
|
||||
})
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
11
templates/src/templates/mod.rs
Normal file
11
templates/src/templates/mod.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
macro_rules! template {
|
||||
($name:ident, $path:literal) => {
|
||||
pub static $name: &'static str = include_str!($path);
|
||||
};
|
||||
}
|
||||
|
||||
template!(FRIENDS, "./friends.html");
|
||||
template!(INDEX, "./index.html");
|
||||
template!(OPEN_MODAL, "./open_modal.html");
|
||||
template!(PROFILE, "./profile.html");
|
||||
template!(TEMPLATE, "./template.html");
|
||||
76
templates/src/templates/open_modal.html
Normal file
76
templates/src/templates/open_modal.html
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<div class="modal fade" id="openFriendModal" tabindex="-1" aria-labelledby="openFriendLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5 me-2" id="openFriendLabel"></h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<!-- PP - Name - Pool -->
|
||||
<div class="modal-body">
|
||||
<div class="mb-3 text-center">
|
||||
<img alt="" class="rounded-pill profile-pic" style="height:9rem" id="openFriendModalPic" src="">
|
||||
<div class="display-inline-grid ms-3">
|
||||
<span id="openFriendModalName">
|
||||
<h5 class="name display-5 fw-bold"></h5>
|
||||
<span id="addCloseFriend">
|
||||
<i class="fa-regular fa-star text-warning"></i>
|
||||
<i hidden class="spinner-border spinner-border-sm"></i>
|
||||
</span>
|
||||
<span id="removeCloseFriend">
|
||||
<i class="fa-solid fa-star text-warning"></i>
|
||||
<i hidden class="spinner-border spinner-border-sm"></i>
|
||||
</span>
|
||||
<span class="fs-6 text-muted pool">Piscine de</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Bio & Socials -->
|
||||
<div hidden class="rounded bg-dark-subtle m-2 p-1" id="FP-bio">
|
||||
<div hidden class="row text-center" id="FP-socials-row">
|
||||
<div hidden class="col no-wrap" id="FP-github">
|
||||
<i class="fa-brands fa-github icon-length"></i>
|
||||
<a class="{{ 'no-click' if kiosk else '' }}" href=""></a>
|
||||
</div>
|
||||
<div hidden class="col no-wrap" id="FP-website">
|
||||
<i class="fa-solid fa-globe icon-length"></i>
|
||||
<a class="{{ 'no-click' if kiosk else '' }}" href=""></a>
|
||||
</div>
|
||||
<div hidden class="col no-wrap" id="FP-discord">
|
||||
<i class="fa-brands fa-discord icon-length"></i> <a></a>
|
||||
</div>
|
||||
</div>
|
||||
<div hidden class="div text-center m-1 justify" style="text-align: justify" id="FP-text"></div>
|
||||
</div>
|
||||
<!-- Buttons -->
|
||||
<div class="row text-center">
|
||||
<div class="col-md mb-1">
|
||||
<button type="button" class="btn btn-sm btn-primary" id="openFriendShowCluster">
|
||||
<i class="fa-solid fa-magnifying-glass"></i> Afficher dans le Cluster
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md mb-1">
|
||||
<a id="openFriendProfile">
|
||||
<button type="button" class="btn btn-sm btn-secondary">
|
||||
<i class="fa-solid fa-circle-info"></i> Voir l'intra de l'utilisateur
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md mb-1">
|
||||
<button hidden type="button" class="btn btn-sm btn-danger" id="openFriendLabelDeleteFriend">
|
||||
<i class="fa-solid fa-user-minus"></i>
|
||||
<i hidden class="spinner-border spinner-border-sm"></i>
|
||||
Supprimer des contacts
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-success" id="openFriendLabelAddFriend">
|
||||
<i class="fa-solid fa-user-plus"></i><i hidden class="spinner-border spinner-border-sm"></i>
|
||||
Ajouter au contacts
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
109
templates/src/templates/profile.html
Normal file
109
templates/src/templates/profile.html
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
{% extends 'template.html' %}
|
||||
{% block css %}
|
||||
<style>
|
||||
.align-vertical {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.badge-sm {
|
||||
font-size: 1rem;
|
||||
}
|
||||
</style>
|
||||
<link href="/static/css/friends.css?v={{ version }}" rel="stylesheet">
|
||||
<link href="/static/css/profile.css?v={{ version }}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% include 'open_modal.html' %}
|
||||
<div class="container mt-2">
|
||||
<div class="p-5 mb-4 bg-light-subtle rounded-3 shadow-sm">
|
||||
<div class="container-fluid py-5">
|
||||
<div class="mb-4 text-center">
|
||||
<img class="rounded-pill profile-pic" style="height:9rem" src="{{ user.image }}" alt="{{ user.name }}'s pic">
|
||||
<div class="display-inline-grid ms-3">
|
||||
<h1 class="display-5 fw-bold">
|
||||
{{ user.name }}
|
||||
{% if user.pool %}<span class="fs-6 text-muted ">Piscine de {{ user.pool }}</span>{% endif %}
|
||||
</h1>
|
||||
<span class="text-left">
|
||||
<i class="fa-solid {{ "fa-2=xs fa-circle online" if user.position else "fa-xs fa-person-walking offline" }}"></i>
|
||||
{{ user.position if user.position else "Absent" }} {{ user.last_active if user.last_active or ""}}
|
||||
{% if user.position %}<a class="fa-solid fa-users-viewfinder" href="/goto/{{ user.position }}"></a>{% endif %}
|
||||
</span>
|
||||
<span class="text-left">
|
||||
<button {{ 'hidden' if user.is_friend else '' }}
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline-success online"
|
||||
id="addLocalFriend">
|
||||
<i class="fa-solid fa-user-plus"></i>
|
||||
<i hidden class="spinner-border spinner-border-sm"></i>
|
||||
Ajouter {{ user.name }} en ami
|
||||
</button>
|
||||
<button {{ 'hidden' if not user.is_friend else '' }}
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
id="removeLocalFriend">
|
||||
<i class="fa-solid fa-user-minus"></i>
|
||||
<i hidden class="spinner-border spinner-border-sm"></i>
|
||||
Retirer {{ user.name }} des amis
|
||||
</button>
|
||||
<button type="button"
|
||||
onclick="newTab('https://profile.intra.42.fr/users/{{ user.name }}');"
|
||||
class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fa-solid fa-circle-info"></i>
|
||||
Profil intra de {{ user.name }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fs-5">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
{%- if user.github or user.website or user.discord -%}
|
||||
<p class="mb-0">Contacts</p>
|
||||
<ul>
|
||||
{%- if 'github' in user and user.github -%}
|
||||
<li>
|
||||
<i class="fa-brands fa-github icon-length"></i> <a href="{{ user.github }}">{{ user.github |replace("https://github.com/", "") }}</a>
|
||||
</li>
|
||||
{%- endif -%}
|
||||
{%- if 'website' in user and user.website -%}
|
||||
<li>
|
||||
<i class="fa-solid fa-globe icon-length"></i> <a href="{{ user.website }}">{{ user.website |replace("https://", "")|replace("http://", "") }}</a>
|
||||
</li>
|
||||
{%- endif -%}
|
||||
{%- if 'discord' and user.discord -%}
|
||||
<li>
|
||||
<i class="fa-brands fa-discord icon-length"></i> <a>{{ user.discord }}</a>
|
||||
</li>
|
||||
{%- endif -%}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-8" style="text-align: justify;">{{ user.recit if user.recit and user.recit else '' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script>
|
||||
let addLocalFriend = document.getElementById('addLocalFriend');
|
||||
let removeLocalFriend = document.getElementById('removeLocalFriend');
|
||||
|
||||
if (addLocalFriend)
|
||||
addLocalFriend.addEventListener('click', async () => {
|
||||
await addFriend('{{ user.name }}', '#addLocalFriend');
|
||||
addLocalFriend.hidden = true;
|
||||
removeLocalFriend.hidden = false;
|
||||
})
|
||||
|
||||
if (removeLocalFriend)
|
||||
removeLocalFriend.addEventListener('click', async () => {
|
||||
await deleteFriend('{{ user.name }}', '#removeLocalFriend');
|
||||
addLocalFriend.hidden = false;
|
||||
removeLocalFriend.hidden = true;
|
||||
})
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
81
templates/src/templates/template.html
Normal file
81
templates/src/templates/template.html
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
{%- set version='9' %}
|
||||
<!doctype html>
|
||||
<html lang="fr" data-bs-theme="dark">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>FFT - Maix.me</title>
|
||||
<link href="/static/css/bootstrap.min.css?v={{ version }}" rel="stylesheet">
|
||||
<link href="/static/css/common.css?v={{ version }}" rel="stylesheet">
|
||||
<link rel="manifest" href="/static/manifest.json?v={{ version }}">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/static/img/favicon.ico">
|
||||
<link rel="apple-touch-icon" href="/static/img/apple-touch-icon.png"/>
|
||||
<meta name="theme-color" content="rgb(43, 48, 53)"/>
|
||||
{% block css %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary mb-2 shadow">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand me-2 position-relative " href="/">
|
||||
<img src="/static/img/android-chrome-192x192.png" alt="Logo" width="24" height="24" class="d-inline-block align-text-top">
|
||||
FFT<span class="text-muted text-beta"></span>
|
||||
</a>
|
||||
<button class="btn btn-secondary hide-navbar" hidden id="qc-friends">
|
||||
<i class="fa-solid fa-user-group"></i>
|
||||
</button>
|
||||
<button class="btn btn-secondary hide-navbar" hidden id="qc-cluster">
|
||||
<i class="fa-solid fa-layer-group"></i>
|
||||
</button>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
|
||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="/">Clusters</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="/friends/">Amis</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="d-flex" role="search">
|
||||
<button type="button" class="btn btn-outline-secondary me-1" onclick="newTab('https://github.com/maix0/fft');" aria-label="Github">
|
||||
<i class="fa-brands fa-github"></i> <i hidden class="spinner-border spinner-border-sm"></i>
|
||||
</button>
|
||||
<input class="form-control" list="global_suggestions" id="globalSearch" type="search" placeholder="Rechercher..." aria-label="Search">
|
||||
<datalist id="global_suggestions"></datalist>
|
||||
<button type="button" id="globalSearchButton" aria-label="Search" class="btn btn-outline-light mx-1">
|
||||
<i class="fa-solid fa-magnifying-glass"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-light" aria-label="Settings"
|
||||
onclick="location.href = '/settings/';">
|
||||
<i class="fa-solid fa-gear"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="toast-container position-fixed top-0 end-0 p-3">
|
||||
<div id="liveToast" class="toast align-items-center text-bg-danger border-0" role="alert" aria-live="assertive"
|
||||
aria-atomic="true">
|
||||
<div class="d-flex">
|
||||
<div class="toast-body" id="toast_body">
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"
|
||||
aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% block content %}{% endblock %}
|
||||
<script async src="/static/js/bootstrap.min.js?v={{ version }}"></script>
|
||||
<script src="/static/js/common.js?v={{ version }}"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
|
||||
<link href="/static/fontawesome/css/fontawesome.min.css?v={{ version }}" rel="stylesheet">
|
||||
<link href="/static/fontawesome/css/solid.min.css?v={{ version }}" rel="stylesheet">
|
||||
<link href="/static/fontawesome/css/regular.min.css?v={{ version }}" rel="stylesheet">
|
||||
<link href="/static/fontawesome/css/brands.min.css?v={{ version }}" rel="stylesheet">
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue