Sli 271d57051e Upgrade to fontawesome 6
* Adapt fontawesome usage when needed
* Fix uv guide not importing css
* Remove utf8 usage for fontawesome
2024-10-03 00:06:03 +02:00

218 lines
7.9 KiB
Django/Jinja

{% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import paginate_alpine %}
{% block title %}
{% trans %}UV Guide{% endtrans %}
{% endblock %}
{% block additional_css %}
<link rel="stylesheet" href="{{ static('pedagogy/css/pedagogy.scss') }}">
{% endblock %}
{% block head %}
{{ super() }}
<meta name="viewport" content="width=device-width, initial-scale=0.6, maximum-scale=2">
{% endblock head %}
{% block content %}
{% if can_create_uv %}
<div class="action-bar">
<p>
<a href="{{ url('pedagogy:uv_create') }}">{% trans %}Create UV{% endtrans %}</a>
</p>
<p>
<a href="{{ url('pedagogy:moderation') }}">{% trans %}Moderate comments{% endtrans %}</a>
</p>
</div>
<br/>
{% endif %}
<div class="pedagogy" x-data="uv_search" x-cloak>
<form id="search_form">
<div class="search-form-container">
<div class="search-bar">
<input
id="search_input"
class="search-bar-input"
type="text"
name="search"
x-model.debounce.500ms="search"
/>
</div>
<div class="radio-department">
<div class="radio-guide">
{% set departments = [
("EDIM", "EDIM"), ("ENERGIE", "EE"), ("IMSI", "IMSI"),
("INFO", "GI"), ("GMC", "MC"), ("HUMA", "HUMA"), ("TC", "TC")
] %}
{% for (display_name, real_name) in departments %}
<input
type="checkbox"
name="department"
id="radio{{ real_name }}"
value="{{ real_name }}"
x-model="department"
/>
<label for="radio{{ real_name }}">{% trans %}{{ display_name }}{% endtrans %}</label>
{% endfor %}
</div>
</div>
<div class="radio-credit-type">
<div class="radio-guide">
{% for credit_type in ["CS", "TM", "EC", "QC", "OM"] %}
<input
type="checkbox"
name="credit_type"
id="radio{{ credit_type }}"
value="{{ credit_type }}"
x-model="credit_type"
/>
<label for="radio{{ credit_type }}">{% trans %}{{ credit_type }}{% endtrans %}</label>
{% endfor %}
</div>
</div>
<div class="radio-semester">
<div class="radio-guide">
<input type="checkbox" name="semester" id="radioAUTUMN" value="AUTUMN" x-model="semester"/>
<label for="radioAUTUMN"><i class="fa fa-leaf"></i></label>
<input type="checkbox" name="semester" id="radioSPRING" value="SPRING" x-model="semester"/>
<label for="radioSPRING"><i class="fa-regular fa-sun"></i></label>
</div>
</div>
</div>
</form>
<table id="dynamic_view">
<thead>
<tr>
<td>{% trans %}UV{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Department{% endtrans %}</td>
<td>{% trans %}Credit type{% endtrans %}</td>
<td><i class="fa fa-leaf"></i></td>
<td><i class="fa-regular fa-sun"></i></td>
{% if can_create_uv %}
<td>{% trans %}Edit{% endtrans %}</td>
<td>{% trans %}Delete{% endtrans %}</td>
{% endif %}
</tr>
</thead>
<tbody id="dynamic_view_content" :aria-busy="loading">
<template x-for="uv in uvs.results" :key="uv.id">
<tr @click="window.location.href = `/pedagogy/uv/${uv.id}`" class="clickable">
<td><a :href="`/pedagogy/uv/${uv.id}`" x-text="uv.code"></a></td>
<td x-text="uv.title"></td>
<td x-text="uv.department"></td>
<td x-text="uv.credit_type"></td>
<td><i :class="uv.semester.includes('AUTUMN') && 'fa fa-leaf'"></i></td>
<td><i :class="uv.semester.includes('SPRING') && 'fa-regular fa-sun'"></i></td>
{% if can_create_uv -%}
<td><a :href="`/pedagogy/uv/${uv.id}/edit`">{% trans %}Edit{% endtrans %}</a></td>
<td><a :href="`/pedagogy/uv/${uv.id}/delete`">{% trans %}Delete{% endtrans %}</a></td>
{%- endif -%}
</tr>
</template>
</tbody>
</table>
{{ paginate_alpine("page", "max_page()") }}
</div>
<script>
{#
How does this work :
The page contains two main elements : the form and the results.
The form contains multiple inputs, allowing the user to apply the filter of its choice.
Each modification of those filters will modify the GET parameters of the URL,
then fetch the corresponding data from the API.
This data will then be displayed on the result part of the page.
#}
const page_default = 1;
const page_size_default = 100;
document.addEventListener("alpine:init", () => {
Alpine.data("uv_search", () => ({
uvs: [],
loading: false,
page: page_default,
page_size: page_size_default,
search: "",
department: [],
credit_type: [],
semester: [],
to_change: [],
pushstate: History.PUSH,
update: undefined,
async initialize_args() {
let url = new URLSearchParams(window.location.search);
this.pushstate = History.REPLACE;
this.page = parseInt(url.get("page")) || page_default;;
this.page_size = parseInt(url.get("page_size")) || page_size_default;
this.search = url.get("search") || "";
this.department = url.getAll("department");
this.credit_type = url.getAll("credit_type");
{# The semester is easier to use on the backend as an enum (spring/autumn/both/none)
and easier to use on the frontend as an array ([spring, autumn]).
Thus there is some conversion involved when both communicate together #}
this.semester = url.has("semester") ?
url.get("semester").split("_AND_") : [];
this.update()
},
async init() {
this.update = Alpine.debounce(async () => {
{# Create the whole url before changing everything all at once #}
let first = this.to_change.shift();
let url = update_query_string(first.param, first.value, History.NONE);
this.to_change.forEach((value) => {
url = update_query_string(value.param, value.value, History.NONE, url);
})
update_query_string(first.param, first.value, this.pushstate, url);
await this.fetch_data(); {# reload data on form change #}
this.to_change = [];
this.pushstate = History.PUSH;
}, 50);
let search_params = ["search", "department", "credit_type", "semester"];
let pagination_params = ["page", "page_size"];
search_params.forEach((param) => {
this.$watch(param, async (value) => {
if (this.pushstate != History.PUSH){
{# This means that we are doing a mass param edit #}
return;
}
{# Reset pagination on search #}
this.page = page_default;
this.page_size = page_size_default;
});
});
search_params.concat(pagination_params).forEach((param) => {
this.$watch(param, async (value) => {
this.to_change.push({ param: param, value: value })
this.update();
});
});
window.addEventListener("popstate", async (event) => {
await this.initialize_args();
});
await this.initialize_args();
},
async fetch_data() {
this.loading = true;
const url = "{{ url("api:fetch_uvs") }}" + window.location.search;
this.uvs = await (await fetch(url)).json();
this.loading = false;
},
max_page() {
return Math.ceil(this.uvs.count / this.page_size);
}
}))
})
</script>
{% endblock content %}