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 :
========================
* 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)
* Buyer as user object ?
* Cache
Manage paybacks :
=================
@ -13,16 +14,14 @@ Manage paybacks :
TODO :
======
* Colgroups + CSS hover
* Add / Edit a bill
* Bug in connection form
* JSON output
Tests :
=======
* Remember me ?
Tests passed (quick tests) :
============================
To test :
=========
* Connection form
* Remember me ?
* Edit notice
* Add / Edit user
* Change password

View File

@ -1,36 +1,45 @@
<?php
// TODO : Users in
// TODO : date format
require_once('data/config.php');
require_once('Storage.class.php');
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 $fields = array(
'id'=>'key',
'date'=>'date',
'users_in'=>'string',
'users_in'=>'string', // TODO
'buyer'=>'int',
'amount'=>'float',
'what'=>'text'
);
public function __construct() {
parent::__construct();
}
// Getters
// =======
public function getId() {
return $this->id;
}
public function getDate() {
return $this->date;
public function getDate($format = "d-m-Y H:i") {
return $this->date->format($format);
}
public function getUsersIn() {
return $this->users_in;
}
public function getGuests() {
return $this->guests;
}
public function getBuyer() {
return $this->buyer;
}
@ -49,17 +58,20 @@
$this->id = (int) $id;
}
public function setDate($date_day, $date_month, $date_year) {
if((int) $date_day < 10) $date_day = "0".(int) $date_day;
if((int) $date_month < 10) $date_month = "0".(int) $date_month;
public function setDate($minute, $hour, $day, $month, $year) {
if((int) $minute < 10) $minute = '0'.$minute;
$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) {
$this->users_in = $users_in;
}
public function setGuests($guests) {
$this->guests = $guests;
}
public function setBuyer($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) {
$return = false;
switch($type) {
case 'key':
case 'int':
$return = 'INT(11)';
break;
case 'key':
$return = 'INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY';
break;
case 'float':
$return = 'FLOAT';
break;
case 'string':
$return = 'VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci';
break;
@ -117,6 +124,9 @@ class Storage {
if(!empty($fields) && is_array($fields)) {
foreach($fields as $field=>$value) {
if($fields[$field] == 'date')
$value = $value->format('Y-m-d H:i:s');
$query->bindParam(':'.$field, $value);
}
}
@ -191,6 +201,9 @@ class Storage {
foreach($this->fields as $field=>$type) {
if(isset($this->$field)) {
if($fields[$field] == 'date')
$value = $value->format('Y-m-d H:i:s');
$query->bindParam(':'.$field, $this->$field);
}
}
@ -218,6 +231,9 @@ class Storage {
foreach($this->fields as $field=>$type) {
if(!empty($this->$field)) {
if($fields[$field] == 'date')
$value = $value->format('Y-m-d H:i:s');
$query->bindParam(':'.$field, $this->$field);
}
}

View File

@ -37,3 +37,17 @@
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':
if(!empty($_GET['id'])) {
$invoice = new Invoice();
$invoice->load(array('id'=>(int) $_GET['id']), true);
$invoice = $invoice->load(array('id'=>(int) $_GET['id']), true);
$date_day = '';
$date_month = '';
$date_year = '';
$date_hour = $invoice->getDate('a');
$date_day = $invoice->getDate('d');
$date_month = $invoice->getDate('m');
$date_year = $invoice->getDate('Y');
$amount = $invoice->getAmount();
$what = $invoice->getWhat();
$users_in = explode(',', $invoice->getUsersIn());
@ -296,30 +297,35 @@
if(!empty($_POST['date_year'])) $date_year = $_POST['date_year'];
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')) {
$invoice = new Invoice();
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];
if($_POST['amount'] <= 0) {
$tpl->assign('error', 'Negative amount.');
}
$invoice->setUsersIn($users_in);
else {
$invoice = new Invoice();
$invoice->save();
header('location: index.php');
exit();
if(!empty($_POST['id']))
$invoice->setId($_POST['id']);
$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 {
$tpl->assign('error', 'Token error. Please resubmit the form.');
@ -333,6 +339,7 @@
$tpl->assign('months', range(1, 12));
$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('month_post', (!empty($date_month) ? (int) $date_month : (int) date('m')));
$tpl->assign('year_post', (!empty($date_year) ? (int) $date_year : (int) date('Y')));

View File

@ -12,10 +12,16 @@ table {
margin: auto;
text-align: center;
max-width: 100%;
border-collapse: collapse;
}
table td, table th {
padding: 0.5em;
border: 1px solid black;
}
table th {
background-color: #DDD;
}
h2 {
@ -88,9 +94,13 @@ input[type=submit] {
text-align: center
}
#edit_password_form, #edit_user_form, #invoice_form {
width: 50%;
margin-left: 15%;
#edit_password_form, #edit_user_form, #invoice_form, #notice_form {
width: 67%;
margin: auto;
}
#edit_password_form p, #edit_user_form p, #notice_form p {
text-align: center;
}
#edit_user_admin_rights {
@ -98,13 +108,25 @@ input[type=submit] {
}
#textarea_notice {
width: 50%;
width: 75%;
}
textarea#what {
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 {
margin: 0;
}

View File

@ -6,8 +6,8 @@
{if condition="$view == 'list_users'"}
<h2>List of users</h2>
<p>You can also <a href="?do=add_user">add a user</a>.</p>
<table>
<p class="center">You can also <a href="?do=add_user">add a user</a>.</p>
<table id="edit_users">
<tr>
<th>Id</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/main.js"></script>
</body>
</html>

View File

@ -7,7 +7,7 @@
<div id="quick_summary">
<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.
<table>
<table id="balance_table">
<tr>
<th>Owes\To</th>
{loop="users"}
@ -27,7 +27,7 @@
<div id="detailed_summary">
<h2>Detailed list of bills for last month</h2>
<table>
<table id="list_expenses">
<tr>
<th>Date</th>
<th>Paid by</th>
@ -40,7 +40,7 @@
{loop="invoices"}
<tr>
<td>{$value->getDate()}</td>
<td>{$value->getBuyer()}</td>
<td>{$value->getBuyer()->getDisplayName()}</td>
<td>{$value->getUsersIn()}</td>
<td>{$value->getAmount()}</td>
<td>{$value->getWhat()}</td>

View File

@ -38,3 +38,23 @@ function toggle_password(id) {
else
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>
<form method="post" action="index.php?do=new_invoice" id="invoice_form">
<p>
<label for="what">What ? </label>
</p>
<textarea name="what" id="what" rows="10">{$what_post}</textarea>
<p>
<label for="amount">Amount : </label>
<input type="text" name="amount" id="amount" {if condition="$amount_post != 0"} value="{$amount_post}" {/if} size="5"/> {$currency}
</p>
<p>
<label for="date_day">Date : </label>
<select name="date_day" id="date_day">
{loop="days"}
<option value="{$value}" {if condition="$value == $day_post"}selected{/if}>{$value}</option>
{/loop}
</select> /
<select name="date_month" id="date_month" onchange="set_days_month_year();">
{loop="months"}
<option value="{$value}" {if condition="$value == $month_post"}selected{/if}>{$value}</option>
{/loop}
</select> /
<select name="date_year" id="date_year" onchange="set_days_month_year();">
{loop="years"}
<option value="{$value}" {if condition="$value == $year_post"}selected{/if}>{$value}</option>
{/loop}
</select>
</p>
<p>
Users in ?
<fieldset>
<legend>Expense</legend>
<p>
<label for="what">What ? </label>
</p>
<textarea name="what" id="what" rows="10">{$what_post}</textarea>
<p>
<label for="amount">Amount : </label>
<input type="text" name="amount" id="amount" {if condition="$amount_post != 0"} value="{$amount_post}" {/if} size="5"/> {$currency}
</p>
<p>
<label for="date_day">Date : </label>
<select name="date_day" id="date_day">
{loop="days"}
<option value="{$value}" {if condition="$value == $day_post"}selected{/if}>{$value}</option>
{/loop}
</select> /
<select name="date_month" id="date_month" onchange="set_days_month_year();">
{loop="months"}
<option value="{$value}" {if condition="$value == $month_post"}selected{/if}>{$value}</option>
{/loop}
</select> /
<select name="date_year" id="date_year" onchange="set_days_month_year();">
{loop="years"}
<option value="{$value}" {if condition="$value == $year_post"}selected{/if}>{$value}</option>
{/loop}
</select>
<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"}
<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}
</p>
<p>
</fieldset>
<p class="center">
<input type="submit" value="Add"/>
{if condition="$id != 0"}<input type="hidden" name="id" value="{$id}"/>{/if}
<input type="hidden" name="token" value="{$token}"/>

View File

@ -9,7 +9,7 @@
<textarea name="notice" rows="15" id="textarea_notice">{$notice}</textarea>
</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="hidden" name="token" value="{$token}"/>
</p>