Sith/pedagogy/templates/pedagogy/guide.jinja

207 lines
7.6 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="{{ scss('pedagogy/css/pedagogy.scss') }}">
<link rel="stylesheet" href="{{ scss('core/pagination.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 fa-sun-o"></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 fa-sun-o"></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 fa-sun-o'"></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: [],
pushstate: History.PUSH,
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_") : [];
{# Wait for all watch callbacks to be called #}
await (new Promise(resolve => setTimeout(resolve, 100)));
this.pushstate = History.PUSH;
},
async init() {
await this.initialize_args();
let search_params = ["search", "department", "credit_type", "semester"];
let pagination_params = ["page", "page_size"];
this.fetch_data(); {# load initial data #}
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) => {
console.log(param + " " + value)
update_query_string(param, value, this.pushstate);
await this.fetch_data(); {# reload data on form change #}
});
});
window.addEventListener("popstate", () => {
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 %}