feat: navbar header containers
BREAKING CHANGE: major navigation refactor
This commit is contained in:
parent
4c58699d1f
commit
1348c91c3c
@ -160,7 +160,7 @@ h4
|
|||||||
--current-header-height: var(--header-height-collapsed)
|
--current-header-height: var(--header-height-collapsed)
|
||||||
position: relative
|
position: relative
|
||||||
background-color: white
|
background-color: white
|
||||||
transition: padding-left .2s ease-out
|
transition: padding-left .2s ease-out, margin-top 0.2s cubic-bezier(0.03, 0.43, 0.58, 1)
|
||||||
margin-top: var(--current-header-height)
|
margin-top: var(--current-header-height)
|
||||||
margin-left: 0
|
margin-left: 0
|
||||||
|
|
||||||
@ -173,6 +173,9 @@ h4
|
|||||||
> .container
|
> .container
|
||||||
margin: 20px 0
|
margin: 20px 0
|
||||||
|
|
||||||
|
.navbar__container-radio:checked ~ * &
|
||||||
|
margin-top: calc(var(--current-header-height) + 40px)
|
||||||
|
|
||||||
.main__content, .modal__content
|
.main__content, .modal__content
|
||||||
a
|
a
|
||||||
text-decoration: underline
|
text-decoration: underline
|
||||||
@ -228,6 +231,7 @@ input[type="submit"],
|
|||||||
input[type="button"],
|
input[type="button"],
|
||||||
button,
|
button,
|
||||||
.btn
|
.btn
|
||||||
|
font-family: var(--font-base)
|
||||||
outline: 0
|
outline: 0
|
||||||
border: 0
|
border: 0
|
||||||
box-shadow: 0
|
box-shadow: 0
|
||||||
@ -639,6 +643,9 @@ section
|
|||||||
z-index: 19
|
z-index: 19
|
||||||
pointer-events: none
|
pointer-events: none
|
||||||
|
|
||||||
|
.navbar__container-radio:checked ~ &
|
||||||
|
top: calc(80px + var(--header-height))
|
||||||
|
|
||||||
@media (max-width: 768px)
|
@media (max-width: 768px)
|
||||||
.ribbon
|
.ribbon
|
||||||
top: calc(20px + var(--header-height-collapsed))
|
top: calc(20px + var(--header-height-collapsed))
|
||||||
|
|||||||
@ -1,48 +1,89 @@
|
|||||||
import { Utility } from '../../core/utility';
|
import { Utility } from '../../core/utility';
|
||||||
import './navbar.sass';
|
import './navbar.sass';
|
||||||
|
import * as throttle from 'lodash.throttle';
|
||||||
|
|
||||||
|
export const HEADER_CONTAINER_UTIL_SELECTOR = '.navbar__list-item--container-selector .navbar__link-wrapper';
|
||||||
export const LANGUAGE_SELECT_UTIL_SELECTOR = '[uw-language-select]';
|
const HEADER_CONTAINER_INITIALIZED_CLASS = '.navbar-header-container--initialized';
|
||||||
const LANGUAGE_SELECT_INITIALIZED_CLASS = 'language-select--initialized';
|
|
||||||
|
|
||||||
|
|
||||||
@Utility({
|
@Utility({
|
||||||
selector: LANGUAGE_SELECT_UTIL_SELECTOR,
|
selector: HEADER_CONTAINER_UTIL_SELECTOR,
|
||||||
})
|
})
|
||||||
export class LanguageSelectUtil {
|
export class NavHeaderContainerUtil {
|
||||||
_element;
|
_element;
|
||||||
checkbox;
|
radioButton;
|
||||||
|
closeButton;
|
||||||
|
container;
|
||||||
|
|
||||||
|
wasOpen;
|
||||||
|
|
||||||
|
_throttleUpdateWasOpen;
|
||||||
|
|
||||||
constructor(element) {
|
constructor(element) {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
throw new Error('Language Select utility needs to be passed an element!');
|
throw new Error('Navbar Header Container utility needs to be passed an element!');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.classList.contains(LANGUAGE_SELECT_INITIALIZED_CLASS)) {
|
if (element.classList.contains(HEADER_CONTAINER_INITIALIZED_CLASS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._element = element;
|
this._element = element;
|
||||||
this.checkbox = element.querySelector('#lang-checkbox');
|
this.radioButton = document.getElementById(`${this._element.id}-radio`);
|
||||||
|
if (!this.radioButton) {
|
||||||
|
throw new Error('Navbar Header Container utility could not find associated radio button!');
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener('click', event => this.close(event));
|
this.closeButton = document.getElementById('container-radio-none');
|
||||||
|
if (!this.closeButton) {
|
||||||
|
throw new Error('Navbar Header Container utility could not find radio button for closing!');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.container = document.getElementById(`${this._element.id}-container`);
|
||||||
|
if (!this.container) {
|
||||||
|
throw new Error('Navbar Header Container utility could not find associated container!');
|
||||||
|
}
|
||||||
|
|
||||||
element.classList.add(LANGUAGE_SELECT_INITIALIZED_CLASS);
|
const closer = this.container.querySelector('.navbar__container-list-closer');
|
||||||
|
if (closer) {
|
||||||
|
closer.classList.add('navbar__container-list-closer--hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateWasOpen();
|
||||||
|
this.throttleUpdateWasOpen = throttle(this.updateWasOpen.bind(this), 100, { leading: false, trailing: true });
|
||||||
|
|
||||||
|
this._element.classList.add(HEADER_CONTAINER_INITIALIZED_CLASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
close(event) {
|
start() {
|
||||||
if (!this._element.contains(event.target) && window.document.contains(event.target)) {
|
window.addEventListener('click', this.clickHandler.bind(this));
|
||||||
this.checkbox.checked = false;
|
this.radioButton.addEventListener('change', this.throttleUpdateWasOpen.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
clickHandler() {
|
||||||
|
if (!this.container.contains(event.target) && window.document.contains(event.target) && this.wasOpen) {
|
||||||
|
this.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
close() {
|
||||||
// TODO
|
this.radioButton.checked = false;
|
||||||
|
this.closeButton.checked = true;
|
||||||
|
this.throttleUpdateWasOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isOpen() {
|
||||||
|
return this.radioButton.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateWasOpen() {
|
||||||
|
this.wasOpen = this.isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() { /* TODO */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const NavbarUtils = [
|
export const NavbarUtils = [
|
||||||
LanguageSelectUtil,
|
NavHeaderContainerUtil,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -21,28 +21,115 @@
|
|||||||
|
|
||||||
.navbar
|
.navbar
|
||||||
position: fixed
|
position: fixed
|
||||||
display: flex
|
|
||||||
flex-direction: row
|
|
||||||
align-items: center
|
|
||||||
justify-content: flex-start
|
|
||||||
right: 0
|
right: 0
|
||||||
top: 0
|
top: 0
|
||||||
left: var(--asidenav-width-xl)
|
left: var(--asidenav-width-xl)
|
||||||
height: var(--header-height)
|
min-height: var(--header-height)
|
||||||
background-color: var(--color-primary)
|
background-color: var(--color-primary)
|
||||||
color: white
|
color: white
|
||||||
z-index: 20
|
z-index: 20
|
||||||
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2)
|
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2)
|
||||||
overflow: auto
|
overflow: auto
|
||||||
transition: all 0.2s cubic-bezier(0.03, 0.43, 0.58, 1)
|
transition: all 0.2s cubic-bezier(0.03, 0.43, 0.58, 1)
|
||||||
|
margin: 0
|
||||||
|
padding: 10px 0
|
||||||
|
|
||||||
@media (max-width: 1199px)
|
@media (max-width: 1199px)
|
||||||
.navbar
|
|
||||||
left: var(--asidenav-width-lg)
|
left: var(--asidenav-width-lg)
|
||||||
|
|
||||||
@media (max-width: 768px)
|
@media (max-width: 768px), (max-height: 500px)
|
||||||
.navbar
|
|
||||||
left: 0
|
left: 0
|
||||||
|
min-height: var(--header-height-collapsed)
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
@media (max-width: 768px), (max-height: 500px)
|
||||||
|
.navbar__link-wrapper
|
||||||
|
height: var(--header-height-collapsed)
|
||||||
|
|
||||||
|
.navbar__list-wrapper
|
||||||
|
display: flex
|
||||||
|
flex-flow: row nowrap
|
||||||
|
justify-content: space-between
|
||||||
|
align-items: center
|
||||||
|
margin: 0 40px
|
||||||
|
|
||||||
|
.navbar__list-left
|
||||||
|
margin-right: 40px
|
||||||
|
|
||||||
|
.navbar__list
|
||||||
|
display: flex
|
||||||
|
flex-flow: row nowrap
|
||||||
|
justify-content: flex-end
|
||||||
|
align-items: center
|
||||||
|
list-style-type: none
|
||||||
|
|
||||||
|
&.navbar__list-left
|
||||||
|
justify-content: flex-start
|
||||||
|
|
||||||
|
& > *
|
||||||
|
display: block
|
||||||
|
|
||||||
|
.navbar__container-list
|
||||||
|
position: relative
|
||||||
|
width: 100%
|
||||||
|
/* margin: 10px 0 0 0 */
|
||||||
|
padding: 0 40px
|
||||||
|
overflow: hidden
|
||||||
|
|
||||||
|
& > ul
|
||||||
|
display: flex
|
||||||
|
flex-flow: row nowrap
|
||||||
|
align-items: center
|
||||||
|
overflow: auto
|
||||||
|
list-style-type: none
|
||||||
|
justify-content: flex-end
|
||||||
|
|
||||||
|
& > *
|
||||||
|
display: block
|
||||||
|
margin-right: 12px
|
||||||
|
|
||||||
|
&:last-child
|
||||||
|
margin-right: 0
|
||||||
|
|
||||||
|
&.navbar__container-list--left > ul
|
||||||
|
justify-content: flex-start
|
||||||
|
|
||||||
|
@media (max-width: 768px), (max-height: 500px)
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
visibility: collapse
|
||||||
|
margin: 0
|
||||||
|
height: 0
|
||||||
|
transition: height 0.2s cubic-bezier(0.03, 0.43, 0.58, 1), margin 0.2s cubic-bezier(0.03, 0.43, 0.58, 1)
|
||||||
|
|
||||||
|
.navbar__container-list-closer
|
||||||
|
position: absolute
|
||||||
|
top: 5px
|
||||||
|
right: 10px
|
||||||
|
width: 20px
|
||||||
|
height: 20px
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
transform-origin: 10px 10px
|
||||||
|
transform: rotate(-0.25turn)
|
||||||
|
&.navbar__container-list--left
|
||||||
|
transform: rotate(0.25turn)
|
||||||
|
|
||||||
|
opacity: 0.5
|
||||||
|
transition: transform 0.2s, opacity 0.2s ease
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
opacity: 1
|
||||||
|
|
||||||
|
transform: scale(1.4)
|
||||||
|
|
||||||
|
&.navbar__container-list-closer--hidden
|
||||||
|
visibility: hidden
|
||||||
|
|
||||||
|
&.navbar__container-list--left .navbar__container-list-closer
|
||||||
|
left: 14.5px
|
||||||
|
right: auto
|
||||||
|
|
||||||
|
|
||||||
// links
|
// links
|
||||||
.navbar__link-wrapper
|
.navbar__link-wrapper
|
||||||
@ -67,12 +154,13 @@
|
|||||||
padding: 2px 4px
|
padding: 2px 4px
|
||||||
text-transform: uppercase
|
text-transform: uppercase
|
||||||
font-weight: 600
|
font-weight: 600
|
||||||
|
font-size: 16px
|
||||||
|
|
||||||
@media (min-width: 769px)
|
@media (min-width: 769px) and (min-height: 501px)
|
||||||
.navbar__link-wrapper
|
.navbar__link-wrapper
|
||||||
border: 1px solid rgba(255, 255, 255, 0.7)
|
border: 1px solid rgba(255, 255, 255, 0.7)
|
||||||
|
|
||||||
@media (max-width: 768px)
|
@media (max-width: 768px), (max-height: 500px)
|
||||||
.navbar__link-wrapper
|
.navbar__link-wrapper
|
||||||
box-shadow: none
|
box-shadow: none
|
||||||
min-width: 0
|
min-width: 0
|
||||||
@ -86,22 +174,42 @@
|
|||||||
transform: scale(0.65)
|
transform: scale(0.65)
|
||||||
margin-bottom: 0
|
margin-bottom: 0
|
||||||
|
|
||||||
// navbar list
|
.navbar__container-link
|
||||||
.navbar__list
|
display: block
|
||||||
white-space: nowrap
|
|
||||||
|
@media (min-width: 769px) and (min-height: 501px)
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.7)
|
||||||
|
text-decoration: none
|
||||||
|
|
||||||
+ .navbar__list
|
@media (max-width: 768px), (max-height: 500px)
|
||||||
margin-left: 12px
|
text-decoration: underline
|
||||||
|
|
||||||
@media (min-width: 769px)
|
height: 30px
|
||||||
.navbar__list:last-of-type
|
color: var(--color-lightwhite) !important
|
||||||
padding-right: 40px
|
background-color: rgba(0, 0, 0, 0) !important
|
||||||
|
padding: 5px 10px
|
||||||
|
text-transform: uppercase
|
||||||
|
font-weight: 600
|
||||||
|
font-size: 16px
|
||||||
|
outline: 0
|
||||||
|
min-width: 0
|
||||||
|
transition: none
|
||||||
|
cursor: pointer
|
||||||
|
|
||||||
|
&:not(.navbar__container-link--active):hover
|
||||||
|
text-decoration: none
|
||||||
|
|
||||||
|
@media (min-width: 769px) and (min-height: 501px)
|
||||||
|
background-color: var(--color-dark) !important
|
||||||
|
color: var(--color-lightwhite) !important
|
||||||
|
|
||||||
|
&.navbar__container-link--active
|
||||||
|
text-decoration: none
|
||||||
|
|
||||||
|
@media (min-width: 769px) and (min-height: 501px)
|
||||||
|
background-color: var(--color-lightwhite) !important
|
||||||
|
color: var(--color-dark) !important
|
||||||
|
|
||||||
@media (max-width: 768px)
|
|
||||||
.navbar__list
|
|
||||||
+ .navbar__list
|
|
||||||
margin-left: 0
|
|
||||||
padding-right: 40px
|
|
||||||
|
|
||||||
// list item
|
// list item
|
||||||
.navbar__list-item
|
.navbar__list-item
|
||||||
@ -124,34 +232,10 @@
|
|||||||
&:not(.navbar__list-item--favorite) + .navbar__list-item--lang-wrapper
|
&:not(.navbar__list-item--favorite) + .navbar__list-item--lang-wrapper
|
||||||
margin-left: 0
|
margin-left: 0
|
||||||
|
|
||||||
.navbar__list-left
|
|
||||||
flex: 5
|
|
||||||
padding-left: 40px
|
|
||||||
|
|
||||||
@media (max-width: 768px)
|
|
||||||
.navbar__list-left
|
|
||||||
padding-left: 0
|
|
||||||
|
|
||||||
// "Favorites" list item, only visible on small screens and logged in
|
// "Favorites" list item, only visible on small screens and logged in
|
||||||
.navbar__list-item
|
|
||||||
&.navbar__list-item--favorite
|
|
||||||
display: none
|
|
||||||
|
|
||||||
.navbar__list-item--favorite
|
|
||||||
display: none
|
|
||||||
background-color: var(--color-primary)
|
|
||||||
|
|
||||||
.logged-in
|
|
||||||
.navbar__list
|
|
||||||
li.navbar__list-item--favorite,
|
|
||||||
.navbar__list-item--favorite
|
|
||||||
display: inline-block
|
|
||||||
|
|
||||||
@media (min-width: 426px)
|
@media (min-width: 426px)
|
||||||
.logged-in
|
.navbar__list-item--favorite
|
||||||
.navbar__list
|
display: none !important
|
||||||
.navbar__list-item--favorite
|
|
||||||
display: none !important
|
|
||||||
|
|
||||||
.navbar__list-item--active
|
.navbar__list-item--active
|
||||||
background-color: var(--color-lightwhite)
|
background-color: var(--color-lightwhite)
|
||||||
@ -163,7 +247,7 @@
|
|||||||
.navbar__list-item--active .navbar__link-wrapper
|
.navbar__list-item--active .navbar__link-wrapper
|
||||||
color: var(--color-dark)
|
color: var(--color-dark)
|
||||||
|
|
||||||
.navbar .navbar__list-item:not(.navbar__list-item--active):not(.navbar__list-item--favorite):hover .navbar__link-wrapper, #lang-checkbox:checked ~ * .navbar__link-wrapper
|
.navbar__list-item:not(.navbar__list-item--active):hover .navbar__link-wrapper
|
||||||
background-color: var(--color-dark)
|
background-color: var(--color-dark)
|
||||||
color: var(--color-lightwhite)
|
color: var(--color-lightwhite)
|
||||||
|
|
||||||
@ -186,43 +270,5 @@
|
|||||||
display: block
|
display: block
|
||||||
height: var(--header-height-collapsed)
|
height: var(--header-height-collapsed)
|
||||||
|
|
||||||
@media (max-width: 768px)
|
.navbar__container-radio--none, .navbar__container-radio
|
||||||
.navbar,
|
|
||||||
.navbar__pushdown
|
|
||||||
height: var(--header-height-collapsed)
|
|
||||||
|
|
||||||
.navbar__link-wrapper
|
|
||||||
height: var(--header-height-collapsed)
|
|
||||||
|
|
||||||
@media (max-height: 500px)
|
|
||||||
.navbar,
|
|
||||||
.navbar__pushdown
|
|
||||||
height: var(--header-height-collapsed)
|
|
||||||
|
|
||||||
.navbar__link-wrapper
|
|
||||||
height: var(--header-height-collapsed)
|
|
||||||
|
|
||||||
#lang-dropdown
|
|
||||||
display: none
|
display: none
|
||||||
position: fixed
|
|
||||||
top: var(--header-height)
|
|
||||||
right: 0
|
|
||||||
min-width: 200px
|
|
||||||
z-index: 10
|
|
||||||
background-color: white
|
|
||||||
border-radius: 2px
|
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3)
|
|
||||||
|
|
||||||
select
|
|
||||||
display: block
|
|
||||||
|
|
||||||
button
|
|
||||||
display: block
|
|
||||||
width: 100%
|
|
||||||
|
|
||||||
#lang-checkbox:checked ~ #lang-dropdown
|
|
||||||
display: block
|
|
||||||
|
|
||||||
@media (max-width: 768px)
|
|
||||||
#lang-dropdown
|
|
||||||
top: var(--header-height-collapsed)
|
|
||||||
|
|||||||
@ -493,7 +493,7 @@ CorrectorsPlaceholder: Korrektoren...
|
|||||||
CorrectorsDefaulted: Korrektoren-Liste wurde aus bisherigen Übungsblättern diesen Kurses generiert. Es sind keine Daten gespeichert.
|
CorrectorsDefaulted: Korrektoren-Liste wurde aus bisherigen Übungsblättern diesen Kurses generiert. Es sind keine Daten gespeichert.
|
||||||
|
|
||||||
Users: Benutzer
|
Users: Benutzer
|
||||||
HomeHeading: Aktuelle Termine
|
NewsHeading: Aktuelles
|
||||||
LoginHeading: Authentifizierung
|
LoginHeading: Authentifizierung
|
||||||
LoginTitle: Authentifizierung
|
LoginTitle: Authentifizierung
|
||||||
ProfileHeading: Benutzereinstellungen
|
ProfileHeading: Benutzereinstellungen
|
||||||
@ -508,9 +508,9 @@ NotificationSettingsHeading displayName@Text: Benachrichtigungs-Einstellungen f
|
|||||||
TokensLastReset: Tokens zuletzt invalidiert
|
TokensLastReset: Tokens zuletzt invalidiert
|
||||||
TokensResetSuccess: Authorisierungs-Tokens invalidiert
|
TokensResetSuccess: Authorisierungs-Tokens invalidiert
|
||||||
|
|
||||||
HomeOpenAllocations: Offene Zentralanmeldungen
|
NewsOpenAllocations: Offene Zentralanmeldungen
|
||||||
HomeUpcomingSheets: Anstehende Übungsblätter
|
NewsUpcomingSheets: Anstehende Übungsblätter
|
||||||
HomeUpcomingExams: Bevorstehende Prüfungen
|
NewsUpcomingExams: Bevorstehende Prüfungen
|
||||||
|
|
||||||
NumCourses num@Int64: #{num} #{pluralDE num "Kurs" "Kurse"}
|
NumCourses num@Int64: #{num} #{pluralDE num "Kurs" "Kurse"}
|
||||||
CloseAlert: Schliessen
|
CloseAlert: Schliessen
|
||||||
@ -1114,7 +1114,7 @@ InvalidRoute: Konnte URL nicht interpretieren
|
|||||||
|
|
||||||
MenuOpenCourses: Kurse mit offener Registrierung
|
MenuOpenCourses: Kurse mit offener Registrierung
|
||||||
MenuOpenAllocations: Aktive Zentralanmeldungen
|
MenuOpenAllocations: Aktive Zentralanmeldungen
|
||||||
MenuHome: Aktuell
|
MenuNews: Aktuell
|
||||||
MenuInformation: Informationen
|
MenuInformation: Informationen
|
||||||
MenuLegal: Rechtliche Informationen
|
MenuLegal: Rechtliche Informationen
|
||||||
MenuDataProt: Datenschutzerklärung
|
MenuDataProt: Datenschutzerklärung
|
||||||
@ -1561,7 +1561,6 @@ ExamBonusRule: Prüfungsbonus aus Übungsbetrieb
|
|||||||
ExamNoBonus': Kein automatischer Bonus
|
ExamNoBonus': Kein automatischer Bonus
|
||||||
ExamBonusPoints': Umrechnung von Übungspunkten
|
ExamBonusPoints': Umrechnung von Übungspunkten
|
||||||
ExamBonusManual': Manuelle Berechnung
|
ExamBonusManual': Manuelle Berechnung
|
||||||
ExamGradesExplanation: Diese Ansicht zeigt die selben Daten an, wie die Tabelle von Prüfungsteilnehmern. Anpassen der Teilnehmerdaten und Ergebnisse ist nur dort möglich. Hier können Sie vor Allem einsehen und markieren, welche Prüfungsleistungen von den zuständigen Prüfungsbeauftragten bereits vollständig bearbeitet wurden.
|
|
||||||
|
|
||||||
ExamRegisterForOccurrence: Anmeldung zur Klausur erfolgt durch Anmeldung zu einem Termin/Raum
|
ExamRegisterForOccurrence: Anmeldung zur Klausur erfolgt durch Anmeldung zu einem Termin/Raum
|
||||||
|
|
||||||
|
|||||||
@ -491,7 +491,7 @@ CorrectorsPlaceholder: Correctors...
|
|||||||
CorrectorsDefaulted: List of correctors was automatically generated based on those of preceding sheets for this course. No data has been saved, yet.
|
CorrectorsDefaulted: List of correctors was automatically generated based on those of preceding sheets for this course. No data has been saved, yet.
|
||||||
|
|
||||||
Users: Users
|
Users: Users
|
||||||
HomeHeading: Home
|
NewsHeading: News
|
||||||
LoginHeading: Authentication
|
LoginHeading: Authentication
|
||||||
LoginTitle: Authentication
|
LoginTitle: Authentication
|
||||||
ProfileHeading: Settings
|
ProfileHeading: Settings
|
||||||
@ -506,9 +506,9 @@ NotificationSettingsHeading displayName: Notification settings for #{displayName
|
|||||||
TokensLastReset: Tokens last reset
|
TokensLastReset: Tokens last reset
|
||||||
TokensResetSuccess: Successfully invalidated all authorisation tokens
|
TokensResetSuccess: Successfully invalidated all authorisation tokens
|
||||||
|
|
||||||
HomeOpenAllocations: Active central allocations
|
NewsOpenAllocations: Active central allocations
|
||||||
HomeUpcomingSheets: Upcoming exercise sheets
|
NewsUpcomingSheets: Upcoming exercise sheets
|
||||||
HomeUpcomingExams: Upcoming exams
|
NewsUpcomingExams: Upcoming exams
|
||||||
|
|
||||||
NumCourses num: #{num} #{pluralEN num "course" "courses"}
|
NumCourses num: #{num} #{pluralEN num "course" "courses"}
|
||||||
CloseAlert: Close
|
CloseAlert: Close
|
||||||
@ -1113,7 +1113,7 @@ InvalidRoute: Could not interpret url
|
|||||||
|
|
||||||
MenuOpenCourses: Courses with open registration
|
MenuOpenCourses: Courses with open registration
|
||||||
MenuOpenAllocations: Active central allocations
|
MenuOpenAllocations: Active central allocations
|
||||||
MenuHome: Home
|
MenuNews: News
|
||||||
MenuInformation: Information
|
MenuInformation: Information
|
||||||
MenuLegal: Legal
|
MenuLegal: Legal
|
||||||
MenuDataProt: Data protection
|
MenuDataProt: Data protection
|
||||||
@ -1559,7 +1559,6 @@ ExamBonusRule: Bonus points from exercises
|
|||||||
ExamNoBonus': No automatic exam bonus
|
ExamNoBonus': No automatic exam bonus
|
||||||
ExamBonusPoints': Compute from exercise achievements
|
ExamBonusPoints': Compute from exercise achievements
|
||||||
ExamBonusManual': Manual computation
|
ExamBonusManual': Manual computation
|
||||||
ExamGradesExplanation: This view shows the same data as the table of exam participants. Changing participant's data and achievements is only possible via the table of exam participants. Primarily, this view allows you to check and adjust which exam achievements were properly handled by the relevant exam offices.
|
|
||||||
|
|
||||||
ExamRegisterForOccurrence: Registration for this exam is done by registering for an occurrence/room
|
ExamRegisterForOccurrence: Registration for this exam is done by registering for an occurrence/room
|
||||||
|
|
||||||
|
|||||||
2
routes
2
routes
@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
/metrics MetricsR GET
|
/metrics MetricsR GET
|
||||||
|
|
||||||
/ HomeR GET !free
|
/ NewsR GET !free
|
||||||
/users UsersR GET POST -- no tags, i.e. admins only
|
/users UsersR GET POST -- no tags, i.e. admins only
|
||||||
/users/#CryptoUUIDUser AdminUserR GET POST
|
/users/#CryptoUUIDUser AdminUserR GET POST
|
||||||
/users/#CryptoUUIDUser/delete AdminUserDeleteR POST
|
/users/#CryptoUUIDUser/delete AdminUserDeleteR POST
|
||||||
|
|||||||
@ -101,7 +101,7 @@ import Data.List (cycle)
|
|||||||
|
|
||||||
-- Import all relevant handler modules here.
|
-- Import all relevant handler modules here.
|
||||||
-- (HPack takes care to add new modules to our cabal file nowadays.)
|
-- (HPack takes care to add new modules to our cabal file nowadays.)
|
||||||
import Handler.Home
|
import Handler.News
|
||||||
import Handler.Info
|
import Handler.Info
|
||||||
import Handler.Help
|
import Handler.Help
|
||||||
import Handler.Profile
|
import Handler.Profile
|
||||||
|
|||||||
2735
src/Foundation.hs
2735
src/Foundation.hs
File diff suppressed because it is too large
Load Diff
@ -168,7 +168,7 @@ instance RenderMessage UniWorX MsgLanguage where
|
|||||||
| ("en" : _) <- lang' = mr MsgEnglish
|
| ("en" : _) <- lang' = mr MsgEnglish
|
||||||
| otherwise = lang
|
| otherwise = lang
|
||||||
where
|
where
|
||||||
mr = renderMessage foundation ls
|
mr = renderMessage foundation $ lang : filter (/= lang) ls
|
||||||
|
|
||||||
embedRenderMessage ''UniWorX ''MessageStatus ("Message" <>)
|
embedRenderMessage ''UniWorX ''MessageStatus ("Message" <>)
|
||||||
embedRenderMessage ''UniWorX ''NotificationTrigger $ ("NotificationTrigger" <>) . concat . drop 1 . splitCamel
|
embedRenderMessage ''UniWorX ''NotificationTrigger $ ("NotificationTrigger" <>) . concat . drop 1 . splitCamel
|
||||||
|
|||||||
@ -1089,5 +1089,6 @@ postEUsersR tid ssh csh examn = do
|
|||||||
|
|
||||||
siteLayoutMsg (prependCourseTitle tid ssh csh MsgExamUsersHeading) $ do
|
siteLayoutMsg (prependCourseTitle tid ssh csh MsgExamUsersHeading) $ do
|
||||||
setTitleI $ prependCourseTitle tid ssh csh MsgExamUsersHeading
|
setTitleI $ prependCourseTitle tid ssh csh MsgExamUsersHeading
|
||||||
let computedValuesTip = $(i18nWidgetFile "exam-users/computed-values-tip")
|
let computedValuesTip = notificationWidget NotificationBroad Warning
|
||||||
|
$(i18nWidgetFile "exam-users/computed-values-tip")
|
||||||
$(widgetFile "exam-users")
|
$(widgetFile "exam-users")
|
||||||
|
|||||||
@ -441,4 +441,5 @@ postEGradesR tid ssh csh examn = do
|
|||||||
|
|
||||||
siteLayoutMsg (prependCourseTitle tid ssh csh MsgExamOfficeExamUsersHeading) $ do
|
siteLayoutMsg (prependCourseTitle tid ssh csh MsgExamOfficeExamUsersHeading) $ do
|
||||||
setTitleI $ prependCourseTitle tid ssh csh MsgExamOfficeExamUsersHeading
|
setTitleI $ prependCourseTitle tid ssh csh MsgExamOfficeExamUsersHeading
|
||||||
|
let examGradesExplanation = notificationWidget NotificationBroad Info $(i18nWidgetFile "exam-office/exam-grades-explanation")
|
||||||
$(widgetFile "exam-office/exam-results")
|
$(widgetFile "exam-office/exam-results")
|
||||||
|
|||||||
@ -30,4 +30,5 @@ postEEGradesR tid ssh coursen examn = do
|
|||||||
|
|
||||||
siteLayoutMsg (MsgExternalExamGrades coursen examn) $ do
|
siteLayoutMsg (MsgExternalExamGrades coursen examn) $ do
|
||||||
setTitleI MsgBreadcrumbExternalExamGrades
|
setTitleI MsgBreadcrumbExternalExamGrades
|
||||||
|
let examGradesExplanation = notificationWidget NotificationBroad Info $(i18nWidgetFile "exam-office/exam-grades-explanation")
|
||||||
$(widgetFile "exam-office/externalExamGrades")
|
$(widgetFile "exam-office/externalExamGrades")
|
||||||
|
|||||||
@ -80,7 +80,7 @@ examOfficeUserInvitationConfig = InvitationConfig{..}
|
|||||||
return res
|
return res
|
||||||
invitationSuccessMsg _ _ =
|
invitationSuccessMsg _ _ =
|
||||||
return $ SomeMessage MsgExamOfficeUserInvitationAccepted
|
return $ SomeMessage MsgExamOfficeUserInvitationAccepted
|
||||||
invitationUltDest _ _ = return $ SomeRoute HomeR
|
invitationUltDest _ _ = return $ SomeRoute NewsR
|
||||||
|
|
||||||
|
|
||||||
makeExamOfficeUsersForm :: Maybe (Set (Either UserEmail UserId)) -> Form (Set (Either UserEmail UserId))
|
makeExamOfficeUsersForm :: Maybe (Set (Either UserEmail UserId)) -> Form (Set (Either UserEmail UserId))
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
module Handler.Home where
|
module Handler.News where
|
||||||
|
|
||||||
import Import
|
import Import
|
||||||
|
|
||||||
@ -9,21 +9,25 @@ import Database.Esqueleto.Utils.TH
|
|||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
import qualified Database.Esqueleto.Utils as E
|
import qualified Database.Esqueleto.Utils as E
|
||||||
|
|
||||||
getHomeR :: Handler Html
|
getNewsR :: Handler Html
|
||||||
getHomeR = do
|
getNewsR = do
|
||||||
muid <- maybeAuthId
|
muid <- maybeAuthId
|
||||||
defaultLayout $ do
|
defaultLayout $ do
|
||||||
setTitleI MsgHomeHeading
|
setTitleI MsgNewsHeading
|
||||||
|
|
||||||
|
when (is _Nothing muid) $
|
||||||
|
notificationWidget NotificationBroad Info $(i18nWidgetFile "pitch")
|
||||||
|
|
||||||
case muid of
|
case muid of
|
||||||
Just uid -> do
|
Just uid -> do
|
||||||
homeUpcomingExams uid
|
newsUpcomingExams uid
|
||||||
homeUpcomingSheets uid
|
newsUpcomingSheets uid
|
||||||
Nothing ->
|
Nothing ->
|
||||||
$(i18nWidgetFile "unauth-home")
|
$(i18nWidgetFile "unauth-news")
|
||||||
|
|
||||||
|
|
||||||
homeUpcomingSheets :: UserId -> Widget
|
newsUpcomingSheets :: UserId -> Widget
|
||||||
homeUpcomingSheets uid = do
|
newsUpcomingSheets uid = do
|
||||||
cTime <- liftIO getCurrentTime
|
cTime <- liftIO getCurrentTime
|
||||||
let tableData :: E.LeftOuterJoin
|
let tableData :: E.LeftOuterJoin
|
||||||
(E.InnerJoin (E.InnerJoin (E.SqlExpr (Entity CourseParticipant)) (E.SqlExpr (Entity Course))) (E.SqlExpr (Entity Sheet)))
|
(E.InnerJoin (E.InnerJoin (E.SqlExpr (Entity CourseParticipant)) (E.SqlExpr (Entity Course))) (E.SqlExpr (Entity Sheet)))
|
||||||
@ -121,11 +125,11 @@ homeUpcomingSheets uid = do
|
|||||||
, dbtCsvEncode = noCsvEncode
|
, dbtCsvEncode = noCsvEncode
|
||||||
, dbtCsvDecode = Nothing
|
, dbtCsvDecode = Nothing
|
||||||
}
|
}
|
||||||
$(widgetFile "home/upcomingSheets")
|
$(widgetFile "news/upcomingSheets")
|
||||||
|
|
||||||
|
|
||||||
homeUpcomingExams :: UserId -> Widget
|
newsUpcomingExams :: UserId -> Widget
|
||||||
homeUpcomingExams uid = do
|
newsUpcomingExams uid = do
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
((Any hasExams, examTable), warningDays) <- liftHandler . runDB $ do
|
((Any hasExams, examTable), warningDays) <- liftHandler . runDB $ do
|
||||||
User {userWarningDays} <- get404 uid
|
User {userWarningDays} <- get404 uid
|
||||||
@ -255,6 +259,6 @@ homeUpcomingExams uid = do
|
|||||||
|
|
||||||
(, userWarningDays) <$> dbTable examDBTableValidator examDBTable
|
(, userWarningDays) <$> dbTable examDBTableValidator examDBTable
|
||||||
|
|
||||||
$(widgetFile "home/upcomingExams")
|
$(widgetFile "news/upcomingExams")
|
||||||
|
|
||||||
|
|
||||||
@ -850,17 +850,14 @@ postCsvOptionsR = do
|
|||||||
, formAttrs = [ asyncSubmitAttr | isModal ]
|
, formAttrs = [ asyncSubmitAttr | isModal ]
|
||||||
}
|
}
|
||||||
|
|
||||||
postLangR :: Handler ()
|
postLangR :: Handler Void
|
||||||
postLangR = do
|
postLangR = do
|
||||||
((langRes, _), _) <- runFormPost $ identifyForm FIDLanguage langForm
|
requestedLang <- selectLanguage' appLanguages . hoistMaybe <$> lookupGlobalPostParam PostLanguage
|
||||||
|
lang' <- runDB . updateUserLanguage $ Just requestedLang
|
||||||
|
|
||||||
formResult langRes $ \(lang, route) -> do
|
app <- getYesod
|
||||||
lang' <- runDB . updateUserLanguage $ Just lang
|
let mr | Just lang'' <- lang' = renderMessage app . map (Text.intercalate "-") . reverse . inits $ Text.splitOn "-" lang''
|
||||||
|
| otherwise = renderMessage app []
|
||||||
app <- getYesod
|
addMessage Success . toHtml $ mr MsgLanguageChanged
|
||||||
let mr | Just lang'' <- lang' = renderMessage app . map (Text.intercalate "-") . reverse . inits $ Text.splitOn "-" lang''
|
|
||||||
| otherwise = renderMessage app []
|
|
||||||
addMessage Success . toHtml $ mr MsgLanguageChanged
|
|
||||||
redirect route
|
|
||||||
|
|
||||||
invalidArgs ["Language form required"]
|
redirect . fromMaybe NewsR =<< lookupGlobalGetParam GetReferer
|
||||||
|
|||||||
@ -399,7 +399,7 @@ invitationR' InvitationConfig{..} = liftHandler $ do
|
|||||||
Nothing -> do
|
Nothing -> do
|
||||||
addMessageI Info MsgInvitationDeclined
|
addMessageI Info MsgInvitationDeclined
|
||||||
deleteBy . UniqueInvitation itEmail $ invRef @junction fid
|
deleteBy . UniqueInvitation itEmail $ invRef @junction fid
|
||||||
return . Just $ SomeRoute HomeR
|
return . Just $ SomeRoute NewsR
|
||||||
Just (jData, formCtx) -> do
|
Just (jData, formCtx) -> do
|
||||||
let junction = review _InvitableJunction (invitee, fid, jData)
|
let junction = review _InvitableJunction (invitee, fid, jData)
|
||||||
mResult <- invitationInsertHook itEmail fEnt iData junction formCtx $ insertUniqueEntity junction
|
mResult <- invitationInsertHook itEmail fEnt iData junction formCtx $ insertUniqueEntity junction
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import Model.Submission as Import
|
|||||||
import Model.Tokens as Import
|
import Model.Tokens as Import
|
||||||
import Utils.Tokens as Import
|
import Utils.Tokens as Import
|
||||||
import Utils.Frontend.Modal as Import
|
import Utils.Frontend.Modal as Import
|
||||||
|
import Utils.Frontend.Notification as Import
|
||||||
import Utils.Lens as Import
|
import Utils.Lens as Import
|
||||||
|
|
||||||
import Settings as Import
|
import Settings as Import
|
||||||
|
|||||||
@ -46,6 +46,7 @@ import Data.Scientific
|
|||||||
import Data.Time.Clock (NominalDiffTime, nominalDay)
|
import Data.Time.Clock (NominalDiffTime, nominalDay)
|
||||||
|
|
||||||
import Utils
|
import Utils
|
||||||
|
import Utils.Frontend.Notification
|
||||||
-- import Utils.Message
|
-- import Utils.Message
|
||||||
-- import Utils.PathPiece
|
-- import Utils.PathPiece
|
||||||
-- import Utils.Route
|
-- import Utils.Route
|
||||||
@ -869,26 +870,15 @@ wformMessage :: (MonadHandler m) => Message -> WForm m ()
|
|||||||
wformMessage = void . aFormToWForm . aformMessage
|
wformMessage = void . aFormToWForm . aformMessage
|
||||||
|
|
||||||
formMessage :: (MonadHandler m) => Message -> MForm m (FormResult (), FieldView site)
|
formMessage :: (MonadHandler m) => Message -> MForm m (FormResult (), FieldView site)
|
||||||
formMessage Message{..} = do
|
formMessage msg = do
|
||||||
return (FormSuccess (), FieldView
|
return (FormSuccess (), FieldView
|
||||||
{ fvLabel = mempty
|
{ fvLabel = mempty
|
||||||
, fvTooltip = Nothing
|
, fvTooltip = Nothing
|
||||||
, fvId = idFormMessageNoinput
|
, fvId = idFormMessageNoinput
|
||||||
, fvErrors = Nothing
|
, fvErrors = Nothing
|
||||||
, fvRequired = False
|
, fvRequired = False
|
||||||
, fvInput = [whamlet|
|
, fvInput = notification NotificationNarrow msg
|
||||||
$newline never
|
|
||||||
<div .notification .notification-#{toPathPiece messageStatus} .fa-#{maybe defaultIcon iconText messageIcon}>
|
|
||||||
<div .notification__content>
|
|
||||||
#{messageContent}
|
|
||||||
|]
|
|
||||||
})
|
})
|
||||||
where
|
|
||||||
defaultIcon = case messageStatus of
|
|
||||||
Success -> "check-circle"
|
|
||||||
Info -> "info-circle"
|
|
||||||
Warning -> "exclamation-circle"
|
|
||||||
Error -> "exclamation-triangle"
|
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
-- Form evaluation --
|
-- Form evaluation --
|
||||||
|
|||||||
43
src/Utils/Frontend/Notification.hs
Normal file
43
src/Utils/Frontend/Notification.hs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
module Utils.Frontend.Notification
|
||||||
|
( NotificationType(..)
|
||||||
|
, notification
|
||||||
|
, notificationWidget
|
||||||
|
) where
|
||||||
|
|
||||||
|
import ClassyPrelude.Yesod
|
||||||
|
import Settings
|
||||||
|
|
||||||
|
import Utils.Message
|
||||||
|
import Utils.Icon
|
||||||
|
|
||||||
|
import Control.Lens
|
||||||
|
import Control.Lens.Extras (is)
|
||||||
|
|
||||||
|
|
||||||
|
data NotificationType
|
||||||
|
= NotificationNarrow
|
||||||
|
| NotificationBroad
|
||||||
|
deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable)
|
||||||
|
|
||||||
|
makePrisms ''NotificationType
|
||||||
|
|
||||||
|
|
||||||
|
notification :: NotificationType
|
||||||
|
-> Message
|
||||||
|
-> WidgetFor site ()
|
||||||
|
notification nType Message{ messageIcon = messageIcon', .. }
|
||||||
|
= $(widgetFile "widgets/notification")
|
||||||
|
where
|
||||||
|
messageIcon = fromMaybe defaultIcon messageIcon'
|
||||||
|
defaultIcon = case messageStatus of
|
||||||
|
Success -> IconNotificationSuccess
|
||||||
|
Info -> IconNotificationInfo
|
||||||
|
Warning -> IconNotificationWarning
|
||||||
|
Error -> IconNotificationError
|
||||||
|
|
||||||
|
notificationWidget :: Yesod site
|
||||||
|
=> NotificationType
|
||||||
|
-> MessageStatus
|
||||||
|
-> WidgetFor site ()
|
||||||
|
-> WidgetFor site ()
|
||||||
|
notificationWidget nType ms = notification nType <=< messageWidget ms
|
||||||
@ -63,44 +63,58 @@ data Icon
|
|||||||
| IconApplicationVeto
|
| IconApplicationVeto
|
||||||
| IconApplicationFiles
|
| IconApplicationFiles
|
||||||
| IconTooltipDefault
|
| IconTooltipDefault
|
||||||
deriving (Eq, Ord, Enum, Bounded, Show, Read)
|
| IconNotificationSuccess
|
||||||
|
| IconNotificationInfo
|
||||||
|
| IconNotificationWarning
|
||||||
|
| IconNotificationError
|
||||||
|
| IconFavourite
|
||||||
|
| IconLanguage
|
||||||
|
| IconNavContainerClose
|
||||||
|
deriving (Eq, Ord, Enum, Bounded, Show, Read, Generic, Typeable)
|
||||||
|
|
||||||
iconText :: Icon -> Text
|
iconText :: Icon -> Text
|
||||||
iconText = \case
|
iconText = \case
|
||||||
IconNew -> "seedling"
|
IconNew -> "seedling"
|
||||||
IconOK -> "check"
|
IconOK -> "check"
|
||||||
IconNotOK -> "times"
|
IconNotOK -> "times"
|
||||||
IconWarning -> "exclamation"
|
IconWarning -> "exclamation"
|
||||||
IconProblem -> "bolt"
|
IconProblem -> "bolt"
|
||||||
IconVisible -> "eye"
|
IconVisible -> "eye"
|
||||||
IconInvisible -> "eye-slash"
|
IconInvisible -> "eye-slash"
|
||||||
IconCourse -> "graduation-cap"
|
IconCourse -> "graduation-cap"
|
||||||
IconEnrolTrue -> "user-plus"
|
IconEnrolTrue -> "user-plus"
|
||||||
IconEnrolFalse -> "user-slash"
|
IconEnrolFalse -> "user-slash"
|
||||||
IconPlanned -> "cog"
|
IconPlanned -> "cog"
|
||||||
IconAnnounce -> "bullhorn"
|
IconAnnounce -> "bullhorn"
|
||||||
IconExam -> "poll-h"
|
IconExam -> "poll-h"
|
||||||
IconExamRegisterTrue -> "calendar-check"
|
IconExamRegisterTrue -> "calendar-check"
|
||||||
IconExamRegisterFalse -> "calendar-times"
|
IconExamRegisterFalse -> "calendar-times"
|
||||||
IconCommentTrue -> "comment-alt"
|
IconCommentTrue -> "comment-alt"
|
||||||
IconCommentFalse -> "comment-slash" -- comment-alt-slash is not available for free
|
IconCommentFalse -> "comment-alt-slash"
|
||||||
IconLink -> "link"
|
IconLink -> "link"
|
||||||
IconFileDownload -> "file-download"
|
IconFileDownload -> "file-download"
|
||||||
IconFileUpload -> "file-upload"
|
IconFileUpload -> "file-upload"
|
||||||
IconFileZip -> "file-archive"
|
IconFileZip -> "file-archive"
|
||||||
IconFileCSV -> "file-csv"
|
IconFileCSV -> "file-csv"
|
||||||
IconSFTQuestion -> "question-circle" -- for SheetFileType only, should all be round (similar)
|
IconSFTQuestion -> "question-circle" -- for SheetFileType only, should all be round (similar)
|
||||||
IconSFTHint -> "life-ring" -- for SheetFileType only
|
IconSFTHint -> "life-ring" -- for SheetFileType only
|
||||||
IconSFTSolution -> "exclamation-circle" -- for SheetFileType only
|
IconSFTSolution -> "exclamation-circle" -- for SheetFileType only
|
||||||
IconSFTMarking -> "check-circle" -- for SheetFileType only
|
IconSFTMarking -> "check-circle" -- for SheetFileType only
|
||||||
IconEmail -> "envelope"
|
IconEmail -> "envelope"
|
||||||
IconRegisterTemplate -> "file-alt"
|
IconRegisterTemplate -> "file-alt"
|
||||||
IconApplyTrue -> "file-alt"
|
IconApplyTrue -> "file-alt"
|
||||||
IconApplyFalse -> "trash"
|
IconApplyFalse -> "trash"
|
||||||
IconNoCorrectors -> "user-slash"
|
IconNoCorrectors -> "user-slash"
|
||||||
IconApplicationVeto -> "times"
|
IconApplicationVeto -> "times"
|
||||||
IconApplicationFiles -> "file-alt"
|
IconApplicationFiles -> "file-alt"
|
||||||
IconTooltipDefault -> "question-circle"
|
IconTooltipDefault -> "question-circle"
|
||||||
|
IconNotificationSuccess -> "check-circle"
|
||||||
|
IconNotificationInfo -> "info-circle"
|
||||||
|
IconNotificationWarning -> "exclamation-circle"
|
||||||
|
IconNotificationError -> "exclamation-triangle"
|
||||||
|
IconFavourite -> "star"
|
||||||
|
IconLanguage -> "flag-alt"
|
||||||
|
IconNavContainerClose -> "chevron-up"
|
||||||
|
|
||||||
instance Universe Icon
|
instance Universe Icon
|
||||||
instance Finite Icon
|
instance Finite Icon
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
module Utils.Lens.TH
|
module Utils.Lens.TH
|
||||||
( makeLenses_, makeClassyFor_
|
( lensRules_
|
||||||
|
, makeLenses_, makeClassyFor_
|
||||||
, multifocusG, multifocusL
|
, multifocusG, multifocusL
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ module Utils.Message
|
|||||||
, addMessage, addMessageI, addMessageIHamlet, addMessageFile, addMessageWidget
|
, addMessage, addMessageI, addMessageIHamlet, addMessageFile, addMessageWidget
|
||||||
, statusToUrgencyClass
|
, statusToUrgencyClass
|
||||||
, Message(..)
|
, Message(..)
|
||||||
, messageIconI
|
, messageIconI, messageIconIHamlet, messageIconWidget
|
||||||
, messageI, messageIHamlet, messageFile, messageWidget, messageTooltip
|
, messageI, messageIHamlet, messageFile, messageWidget, messageTooltip
|
||||||
) where
|
) where
|
||||||
|
|
||||||
@ -163,6 +163,15 @@ messageIHamlet ms iHamlet = do
|
|||||||
let mi = Nothing
|
let mi = Nothing
|
||||||
Message ms <$> withUrlRenderer (iHamlet $ toHtml . mr) <*> pure mi
|
Message ms <$> withUrlRenderer (iHamlet $ toHtml . mr) <*> pure mi
|
||||||
|
|
||||||
|
messageIconIHamlet :: ( MonadHandler m
|
||||||
|
, RenderMessage (HandlerSite m) msg
|
||||||
|
, HandlerSite m ~ site
|
||||||
|
) => MessageStatus -> Icon -> HtmlUrlI18n msg (Route site) -> m Message
|
||||||
|
messageIconIHamlet messageStatus (Just -> messageIcon) iHamlet = do
|
||||||
|
mr <- getMessageRender
|
||||||
|
messageContent <- withUrlRenderer (iHamlet $ toHtml . mr)
|
||||||
|
return Message{..}
|
||||||
|
|
||||||
addMessageFile :: MessageStatus -> FilePath -> ExpQ
|
addMessageFile :: MessageStatus -> FilePath -> ExpQ
|
||||||
addMessageFile mc tPath = [e|addMessageIHamlet mc $(ihamletFile tPath)|]
|
addMessageFile mc tPath = [e|addMessageIHamlet mc $(ihamletFile tPath)|]
|
||||||
|
|
||||||
@ -189,6 +198,15 @@ messageWidget mc wgt = do
|
|||||||
PageContent{pageBody} <- liftHandler $ widgetToPageContent wgt
|
PageContent{pageBody} <- liftHandler $ widgetToPageContent wgt
|
||||||
messageIHamlet mc (const pageBody :: HtmlUrlI18n (SomeMessage site) (Route site))
|
messageIHamlet mc (const pageBody :: HtmlUrlI18n (SomeMessage site) (Route site))
|
||||||
|
|
||||||
|
messageIconWidget :: forall m site.
|
||||||
|
( MonadHandler m
|
||||||
|
, HandlerSite m ~ site
|
||||||
|
, Yesod site
|
||||||
|
) => MessageStatus -> Icon -> WidgetFor site () -> m Message
|
||||||
|
messageIconWidget ms mi wgt = do
|
||||||
|
PageContent{pageBody} <- liftHandler $ widgetToPageContent wgt
|
||||||
|
messageIconIHamlet ms mi (const pageBody :: HtmlUrlI18n (SomeMessage site) (Route site))
|
||||||
|
|
||||||
|
|
||||||
getMessages :: MonadHandler m => m [Message]
|
getMessages :: MonadHandler m => m [Message]
|
||||||
getMessages = fmap decodeMessage <$> ClassyPrelude.Yesod.getMessages
|
getMessages = fmap decodeMessage <$> ClassyPrelude.Yesod.getMessages
|
||||||
|
|||||||
@ -58,6 +58,7 @@ data GlobalPostParam = PostFormIdentifier
|
|||||||
| PostDBCsvImportAction
|
| PostDBCsvImportAction
|
||||||
| PostLoginDummy
|
| PostLoginDummy
|
||||||
| PostExamAutoOccurrencePrevious
|
| PostExamAutoOccurrencePrevious
|
||||||
|
| PostLanguage
|
||||||
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)
|
||||||
|
|
||||||
instance Universe GlobalPostParam
|
instance Universe GlobalPostParam
|
||||||
|
|||||||
@ -1,5 +1,11 @@
|
|||||||
$newline never
|
$newline never
|
||||||
$if not isModal
|
$if not isModal
|
||||||
|
$with containers <- filter isNavHeaderContainer nav
|
||||||
|
$if not (null containers)
|
||||||
|
<input name=nav-container type=radio .navbar__container-radio--none checked #container-radio-none>
|
||||||
|
$forall (_, containerIdent, _, _) <- containers
|
||||||
|
<input name=nav-container type=radio .navbar__container-radio ##{containerIdent}-radio>
|
||||||
|
|
||||||
<!-- secondary navigation at the side -->
|
<!-- secondary navigation at the side -->
|
||||||
^{asidenav}
|
^{asidenav}
|
||||||
<!-- navigation -->
|
<!-- navigation -->
|
||||||
@ -15,7 +21,7 @@ $if not isModal
|
|||||||
|
|
||||||
$if not isModal
|
$if not isModal
|
||||||
<!-- breadcrumbs -->
|
<!-- breadcrumbs -->
|
||||||
$if not $ Just HomeR == mcurrentRoute
|
$if not $ Just NewsR == mcurrentRoute
|
||||||
^{breadcrumbsWgt}
|
^{breadcrumbsWgt}
|
||||||
|
|
||||||
<div .main__content-body>
|
<div .main__content-body>
|
||||||
|
|||||||
@ -3,7 +3,5 @@ $newline never
|
|||||||
^{closeWgt}
|
^{closeWgt}
|
||||||
<section>
|
<section>
|
||||||
$if hasUsers
|
$if hasUsers
|
||||||
<div .notification .notification-info .fa-question .notification--broad>
|
^{examGradesExplanation}
|
||||||
<div .notification__content>
|
|
||||||
_{MsgExamGradesExplanation}
|
|
||||||
^{examUsersTable}
|
^{examUsersTable}
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
$newline never
|
$newline never
|
||||||
$if hasUsers
|
$if hasUsers
|
||||||
<div .notification .notification-info .fa-question .notification--broad>
|
^{examGradesExplanation}
|
||||||
<div .notification__content>
|
|
||||||
_{MsgExamGradesExplanation}
|
|
||||||
^{table}
|
^{table}
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
$newline never
|
||||||
|
<p>
|
||||||
|
Diese Ansicht zeigt die selben Daten an, wie die Tabelle von Prüfungsteilnehmern.<br />
|
||||||
|
Anpassen der Teilnehmerdaten und Ergebnisse ist nur dort möglich.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Hier können Sie vor Allem einsehen und markieren, welche #
|
||||||
|
Prüfungsleistungen von den zuständigen Prüfungsbeauftragten bereits #
|
||||||
|
vollständig bearbeitet wurden.
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
$newline never
|
||||||
|
<p>
|
||||||
|
This view shows the same data as the table of exam participants.<br />
|
||||||
|
Changing participant's data and achievements is only possible via #
|
||||||
|
the table of exam participants.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Primarily, this view allows you to check and adjust which exam #
|
||||||
|
achievements were properly handled by the relevant exam offices.
|
||||||
@ -1,24 +1,22 @@
|
|||||||
$newline never
|
$newline never
|
||||||
<div .notification .notification-warning .fa-exclamation-triangle .notification--broad>
|
<p>
|
||||||
<div .notification__content>
|
Die Tabelle enthält Werte, die automatisch berechnet wurden.
|
||||||
<p>
|
<p>
|
||||||
Die Tabelle enthält Werte, die automatisch berechnet wurden.
|
Automatisch berechnete Werte (Bonus und Prüfungsergebnis) werden weder dem #
|
||||||
<p>
|
entsprechenden Teilnehmer angezeigt, noch an das Prüfungsamt gemeldet #
|
||||||
Automatisch berechnete Werte (Bonus und Prüfungsergebnis) werden weder dem #
|
bevor sie manuell übernommen wurden.<br />
|
||||||
entsprechenden Teilnehmer angezeigt, noch an das Prüfungsamt gemeldet #
|
Hierzu können Sie die Aktion „Berechnetes Prüfungsergebnis übernehmen“ #
|
||||||
bevor sie manuell übernommen wurden.<br />
|
verwenden.
|
||||||
Hierzu können Sie die Aktion „Berechnetes Prüfungsergebnis übernehmen“ #
|
<p>
|
||||||
verwenden.
|
Sie können die automatisch berechneten Werte auch manuell (via CSV-Import) #
|
||||||
<p>
|
überschreiben.<br />
|
||||||
Sie können die automatisch berechneten Werte auch manuell (via CSV-Import) #
|
Wenn die so gesetzten Werte nicht den automatisch Berechneten entsprechen #
|
||||||
überschreiben.<br />
|
sind sie <i>inkonsistent</i>.
|
||||||
Wenn die so gesetzten Werte nicht den automatisch Berechneten entsprechen #
|
<p>
|
||||||
sind sie <i>inkonsistent</i>.
|
Automatisch berechnete Werte sind gekennzeichnet wie folgt:
|
||||||
<p>
|
|
||||||
Automatisch berechnete Werte sind gekennzeichnet wie folgt:
|
|
||||||
|
|
||||||
<table style="font-weight: normal">
|
<table style="font-weight: normal">
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 0 7px 0 0" .table__td .table__td--automatic>Automatisch berechnet
|
<td style="padding: 0 7px 0 0" .table__td .table__td--automatic>Automatisch berechnet
|
||||||
<td style="padding: 0 7px" .table__td>Normaler Wert
|
<td style="padding: 0 7px" .table__td>Normaler Wert
|
||||||
<td style="padding: 0 0 0 7px" .table__td .table__td--overriden>Inkonsistent
|
<td style="padding: 0 0 0 7px" .table__td .table__td--overriden>Inkonsistent
|
||||||
|
|||||||
@ -1,23 +1,21 @@
|
|||||||
$newline never
|
$newline never
|
||||||
<div .notification .notification-warning .fa-exclamation-triangle .notification--broad>
|
<p>
|
||||||
<div .notification__content>
|
This table contains values that were computed automatically.
|
||||||
<p>
|
<p>
|
||||||
This table contains values that were computed automatically.
|
Values computed automatically (bonus and result) are shown to neither the #
|
||||||
<p>
|
participant nor relevant exam offices until they are manually accepted.<br />
|
||||||
Values computed automatically (bonus and result) are shown to neither the #
|
To do this you may use the action “Accept computed result”.
|
||||||
participant nor relevant exam offices until they are manually accepted.<br />
|
<p>
|
||||||
To do this you may use the action “Accept computed result”.
|
You are also able to override the automatically computed values manually #
|
||||||
<p>
|
(via CSV import).<br />
|
||||||
You are also able to override the automatically computed values manually #
|
|
||||||
(via CSV import).<br />
|
|
||||||
|
|
||||||
If values thus overriden do not match the automatically computed values #
|
If values thus overriden do not match the automatically computed values #
|
||||||
they are considered <i>inconsistent</i>.
|
they are considered <i>inconsistent</i>.
|
||||||
<p>
|
<p>
|
||||||
Automatically computed values are marked as follows:
|
Automatically computed values are marked as follows:
|
||||||
|
|
||||||
<table style="font-weight: normal">
|
<table style="font-weight: normal">
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 0 7px 0 0" .table__td .table__td--automatic>Automatically computed
|
<td style="padding: 0 7px 0 0" .table__td .table__td--automatic>Automatically computed
|
||||||
<td style="padding: 0 7px" .table__td>Normal value
|
<td style="padding: 0 7px" .table__td>Normal value
|
||||||
<td style="padding: 0 0 0 7px" .table__td .table__td--overriden>Inconsistent
|
<td style="padding: 0 0 0 7px" .table__td .table__td--overriden>Inconsistent
|
||||||
|
|||||||
11
templates/i18n/pitch/de-de-formal.hamlet
Normal file
11
templates/i18n/pitch/de-de-formal.hamlet
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
$newline never
|
||||||
|
<p>
|
||||||
|
Uni2work ist ein Lehrverwaltungssystem, welches an der #
|
||||||
|
Ludwig-Maximilians-Universität München entwickelt und eingesetzt #
|
||||||
|
wird.
|
||||||
|
<p>
|
||||||
|
Insbesondere unterstützt Uni2work teilnehmende Institute bei #
|
||||||
|
Übungsbetrieb, Prüfungs- und Notenverwaltung und bietet vollständige #
|
||||||
|
Kurshomepages inkl. Vorlesungsmaterial und Terminen.
|
||||||
|
|
||||||
|
|
||||||
8
templates/i18n/pitch/en-eu.hamlet
Normal file
8
templates/i18n/pitch/en-eu.hamlet
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
$newline never
|
||||||
|
<p>
|
||||||
|
Uni2work is a teaching management system, developed and deployed at #
|
||||||
|
Ludwig-Maximilians-Universität München.
|
||||||
|
<p>
|
||||||
|
Uni2work supports participating departments in managing their course #
|
||||||
|
exercises, exams and exam achievements, and provides complete course #
|
||||||
|
homepages including course material and dates.
|
||||||
@ -1,6 +1,6 @@
|
|||||||
$newline never
|
$newline never
|
||||||
<section>
|
<section>
|
||||||
<h2>_{MsgHomeUpcomingExams}
|
<h2>_{MsgNewsUpcomingExams}
|
||||||
$if hasExams
|
$if hasExams
|
||||||
^{examTable}
|
^{examTable}
|
||||||
$else
|
$else
|
||||||
@ -1,4 +1,4 @@
|
|||||||
$newline never
|
$newline never
|
||||||
<section>
|
<section>
|
||||||
<h2>_{MsgHomeUpcomingSheets}
|
<h2>_{MsgNewsUpcomingSheets}
|
||||||
^{sheetTable}
|
^{sheetTable}
|
||||||
@ -28,12 +28,9 @@ $newline never
|
|||||||
<div .asidenav__link-label>#{courseName}
|
<div .asidenav__link-label>#{courseName}
|
||||||
<div .asidenav__nested-list-wrapper>
|
<div .asidenav__nested-list-wrapper>
|
||||||
<ul .asidenav__nested-list.list--iconless>
|
<ul .asidenav__nested-list.list--iconless>
|
||||||
$forall (MenuItem{menuItemType, menuItemLabel}, route) <- pageActions
|
$forall (NavLink{navLabel}, route) <- pageActions
|
||||||
$case menuItemType
|
<li .asidenav__nested-list-item>
|
||||||
$of PageActionPrime
|
<a .asidenav__link-wrapper href=#{route}>_{navLabel}
|
||||||
<li .asidenav__nested-list-item>
|
|
||||||
<a .asidenav__link-wrapper href=#{route}>_{menuItemLabel}
|
|
||||||
$of _
|
|
||||||
|
|
||||||
<div .asidenav__sigillum>
|
<div .asidenav__sigillum>
|
||||||
<img src=@{StaticR img_lmu_sigillum_svg}>
|
<img src=@{StaticR img_lmu_sigillum_svg}>
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
.breadcrumbs__container {
|
|
||||||
position: relative;
|
|
||||||
color: var(--color-lightwhite);
|
|
||||||
padding: 4px 13px;
|
|
||||||
background-color: var(--color-dark);
|
|
||||||
line-height: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 426px) {
|
|
||||||
.breadcrumbs__container {
|
|
||||||
padding: 7px 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 769px) {
|
|
||||||
.breadcrumbs__container {
|
|
||||||
padding: 7px 40px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a.breadcrumbs__link {
|
|
||||||
color: var(--color-lightwhite);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--color-white);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.breadcrumbs__item {
|
|
||||||
padding-right: 14px;
|
|
||||||
position: relative;
|
|
||||||
line-height: 28px;
|
|
||||||
opacity: 0.8;
|
|
||||||
z-index: 1;
|
|
||||||
margin-right: 10px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 11px;
|
|
||||||
right: 0;
|
|
||||||
width: 7px;
|
|
||||||
height: 7px;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 0;
|
|
||||||
border-bottom-width: 1px;
|
|
||||||
border-right-width: 1px;
|
|
||||||
border-color: var(--color-white);
|
|
||||||
transform: rotate(-45deg);
|
|
||||||
z-index: 10;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.breadcrumbs__last-item {
|
|
||||||
line-height: 28px;
|
|
||||||
vertical-align: bottom;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
@ -1,13 +1,6 @@
|
|||||||
$newline never
|
$newline never
|
||||||
<footer .footer>
|
<footer .footer>
|
||||||
<div .footer-links>
|
<div .footer-links>
|
||||||
$forall (MenuItem{menuItemType, menuItemRoute = _, menuItemIcon = _, menuItemLabel, menuItemModal = _}, menuIdent, route) <- menuTypes
|
$forall (NavFooter{ navLink }, navIdent, navRoute', _) <- filter isNavFooter nav
|
||||||
$case menuItemType
|
<a href=#{navRoute'} ##{navIdent}>
|
||||||
$of Footer
|
_{navLink}
|
||||||
$# Not used but available (remove ` = _` from the pattern match above, as needed):
|
|
||||||
$# highlight (urlRoute menuItemRoute) :: Bool -- ^ Is this menu item currently active (i.e.: are we on this page)
|
|
||||||
$# menuItemModal :: Bool -- ^ Should this menu item open a modal instead of being a normal link
|
|
||||||
$# menuItemIcon :: Maybe Text -- ^ Should this menu item have an icon, if yes, then the name of the icon
|
|
||||||
<a href=#{route} ##{menuIdent}>
|
|
||||||
_{SomeMessage menuItemLabel}
|
|
||||||
$of _
|
|
||||||
|
|||||||
11
templates/widgets/navbar/container-radio.cassius
Normal file
11
templates/widgets/navbar/container-radio.cassius
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
##{containerIdent}-radio:checked ~ * ##{containerIdent}-container
|
||||||
|
visibility: visible
|
||||||
|
margin: 14px 0 0 0
|
||||||
|
height: 30px
|
||||||
|
|
||||||
|
##{containerIdent}-radio:checked ~ * ##{containerIdent}
|
||||||
|
background-color: var(--color-dark)
|
||||||
|
color: var(--color-lightwhite)
|
||||||
|
|
||||||
|
.navbar__link-icon
|
||||||
|
opacity: 1
|
||||||
5
templates/widgets/navbar/container.hamlet
Normal file
5
templates/widgets/navbar/container.hamlet
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
$newline never
|
||||||
|
<label .navbar__link-wrapper for=#{navIdent}-radio ##{navIdent}>
|
||||||
|
<div .navbar__link-icon>
|
||||||
|
<i .fas .fa-2x .fa-#{iconText navIcon}>
|
||||||
|
<div .navbar__link-label>_{SomeMessage navLabel}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
$newline never
|
$newline never
|
||||||
<a .navbar__link-wrapper href=#{route} ##{menuIdent}>
|
<a .navbar__link-wrapper href=#{route} ##{ident}>
|
||||||
$if isJust menuItemIcon
|
<div .navbar__link-icon>
|
||||||
<div .navbar__link-icon>
|
<i .fas .fa-2x .fa-#{iconText navIcon}>
|
||||||
<i .fas .fa-2x .fa-#{fromMaybe "none" menuItemIcon}>
|
<div .navbar__link-label>_{SomeMessage navLabel}
|
||||||
<div .navbar__link-label>_{SomeMessage menuItemLabel}
|
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
$newline never
|
||||||
|
$maybe csrf <- csrfToken
|
||||||
|
<input type=hidden name=#{defaultCsrfParamName} value=#{csrf}>
|
||||||
|
$forall (k, v) <- navData
|
||||||
|
<input type=hidden name=#{k} value=#{v}>
|
||||||
|
<button .navbar__container-link :highlightNav iN:.navbar__container-link--active type=submit>
|
||||||
|
_{SomeMessage navLabel}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
$newline never
|
||||||
|
<a .navbar__container-link :highlightNav iN:.navbar__container-link--active href=#{route} ##{ident}>
|
||||||
|
_{SomeMessage navLabel}
|
||||||
@ -1,48 +1,54 @@
|
|||||||
$newline never
|
$newline never
|
||||||
<div .navbar-container>
|
<div .navbar-container>
|
||||||
<div .navbar-shadow>
|
<div .navbar-shadow>
|
||||||
<nav .navbar.js-sticky-navbar>
|
<nav .navbar>
|
||||||
|
<div .navbar__list-wrapper>
|
||||||
<ul .navbar__list.list--inline.navbar__list-left>
|
<ul .navbar__list.navbar__list-left>
|
||||||
$# manually add favorites to navbar for small screens
|
$# manually add favorites to navbar for small screens
|
||||||
<li .navbar__list-item.navbar__list-item--favorite>
|
<li .navbar__list-item.navbar__list-item--favorite>
|
||||||
<a .navbar__link-wrapper href="#">
|
<a .navbar__link-wrapper href="#">
|
||||||
<div .navbar__link-icon>
|
|
||||||
<i .fas .fa-2x .fa-star>
|
|
||||||
<div .navbar__link-label>_{MsgNavigationFavourites}
|
|
||||||
|
|
||||||
$forall (menuItem@MenuItem{menuItemType, menuItemRoute, menuItemModal}, menuIdent, _) <- menuTypes
|
|
||||||
$case menuItemType
|
|
||||||
$of NavbarAside
|
|
||||||
<li .navbar__list-item :highlight (urlRoute menuItemRoute):.navbar__list-item--active>
|
|
||||||
$if menuItemModal
|
|
||||||
^{navbarModal (menuItem, menuIdent)}
|
|
||||||
$else
|
|
||||||
^{navbarItem (menuItem, menuIdent)}
|
|
||||||
$of _
|
|
||||||
|
|
||||||
<ul .navbar__list.list--inline>
|
|
||||||
$forall (menuItem@MenuItem{menuItemType, menuItemRoute, menuItemModal}, menuIdent, _) <- menuTypes
|
|
||||||
$case menuItemType
|
|
||||||
$of NavbarRight
|
|
||||||
<li .navbar__list-item :highlight (urlRoute menuItemRoute):.navbar__list-item--active>
|
|
||||||
$if menuItemModal
|
|
||||||
^{navbarModal (menuItem, menuIdent)}
|
|
||||||
$else
|
|
||||||
^{navbarItem (menuItem, menuIdent)}
|
|
||||||
$of NavbarSecondary
|
|
||||||
<li .navbar__list-item :highlight (urlRoute menuItemRoute):.navbar__list-item--active>
|
|
||||||
$if menuItemModal
|
|
||||||
^{navbarModal (menuItem, menuIdent)}
|
|
||||||
$else
|
|
||||||
^{navbarItem (menuItem, menuIdent)}
|
|
||||||
$of _
|
|
||||||
<li .navbar__list-item--lang-wrapper uw-language-select>
|
|
||||||
<input type="checkbox" id="lang-checkbox" uw-no-checkbox>
|
|
||||||
<div id="lang-dropdown">
|
|
||||||
^{langFormView'}
|
|
||||||
<div .navbar__list-item .navbar__list-item--language>
|
|
||||||
<label .navbar__link-wrapper for="lang-checkbox">
|
|
||||||
<div .navbar__link-icon>
|
<div .navbar__link-icon>
|
||||||
<i .fas .fa-2x .fa-flag-alt>
|
<i .fas .fa-2x .fa-#{iconText IconFavourite}>
|
||||||
<div .navbar__link-label>_{MsgMenuLanguage}
|
<div .navbar__link-label>_{MsgNavigationFavourites}
|
||||||
|
|
||||||
|
$forall n <- filter isNavHeaderPrimary nav
|
||||||
|
$case view _1 n
|
||||||
|
$of NavHeader{ navLink }
|
||||||
|
<li .navbar__list-item :highlightNav navLink:.navbar__list-item--active>
|
||||||
|
^{navWidget n}
|
||||||
|
$of NavHeaderContainer{}
|
||||||
|
<li .navbar__list-item.navbar__list-item--container-selector>
|
||||||
|
^{navWidget n}
|
||||||
|
$of _
|
||||||
|
|
||||||
|
<ul .navbar__list>
|
||||||
|
$forall n <- filter isNavHeaderSecondary $ reverse nav
|
||||||
|
$case view _1 n
|
||||||
|
$of NavHeader{ navLink }
|
||||||
|
<li .navbar__list-item :highlightNav navLink:.navbar__list-item--active>
|
||||||
|
^{navWidget n}
|
||||||
|
$of NavHeaderContainer{}
|
||||||
|
<li .navbar__list-item.navbar__list-item--container-selector>
|
||||||
|
^{navWidget n}
|
||||||
|
$of _
|
||||||
|
|
||||||
|
|
||||||
|
$forall n@(NavHeaderContainer{ navHeaderRole }, containerIdent, _, ns) <- filter isNavHeaderContainer nav
|
||||||
|
<div .navbar__container-list :navHeaderRole == NavHeaderPrimary:.navbar__container-list--left ##{containerIdent}-container>
|
||||||
|
<label .navbar__container-list-closer for=container-radio-none>
|
||||||
|
<i .fas .fa-fw .fa-#{iconText IconNavContainerClose}>
|
||||||
|
<ul>
|
||||||
|
$forall iN@(nl, _, _) <- ns
|
||||||
|
<li .navbar__container-list-item :highlightNav nl:.navbar__container-list-item--active>
|
||||||
|
^{navContainerItemWidget n iN}
|
||||||
|
|
||||||
|
|
||||||
|
$# <li .navbar__list-item--lang-wrapper uw-language-select>
|
||||||
|
$# <input type="checkbox" id="lang-checkbox" uw-no-checkbox>
|
||||||
|
$# <div id="lang-dropdown">
|
||||||
|
$# ^{langFormView'}
|
||||||
|
$# <div .navbar__list-item .navbar__list-item--language>
|
||||||
|
$# <label .navbar__link-wrapper for="lang-checkbox">
|
||||||
|
$# <div .navbar__link-icon>
|
||||||
|
$# <i .fas .fa-2x .fa-flag-alt>
|
||||||
|
$# <div .navbar__link-label>_{MsgMenuLanguage}
|
||||||
|
|||||||
4
templates/widgets/notification.hamlet
Normal file
4
templates/widgets/notification.hamlet
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
$newline never
|
||||||
|
<div .notification .notification-#{toPathPiece messageStatus} .fa-#{iconText messageIcon} :is _NotificationBroad nType:.notification--broad>
|
||||||
|
<div .notification__content>
|
||||||
|
#{messageContent}
|
||||||
@ -2,22 +2,12 @@ $newline never
|
|||||||
<div .pagenav>
|
<div .pagenav>
|
||||||
$if hasPrimaryPageActions
|
$if hasPrimaryPageActions
|
||||||
<div .pagenav-prime>
|
<div .pagenav-prime>
|
||||||
$forall (MenuItem{menuItemLabel, menuItemType, menuItemModal}, menuIdent, route) <- menuTypes
|
$forall n@(NavPageActionPrimary{}, _, _, _) <- nav
|
||||||
$case menuItemType
|
<div .pagenav__list-item>
|
||||||
$of PageActionPrime
|
^{navWidget n}
|
||||||
<div .pagenav__list-item>
|
|
||||||
$if menuItemModal
|
|
||||||
<div uw-modal data-modal-trigger=#{menuIdent} data-modal-closeable>
|
|
||||||
<a .pagenav__link-wrapper href=#{route} ##{menuIdent}>_{SomeMessage menuItemLabel}
|
|
||||||
$of _
|
|
||||||
$if hasSecondaryPageActions
|
$if hasSecondaryPageActions
|
||||||
<div .pagenav-secondary>
|
<div .pagenav-secondary>
|
||||||
<div .pagenav-secondary__list>
|
<div .pagenav-secondary__list>
|
||||||
$forall (MenuItem{menuItemLabel, menuItemType, menuItemModal}, menuIdent, route) <- menuTypes
|
$forall n@(NavPageActionPrimary{}, _, _, _) <- nav
|
||||||
$case menuItemType
|
<div .pagenav__list-item.pagenav__list-item--secondary>
|
||||||
$of PageActionSecondary
|
^{navWidget n}
|
||||||
<div .pagenav__list-item.pagenav__list-item--secondary>
|
|
||||||
$if menuItemModal
|
|
||||||
<div uw-modal data-modal-trigger=#{menuIdent} data-modal-closeable>
|
|
||||||
<a .pagenav__link-wrapper.pagenav__link-wrapper--secondary href=#{route} ##{menuIdent}>_{SomeMessage menuItemLabel}
|
|
||||||
$of _
|
|
||||||
|
|||||||
2
templates/widgets/pageaction/primary.hamlet
Normal file
2
templates/widgets/pageaction/primary.hamlet
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
$newline never
|
||||||
|
<a .pagenav__link-wrapper href=#{route} ##{ident}>_{SomeMessage navLabel}
|
||||||
2
templates/widgets/pageaction/secondary.hamlet
Normal file
2
templates/widgets/pageaction/secondary.hamlet
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
$newline never
|
||||||
|
<a .pagenav__link-wrapper.pagenav__link-wrapper--secondary href=#{route} ##{ident}>_{SomeMessage navLabel}
|
||||||
Reference in New Issue
Block a user