Users using the Tequila login are invited to migrate to gitlab.epfl.ch. This instance will close for Tequila users by May 2020.

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 %>