Design improvements

This commit is contained in:
Phyks 2013-08-30 20:07:52 +02:00
parent 17871b984c
commit 51f3aa1491
13 changed files with 274 additions and 87 deletions

17
TODO
View File

@ -3,9 +3,10 @@
inc/Invoices.class.php : inc/Invoices.class.php :
======================== ========================
* Better way to store users in ? => reprendre cette partie * Modify store() method to handle storage
* Modify load() method to handle JOIN
* Modify load() method to handle complex queries (such as WHERE date < DATE_1 AND date > DATE_2) * Modify load() method to handle complex queries (such as WHERE date < DATE_1 AND date > DATE_2)
* Buyer as user object ? * Cache
Manage paybacks : Manage paybacks :
================= =================
@ -13,16 +14,14 @@ Manage paybacks :
TODO : TODO :
====== ======
* Colgroups + CSS hover
* Add / Edit a bill * Add / Edit a bill
* Bug in connection form * JSON output
Tests : To test :
======= =========
* Remember me ?
Tests passed (quick tests) :
============================
* Connection form * Connection form
* Remember me ?
* Edit notice * Edit notice
* Add / Edit user * Add / Edit user
* Change password * Change password

View File

@ -1,36 +1,45 @@
<?php <?php
// TODO : Users in
// TODO : date format
require_once('data/config.php'); require_once('data/config.php');
require_once('Storage.class.php'); require_once('Storage.class.php');
class Invoice extends Storage { class Invoice extends Storage {
protected $id = 0, $date, $users_in, $buyer, $amount, $what; protected $id = 0, $date, $users_in, $guests, $buyer, $amount, $what;
// users_in is an array of user ids
// date is a DateTime object
// buyer is a User object
// guests is an array with same keys as users_in
protected $TABLE_NAME = "Invoices"; protected $TABLE_NAME = "Invoices";
protected $fields = array( protected $fields = array(
'id'=>'key', 'id'=>'key',
'date'=>'date', 'date'=>'date',
'users_in'=>'string', 'users_in'=>'string', // TODO
'buyer'=>'int', 'buyer'=>'int',
'amount'=>'float', 'amount'=>'float',
'what'=>'text' 'what'=>'text'
); );
public function __construct() {
parent::__construct();
}
// Getters // Getters
// ======= // =======
public function getId() { public function getId() {
return $this->id; return $this->id;
} }
public function getDate() { public function getDate($format = "d-m-Y H:i") {
return $this->date; return $this->date->format($format);
} }
public function getUsersIn() { public function getUsersIn() {
return $this->users_in; return $this->users_in;
} }
public function getGuests() {
return $this->guests;
}
public function getBuyer() { public function getBuyer() {
return $this->buyer; return $this->buyer;
} }
@ -49,17 +58,20 @@
$this->id = (int) $id; $this->id = (int) $id;
} }
public function setDate($date_day, $date_month, $date_year) { public function setDate($minute, $hour, $day, $month, $year) {
if((int) $date_day < 10) $date_day = "0".(int) $date_day; if((int) $minute < 10) $minute = '0'.$minute;
if((int) $date_month < 10) $date_month = "0".(int) $date_month;
$this->date = $date_year.$date_month.$date_day; $this->date = DateTime::createFromFormat('Y-n-j G:i', $year.'-'.(int) $month.'-'.(int) $day.' '.(int) $hour.':'.$minute);
} }
public function setUsersIn($users_in) { public function setUsersIn($users_in) {
$this->users_in = $users_in; $this->users_in = $users_in;
} }
public function setGuests($guests) {
$this->guests = $guests;
}
public function setBuyer($buyer) { public function setBuyer($buyer) {
$this->buyer = (int) $buyer; $this->buyer = (int) $buyer;
} }

View File

