Finished version 1.0.

* Switched from a server-side rendering model to client-side. Now, the editing
  view is the same view as the print view, using CSS media qualifiers to switch
  between the eiting view on screen and the print view. This makes all the
  server-side code unnecessary.
* Added logic to rewrite the service headers when the user changes the date so
  that they include the correct dates and grey out fifth Sundays and Wednesdays
  as appropriate.
This commit is contained in:
Jonathan Bernard 2014-02-26 03:47:39 +00:00
parent 689c9a67bf
commit e66ddbdf2a
6 changed files with 239 additions and 489 deletions

View File

@ -1,132 +0,0 @@
package com.jdbernard.nurseryschedule
import com.jdbernard.util.SmartConfig
import groovy.json.JsonBuilder
import groovy.json.JsonException
import groovy.json.JsonSlurper
import groovy.text.SimpleTemplateEngine
import java.util.Calendar
import java.util.regex.Matcher
import javax.servlet.ServletConfig
import javax.servlet.ServletException
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.servlet.http.HttpSession
import static java.util.Calendar.*
import static javax.servlet.http.HttpServletResponse.*
public class NurseryScheduleServlet extends HttpServlet {
/// This service currently only supports one user.
private String username
private String password
private String outputDirectory
private static SimpleDateFormat dataDateFormat =
new SimpleDateFormat("yyyy-MM-dd")
private static SimpleDateFormat displayDateFormat =
new SimpleDateFormat("MMM dd")
void init(ServletConfig config) {
this.username = config.getInitParameter("username")
this.password = config.getInitParameter("password")
this.outputDirectory = config.getInitParameter("outputDirectory") }
void doPost(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession(true);
/// If the user is posting to `/login` then let's try to authenticate
/// them. We don't care about the state of the existing session.
if (request.servletPath == '/login') {
/// Parse the username/password from the request.
def requestBody
try { requestBody = new JsonSlurper().parse(request.reader) }
catch (JsonException jsone) {
response.status = SC_BAD_REQUEST
return }
/// If no username or password are configured we cannot
/// authenticate the user.
if (!password || !username) {
response.status = SC_UNAUTHORIZED
return }
/// If the username or password do not match, deny the request.
if (username != requestBody.username ||
password != requestBody.password) {
response.status = SC_UNAUTHORIZED
return }
response.status = SC_OK
session.setAttribute('authenticated', 'true')
session.setAttribute('username', requestBody.username)
writeJSON([status: "ok"], response)
return }
/// The other endpoints require the user to be authenticated.
if (!((boolean)session.getAttribute('authenticated'))) {
response.status = SC_UNAUTHORIZED
return }
switch (request.servletPath) {
case ~ '/make-schedule':
/// Parse the request body.
def data
try { data = new JsonSlurper().parse(request.reader) }
catch (JsonException jsone) {
response.status = SC_BAD_REQUEST
return }
/// Calculate dates and modify the schedule data to include
/// them.
def cal = Calendar.getInstance()
cal.time = dataDateFormat.parse(data.date)
int month = cal.get(MONTH)
cal.set(HOUR_OF_DAY, 0)
cal.set(MINUTE, 0)
cal.set(SECOND, 0)
cal.set(MILLISECOND, 0)
['first', 'second', 'third', 'fourth', 'fifth'].eachWithIndex
{ dayNum, idx ->
/// Set the sunday
cal.set(DAY_OF_WEEK, SUNDAY)
cal.set(DAY_OF_WEEK_IN_MONTH, idx + 1)
if (cal.get(MONTH) == month)
data.sundays[dayNum].date = displayDateFormat.format(cal.time)
/// Set the wednesday. Note that we have to reformat the
/// input data from {"first": ["PersonA", "PersonB"]} to
/// {"date": "Oct. 6", "volunteers": ["PersonA", PersonB"]}
def volunteers = data.wednesdays[dayNum]
data.wednesdays[dayNum] = [:]
data.wednesdays[dayNum].volunteers = volunteers
if (cal.get(MONTH) == month)
data.wednesdays[dayNum].date =
displayDateFormat.format(cal.time) }
cal.set(DAY_OF_WEEK_IN_MONTH, 2)
data.sundays['second'].date = displayDateFormat.format(cal.time)
/// Build the resulting page.
def engine = new SimpleTemplateEngine()
def scheduleTemplate = engine.createTemplate(
this.getClass().getResource('schedule.template'))
def output = scheduleTemplate.make(data)
/// Write the results to file.
new File(outputDirectory, data.date + ".html").text = output
response.status = SC_OK
default:
response.status = SC_NOT_FOUND
return
}
}
}

