Commit d6808246 authored by Timothée Floure's avatar Timothée Floure

Redesign whole UI with bootstrap

parent fcbb29b6
Pipeline #1119 passed with stages
in 15 minutes and 24 seconds
......@@ -130,9 +130,7 @@ get '/' => sub {
};
get '/login' => sub {
template 'login', {
'unipoly_projects' => $unipoly_projects,
}
template 'login';
};
post '/login' => sub {
......@@ -159,6 +157,13 @@ post '/login' => sub {
redirect 'login';
};
get '/register' => sub {
template 'register', {
'projects' => $unipoly_projects,
}
};
any '/session_expired' => sub {
unless (defined session('username')) {
return redirect '/login';
......@@ -320,6 +325,8 @@ get '/subscriptions' => sub {
'members' => \@members,
'active_members_by_year' => \%active_members_by_year,
'inactive_members_by_year' => \%inactive_members_by_year,
}, {
layout => 'large',
};
};
......@@ -369,6 +376,8 @@ get '/list/all' => sub {
template 'admin/members', {
'members' => \@members,
'title' => 'All members (even old ones)',
}, {
layout => 'large',
};
};
......@@ -537,7 +546,7 @@ get '/member/:username/password' => sub {
unless (check_password_token($username, $token)) {
status '403';
return "Unauthorized (no such token)";
return "Unauthorized (invalid token)";
}
template 'password_token', {
......@@ -600,9 +609,9 @@ get '/member/:username' => sub {
template 'member', {
'member' => $member,
'projects' => $selected_projects,
'selected_projects' => $selected_projects,
'mailing_lists' => $mailing_lists,
'unipoly_projects' => $unipoly_projects,
'projects' => $unipoly_projects,
};
};
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
/* Base */
html {
font-family: "et-book", serif;
}
body {
margin: 4%;
background-color: #fffff5;
color: #101010;
}
h1, h2, h3 {
font-weight: normal;
}
strong {
color: #000;
}
.form-control {
margin-bottom: 8px;
border-radius: 0;
}
.btn {
border-radius: 0;
text-decoration: none;
}
/* Layout */
.page-standard, .page-large {
margin-left: auto;
margin-right: auto;
}
.page-standard {
width: 600px;
}
.page-large {
width: 1100px;
}
#footer {
margin-top: 20px;
text-align: center;
}
.table-large {
position: absolute;
left: 15%;
right: 15%;
width: 70%;
margin-bottom: 20px;
}
/* Misc */
#login-logo {
width: 100px;
}
#login-form {
margin-top: 10px;
}
.padding-sm-left {
padding-left: 0;
padding-right: 4px;
}
.padding-sm-right {
padding-left: 4px;
padding-right: 0;
}
.padding-sm {
padding-left: 4px;
padding-right: 4px;
}
.no-padding {
padding 0 !important;
}
.no-margin {
margin: 0 !important;
}
.project-list {
list-style: none;
margin-top: 10px;
padding: 0;
}
.form-group-align {
margin-top: 22px;
}
body {
margin: 0;
margin-bottom: 20px;
padding: 0;
background-color: #ddd;
font-family: "sans-serif";
font-size: 13px;
}
h1, h2, h3, h4, h5 {
margin: 1.2em 0 0.6em 0;
}
* > h1:first-child {
margin-top: 0px;
}
h1 {
font-size: 28px;
color: #000;
}
table {
border-spacing: 0px;
border-collapse: collapse;
}
table th {
text-align: left;
}
tr {
vertical-align: top;
}
td {
padding: 3px 0.8em 3px 0.8em;
}
a {color: #03c}
a:hover {
background-color: #03c;
color: white;
text-decoration: none;
}
#page-header {
margin-bottom: 10px;
text-align: right;
}
#page {
background-color: #fff;
box-shadow: 0px 2px 5px #444;
width: 80%;
max-width: 800px;
margin-left: auto;
padding: 1.6em;
margin-right: auto;
margin-top: 15px;
overflow-x: auto;
}
#content {
background-color: white;
border: 3px solid #aaa;
border-top: none;
padding: 25px;
width: 500px;
}
#sidebar {
float: right;
width: 175px;
}
p {
margin: 1.6em 0 0.8em 0;
}
code, tt {
font-family: monospace;
}
#footer {
clear: both;
padding-top: 2em;
text-align: center;
font-family: sans-serif;
font-size: 10px;
}
.nobullets {
list-style-type: none;
}
.description {
margin: 0;
color: #444;
font-style: italic;
}
.message {
clear: both;
border: 1px solid #444;
border-radius: 4px;
box-shadow: 0px 2px 5px #444;
padding: 8px;
text-align: center;
background-color: #ffa;
margin-bottom: 10px;
}
table.firstalignright tr > td:first-child {
text-align: right;
}
.buttonbox {
margin: 1.6em 0 1.6em 0;
text-align: center;
}
.buttonbox button {
padding: 0.4em;
}
input[type=text], input[type=password], select {
background-color: inherit;
color: inherit;
padding: 4px;
border: 1px solid #aaa;
}
input[type=text][disabled], input[type=password][disabled], select[disabled] {
background-color: #ddd;
}
#login_logo {
margin: 0 1.6em 1.6em 0;
}
@media (min-width: 680px) {
#login_logo {
float: left;
}
}
table.subscriptions_table {
min-width: 100%;
}
table.big_table tr,
table.subscriptions_table tr {
vertical-align: middle;
}
table.subscriptions_table tr > td:last-child {
text-align: center;
}
table.big_table tr:hover,
table.subscriptions_table tr:hover {
background-color: #ddd;
}
table.subscriptions_table input[type=text] {
width: 8em;
}
table.big_table {
background-color: inherit;
}
.float_right {
float: right;
margin: 1em;
}
.hidden {
display: none;
}
.logo {
width: 150px;
}
<img class="float_right logo" src="unipoly.png" alt="Unipoly"/>
<h1>Member administration</h1>
<p>
This is the administration page for committee members. Normal members are
redirected on their <a href="member/<% session.username %>">member page</a>
<b>[1]</b> after login.
after login.
<ul>
<li><a href="list/all">Member index</a>: list of all Unipoly members.</li>
<li><a href="/admin/passwords">Password management</a>: generate and send
password reinitialization mails.</li>
<li><a href="subscriptions">Subscriptions page</a>: the cashier can use the
......@@ -21,34 +18,25 @@
Any committee member can create an account by preloading a registration
request filled by the future member. Once a request has been processed, it is
<a href="/list/registrations">archived</a>.
</p>
<table class="big_table">
<table class="table table-striped">
<thead>
<tr>
<th>Created on</td>
<th>Username</th>
<th>Common name</th>
<th>Mail</th>
<th>Projects</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% foreach request in registration_requests %>
<tr>
<td><% request.timestamp_as_string %> </td>
<td><% request.username %></td>
<td><% request.common_name %></td>
<td><% request.mail %></td>
<td><% request.projects %></td>
<td><a href="/review/<% request.id %>">Review &raquo;</td>
</tr>
<% end %>
</table>
</p>
<hr />
<p>
<b>[1]</b>: Authorized people may use the member page of someone else by
editing the username in the URL. It may be useful if something goes wrong;
however the members are supposed to change their respective information by
themselves.
</p>
</tbody>
</table>
<h1><% title %></h1>
<div class="page-standard">
<h1>All members</h1>
<% if message %>
<div class="message">
<% message %>
<p>
This list include past members whose account is expired. It contains <%
members.size %> entr<% if members.size > 1 %>ies<% else %>y<% end %>.
</p>
</div>
<% end %>
<p>
<% members.size %> entr<% if members.size > 1 %>ies<% else %>y<% end %>
in the list.
</p>
<table class="big_table">
<tr>
<th style="width:20%">First name</th>
<th style="width:20%">Last name</th>
<th style="width:30%">Mail</th>
<th style="width:15%">Phone</th>
<th style="width:15%"">Expire on</th>
</tr>
<% foreach member in members %>
<tr>
<td><% member.firstName %></td>
<td><% member.lastName %></td>
<td><% member.mail %></td>
<td><% member.mobile %></td>
<td><% member.subscription_expire_as_string %></td>
</tr>
<% end %>
<table class="table table-striped">
<thead>
<tr>
<th>Username</th>
<th>First name</th>
<th>Last name</th>
<th>Mail</th>
<th>Phone</th>
<th>Expire on</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% foreach member in members %>
<tr>
<td><% member.uid %></td>
<td><% member.firstName %></td>
<td><% member.lastName %></td>
<td><% member.mail %></td>
<td><% member.mobile %></td>
<td><% member.subscription_expire_as_string %></td>
<td><a href="/member/<% member.username %>" class="btn btn-secondary btn-sm btn-block">Edit &raquo;</a></td>
</tr>
<% end %>
</tbody>
</table>
This diff is collapsed.
<h1>Password reset tokens</h1>
<h1><% title %></h1>
<p>
This page allows you to generate and send password reset links by emails.
</p>
<% if message %>
<div class="message">
<% message %>
</div>
<% end %>
<h2>Create a new token</h2>
<form method="POST">
<div class="container">
<div class="row">
<div class="col-8 padding-sm-left">
<input type="text" name="create_for" class="form-control" placeholder="Email of the user"/>
</div>
<div class="col-4 padding-sm-right">
<button type="submit" class="btn btn-primary btn-block">Send by email &raquo;</button>
</div>
</div>
</div>
</form>
<h2>Existing tokens</h2>
<h2>Active tokens</h2>
<% if token_map.size == 0 %>
There are currently no valid tokens.
<% else %>
<table class="big_table">
<tr>
<th>Username</th>
<th>Number of tokens</th>
<th></th>
</tr>
<% foreach member in token_map.keys %>
<tr>
<td><% member %></td>
<td><% token_map.$member %></td>
<td>
<form method="POST">
<input class="hidden" name="delete_for" type="text" value="<% member %>"/>
<button type="submit">delete</button>
</form>
</td>
</tr>
<% end %>
<table class="table table-striped">
<thead>
<tr>
<th>Username</th>
<th>Number of tokens</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% foreach member in token_map.keys %>
<tr>
<td><% member %></td>
<td><% token_map.$member %></td>
<td>
<form method="POST">
<input class="sr-only" name="delete_for" type="text" value="<% member %>"/>
<button type="submit" class="btn btn-secondary btn-sm btn-block">Delete &raquo;</button>
</form>
</td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
<h2>Create a new token</h2>
<form method="POST">
Username:
<input name="create_for" type="text"/>
<button type="submit">create password reset link</button>
</form>
<h1>All registration requests</h1>
<p>
Any committee member can create an account either <a href="/new_member">mannually</a>
or by preloading a a registration request filled by the future member:
</p>
Any committee member can create an account either <a href="/new_member">mannually</a>
or by preloading a a registration request filled by the future member:
<table class="big_table">
<tr>
<th>Created on</td>
<th>Username</th>
<th>Common name</th>
<th>Mail</th>
<th>Projects</th>
<th>Archived</th>
<th>Actions</th>
</tr>
<% foreach request in registration_requests %>
<tr>
<td><% request.timestamp_as_string %> </td>
<td><% request.username %></td>
<td><% request.common_name %></td>
<td><% request.mail %></td>
<td><% request.projects %></td>
<td><% request.archived %></td>
<td><a href="/review/<% request.id %>">Review &raquo;</td>
</tr>
<% end %>
<table class="table table-striped">
<thead>
<tr>
<th>Created on</td>
<th>Common name</th>
<th>Email</th>
<th>Archived</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% foreach request in registration_requests %>
<tr>
<td><% request.timestamp_as_string %> </td>
<td><% request.username %></td>
<td><% request.mail %></td>
<td><% request.archived %></td>
<td><a href="/review/<% request.id %>">Details &raquo;</td>
</tr>
<% end %>
</tbody>
</table>
<div class="page-standard">
<h1>Subscriptions</h1>
<div class="alert alert-secondary" role="alert">
The account expiration is set to:
<ul class="no-margin">
<li>October if paid (for a semester) in December-May</li>
<li>March if paid (for a semester) in June-November</li>
</ul>
</div>
<p>
This scheme allows members to renew their subscriptions at the beginning of
a semester.
</p>
</div>
<% block table_row %>
<tr>
<td><% member.username %></td>
<td><% member.firstName %></td>
<td><% member.lastName %></td>
<td>
<input type="text" name="<% member.username %>-date"
value="<% member.subscription_expire_as_string %>"/>
<input type="text" class="form-control no-margin btn-sm" name="<% member.username %>-date" value="<% member.subscription_expire_as_string %>"/>
</td>
<td>
<input type="radio" name="<% member.username %>-paid" value="semester"/> Sem.
......@@ -15,13 +30,13 @@
<% end %>
<% block subscription_table %>
<table class="subscriptions_table">
<table class="table table-striped">
<tr>
<th style="width:25%">Username</th>
<th style="width:15%">First name</th>
<th style="width:15%">Last name</th>
<th style="width:15%">Until</th>
<th style="width:30%">Pay now?</th>
<th>Username</th>
<th>First name</th>
<th>Last name</th>
<th>Until</th>
<th>Pay now?</th>
</tr>
<% foreach member in members %>
<% include table_row member=member %>
......@@ -37,35 +52,15 @@
<% end %>
<form method="POST" action="">
<h1>Subscriptions</h1>
<% if message %>
<div class="message">
<% message %>
</div>
<% end %>
<p>
The account expiration is set to:
<ul>
<li>October if paid (for a semester) in December-May</li>
<li>March if paid (for a semester) in June-November</li>
</ul>
This scheme allows members to renew their subscriptions at the beginning of
a semester.
</p>
<h2>Active members</h2>
<% include subscriptions_by_year members_by_year=active_members_by_year %>
<h2>Old members</h2>
<% include subscriptions_by_year members_by_year=inactive_members_by_year %>
<p>
<div class="alert alert-warning" role="alert">
Be responsible: it is not fun to fix the dates in the LDAP if you mess up.
</p>
<div class="buttonbox">
<button class="big" type="submit">Submit changes</button>
</div>
<button class="btn btn-outline-primary btn-lg btn-block" type="submit">Submit changes &raquo;</button>
</form>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-type" content="text/html; charset=<% settings.charset %>" />
<title>Unipoly - Member management</title>
<link rel="stylesheet" href="<% request.uri_base %>/css/bootstrap.min.css" />
<link rel="stylesheet" href="<% request.uri_base %>/css/main.css" />
</head>
<body>
<div id="header">
</div>
<div class="page-large">
<div class="page-standard">
<div id="page-header">
<% if flash.message %>
<div class="alert alert-info" role="alert">
<% flash.message %>
</div>
<% end %>
<% if session.username %>
<nav class="nav justify-content-end">
<% if session.admin %>
<a class="nav-link" href="<% request.uri_base %>/admin">Admin</a>
<a class="nav-link" href="<% request.uri_base %>/list/all">Member index</a>
<% end %>
<a class="nav-link" href="<% request.uri_base %>/member/<% session.username %>">My member page</a>
<a class="nav-link" href="<% request.uri_base %>/logout">Logout</a>
</nav>
<% end %>
</div>
</div>
<% content %>
</div>
<div id="footer">
<span class="text-muted">
<a href="https://gnugeneration.epfl.ch/">GNU Generation</a>
Powered by <a href="http://perldancer.org/">Dancer</a> <% dancer_version %>
</span>
</div>
</body>
</html>
......@@ -4,30 +4,35 @@
<head>
<meta http-equiv="Content-type" content="text/html; charset=<% settings.charset %>" />
<title>Unipoly - Member management</title>
<link rel="stylesheet" href="<% request.uri_base %>/css/style.css" />
<link rel="stylesheet" href="<% request.uri_base %>/css/bootstrap.min.css" />
<link rel="stylesheet" href="<% request.uri_base %>/css/main.css" />
</head>
<body>
<div id="header">
</div>
<div id="page">
<div class="page-standard">
<div id="page-header">
<% if flash.message %>
<div class="message">
<div class="alert alert-info" role="alert">
<% flash.message %>
</div>
<% end %>
<% if session.username %>
<nav class="nav justify-content-end">
<% if session.admin %>