@ -0,0 +1,90 @@
<?php
require_once('data/config.php');
require_once('Storage.class.php');
class Payback extends Storage {
protected $id = 0, $invoice_id, $amount, $from, $to;
protected $TABLE_NAME = "Paybacks";
protected $fields = array(
'id'=>'key',
'invoice_id'=>'int',
'amount'=>'float',
'from'=>'int',
'to'=>'int'
);
public function __construct() {
parent::__construct();
}
// Getters
// =======
public function getId() {
return (int) $this->id;
}
public function getInvoice() {
return (int) $this->invoice_id;
}
public function getAmount() {
return (float) $this->amount;
}
public function getFrom() {
return (int) $this->from;
}
public function getTo() {
return (int) $this->to;
}
// Setters
// =======
public function setId($id) {
$this->id = (int) $id;
}
public function setInvoice($invoice_id) {
$this->invoice_id = (int) $invoice_id;
}
public function setAmount($amount) {
$this->amount = (float) $amount;
}
public function setFrom($from) {
$this->from = (int) $from;
}
public function setTo($to) {
$this->to = (int) $to;
}
// Restores object from array
// ==========================
public function sessionRestore($data, $serialized = false) {
if($serialized)
$data = unserialize($data);
$this->setId($data['id']);
$this->setInvoice($data['invoice_id']);
$this->setAmount($data['amount']);
$this->setFrom($data['from']);
$this->setTo($data['to']);
}
// Maps htmlspecialchars on the class before display
// =================================================
public function secureDisplay() {
$this->id = (int) $this->id;
$this->invoice_id = (int) $this->invoice_id;
$this->amount = (float) $this->amount;
$this->from = (int) $this->from;
$this->to = (int) $this->to;
}
}

View File