View File

@ -16,65 +16,123 @@ article,aside,details,figcaption,figure,
footer,header,hgroup,menu,nav,section {
display:block; }
// Fonts
@import url(http://fonts.googleapis.com/css?family=Abel);
html { font-size: 90%; }
body {
line-height: normal;
font-family: "Abel";
line-height: 1.1em;
margin: 2rem; }
.header { position: relative; }
.inactive { opacity: 0.5; }
.load-dialog {
background: rgba(0, 0, 0, 0.5);
bottom: 0;
display: none;
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 10;
div {
text-align: center;
margin-top: 4rem;
span {
background: whitesmoke;
border-radius: 3px;
padding: 1em;
#fileLoad { width: 30rem; } } } }
.tools {
position: absolute;
top: 0;
right: 0;
input[type="date"] {
border: solid thin gray;
border-radius: 3px; }
ul {
list-style: none;
@media screen {
body { line-height: normal; }
li {
padding-right: 0.5rem;
display: inline-block; } }
.publish { text-decoration: line-through; } }
.print-only { display: none; }
.sundays { border-right: solid thin lightgray; }
.wednesdays { padding-left: 2rem; }
.load-dialog {
background: rgba(0, 0, 0, 0.5);
bottom: 0;
display: none;
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 10;
div {
text-align: center;
margin-top: 4rem;
span {
background: whitesmoke;
border-radius: 3px;
padding: 1em;
#fileLoad { width: 30rem; } } } }
.sundays {
border-right: solid thin lightgray;
.remove { left: 7.5rem; }
.clone { left: 6.5rem; } }
.wednesdays {
padding-left: 2rem;
.remove { left: 11.5rem; }
.clone { left: 10.5rem; } }
.sundays, .wednesdays { width: 49%; }
.teacher-needed {
color: #A00;
font-style: italic; }
header { position: relative; }
.tools {
position: absolute;
top: 0;
right: 0;
input[type="date"] {
border: solid thin gray;
border-radius: 3px; }
ul {
list-style: none;
li {
padding-right: 0.5rem;
display: inline-block; } }
.publish { text-decoration: line-through; } }
}
@media print {
body {
line-height: 1.1em; }
.load-dialog, .tools, .teacher-needed, .screen-only { display: none; }
.sundays, .wednesdays { width: 100%; }
.sundays { border-bottom: solid thin lightgray; }
}
h5 {
font-size: small;
font-weight: normal;
text-decoration: underline; }
.first, .second, .third, .fourth, .fifth {
display: inline-block;
margin-bottom: 2rem;
vertical-align: top;
width: 19em; }
.date { }
ul { list-style: none; }
.toddlers, .infants {
display: inline-block;
vertical-align: top;
width: 9rem; }
div.am, div.pm, .wednesdays ul {
margin-top: 0.5rem; }
.sundays, .wednesdays {
display: inline-block;
line-height: 1.1rem;
margin-top: 2rem;
vertical-align: top;
width: 49%;
.teacher-needed {
color: #A00;
font-style: italic; }
li {
position: relative;
@ -98,11 +156,3 @@ body {
&.editing:hover {
.clone, .remove { display: none; } } } }
.sundays {
.remove { left: 7.5rem; }
.clone { left: 6.5rem; } }
.wednesdays {
.remove { left: 11.5rem; }
.clone { left: 10.5rem; } }

View File

@ -1,122 +1,116 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/nursery-schedule.css">
<meta charset="UTF-8">
<!--<link rel="stylesheet" type="text/css" href="css/nursery-schedule.css">-->
<link rel="stylesheet" type="text/css" href="css/schedule-maker.css">
<!-- PROD
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.10.3/jquery.min.js" defer></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js" defer></script>
-->
<!-- DEV
-->
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.js" defer></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.2/jquery-ui.js" defer></script>
<script type="text/javascript" src="js/schedule-maker.js" defer></script>
</head>
<body>
<header>
<h1>Nursery Schedule<span class=screen-only> Maker</span>,
<span class="month-name"></span></h1>
<div class=tools><ul>
<h4 class=month>Creating the schedule for the month of:
<input class=schedule-date type=date></h4>
<li><a href="#" class=save>Save Schedule Data</a></li>
<li><a href="#" class=load>Load Schedule Data</a></li>
</ul></div>
</header>
<section class=sundays>
<div class=first>
<h3>First Sunday, <span class=date>Oct. 6th<span></h3>
<h3>First Sunday</h3>
<div class=am>
<div class=toddlers>
<h5>Toddlers</h5>
<ul>
<li>Sara Bernard</li>
<li>Priscilla Reid</li>
<li>Becca Torres</li>
</ul>
</div>
<div class=infants>
<h5>Infants</h5>
<ul>
<li>Latonya Kirton</li>
<li>Susan Vacca</li>
<li>Brandie Kristoff</li>
</ul>
</div>
</div>
<div class=pm>
<h5>Evening Service</h5>
<ul>
<li>Susan Miller</li>
<li><em>TBD</em></li>
</ul>
</div>
</div>
<div class=second>
<h3>Second Sunday, <span class=date>Oct. 13th</span></h3>
<h3>Second Sunday</h3>
<div class=am>
<div class=toddlers>
<h5>Toddlers</h5>
<ul>
<li>Sara Bernard</li>
<li>Priscilla Reid</li>
<li>Rebecca Dombroski</li>
</ul>
</div>
<div class=infants>
<h5>Infants</h5>
<ul>
<li>Latonya Kirton</li>
<li>Susan Vacca</li>
<li>Mila Hill</li>
</ul>
</div>
</div>
<div class=pm>
<h5>Evening Service</h5>
<ul>
<li>Zoe Torres</li>
<li>Hannah Torres</li>
</ul>
</div>
</div>
<div class=third>
<h3>Third Sunday, <span class=date>Oct. 20th</span></h3>
<h3>Third Sunday</h3>
<div class=am>
<div class=toddlers>
<h5>Toddlers</h5>
<ul>
<li>Sara Bernard</li>
<li>Priscilla Reid</li>
<li>Della Borrego</li>
</ul>
</div>
<div class=infants>
<h5>Infants</h5>
<ul>
<li>Latonya Kirton</li>
<li>Susan Vacca</li>
<li>Deborah McDonald</li>
</ul>
</div>
</div>
<div class=pm>
<h5>Evening Service</h5>
<ul>
<li><em>TBD</em></li>
<li>Alayna Robert</li>
</ul>
</div>
</div>
<div class=fourth>
<h3>Fourth Sunday, <span class=date>Oct. 27th</span></h3>
<h3>Fourth Sunday</h3>
<div class=am>
<div class=toddlers>
<h5>Toddlers</h5>
<ul>
<li>Sara Bernard</li>
<li>Priscilla Reid</li>
<li><em>TBD</em></li>
</ul>
</div>
<div class=infants>
<h5>Infants</h5>
<ul>
<li>Latonya Kirton</li>
<li>Susan Vacca</li>
<li>Crystal Johnson</li>
</ul>
</div>
</div>
<div class=pm>
<h5>Evening Service</h5>
<ul>
<li>Deidra Dawson</li>
<li>Shekinah Dawson</li>
</ul>
</div>
</div>
@ -127,71 +121,63 @@
<div class=toddlers>
<h5>Toddlers</h5>
<ul>
<li>Sara Bernard</li>
<li>Priscilla Reid</li>
<li>Julie Froese</li>
</ul>
</div>
<div class=infants>
<h5>Infants</h5>
<ul>
<li>Latonya Kirton</li>
<li>Susan Vacca</li>
<li><em>TBD</em></li>
</ul>
</div>
</div>
<div class=pm>
<h5>Evening Service</h5>
<ul>
<li><em>TBD</em></li>
<li><em>TBD</em></li>
</ul>
</div>
</div>
</section>
<section class=wednesdays>
<div class=first>
<h3>First Wednesday, <span class=date>Oct. 2nd</span></h3>
<h3>First Wednesday</h3>
<ul>
<li>Courtney or Bryan Bootka</li>
<li>Gabby Galvez</li>
</ul>
</div>
<div class=second>
<h3>Second Wednesday, <span class=date>Oct. 9th</span></h3>
<h3>Second Wednesday</h3>
<ul>
<li>Reeta or Lionel Aguilar</li>
<li>Stephanie Langley</li>
</ul>
</div>
<div class=third>
<h3>Third Wednesday, <span class=date>Oct. 16th</span></h3>
<h3>Third Wednesday</h3>
<ul>
<li>Christina Grooms</li>
<li>Lanell Hanson</li>
</ul>
</div>
<div class=fourth>
<h3>Fourth Wednesday, <span class=date>Oct. 23rd</span></h3>
<h3>Fourth Wednesday</h3>
<ul>
<li>Cassie Hernandez</li>
<li>Regena Dumas</li>
</ul>
</div>
<div class=fifth>
<h3>Fifth Wednesday, <span class=date>Oct. 30th</span></h3>
<h3>Fifth Wednesday</h3>
<ul>
<li>Regina Noble</li>
<li><em>TBD</em></li>
</ul>
</div>
</section>
<div class=load-dialog>
<div>
<span><input type=file id=fileLoad><a href="#">Load It!</a></span>
</div>
</div>
<script type="text/javascript">
window.onload = function() { ScheduleMaker.init(); };
</script>
</body>
</html>

View File

@ -8,6 +8,15 @@
SM.minWedTeachers = 2;
SM.views = [];
SM.date = new Date();
SM.monthNames = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
SM.monthAbbr = ["Jan.", "Feb.", "Mar.", "Apr.", "May", "June", "July",
"Aug.", "Sep.", "Oct.", "Nov.", "Dec."];
SM.nameForNumber = ["first", "second", "third", "fourth", "fifth"]
SM.ServiceView = function(element) {
@ -213,6 +222,7 @@
// Restore the date.
$('input[type=date]').val(data.date);
SM.updateDate();
// Restore Sunday services
for(var day in data.sundays) {
@ -234,12 +244,85 @@
var liEl = view.newName({target: $ulEl[0]});
$(liEl).find('span.name').text(personName); });
view.updatePlaceholders({target: $ulEl[0]}); } }
fileReader.readAsText(fileName, "UTF-8");
};
SM.publicShedule = function() {
// TODO
SM.updateDate = function() {
var schedDate = new Date($('.schedule-date').val() + ":");
if (schedDate.getMonth() == SM.date.getMonth() &&
schedDate.getYear() == SM.date.getYear()) { return }
SM.date = schedDate;
$('.month-name').text(
SM.monthNames[SM.date.getMonth()] + " " +
(SM.date.getYear() + 1900));
// Find the Sundays.
var tempDate = new Date(SM.date) // First find the first Sunday.
tempDate.setDate(1); // Start at the first day of the month
if (tempDate.getDay() != 0) { // If it is not a sunday
// then figure out how many days until the next Sunday (based on
// what day it is), add one (because setDate is 1-indexed) and add
// that so that we have the first Sunday of the month.
tempDate.setDate((7 - tempDate.getDay()) + 1) }
// Now iterate through the sundays in this month and update the view.
for (var i = 0; i < 5; i++) {
var $h3 = $(".sundays ." + SM.nameForNumber[i] + " h3");
var text = SM.nameForNumber[i];
text = text[0].toUpperCase() + text.slice(1) + " Sunday";
if (tempDate.getMonth() == SM.date.getMonth()) {
text += ', ' + SM.monthAbbr[tempDate.getMonth()] +
" " + tempDate.getDate();
if (text[text.length - 1] == '1'
&& text[text.length - 2] != '1') text += "st";
else if (text[text.length - 1] == '2') text += "nd";
else if (text[text.length - 1] == '3') text += "rd";
else text += "th";
$h3.closest('div').removeClass('inactive'); }
else {
text = "No " + text;
$h3.closest('div').addClass('inactive'); }
$h3.text(text);
tempDate.setDate(tempDate.getDate() + 7); }
// Find the Wednesdays.
var tempDate = new Date(SM.date) // First find the first Wednesday.
tempDate.setDate(1); // Start at the first day of the month
if (tempDate.getDay() != 3) { // figure out how many days until
// the next Sunday (based on what day it is), add one (because
// setDate is 1-indexed), add 3 to get to the next wednesday and
// add that so that we have the first Wednesday of the month.
tempDate.setDate((((7 - tempDate.getDay()) + 1) % 7) + 3) }
// Now iterate through the wednesdays in this month and update the view.
for (var i = 0; i < 5; i++) {
var $h3 = $(".wednesdays ." + SM.nameForNumber[i] + " h3");
var text = SM.nameForNumber[i];
text = text[0].toUpperCase() + text.slice(1) + " Wednesday";
if (tempDate.getMonth() == SM.date.getMonth()) {
text += ', ' + SM.monthAbbr[tempDate.getMonth()] +
" " + tempDate.getDate();
if (text[text.length - 1] == '1'
&& text[text.length - 2] != '1') text += "st";
else if (text[text.length - 1] == '2') text += "nd";
else if (text[text.length - 1] == '3') text += "rd";
else text += "th";
$h3.closest('div').removeClass('inactive'); }
else {
text = "No " + text;
$h3.closest('div').addClass('inactive'); }
$h3.text(text);
tempDate.setDate(tempDate.getDate() + 7); }
};
SM.init = function() {
@ -253,6 +336,8 @@
$('.publish').on('click', SM.publishSchedule);
$('.load-dialog').on('click', SM.hideLoadDialog);
$('.load-dialog a').on('click', SM.loadSchedule);
$('.schedule-date').on('change', SM.updateDate);
$('.schedule-date').on('keyup', SM.updateDate);
};
}).call(this);

View File

@ -1,182 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="css/nursery-schedule.css">
<link rel="stylesheet" type="text/css" href="css/schedule-maker.css">
<!-- PROD
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.10.3/jquery.min.js" defer></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js" defer></script>
-->
<!-- DEV
-->
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.js" defer></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.2/jquery-ui.js" defer></script>
<script type="text/javascript" src="js/schedule-maker.js" defer></script>
</head>
<body>
<div class=header>
<h1>Nursery Schedule Maker</h1>
<div class=tools><ul>
<h4 class=month>Creating the schedule for the month of: <input type=date></h4>
<li><a href="#" class=save>Save Schedule Data</a></li>
<li><a href="#" class=load>Load Schedule Data</a></li>
<li><a href="#" class=publish>Publish Schedule</a></li>
</ul></div>
</div>
<section class=sundays>
<div class=first>
<h3>First Sunday</h3>
<div class=am>
<div class=toddlers>
<h5>Toddlers</h5>
<ul>
</ul>
</div>
<div class=infants>
<h5>Infants</h5>
<ul>
</ul>
</div>
</div>
<div class=pm>
<h5>Evening Service</h5>
<ul>
</ul>
</div>
</div>
<div class=second>
<h3>Second Sunday</h3>
<div class=am>
<div class=toddlers>
<h5>Toddlers</h5>
<ul>
</ul>
</div>
<div class=infants>
<h5>Infants</h5>
<ul>
</ul>
</div>
</div>
<div class=pm>
<h5>Evening Service</h5>
<ul>
</ul>
</div>
</div>
<div class=third>
<h3>Third Sunday</h3>
<div class=am>
<div class=toddlers>
<h5>Toddlers</h5>
<ul>
</ul>
</div>
<div class=infants>
<h5>Infants</h5>
<ul>
</ul>
</div>
</div>
<div class=pm>
<h5>Evening Service</h5>
<ul>
</ul>
</div>
</div>
<div class=fourth>
<h3>Fourth Sunday</h3>
<div class=am>
<div class=toddlers>
<h5>Toddlers</h5>
<ul>
</ul>
</div>
<div class=infants>
<h5>Infants</h5>
<ul>
</ul>
</div>
</div>
<div class=pm>
<h5>Evening Service</h5>
<ul>
</ul>
</div>
</div>
<div class=fifth>
<h3>Fifth Sunday</h3>
<div class=am>
<div class=toddlers>
<h5>Toddlers</h5>
<ul>
</ul>
</div>
<div class=infants>
<h5>Infants</h5>
<ul>
</ul>
</div>
</div>
<div class=pm>
<h5>Evening Service</h5>
<ul>
</ul>
</div>
</div>
</section>
<section class=wednesdays>
<div class=first>
<h3>First Wednesday</h3>
<ul>
</ul>
</div>
<div class=second>
<h3>Second Wednesday</h3>
<ul>
</ul>
</div>
<div class=third>
<h3>Third Wednesday</h3>
<ul>
</ul>
</div>
<div class=fourth>
<h3>Fourth Wednesday</h3>
<ul>
</ul>
</div>
<div class=fifth>
<h3>Fifth Wednesday</h3>
<ul>
</ul>
</div>
</section>
<div class=load-dialog>
<div>
<span><input type=file id=fileLoad><a href="#">Load It!</a></span>
</div>
</div>
<script type="text/javascript">
window.onload = function() { ScheduleMaker.init(); };
</script>
</body>
</html>

View File

@ -1,57 +0,0 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/nursery-schedule.css">
</head>
<body>
<section class=sundays><%
['first', 'second', 'third', 'fourth', 'fifth'].each {
dayNumber ->
if (sundays[dayNumber].date) { %>
<div class=${dayNumber}>
<h3>${dayNumber.capitalize()} Sunday, ${sundays[dayNumber].date}</h3>
<div class=am>
<div class=toddlers>
<h5>Toddlers</h5>
<ul><%
sundays[dayNumber].toddlers.each { volunteer -> %>
<li>${volunteer}</li><% } %>
</ul>
</div>
<div class=infants>
<h5>Infants</h5>
<ul><%
sundays[dayNumber].infants.each { volunteer -> %>
<li>${volunteer}</li><% } %>
</ul>
</div>
</div>
<div class=pm>
<h5>Evening Service</h5>
<ul><%
sundays[dayNumber].pm.each { volunteer -> %>
<li>${volunteer}</li><% } %>
</ul>
</div>
</div><% }} %>
</section>
<section class=wednesdays><%
['first', 'second', 'third', 'fourth', 'fifth'].each {
dayNumber ->
if (wednesdays[dayNumber].date) { %>
<div class=${dayNumber}>
<h3>${dayNumber.capitalize()} Wednesday, ${wednesdays[dayNumber].date}</h3>
<ul><% wednesdays[dayNumber].volunteers.each {
volunteer -> %>
<li>${volunteer}</li><% } %>
</ul>
</div><% }} %>
</section>
</body>
</html>