Merge branch 'master' into 'live'

Design touches & more information in rating files

See merge request !56
This commit is contained in:
Gregor Kleen 2018-07-18 10:14:04 +02:00
commit e6c3e51448
14 changed files with 136 additions and 114 deletions

View File

@ -1,3 +1,6 @@
* Version 10.07.2018
Bugfixes, wählbares Format für Datum
* Version 04.07.2018 * Version 04.07.2018
Hinweis eingefügt, dass alle Daten des Systems spätestens im Dezember 2018 Hinweis eingefügt, dass alle Daten des Systems spätestens im Dezember 2018

View File

@ -187,7 +187,7 @@ getTimeLocale' = $(timeLocaleMap [("de", "de_DE.utf8")])
appTZ :: TZ appTZ :: TZ
appTZ = $(includeSystemTZ "Europe/Berlin") appTZ = $(includeSystemTZ "Europe/Berlin")
-- Access Control -- Access Control
data AccessPredicate data AccessPredicate
= APPure (Route UniWorX -> Bool -> Reader MsgRenderer AuthResult) = APPure (Route UniWorX -> Bool -> Reader MsgRenderer AuthResult)
@ -478,6 +478,8 @@ instance Yesod UniWorX where
menuTypes <- filterM (menuItemAccessCallback . menuItem) menu menuTypes <- filterM (menuItemAccessCallback . menuItem) menu
isAuth <- isJust <$> maybeAuthId
-- Lookup Favourites & Theme if possible -- TODO: cache this info in a cookie?! -- Lookup Favourites & Theme if possible -- TODO: cache this info in a cookie?!
(favourites',show -> currentTheme) <- do (favourites',show -> currentTheme) <- do
muid <- maybeAuthPair muid <- maybeAuthPair
@ -604,7 +606,7 @@ instance YesodBreadcrumbs UniWorX where
breadcrumb UsersR = return ("Benutzer", Just HomeR) breadcrumb UsersR = return ("Benutzer", Just HomeR)
breadcrumb AdminTestR = return ("Test" , Just HomeR) breadcrumb AdminTestR = return ("Test" , Just HomeR)
breadcrumb (AdminUserR _) = return ("Users" , Just UsersR) breadcrumb (AdminUserR _) = return ("Users" , Just UsersR)
breadcrumb VersionR = return ("Info" , Just HomeR) breadcrumb VersionR = return ("Impressum" , Just HomeR)
breadcrumb ProfileR = return ("Profile" , Just HomeR) breadcrumb ProfileR = return ("Profile" , Just HomeR)
breadcrumb ProfileDataR = return ("Data" , Just ProfileR) breadcrumb ProfileDataR = return ("Data" , Just ProfileR)

View File