@ -64,11 +64,18 @@ class Storage {
public function typeToSQL($type) { public function typeToSQL($type) {
$return = false; $return = false;
switch($type) { switch($type) {
case 'key':
case 'int': case 'int':
$return = 'INT(11)';
break;
case 'key':
$return = 'INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY'; $return = 'INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY';
break; break;
case 'float':
$return = 'FLOAT';
break;
case 'string': case 'string':
$return = 'VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci'; $return = 'VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci';
break; break;
@ -117,6 +124,9 @@ class Storage {
if(!empty($fields) && is_array($fields)) { if(!empty($fields) && is_array($fields)) {
foreach($fields as $field=>$value) { foreach($fields as $field=>$value) {
if($fields[$field] == 'date')
$value = $value->format('Y-m-d H:i:s');
$query->bindParam(':'.$field, $value); $query->bindParam(':'.$field, $value);
} }
} }
@ -191,6 +201,9 @@ class Storage {
foreach($this->fields as $field=>$type) { foreach($this->fields as $field=>$type) {
if(isset($this->$field)) { if(isset($this->$field)) {
if($fields[$field] == 'date')
$value = $value->format('Y-m-d H:i:s');
$query->bindParam(':'.$field, $this->$field); $query->bindParam(':'.$field, $this->$field);
} }
} }
@ -218,6 +231,9 @@ class Storage {
foreach($this->fields as $field=>$type) { foreach($this->fields as $field=>$type) {
if(!empty($this->$field)) { if(!empty($this->$field)) {
if($fields[$field] == 'date')
$value = $value->format('Y-m-d H:i:s');
$query->bindParam(':'.$field, $this->$field); $query->bindParam(':'.$field, $this->$field);
} }
} }

View File

@ -37,3 +37,17 @@
return $return; return $return;
} }
function ampm2int($date) {
if($date == 'am')
return 0;
else
return 1;
}
function int2ampm($hour) {
if($hour == 0)
return 6;
else
return 18;
}

View File

@ -278,11 +278,12 @@
case 'edit_invoice': case 'edit_invoice':
if(!empty($_GET['id'])) { if(!empty($_GET['id'])) {
$invoice = new Invoice(); $invoice = new Invoice();
$invoice->load(array('id'=>(int) $_GET['id']), true); $invoice = $invoice->load(array('id'=>(int) $_GET['id']), true);
$date_day = ''; $date_hour = $invoice->getDate('a');
$date_month = ''; $date_day = $invoice->getDate('d');
$date_year = ''; $date_month = $invoice->getDate('m');
$date_year = $invoice->getDate('Y');
$amount = $invoice->getAmount(); $amount = $invoice->getAmount();
$what = $invoice->getWhat(); $what = $invoice->getWhat();
$users_in = explode(',', $invoice->getUsersIn()); $users_in = explode(',', $invoice->getUsersIn());
@ -296,30 +297,35 @@
if(!empty($_POST['date_year'])) $date_year = $_POST['date_year']; if(!empty($_POST['date_year'])) $date_year = $_POST['date_year'];
if(!empty($_POST['users_in'])) $users_in = $_POST['users_in']; if(!empty($_POST['users_in'])) $users_in = $_POST['users_in'];
if(!empty($_POST['what']) && !empty($_POST['amount']) && (float) $_POST['amount'] != 0 && !empty($_POST['date_day']) && !empty($_POST['date_month']) && !empty($_POST['date_year']) && !empty($_POST['users_in'])) { if(!empty($_POST['what']) && !empty($_POST['amount']) && (float) $_POST['amount'] != 0 && !empty($_POST['date_hour']) && !empty($_POST['date_day']) && !empty($_POST['date_month']) && !empty($_POST['date_year']) && !empty($_POST['users_in'])) {
if(check_token(600, 'new_invoice')) { if(check_token(600, 'new_invoice')) {
$invoice = new Invoice(); if($_POST['amount'] <= 0) {
$tpl->assign('error', 'Negative amount.');
if(!empty($_POST['id']))
$invoice->setId($_POST['id']);
$invoice->setWhat($_POST['what']);
$invoice->setAmount($_POST['amount']);
$invoice->setBuyer($current_user->getId());
$invoice->setDate($date_day, $date_month, $date_year);
$users_in = '';
$guests = array();
foreach($_POST['users_in'] as $user) {
$users_in .= ($users_in != '') ? ', ' : '';
$users_in .= $user.'('.(!empty($_POST['guest_user_'.$user]) ? (int) $_POST['guest_user_'.$user] : '0').')';
$guests[$user] = (int) $_POST['guest_user_'.$user];
} }
$invoice->setUsersIn($users_in); else {
$invoice = new Invoice();
$invoice->save(); if(!empty($_POST['id']))
header('location: index.php'); $invoice->setId($_POST['id']);
exit();
$invoice->setWhat($_POST['what']);
$invoice->setAmount($_POST['amount']);
$invoice->setBuyer($current_user);
$invoice->setDate(0, int2ampm($_POST['date_hour']), $_POST['date_day'], $_POST['date_month'], $_POST['date_year']);
$users_in = array();
$guests = array();
foreach($_POST['users_in'] as $user) {
$users_in[] = (int) $user;
$guests[] = (int) $_POST['guest_user_'.$user];
}
$invoice->setUsersIn($users_in);
$invoice->setGuests($guests);
$invoice->save();
header('location: index.php');
exit();
}
} }
else { else {
$tpl->assign('error', 'Token error. Please resubmit the form.'); $tpl->assign('error', 'Token error. Please resubmit the form.');
@ -333,6 +339,7 @@
$tpl->assign('months', range(1, 12)); $tpl->assign('months', range(1, 12));
$tpl->assign('years', range(date('Y') - 1, date('Y') + 1)); $tpl->assign('years', range(date('Y') - 1, date('Y') + 1));
$tpl->assign('hour_post', (!empty($date_hour) ? (int) ampm2int($date_hour) : (int) ampm2int(date('a'))));
$tpl->assign('day_post', (!empty($date_day) ? (int) $date_day : (int) date('d'))); $tpl->assign('day_post', (!empty($date_day) ? (int) $date_day : (int) date('d')));
$tpl->assign('month_post', (!empty($date_month) ? (int) $date_month : (int) date('m'))); $tpl->assign('month_post', (!empty($date_month) ? (int) $date_month : (int) date('m')));
$tpl->assign('year_post', (!empty($date_year) ? (int) $date_year : (int) date('Y'))); $tpl->assign('year_post', (!empty($date_year) ? (int) $date_year : (int) date('Y')));

View File

@ -12,10 +12,16 @@ table {
margin: auto; margin: auto;
text-align: center; text-align: center;
max-width: 100%; max-width: 100%;
border-collapse: collapse;
} }
table td, table th { table td, table th {
padding: 0.5em; padding: 0.5em;
border: 1px solid black;
}
table th {
background-color: #DDD;
} }
h2 { h2 {
@ -88,9 +94,13 @@ input[type=submit] {
text-align: center text-align: center
} }
#edit_password_form, #edit_user_form, #invoice_form { #edit_password_form, #edit_user_form, #invoice_form, #notice_form {
width: 50%; width: 67%;
margin-left: 15%; margin: auto;
}
#edit_password_form p, #edit_user_form p, #notice_form p {
text-align: center;
} }
#edit_user_admin_rights { #edit_user_admin_rights {
@ -98,13 +108,25 @@ input[type=submit] {
} }
#textarea_notice { #textarea_notice {
width: 50%; width: 75%;
} }
textarea#what { textarea#what {
width: 75%; width: 75%;
} }
#list_expenses tr:hover {
background-color: #00bd00;
}
#balance_table tr:not(:first-child):hover * {
background-color: #00bd00;
}
.highlight_td {
background-color: #00bd00;
}
#install { #install {
margin: 0; margin: 0;
} }

