start
This commit is contained in:
parent
8afdd208c6
commit
8e82ec9523
23 changed files with 3345 additions and 0 deletions
1
froxy-templates/.gitignore
vendored
Normal file
1
froxy-templates/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/target
|
||||
9
froxy-templates/Cargo.toml
Normal file
9
froxy-templates/Cargo.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "froxy-templates"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
minijinja = "2.15.1"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
smol_str = { version = "0.3.5", features = ["serde"] }
|
||||
20
froxy-templates/examples/friend.rs
Normal file
20
froxy-templates/examples/friend.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
use froxy_templates::friends::{self, Friend};
|
||||
|
||||
fn main() {
|
||||
let mut env = minijinja::Environment::new();
|
||||
friends::add_to_context(&mut env);
|
||||
let data = friends::render(
|
||||
&env,
|
||||
friends::FriendsData {
|
||||
friends: std::iter::from_fn(|| Some(Friend {
|
||||
name: "maiboyer".into(),
|
||||
image: "https://friends.42paris.fr/proxy/resize/512/03c61af252becbca11aac5ff49a2e61c/maiboyer.jpg".into(),
|
||||
position: Some("f1r1s1".into()),
|
||||
last_active: None,
|
||||
})).take(10).collect(),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
println!("{data}");
|
||||
}
|
||||
30
froxy-templates/src/friends.rs
Normal file
30
froxy-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>,
|
||||
}
|
||||
11
froxy-templates/src/index.rs
Normal file
11
froxy-templates/src/index.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
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();
|
||||
}
|
||||
9
froxy-templates/src/lib.rs
Normal file
9
froxy-templates/src/lib.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
mod templates;
|
||||
|
||||
pub mod friends;
|
||||
pub mod meta_template;
|
||||
pub mod open_modal;
|
||||
pub mod profile;
|
||||
pub mod index;
|
||||
5
froxy-templates/src/meta_template.rs
Normal file
5
froxy-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
froxy-templates/src/open_modal.rs
Normal file
5
froxy-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();
|
||||
}
|
||||
43
froxy-templates/src/profile.rs
Normal file
43
froxy-templates/src/profile.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
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: SmolStr,
|
||||
pub image: String,
|
||||
pub name: SmolStr,
|
||||
pub pool: Option<SmolStr>,
|
||||
pub id: u64,
|
||||
pub is_friend: bool,
|
||||
pub is_self: 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,
|
||||
}
|
||||
139
froxy-templates/src/templates/friends.html
Normal file
139
froxy-templates/src/templates/friends.html
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
{% 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>
|
||||
<p class="card-text">{{ friend.position if friend.position else 'Absent' }} {{ friend.last_active }}
|
||||
{% if friend.position %}
|
||||
<a class="fa-solid fa-users-viewfinder" href="/goto/{{ friend.position }}"></a>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if add %}
|
||||
<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>
|
||||
{% endif %}
|
||||
</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 %}
|
||||
228
froxy-templates/src/templates/index.html
Normal file
228
froxy-templates/src/templates/index.html
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
{% 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 cluster in clusters %}
|
||||
<input type="radio" class="btn-check cluster_radios" name="btn-radio"
|
||||
data-cluster="{{ cluster.name }}" id="radio{{ cluster.name }}"
|
||||
autocomplete="off" {{ "checked" if actual_cluster == cluster['name'] else "" }}>
|
||||
<label title="{{ (int(cluster.users / (cluster.maximum_places - cluster.dead_pc) * 100) if (cluster.maximum_places - cluster.dead_pc) > 0 else 0 ) }}%" class="btn btn-outline-{{ place_to_btn(cluster) }} m-t" for="radio{{ cluster.name }}">
|
||||
{{ ('<i class="fa-solid fa-person-swimming text-info"></i>' if cluster.name in piscine else '') | safe }}{{ (' ' if cluster.name in piscine and cluster.name in silent else '') | safe }}{{ ('<i class="fa-solid fa-volume-xmark text-secondary"></i> ' if cluster.name in silent else '') | safe }}{{ cluster.name }}
|
||||
<span class="text-muted">{{ cluster.users }}/{{ cluster.maximum_places - cluster.dead_pc }}</span></label>
|
||||
{%- endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-fluid mt-2 scroll p-0">
|
||||
<table class="grid text-center">
|
||||
<tbody>
|
||||
|
||||
{% for index, x in enumerate(map[1:], 0) %}
|
||||
<tr>
|
||||
{%- set countB = namespace(value=1) -%}
|
||||
<td class="range">{{ map[0][index] }}</td>
|
||||
{%- for y in x -%}
|
||||
{%- if y == 'x' -%}
|
||||
<td class="range"> </td>
|
||||
{%- elif y == '|' -%}
|
||||
<td class="range-no-color"></td>
|
||||
{%- elif y == 'h' -%}
|
||||
<td></td>
|
||||
{%- elif y == 'd' -%}
|
||||
<td><i class="fa-solid fa-person-walking fa-2xl"></i></td>
|
||||
{%- elif y == 'f' -%}
|
||||
<td><i class="fa-solid fa-laptop fa-xl"></i></td>
|
||||
{%- elif exrypz(y) == False -%}
|
||||
<td class="small-colors"></td>
|
||||
{%- else -%}
|
||||
{%- set text_color = '' -%}
|
||||
{%- set img = '<i class="fa-solid fa-user"></i>' -%}
|
||||
{%- if y in locations -%}
|
||||
{%- set img = '<img loading="lazy" class="profile-pic2" alt="" src="' + (custom_image(locations[y]['user']['login']) if locations[y]['has_custom_image'] else proxy_images(locations[y]['user']['image']['versions']['small'])) + '">' -%}
|
||||
{%- elif actual_cluster in piscine and y in tutor_station -%}
|
||||
{%- set img = '<i class="fa-solid fa-shield tutor-shield"></i>' -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if actual_cluster in piscine and y in tutor_station -%}
|
||||
{%- set text_color = 'tutor-shield' -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- set relation = '' -%}
|
||||
{%- set currently_in_piscine = y in locations and (locations[y]['user']['pool_month'], locations[y]['user']['pool_year']) in piscine_date -%}
|
||||
{%- set in_piscine_cluster = actual_cluster in piscine -%}
|
||||
{%- if y in locations and locations[y]['user']['id'] not in tutors and currently_in_piscine != in_piscine_cluster -%}
|
||||
{%- set relation = 'wrong-cluster' -%}
|
||||
{%- endif -%}
|
||||
{%- if focus and focus == y %}
|
||||
{%- set relation = 'focus' -%}
|
||||
{%- elif y in locations and locations[y]['me'] -%}
|
||||
{%- set relation = 'me' -%}
|
||||
{%- elif y in locations and locations[y]['whitelist'] -%}
|
||||
{%- set relation = 'whitelist' -%}
|
||||
{%- elif y in locations and locations[y]['close_friend'] -%}
|
||||
{%- set relation = 'close_friend' -%}
|
||||
{%- elif y in locations and locations[y]['friend'] -%}
|
||||
{%- set relation = 'friend' -%}
|
||||
{%- elif y in issues_map and issues_map[y]['count'] >= 1 and issues_map[y]['issue'] == 1 -%}
|
||||
{%- set relation = 'dead' -%}
|
||||
{%- elif y in issues_map and issues_map[y]['count'] >= 1 and issues_map[y]['issue'] == 2 -%}
|
||||
{%- set relation = 'attention' -%}
|
||||
{%- elif y in locations and locations[y]['pool'] and actual_cluster not in piscine -%}
|
||||
{%- set relation = 'pooled' -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if actual_cluster in piscine and y in locations and locations[y]['user']['id'] in tutors -%}
|
||||
{%- set relation = 'tutor' -%}
|
||||
{%- set text_color = '' -%}
|
||||
{%- elif actual_cluster in piscine and y in locations and not locations[y]['user']['tutor'] in tutors and y in tutor_station -%}
|
||||
{%- set relation = 'tutor-spot' -%}
|
||||
{%- set text_color = '' -%}
|
||||
{%- endif -%}
|
||||
{%- if y in locations and locations[y]['admin'] -%}
|
||||
{%- set relation = 'admin' -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if countB.value % 2 == 0 -%}
|
||||
<td data-pos="{{ y }}"
|
||||
data-login="{{ locations[y]['user']['login'] if y in locations else '' }}"
|
||||
class="sm-t case {{ relation }} {{ text_color }}">{{ img|safe }}<br>{{ y.replace(actual_cluster, '') }}
|
||||
</td>
|
||||
{%- else -%}
|
||||
<td data-pos="{{ y }}"
|
||||
data-login="{{ locations[y]['user']['login'] if y in locations else '' }}"
|
||||
class="sm-t case {{ relation }} {{ text_color }}">{{ y.replace(actual_cluster, '') }}<br>{{ img|safe }}
|
||||
</td>
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- set countB.value = countB.value + 1 -%}
|
||||
{%- endfor -%}
|
||||
<td class="range">{{ map[0][index] }}</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
froxy-templates/src/templates/mod.rs
Normal file
11
froxy-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
froxy-templates/src/templates/open_modal.html
Normal file
76
froxy-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>
|
||||
111
froxy-templates/src/templates/profile.html
Normal file
111
froxy-templates/src/templates/profile.html
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
{% 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 'None' not in 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.position %}<a class="fa-solid fa-users-viewfinder" href="/goto/{{ user.position }}"></a>{% endif %}
|
||||
</span>
|
||||
<span class="text-left">
|
||||
{% if !user.is_self %}
|
||||
<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>
|
||||
{% endif %}
|
||||
<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 len(user.github) >= 1 -%}
|
||||
<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 len(user.website) >= 1 -%}
|
||||
<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 len(user.discord) >= 1 -%}
|
||||
<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 len(user.recit) > 0 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
froxy-templates/src/templates/template.html
Normal file
81
froxy-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