@ -34,6 +34,12 @@ getUsersR = do
, sortable (Just "display-name") (i18nCell MsgName) $ \DBRow{ dbrOutput = Entity uid User{..} } -> anchorCellM , sortable (Just "display-name") (i18nCell MsgName) $ \DBRow{ dbrOutput = Entity uid User{..} } -> anchorCellM
(AdminUserR <$> encrypt uid) (AdminUserR <$> encrypt uid)
(toWidget . display $ userDisplayName) (toWidget . display $ userDisplayName)
, sortable (Just "matriculation") (i18nCell MsgMatrikelNr) $ \DBRow{ dbrOutput = Entity uid User{..} } -> anchorCellM
(AdminUserR <$> encrypt uid)
(toWidget . display $ userMatrikelnummer)
-- , sortable (Just "last-name") (i18nCell MsgName) $ \DBRow{ dbrOutput = Entity uid User{..} } -> anchorCellM
-- (AdminUserR <$> encrypt uid)
-- (toWidget . display $ last $ impureNonNull $ words $ userDisplayName)
, sortable Nothing (i18nCell MsgAdminFor) $ \DBRow{ dbrOutput = Entity uid _ } -> mempty , sortable Nothing (i18nCell MsgAdminFor) $ \DBRow{ dbrOutput = Entity uid _ } -> mempty
{ dbCellContents = do { dbCellContents = do
schools <- E.select . E.from $ \(school `E.InnerJoin` userAdmin) -> do schools <- E.select . E.from $ \(school `E.InnerJoin` userAdmin) -> do
@ -78,6 +84,12 @@ getUsersR = do
[ ( "display-name" [ ( "display-name"
, SortColumn $ \user -> user E.^. UserDisplayName , SortColumn $ \user -> user E.^. UserDisplayName
) )
, ( "matriculation"
, SortColumn $ \user -> user E.^. UserMatrikelnummer
)
-- , ( "last-name"
-- , SortColumn $ \user -> (last . impureNonNull . words) <$> (user E.^. UserDisplayName)
-- )
] ]
, dbtFilter = mempty , dbtFilter = mempty
, dbtStyle = def , dbtStyle = def

View File

@ -68,6 +68,8 @@ instance Pretty x => Pretty (CI x) where
data Rating = Rating data Rating = Rating
{ ratingCourseName :: Text { ratingCourseName :: Text
, ratingSheetName :: Text , ratingSheetName :: Text
, ratingCorrectorName :: Maybe Text
, ratingSheetType :: SheetType
, ratingValues :: Rating' , ratingValues :: Rating'
} deriving (Read, Show, Eq, Generic, Typeable) } deriving (Read, Show, Eq, Generic, Typeable)
@ -89,15 +91,18 @@ instance Exception RatingException
getRating :: SubmissionId -> YesodDB UniWorX (Maybe Rating) getRating :: SubmissionId -> YesodDB UniWorX (Maybe Rating)
getRating submissionId = runMaybeT $ do getRating submissionId = runMaybeT $ do
let query = E.select . E.from $ \(submission `E.InnerJoin` sheet `E.InnerJoin` course) -> do let query = E.select . E.from $ \(corrector `E.RightOuterJoin` (submission `E.InnerJoin` sheet `E.InnerJoin` course)) -> do
E.on $ course E.^. CourseId E.==. sheet E.^. SheetCourse E.on $ course E.^. CourseId E.==. sheet E.^. SheetCourse
E.on $ sheet E.^. SheetId E.==. submission E.^. SubmissionSheet E.on $ sheet E.^. SheetId E.==. submission E.^. SubmissionSheet
E.on $ corrector E.?. UserId E.==. submission E.^. SubmissionRatingBy
E.where_ $ submission E.^. SubmissionId E.==. E.val submissionId E.where_ $ submission E.^. SubmissionId E.==. E.val submissionId
-- Yes, we can only pass a tuple through 'E.select' -- Yes, we can only pass a tuple through 'E.select'
return ( course E.^. CourseName return ( course E.^. CourseName
, sheet E.^. SheetName , sheet E.^. SheetName
, corrector E.?. UserDisplayName
, sheet E.^. SheetType
, submission E.^. SubmissionRatingPoints , submission E.^. SubmissionRatingPoints
, submission E.^. SubmissionRatingComment , submission E.^. SubmissionRatingComment
, submission E.^. SubmissionRatingTime , submission E.^. SubmissionRatingTime
@ -105,6 +110,8 @@ getRating submissionId = runMaybeT $ do
[ ( E.unValue -> ratingCourseName [ ( E.unValue -> ratingCourseName
, E.unValue -> ratingSheetName , E.unValue -> ratingSheetName
, E.unValue -> ratingCorrectorName
, E.unValue -> ratingSheetType
, E.unValue -> ratingPoints , E.unValue -> ratingPoints
, E.unValue -> ratingComment , E.unValue -> ratingComment
, E.unValue -> ratingTime , E.unValue -> ratingTime
@ -117,12 +124,14 @@ formatRating cID Rating{ ratingValues = Rating'{..}, ..} = let
doc = renderPretty 1 45 $ foldr (<$$>) mempty doc = renderPretty 1 45 $ foldr (<$$>) mempty
[ "= Bitte nur Bewertung und Kommentare ändern =" [ "= Bitte nur Bewertung und Kommentare ändern ="
, "=============================================" , "============================================="
, "========== Uni2work Bewertungsdatei ==========" , "========== Uni2work Bewertungsdatei ========="
, "======= diese Datei ist UTF8 encodiert ======" , "======= diese Datei ist UTF8 encodiert ======"
, "Informationen zum Übungsblatt:" , "Informationen zum Übungsblatt:"
, indent 2 $ foldr (<$$>) mempty , indent 2 . foldr (<$$>) mempty . catMaybes $
[ "Veranstaltung:" <+> pretty ratingCourseName [ Just $ "Veranstaltung:" <+> pretty ratingCourseName
, "Blatt:" <+> pretty ratingSheetName , Just $ "Blatt:" <+> pretty ratingSheetName
, ("Korrektor:" <+>) . pretty <$> ratingCorrectorName
, Just $ "Bewertung:" <+> pretty (display ratingSheetType)
] ]
, "Abgabe-Id:" <+> pretty (ciphertext cID) , "Abgabe-Id:" <+> pretty (ciphertext cID)
, "=============================================" , "============================================="

View File

@ -39,7 +39,7 @@ $newline never
} }
<body .no-js .#{currentTheme}> <body .no-js .#{currentTheme} :isAuth:.logged-in>
<!-- removes no-js class from body if client supports javascript --> <!-- removes no-js class from body if client supports javascript -->
<script> <script>
document.body.classList.remove('no-js'); document.body.classList.remove('no-js');

View File

@ -153,13 +153,16 @@ h4 {
/* LAYOUT */ /* LAYOUT */
.main { .main {
position: relative;
min-height: calc(100vh - var(--header-height)); min-height: calc(100vh - var(--header-height));
padding: 20px;
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.main { .main {
min-height: calc(100vh - var(--header-height-collapsed)); min-height: calc(100vh - var(--header-height-collapsed));
padding: 10px;
} }
} }
@ -167,7 +170,6 @@ h4 {
position: relative; position: relative;
background-color: white; background-color: white;
overflow: hidden; overflow: hidden;
padding-left: 24%;
transition: padding-left .2s ease-out; transition: padding-left .2s ease-out;
> .container { > .container {
@ -183,42 +185,36 @@ h4 {
} }
} }
@media (max-width: 768px) {
.logged-in {
.main__content {
padding-left: 60px;
}
}
}
@media (max-width: 425px) {
.logged-in {
.main__content {
padding-left: 0;
}
}
}
@media (min-width: 769px) {
.logged-in {
.main__content {
padding-left: calc(24% + 30px);
}
}
}
@media (min-width: 1200px) { @media (min-width: 1200px) {
.main__content { .logged-in {
padding-left: 300px; .main__content {
} padding-left: 320px;
} }
@media (max-width: 768px) {
.main__content {
padding-left: 50px;
}
}
@media (max-width: 425px) {
.main__content {
padding-left: 0;
}
}
.main__content-body {
padding: 30px 40px 60px;
}
@media (max-width: 768px) {
.main__content-body {
padding: 30px 20px 60px;
}
}
@media (max-width: 425px) {
.main__content-body {
padding: 20px 10px 60px;
} }
} }
@ -420,7 +416,7 @@ input[type="button"].btn-info:hover,
} }
/* LIST MODIFIERS */ /* LIST MODIFIERS */
.list--inline > li { .list--inline li {
display: inline-block; display: inline-block;
} }

View File

@ -9,10 +9,12 @@
<li> Studiengänge von Benutzern werden noch ignoriert <li> Studiengänge von Benutzern werden noch ignoriert
<li> Übungsgruppen <li> Übungsgruppen
<li> Klausuren <li> Klausuren
<li> Zentralanmeldungen
<li> <li>
<h4> <h4>
neue geplante Features: neue geplante Features:
<ul> <ul>
<li> Studenplan/Kalender <li> Stundenplan/Kalender
<li> Vollständige Vorlesungshomepages <li> Vollständige Vorlesungshomepages
<li> Vollständige Internationalisierung deutsch/englisch/... <li> Vollständige Internationalisierung deutsch/englisch/...

View File

@ -43,10 +43,7 @@
padding-left: 20px; padding-left: 20px;
margin-left: 20px; margin-left: 20px;
animation: slide-in-alert .2s ease-out forwards; animation: slide-in-alert .2s ease-out forwards;
margin-bottom: 20px;
+ .alert:not(.alert--invisible) {
margin-top: 20px;
}
&:hover { &:hover {
@ -100,13 +97,14 @@
font-weight: 600; font-weight: 600;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
text-align: left;
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.alert__content { .alert__content {
padding: 4px 7px; padding: 4px 7px;
padding-left: 20px; padding-left: 25px;
} }
} }
@ -190,4 +188,6 @@
.alert--invisible { .alert--invisible {
max-height: 0; max-height: 0;
transform: translateX(120%); transform: translateX(120%);
margin-bottom: 0;
overflow: hidden;
} }

View File

@ -9,12 +9,12 @@ $newline never
$nothing $nothing
<tbody> <tbody>
$if null wRows && (dbsEmptyStyle == DBESHeading) $if null wRows && (dbsEmptyStyle == DBESHeading)
<tr> <tr .table__row>
<td colspan=#{show columnCount}> <td .table__td colspan=#{show columnCount}>
_{dbsEmptyMessage} _{dbsEmptyMessage}
$else $else
$forall row <- wRows $forall row <- wRows
<tr .table__row> <tr .table__row>
$forall widget <- row $forall widget <- row
$# cell/body.hamlet $# cell/body.hamlet
^{widget} ^{widget}

View File

@ -3,27 +3,21 @@
document.addEventListener('DOMContentLoaded', function DOMContentLoaded() { document.addEventListener('DOMContentLoaded', function DOMContentLoaded() {
var ASC = 'asc';
var DESC = 'desc';
function setupAsync(wrapper) { function setupAsync(wrapper) {
var table = wrapper.querySelector('#' + #{String $ dbtIdent}); var table = wrapper.querySelector('#' + #{String $ dbtIdent});
var ths = Array.from(table.querySelectorAll('th.sortable')); var ths = Array.from(table.querySelectorAll('th.sortable'));
if (ths) {
// attach click handler to each sortable column if any
ths.forEach(function(th) {
th.addEventListener('click', clickHandler);
});
}
var pagination = wrapper.querySelector('#' + #{String $ dbtIdent} + '-pagination'); var pagination = wrapper.querySelector('#' + #{String $ dbtIdent} + '-pagination');
ths.forEach(function(th) {
th.addEventListener('click', clickHandler);
});
if (pagination) { if (pagination) {
var paginationLinks = Array.from(pagination.querySelectorAll('.pagination-link')); Array.from(pagination.querySelectorAll('.pagination-link'))
// attach click handler to pagination links if any .forEach(function(p) {
paginationLinks.forEach(function(p) { p.addEventListener('click', clickHandler);
p.addEventListener('click', clickHandler); });
});
} }
function clickHandler(event) { function clickHandler(event) {
@ -31,12 +25,20 @@
var url = new URL(window.location.origin + window.location.pathname + getClickDestination(this)); var url = new URL(window.location.origin + window.location.pathname + getClickDestination(this));
url.searchParams.set(#{String $ wIdent "table-only"}, 'yes'); url.searchParams.set(#{String $ wIdent "table-only"}, 'yes');
updateTableFrom(url); updateTableFrom(url);
ths.forEach(function(th) {
// th.removeEventListener('click', clickHandler);
console.log('removed handler from', th);
});
} }
function getClickDestination(el) { function getClickDestination(el) {
var link = el.querySelector('a'); console.log(el);
if (!link) { return false; } if (!el.querySelector('a')) {
return link.getAttribute('href'); return false;
}
return el.querySelector('a').getAttribute('href');
} }
// fetches new sorted table from url with params and replaces contents of current table // fetches new sorted table from url with params and replaces contents of current table
@ -47,14 +49,19 @@
'Accept': 'text/html' 'Accept': 'text/html'
} }
}).then(function(response) { }).then(function(response) {
var contentType = response.headers.get("content-type");
if (!response.ok) { if (!response.ok) {
throw ('Looks like there was a problem fetching ' + url.toString() + '. Status Code: ' + response.status); throw ('Looks like there was a problem fetching ' + url.toString() + '. Status Code: ' + response.status);
} }
return response.text(); return response.text();
}).then(function(data) { }).then(function(data) {
// remove listeners
ths.forEach(function(th) {
th.removeEventListener('click', clickHandler);
});
// replace contents of table body // replace contents of table body
wrapper.innerHTML = data; wrapper.innerHTML = data;
// set up async functionality again // set up async functionality again
setupAsync(wrapper); setupAsync(wrapper);
table.querySelector('tbody').innerHTML = data; table.querySelector('tbody').innerHTML = data;

View File

@ -8,6 +8,7 @@
function init() { function init() {
var favoritesBtn = document.querySelector('.navbar__list-item--favorite'); var favoritesBtn = document.querySelector('.navbar__list-item--favorite');
favoritesBtn.addEventListener('click', function(event) { favoritesBtn.addEventListener('click', function(event) {
favoritesBtn.classList.toggle('navbar__list-item--active');
asideEl.classList.toggle('main__aside--expanded'); asideEl.classList.toggle('main__aside--expanded');
event.preventDefault(); event.preventDefault();
}, true); }, true);
@ -19,21 +20,6 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
var asidenavEl = document.querySelector('.main__aside'); var asidenavEl = document.querySelector('.main__aside');
var mainContentEl = document.querySelector('.main__content');
function adjustHeight() {
window.requestAnimationFrame(function() {
asidenavEl.style.height = mainContentEl.clientHeight + 'px';
});
}
// unbeknownst to the user (below the fold), this happes slightly delayed
// because of dynamic changes to the styles inside the main__content
setTimeout(function() {
adjustHeight();
}, 10);
window.addEventListener('resize', adjustHeight);
window.utils.aside(asidenavEl); window.utils.aside(asidenavEl);

View File

@ -1,12 +1,22 @@
.main__aside { .main__aside {
position: absolute; position: absolute;
display: none;
background-color: var(--color-dark); background-color: var(--color-dark);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
z-index: 1; z-index: 1;
top: 0;
left: 0;
flex: 0 0 300px; flex: 0 0 300px;
min-height: calc(100% - var(--header-height)); min-height: calc(100% - var(--header-height));
transition: all .2s ease-out; transition: all .2s ease-out;
width: 24%; width: 24%;
height: 100%;
}
.logged-in {
.main__aside {
display: block;
}
} }
/* maximum width of 300px for wide screens */ /* maximum width of 300px for wide screens */
@ -168,7 +178,6 @@
color: var(--color-font); color: var(--color-font);
transform: translateX(0); transform: translateX(0);
opacity: 0; opacity: 0;
transition: all .2s ease-out;
width: 0; width: 0;
overflow: hidden; overflow: hidden;
z-index: -1; z-index: -1;

View File

@ -1,26 +1,9 @@
.breadcrumbs__container { .breadcrumbs__container {
position: relative; position: relative;
color: var(--color-font); color: var(--color-font);
margin-left: 40px;
margin-top: 25px;
margin-bottom: 10px; margin-bottom: 10px;
} }
@media (max-width: 768px) {
.breadcrumbs__container {
margin-left: 20px;
}
}
@media (max-width: 425px) {
.breadcrumbs__container {
margin-left: 10px;
margin-top: 10px;
}
}
.breadcrumbs__link { .breadcrumbs__link {
&:hover { &:hover {

View File

@ -233,20 +233,33 @@
} }
} }
/* "Favorites" list item, only visible on small screens */ /* "Favorites" list item, only visible on small screens and logged in */
.navbar__list { .navbar__list-item {
&.navbar__list-item--favorite {
.navbar__list-item--favorite {
display: none; display: none;
background-color: var(--color-primary); }
}
.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 (max-width: 425px) { @media (min-width: 426px) {
.navbar__list { .logged-in {
.navbar__list-item--favorite { .navbar__list {
display: inline-block; .navbar__list-item--favorite {
display: none !important;
}
} }
} }
} }