diff --git a/core/static/bundled/base-bundle-index.ts b/core/static/bundled/base-bundle-index.ts index 8556a99e..eae4ce75 100644 --- a/core/static/bundled/base-bundle-index.ts +++ b/core/static/bundled/base-bundle-index.ts @@ -18,6 +18,7 @@ import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill"; import { limitedChoices } from "#core:alpine/limited-choices"; import { expireOldStorage } from "#core:core/localstorage"; import { default as navbar } from "#core:core/navbar"; +import { getErrorCallbacksExt } from "#core:htmx/error-callback"; import { type NotificationPlugin, notificationsPlugin as notifications, @@ -62,6 +63,9 @@ document.body.addEventListener( }, ); +const errorCallbackExt = getErrorCallbacksExt(); +htmx.registerExtension(errorCallbackExt.name, errorCallbackExt.extension); + Object.assign(window, { htmx }); /** diff --git a/core/static/bundled/htmx/error-callback.ts b/core/static/bundled/htmx/error-callback.ts new file mode 100644 index 00000000..276181d4 --- /dev/null +++ b/core/static/bundled/htmx/error-callback.ts @@ -0,0 +1,74 @@ +interface CustomHtmxExtension { + name: string; + extension: any; +} + +export const getErrorCallbacksExt = () => { + const attrPrefix = "hx-callback-"; + let htmxApi: { attributeValue: (arg0: HTMLElement, arg1: string) => string | null }; + + const getCallback = (elt: HTMLElement, responseCode: number) => { + if (!elt || !responseCode) { + return () => {}; + } + + const code = responseCode.toString(); + + // '*' is the original syntax, as the obvious character for a wildcard. + // The 'x' alternative was added for maximum compatibility with HTML + // templating engines, due to ambiguity around which characters are + // supported in HTML attributes. + // + // Start with the most specific possible attribute and generalize from + // there. + const suffixes = [ + code, + + `${code.substring(0, 2)}*`, + `${code.substring(0, 2)}x`, + + `${code.substring(0, 1)}*`, + `${code.substring(0, 1)}x`, + `${code.substring(0, 1)}**`, + `${code.substring(0, 1)}xx`, + + "*", + "x", + "***", + "xxx", + ]; + if (code.startsWith("4") || code.startsWith("5")) { + suffixes.push("error"); + } + + for (const suffix of suffixes) { + const attr = attrPrefix + suffix; + const callback = htmxApi?.attributeValue(elt, attr); + if (callback) { + return Function("src", "target", callback); + } + } + + return () => {}; + }; + + return { + name: "error-callbacks", + extension: { + init: (api: any) => { + htmxApi = api; + }, + // biome-ignore lint/style/useNamingConvention: HTMX naming convention + htmx_response_error: ( + elt: HTMLElement, + event: { ctx: any; cancelled: boolean }, + ) => { + if (event.cancelled) { + return false; + } + getCallback(elt, event.ctx.response.status)(elt, event.ctx.target); + return true; + }, + }, + } as CustomHtmxExtension; +}; diff --git a/pedagogy/static/pedagogy/css/pedagogy.scss b/pedagogy/static/pedagogy/css/pedagogy.scss index 178b7962..a177ee4e 100644 --- a/pedagogy/static/pedagogy/css/pedagogy.scss +++ b/pedagogy/static/pedagogy/css/pedagogy.scss @@ -168,12 +168,6 @@ $pedagogy-white-text: #f0f0f0; .input-stars { margin-top: 20px; } - - .right { - display: flex; - justify-content: flex-end; - } - } .ue-details-container { @@ -446,4 +440,9 @@ details.accordion>.accordion-content { background-color: $white-color; border-color: $pedagogy-orange; border-right: none; +} + +.right { + display: flex; + justify-content: flex-end; } \ No newline at end of file diff --git a/pedagogy/templates/pedagogy/fragments/comment_report.jinja b/pedagogy/templates/pedagogy/fragments/comment_report.jinja new file mode 100644 index 00000000..d729f545 --- /dev/null +++ b/pedagogy/templates/pedagogy/fragments/comment_report.jinja @@ -0,0 +1,30 @@ +
\ No newline at end of file diff --git a/pedagogy/templates/pedagogy/fragments/ue_comment.jinja b/pedagogy/templates/pedagogy/fragments/ue_comment.jinja index 6361e8d9..3a4e5cd6 100644 --- a/pedagogy/templates/pedagogy/fragments/ue_comment.jinja +++ b/pedagogy/templates/pedagogy/fragments/ue_comment.jinja @@ -47,10 +47,12 @@ {% endif %} {% if comment.author_id == user.id or user.has_perm("pedagogy.delete_comment") %}