Commit 7216d827 authored by Tobias Kallauke's avatar Tobias Kallauke
Browse files

Add flash messages

parent 1efbe1aa
......@@ -6,7 +6,7 @@ MIN_GROUP_ID = 0
class Group
include ActiveModel::Serializers::JSON
attr_accessor :id, :name, :description, :created_at, :valid_until, :type_of_group, :manager
attr_accessor :id, :name, :description, :created_at, :valid_until, :type_of_group, :manager_id
def attributes=(hash)
hash.each do |key, value|
......@@ -23,9 +23,9 @@ class Group
value = Date.iso8601(value) if value.instance_of?(String)
@valid_until = value
end
def attributes
{ 'id' => nil, 'name' => nil, 'description' => nil, 'created_at' => nil, 'valid_until' => nil, 'type_of_group' => nil, 'manager' => nil}
{ 'id' => nil, 'name' => nil, 'description' => nil, 'created_at' => nil, 'valid_until' => nil, 'type_of_group' => nil, 'manager_id' => nil}
end
......@@ -52,9 +52,9 @@ class Group
g = Group.new
g.id = id
j = JSON.parse(res.body)
j = JSON.parse(res.body).slice(*g.attributes.keys)
g.from_json(j[String(id)].to_json)
g.from_json(j.to_json)
g
end
......@@ -113,9 +113,9 @@ class Group
end
end
return users,my_role
end
......
import ky from 'ky';
const api = ky.extend({
hooks: {
beforeRequest: [
(options) => {
const token = (document.querySelector('meta[name="csrf-token"]') as HTMLMetaElement).content
options.headers.set("X-CSRF-TOKEN", token)
}
]
}
})
export default api
const KEY = "flash-messages"
type BOOTSTRAP_TYPES = "primary" | "secondary" | "success" | "danger" | "warning" | "info" | "light" | "dark"
interface Flash {
type: BOOTSTRAP_TYPES
msg: string
}
export function addFlashMessage(f: Flash) {
const list = getFlashMessages()
list.push(f)
sessionStorage.setItem(KEY, JSON.stringify(list))
}
export function getFlashMessages(): Flash[] {
const value = sessionStorage.getItem(KEY)
if (!value) {
return []
}
return JSON.parse(value)
}
export function clearFlashMessages() {
sessionStorage.removeItem(KEY)
}
......@@ -17,24 +17,44 @@ require("custom");
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)
// fetch Polyfills
require('globalthis').shim();
import 'whatwg-fetch'
import {clearFlashMessages, getFlashMessages} from "../custom/flash";
import $ from 'jquery'
import { Tooltip } from 'bootstrap';
import {Tooltip} from 'bootstrap';
import "custom/tutorial.js";
import "custom/info.js";
import "bootstrap/scss/bootstrap.scss";
import "styles/application.scss";
$(() => {
function showAlerts() {
const messages = getFlashMessages()
if (!messages.length)
return
const container = document.querySelector("#alerts")
$.ajaxSetup({
headers: { "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content") },
});
for (const msg of messages) {
const alert = document.createElement("div")
alert.setAttribute("role", "alert")
alert.classList.add("alert", `alert-${msg.type}`)
alert.textContent = msg.msg
$('[data-toggle="tooltip"]').tooltip();
container.appendChild(alert)
}
clearFlashMessages()
}
$(() => {
$('[data-toggle="tooltip"]').tooltip();
showAlerts()
});
if (/^\/group\/\d+\/?$/.test(location.pathname))
......
import $ from 'jquery'
import api from "../custom/api";
import {addFlashMessage} from "../custom/flash";
interface CapabilityPair {
id: number,
......@@ -12,7 +14,7 @@ let loading = false
function setLoading() {
loading = true
const buttons = [ $("#config-save"), $("#permission-save") ]
const buttons = [$("#config-save"), $("#permission-save")]
for (const button of buttons) {
button.children(".spinner-border").removeClass("d-none")
......@@ -25,7 +27,7 @@ $(() => {
const el = $(this)
const data = el.data()
const checked = (this as HTMLInputElement).checked
const pair = { id: data['uid'], capability: data['cid'] }
const pair = {id: data['uid'], capability: data['cid']}
if (data.hasOwnProperty('initial')) {
// Checkbox initial state was On
......@@ -66,13 +68,11 @@ $(() => {
temp = remove.map(e => `user: ${e.id}, capability: ${e.capability}`).join('\n')
console.log(`Permission pairs to remove: ${temp}`)
$.ajax({
type: "POST",
url: "",
data: JSON.stringify({add, remove}),
contentType: "application/json; charset=utf-8",
dataType: "json",
}).then(() => {
api.post("", {json: {add, remove}}).then(() => {
addFlashMessage({type: "success", msg: "Rollenänderungen gespeichert"})
}).catch(() => {
addFlashMessage({type: "danger", msg: "TODO: Role Save Error"})
}).finally(() => {
location.reload()
})
})
......@@ -96,7 +96,7 @@ $(() => {
if (el.tagName !== "INPUT")
continue
const input = <HTMLInputElement> el
const input = <HTMLInputElement>el
const parameter_id = parseInt(input.name.substring("config-".length))
if (isNaN(parameter_id))
alert("Could not read parameter id for input" + input.innerHTML)
......@@ -110,13 +110,11 @@ $(() => {
data.push({parameter_id, value})
}
$.ajax({
type: "POST",
url: location.href + "/config",
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
dataType: "json",
}).then(() => {
api.post(location.href + "/config", {json: data}).then(() => {
addFlashMessage({type: "success", msg: "Konfiguration gespeichert"})
}).catch(() => {
addFlashMessage({type: "danger", msg: "TODO: Config Save Error"})
}).finally(() => {
location.reload()
})
})
......
import $ from 'jquery'
import api from "../custom/api";
import {HTTPError} from "ky";
import {addFlashMessage} from "../custom/flash";
interface roleUpdate {
uid: number
......@@ -6,24 +9,18 @@ interface roleUpdate {
}
function update_roles(updates: roleUpdate[]) {
return $.ajax({
url: location.href + "/roles",
method: "POST",
data: JSON.stringify({updates}),
contentType: "application/json"
}).fail(function (xhr) {
alert(`Role update failed: HTTP ${xhr.status}\n${xhr.responseJSON}`)
return api.post(location.href + "/roles", {json: {updates}}).then(() => {
addFlashMessage({type: "success", msg: "Rollenänderung gespeichert"})
}).catch(() => {
addFlashMessage({type: "danger", msg: "TODO: Role Update Error"})
})
}
function delete_users(users: number[]) {
return $.ajax({
url: location.href + "/members",
method: "DELETE",
data: JSON.stringify(users),
contentType: "application/json"
}).fail(function (xhr) {
alert(`Member delete failed: HTTP ${xhr.status}\n${xhr.responseJSON}`)
return api.delete(location.href + "/members", {json: users}).then(() => {
addFlashMessage({type: "success", msg: "Ausgewählte Mitglieder entfernt"})
}).catch(() => {
addFlashMessage({type: "danger", msg: "TODO: Member Delete Error"})
})
}
......@@ -52,11 +49,13 @@ $(() => {
})
$(".adminEmailCopy").on("click",function (e){
$(".adminEmailCopy").on("click", function (e) {
$(".adminEmailCopy").addClass("buttonToFadeGreen");
setTimeout(function(){$(".adminEmailCopy").removeClass("buttonToFadeGreen");}, 2000);
setTimeout(function () {
$(".adminEmailCopy").removeClass("buttonToFadeGreen");
}, 2000);
var text = $("#admins option:selected").text();
var value= `<input value="${text}" id="selVal" />`;
var value = `<input value="${text}" id="selVal" />`;
$(value).insertAfter('.adminEmailCopy');
$("#selVal").select();
document.execCommand("Copy");
......@@ -118,21 +117,20 @@ $(() => {
button.prop("disabled", true)
$.ajax({
type: "POST",
url: location.href + "/members",
data: JSON.stringify({login}),
contentType: "application/json",
}).done(function () {
location.reload()
}).fail(function (xhr, status, error) {
api.post(location.href + "/members", {json: {login}}).catch(error => {
if (error.name !== "HTTPError")
throw error
const msg = $("#user-add-error")
if (xhr.status === 404) {
const status = (error as HTTPError).response.status
if (status === 404) {
msg.text("Benutzer nicht gefunden")
} else if (xhr.status === 409) {
} else if (status === 409) {
msg.text("Benutzer ist bereits Mitglied")
} else {
msg.text(`HTTP ${xhr.status}\n${status} ${error}`)
msg.text(`HTTP ${status} ${(error as HTTPError).response.statusText}`)
}
msg.removeClass('d-none')
button.prop("disabled", false)
......
......@@ -38,7 +38,7 @@
</li>
</ul>
</div>
<% if cookies[:sessionToken] === 'a' %>
<% if cookies[:sessionToken] === 'a' %>
<span data-toggle="tooltip" data-placement="bottom" title="Tutorial"> <i class="navbar-brand tutorialEnable pointer bi bi-question-circle"></i></span>
<% end %>
<a class="navbar-brand"><%= t('navBar.user') %> <%= session[:user_login] %> </a>
......@@ -60,15 +60,19 @@
</div>
</aside>
<main role="main" class="ml-3 mt-3 mr-5 w-100">
<div id="alerts">
</div>
<%= yield %>
</main>
</div>
<% if cookies[:allow_cookies].blank? %>
<% if cookies[:sessionToken] === 'a' %>
<% if cookies[:sessionToken] === 'a' %>
<%= render 'layouts/tutorial' %>
<% end %>
<% if cookies[:sessionToken] === 'b' %>
<% if cookies[:sessionToken] === 'b' %>
<%= render 'layouts/info' %>
<% end %>
<% end %>
......
......@@ -7,11 +7,14 @@
"@rails/webpacker": "4.3.0",
"@types/jquery": "^3.5.4",
"bootstrap": "^4.5.3",
"globalthis": "^1.0.2",
"jquery": "^3.5.1",
"ky": "^0.28.5",
"popper.js": "^1.16.1",
"ts-loader": "^8.0.11",
"turbolinks": "^5.2.0",
"typescript": "^4.1.2"
"typescript": "^4.1.2",
"whatwg-fetch": "^3.6.2"
},
"version": "0.1.0",
"devDependencies": {
......
......@@ -3379,6 +3379,13 @@ globals@^11.1.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
globalthis@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b"
integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==
dependencies:
define-properties "^1.1.3"
globby@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
......@@ -4226,6 +4233,11 @@ kind-of@^6.0.0, kind-of@^6.0.2:
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
ky@^0.28.5:
version "0.28.5"
resolved "https://registry.yarnpkg.com/ky/-/ky-0.28.5.tgz#4b7ada24fb0440c3898406f3a4986abe60ba213e"
integrity sha512-O5gg9kF4MeyfSw+YkgPAafOPwEUU6xcdGEJKUJmKpIPbLzk3oxUtY4OdBNekG7mawofzkyZ/ZHuR9ev5uZZdAA==
last-call-webpack-plugin@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555"
......@@ -7658,6 +7670,11 @@ websocket-extensions@>=0.1.1:
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
whatwg-fetch@^3.6.2:
version "3.6.2"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c"
integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment