Compare commits

..
1 Commits
Author SHA1 Message Date
imperosol 33d8b0459f ts-ify guide-index.ts 2026-09-07 23:14:51 +02:00
5 changed files with 47 additions and 67 deletions
+6 -11
View File
@@ -15,6 +15,9 @@
{% endblock %} {% endblock %}
{% else %} {% else %}
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{% block additional_css %}
<link rel="stylesheet" href="{{ static("club/list.scss") }}">
{% endblock %}
{% block description -%} {% block description -%}
{% trans %}The list of all clubs existing at UTBM.{% endtrans %} {% trans %}The list of all clubs existing at UTBM.{% endtrans %}
{%- endblock %} {%- endblock %}
@@ -23,12 +26,6 @@
{%- endblock %} {%- endblock %}
{% endif %} {% endif %}
{% block additional_css %}
{% if not is_fragment %}
<link rel="stylesheet" href="{{ static("club/list.scss") }}">
{% endif %}
{% endblock %}
{% from "core/macros.jinja" import paginate_htmx %} {% from "core/macros.jinja" import paginate_htmx %}
{% block content %} {% block content %}
@@ -36,17 +33,15 @@
<h3>{% trans %}Filters{% endtrans %}</h3> <h3>{% trans %}Filters{% endtrans %}</h3>
<form <form
id="club-list-filters" id="club-list-filters"
method="GET" hx-get="{{ url("club:club_list") }}"
hx-action="{{ url("club:club_list") }}"
hx-target="#content" hx-target="#content"
hx-swap="innerHTML" hx-swap="outerHtml"
hx-push-url="true" hx-push-url="true"
hx-disable="find input, find button"
> >
<div class="row gap-4x"> <div class="row gap-4x">
{{ form }} {{ form }}
</div> </div>
<button class="btn btn-blue margin-bottom"> <button type="submit" class="btn btn-blue margin-bottom">
<i class="fa fa-magnifying-glass"></i>{% trans %}Search{% endtrans %} <i class="fa fa-magnifying-glass"></i>{% trans %}Search{% endtrans %}
</button> </button>
</form> </form>
-1
View File
@@ -62,7 +62,6 @@ document.body.addEventListener(
}, },
); );
htmx.config.transitions = true;
Object.assign(window, { htmx }); Object.assign(window, { htmx });
/** /**
@@ -31,7 +31,7 @@
{% if is_fragment %} {% if is_fragment %}
hx-post="{{ action }}" hx-post="{{ action }}"
hx-target="#content" hx-target="#content"
hx-swap="innerHTML" hx-swap="outerHtml"
{% endif %} {% endif %}
>{% trans %}Confirm{% endtrans %}</button> >{% trans %}Confirm{% endtrans %}</button>
@@ -39,7 +39,7 @@
{% if is_fragment %} {% if is_fragment %}
hx-get="{{ previous }}" hx-get="{{ previous }}"
hx-target="#content" hx-target="#content"
hx-swap="innerHTML" hx-swap="outerHtml"
{% else %} {% else %}
action="window.history.back()" action="window.history.back()"
{% endif %} {% endif %}
+2 -2
View File
@@ -39,13 +39,13 @@
<p><button <p><button
hx-get="{{ url('core:file_moderate', file_id=f.id) }}" hx-get="{{ url('core:file_moderate', file_id=f.id) }}"
hx-target="#content" hx-target="#content"
hx-swap="innerHTML" hx-swap="outerHtml"
>{% trans %}Moderate{% endtrans %}</button> - >{% trans %}Moderate{% endtrans %}</button> -
{% set current_page = url('core:file_moderation') + "?page=" + page_obj.number | string %} {% set current_page = url('core:file_moderation') + "?page=" + page_obj.number | string %}
<button <button
hx-get="{{ url('core:file_delete', file_id=f.id) }}?next={{ current_page | urlencode }}&previous={{ current_page | urlencode }}" hx-get="{{ url('core:file_delete', file_id=f.id) }}?next={{ current_page | urlencode }}&previous={{ current_page | urlencode }}"
hx-target="#file-{{ loop.index }}" hx-target="#file-{{ loop.index }}"
hx-swap="innerHTML" hx-swap="outerHtml"
>{% trans %}Delete{% endtrans %}</button></p> >{% trans %}Delete{% endtrans %}</button></p>
</div> </div>
{% endfor %} {% endfor %}
@@ -1,9 +1,5 @@
import { import { getCurrentUrlParams, updateQueryString } from "#core:utils/history";
getCurrentUrlParams, import { type SimpleUeSchema, ueFetchUeList } from "#openapi";
History,
updateQueryString,
} from "#core:utils/history.ts";
import { ueFetchUeList } from "#openapi";
const pageDefault = 1; const pageDefault = 1;
const pageSizeDefault = 100; const pageSizeDefault = 100;
@@ -12,38 +8,42 @@ document.addEventListener("alpine:init", () => {
Alpine.data("ue_search", () => ({ Alpine.data("ue_search", () => ({
ues: { ues: {
count: 0, count: 0,
next: null, next: null as string | null,
previous: null, previous: null as string | null,
results: [], results: [] as SimpleUeSchema[],
}, },
loading: false, loading: false,
page: pageDefault, page: pageDefault,
// biome-ignore lint/style/useNamingConvention: api is in snake_case // biome-ignore lint/style/useNamingConvention: api is in snake_case
page_size: pageSizeDefault, page_size: pageSizeDefault,
search: "", search: "",
department: [], department: [] as string[],
// biome-ignore lint/style/useNamingConvention: api is in snake_case // biome-ignore lint/style/useNamingConvention: api is in snake_case
credit_type: [], credit_type: [] as string[],
semester: [], semester: [] as string[],
// biome-ignore lint/style/useNamingConvention: api is in snake_case // biome-ignore lint/style/useNamingConvention: api is in snake_case
to_change: [], to_change: [] as { param: string; value: string }[],
pushstate: History.Push,
update: undefined, // dummy implementation to make TS happy.
// The real function is initialized in init
update: () => {
console.warn("Update not yet initialized");
},
initializeArgs() { initializeArgs() {
const url = getCurrentUrlParams(); const url = getCurrentUrlParams();
this.pushstate = History.Replace; this.page = Number.parseInt(url.get("page") || pageDefault.toString(), 10);
this.page_size = Number.parseInt(
this.page = Number.parseInt(url.get("page"), 10) || pageDefault; url.get("page_size") || pageSizeDefault.toString(),
this.page_size = Number.parseInt(url.get("page_size"), 10) || pageSizeDefault; 10,
);
this.search = url.get("search") || ""; this.search = url.get("search") || "";
this.department = url.getAll("department"); this.department = url.getAll("department");
this.credit_type = url.getAll("credit_type"); this.credit_type = url.getAll("credit_type");
/* The semester is easier to use on the backend as an enum (spring/autumn/both/none) /* 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]). and easier to use on the frontend as an array ([spring, autumn]).
Thus there is some conversion involved when both communicate together */ Thus there is some conversion involved when both communicate together */
this.semester = url.has("semester") ? url.get("semester").split("_AND_") : []; this.semester = url.get("semester")?.split("_AND_") || [];
this.update(); this.update();
}, },
@@ -51,15 +51,11 @@ document.addEventListener("alpine:init", () => {
async init() { async init() {
this.update = Alpine.debounce(async () => { this.update = Alpine.debounce(async () => {
/* Create the whole url before changing everything all at once */ /* Create the whole url before changing everything all at once */
const first = this.to_change.shift(); for (const val of this.to_change) {
let url = updateQueryString(first.param, first.value, History.None); updateQueryString(val.param, val.value);
for (const value of this.to_change) {
url = updateQueryString(value.param, value.value, History.None, url);
} }
updateQueryString(first.param, first.value, this.pushstate, url);
await this.fetchData(); /* reload data on form change */ await this.fetchData(); /* reload data on form change */
this.to_change = []; this.to_change = [];
this.pushstate = History.Push;
}, 50); }, 50);
const searchParams = ["search", "department", "credit_type", "semester"]; const searchParams = ["search", "department", "credit_type", "semester"];
@@ -67,47 +63,37 @@ document.addEventListener("alpine:init", () => {
for (const param of searchParams) { for (const param of searchParams) {
this.$watch(param, () => { this.$watch(param, () => {
if (this.pushstate !== History.Push) {
/* This means that we are doing a mass param edit */
return;
}
/* Reset pagination on search */ /* Reset pagination on search */
this.page = pageDefault; this.page = pageDefault;
this.page_size = pageSizeDefault; this.page_size = pageSizeDefault;
}); });
} }
for (const param of searchParams.concat(paginationParams)) { for (const param of searchParams.concat(paginationParams)) {
this.$watch(param, (value) => { this.$watch(param, (value: string) => {
this.to_change.push({ param: param, value: value }); this.to_change.push({ param: param, value: value });
this.update(); this.update();
}); });
} }
window.addEventListener("popstate", () => {
this.initializeArgs();
});
this.initializeArgs(); this.initializeArgs();
}, },
async fetchData() { async fetchData() {
this.loading = true; this.loading = true;
const args = {
// biome-ignore lint/style/useNamingConvention: api is in snake_case const res = await ueFetchUeList({
page_size: this.page_size, query: {
}; // biome-ignore lint/style/useNamingConvention: api is in snake_case
for (const [param, value] of new URL( page_size: this.page_size,
window.location.href, // biome-ignore lint/style/useNamingConvention: api is in snake_case
).searchParams.entries()) { credit_type: this.credit_type.length > 0 ? this.credit_type : undefined,
// Deal with array type params semester: this.semester.length > 0 ? this.semester : undefined,
if (["credit_type", "department", "semester"].includes(param)) { department: this.department.length > 0 ? this.department : undefined,
if (args[param] === undefined) { search: this.search || undefined,
args[param] = []; },
} });
args[param].push(value); if (res.data !== undefined) {
} else { this.ues = res.data;
args[param] = value;
}
} }
this.ues = (await ueFetchUeList({ query: args })).data;
this.loading = false; this.loading = false;
}, },