View File

@ -6,8 +6,8 @@
{if condition="$view == 'list_users'"} {if condition="$view == 'list_users'"}
<h2>List of users</h2> <h2>List of users</h2>
<p>You can also <a href="?do=add_user">add a user</a>.</p> <p class="center">You can also <a href="?do=add_user">add a user</a>.</p>
<table> <table id="edit_users">
<tr> <tr>
<th>Id</th> <th>Id</th>
<th>Login</th> <th>Login</th>

View File

@ -1,4 +1,4 @@
<script type="text/javascript" src="js/main.js"></script>
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</body> </body>
</html> </html>

View File

@ -7,7 +7,7 @@
<div id="quick_summary"> <div id="quick_summary">
<h2>Balance</h2> <h2>Balance</h2>
<p class="center">Read <em>line</em> owes <em>case</em> {$currency} to <em>column</em>. You can click on links to confirm the payback. <p class="center">Read <em>line</em> owes <em>case</em> {$currency} to <em>column</em>. You can click on links to confirm the payback.
<table> <table id="balance_table">
<tr> <tr>
<th>Owes\To</th> <th>Owes\To</th>
{loop="users"} {loop="users"}
@ -27,7 +27,7 @@
<div id="detailed_summary"> <div id="detailed_summary">
<h2>Detailed list of bills for last month</h2> <h2>Detailed list of bills for last month</h2>
<table> <table id="list_expenses">
<tr> <tr>
<th>Date</th> <th>Date</th>
<th>Paid by</th> <th>Paid by</th>
@ -40,7 +40,7 @@
{loop="invoices"} {loop="invoices"}
<tr> <tr>
<td>{$value->getDate()}</td> <td>{$value->getDate()}</td>
<td>{$value->getBuyer()}</td> <td>{$value->getBuyer()->getDisplayName()}</td>
<td>{$value->getUsersIn()}</td> <td>{$value->getUsersIn()}</td>
<td>{$value->getAmount()}</td> <td>{$value->getAmount()}</td>
<td>{$value->getWhat()}</td> <td>{$value->getWhat()}</td>

View File

@ -38,3 +38,23 @@ function toggle_password(id) {
else else
document.getElementById(id).type = 'password'; document.getElementById(id).type = 'password';
} }
$(document).ready(function() {
$('#balance_table td').hover(function() {
$(this).closest('tr').find('td,th').addClass('highlight_td');
var col = $(this).index()+1;
$(this).closest('table').find('tr :nth-child('+col+')').addClass('highlight_td');
}, function() {
$(this).closest('tr').find('td,th').removeClass('highlight_td');
var col = $(this).index()+1;
$(this).closest('table').find('tr :nth-child('+col+')').removeClass('highlight_td');
});
$('#balance_table tr:first-child th:not(:first-child)').hover(function() {
var col = $(this).index()+1;
$(this).closest('table').find('tr :nth-child('+col+')').addClass('highlight_td');
}, function() {
var col = $(this).index()+1;
$(this).closest('table').find('tr :nth-child('+col+')').removeClass('highlight_td');
});
});

View File

@ -7,39 +7,46 @@
<h2>Add a bill</h2> <h2>Add a bill</h2>
<form method="post" action="index.php?do=new_invoice" id="invoice_form"> <form method="post" action="index.php?do=new_invoice" id="invoice_form">
<p> <fieldset>
<label for="what">What ? </label> <legend>Expense</legend>
</p> <p>
<textarea name="what" id="what" rows="10">{$what_post}</textarea> <label for="what">What ? </label>
<p> </p>
<label for="amount">Amount : </label> <textarea name="what" id="what" rows="10">{$what_post}</textarea>
<input type="text" name="amount" id="amount" {if condition="$amount_post != 0"} value="{$amount_post}" {/if} size="5"/> {$currency} <p>
</p> <label for="amount">Amount : </label>
<p> <input type="text" name="amount" id="amount" {if condition="$amount_post != 0"} value="{$amount_post}" {/if} size="5"/> {$currency}
<label for="date_day">Date : </label> </p>
<select name="date_day" id="date_day"> <p>
{loop="days"} <label for="date_day">Date : </label>
<option value="{$value}" {if condition="$value == $day_post"}selected{/if}>{$value}</option> <select name="date_day" id="date_day">
{/loop} {loop="days"}
</select> / <option value="{$value}" {if condition="$value == $day_post"}selected{/if}>{$value}</option>
<select name="date_month" id="date_month" onchange="set_days_month_year();"> {/loop}
{loop="months"} </select> /
<option value="{$value}" {if condition="$value == $month_post"}selected{/if}>{$value}</option> <select name="date_month" id="date_month" onchange="set_days_month_year();">
{/loop} {loop="months"}
</select> / <option value="{$value}" {if condition="$value == $month_post"}selected{/if}>{$value}</option>
<select name="date_year" id="date_year" onchange="set_days_month_year();"> {/loop}
{loop="years"} </select> /
<option value="{$value}" {if condition="$value == $year_post"}selected{/if}>{$value}</option> <select name="date_year" id="date_year" onchange="set_days_month_year();">
{/loop} {loop="years"}
</select> <option value="{$value}" {if condition="$value == $year_post"}selected{/if}>{$value}</option>
</p> {/loop}
<p> </select>
Users in ? <select name="date_hour" id="date_hour">
<option value="0" {if condition="$hour_post == 0"}selected{/if}>AM</option>
<option value="1" {if condition="$hour_post == 1"}selected{/if}>PM</option>
</select>
</p>
</fieldset>
<fieldset>
<legend>Users in ?</legend>
{loop="users"} {loop="users"}
<br/><input type="checkbox" name="users_in[]" value="{$value->getId()}" id="users_in_{$value->getId()}" {if condition="$current_user->getId() == $value->getId() || in_array($value->getId(), $users_in)"} checked {/if}/> <label for="users_in_{$value->getId()}">{$value->getDisplayName()}</label> and <input type="text" name="guest_user_{$value->getId()}" id="guest_user_{$value->getId()}" size="1" {if condition="in_array($value->getId(), $users_in)"} value="{$guests[$value->getId()]}" {else} value="0" {/if} onkeyup="guest_user_label({$value->getId()});"/><label for="guest_user_{$value->getId()}" id="guest_user_{$value->getId()}_label"> guest</label>. <br/><input type="checkbox" name="users_in[]" value="{$value->getId()}" id="users_in_{$value->getId()}" {if condition="$current_user->getId() == $value->getId() || in_array($value->getId(), $users_in)"} checked {/if}/> <label for="users_in_{$value->getId()}">{$value->getDisplayName()}</label> and <input type="text" name="guest_user_{$value->getId()}" id="guest_user_{$value->getId()}" size="1" {if condition="in_array($value->getId(), $users_in)"} value="{$guests[$value->getId()]}" {else} value="0" {/if} onkeyup="guest_user_label({$value->getId()});"/><label for="guest_user_{$value->getId()}" id="guest_user_{$value->getId()}_label"> guest</label>.
{/loop} {/loop}
</p> </fieldset>
<p> <p class="center">
<input type="submit" value="Add"/> <input type="submit" value="Add"/>
{if condition="$id != 0"}<input type="hidden" name="id" value="{$id}"/>{/if} {if condition="$id != 0"}<input type="hidden" name="id" value="{$id}"/>{/if}
<input type="hidden" name="token" value="{$token}"/> <input type="hidden" name="token" value="{$token}"/>

View File

@ -9,7 +9,7 @@
<textarea name="notice" rows="15" id="textarea_notice">{$notice}</textarea> <textarea name="notice" rows="15" id="textarea_notice">{$notice}</textarea>
</p> </p>
<p><em>Note :</em> You can use HTML formatting in this form.</p> <p><em>Note :</em> You can use HTML formatting in this form.</p>
<p> <p class="center">
<input type="submit" value="Submit"/> <input type="submit" value="Submit"/>
<input type="hidden" name="token" value="{$token}"/> <input type="hidden" name="token" value="{$token}"/>
</p> </p>