Reworked loan display to accommodate loan notes

Loans are no longer displayed as a unified table, but instead each loan
gets a "card" that contains the relevant data for the loan.

This has had a fairly major html/css re-work as a side effect. Most views are
now structured with two div-based columns so as to avoid the large blank areas
that pure grid can lead to when elements are very different in height.
This commit is contained in:
Erik Thuning 2025-03-18 13:39:48 +01:00
parent afb3020a34
commit c8e3ff0212
7 changed files with 1060 additions and 1108 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -38,18 +38,19 @@ class NewPage extends Page {
$this->fragments['tag']); $this->fragments['tag']);
} }
} }
$out = replace(array('template' => $template), $out = replace(array('id' => '',
$this->fragments['template_management']); 'name' => '',
$out .= replace(array('id' => '', 'brand' => '',
'name' => '', 'serial' => '',
'brand' => '', 'invoice' => '',
'serial' => '', 'tags' => $tags,
'invoice' => '', 'info' => $fields,
'tags' => $tags, 'label' => '',
'info' => $fields, 'label_hidden' => 'hidden',
'label' => '', 'hidden' => 'hidden'),
'hidden' => 'hidden'), $this->fragments['product_details']);
$this->fragments['product_details']); $out .= replace(array('template' => $template),
$this->fragments['template_management']);
return $out; return $out;
} }
} }

@ -233,10 +233,15 @@ abstract class Page extends Responder {
'page' => 'users'), 'page' => 'users'),
$this->fragments['item_link']); $this->fragments['item_link']);
} }
$note = ''; $notes = $loan->get_notes();
$hidden = 'hidden';
if($notes) {
$hidden = '';
}
$misc = '';
if($status !== 'inactive_loan') { if($status !== 'inactive_loan') {
$extend = format_date($loan->get_endtime()); $extend = format_date($loan->get_endtime());
$note = replace(array('id' => $product->get_id(), $misc = replace(array('id' => $product->get_id(),
'end_new' => $extend), 'end_new' => $extend),
$this->fragments['loan_extend_form']); $this->fragments['loan_extend_form']);
} }
@ -251,11 +256,12 @@ abstract class Page extends Responder {
'start_date' => format_date($start), 'start_date' => format_date($start),
'end_date' => format_date($end), 'end_date' => format_date($end),
'initiator' => $initiator_name, 'initiator' => $initiator_name,
'note' => $note), 'hidden' => $hidden,
$this->fragments['user_loan_table_row']); 'notes' => $notes,
'misc' => $misc),
$this->fragments['user_loan_card']);
} }
return replace(array('rows' => $rows), return $rows;
$this->fragments['user_loan_table']);
} }
final protected function build_seen_table($products, $inventory) { final protected function build_seen_table($products, $inventory) {

@ -112,14 +112,18 @@ class ProductPage extends Page {
$rows = ''; $rows = '';
foreach($history as $event) { foreach($history as $event) {
$status = $event->get_status(); $status = $event->get_status();
$itemlink = 'Service'; $userlink = 'Service';
$start = $event->get_starttime(); $start = $event->get_starttime();
$end = $event->get_returntime(); $end = $event->get_returntime();
$notes = $event->get_notes(); $notes = $event->get_notes();
$hidden = 'hidden';
if($notes) {
$hidden = '';
}
$initiator = $event->get_initiator(); $initiator = $event->get_initiator();
$initiator_name = i18n('Unknown'); $initiator_link = i18n('Unknown');
if($initiator) { if($initiator) {
$initiator_name = replace( $initiator_link = replace(
array('id' => $initiator->get_id(), array('id' => $initiator->get_id(),
'name' => $initiator->get_name(), 'name' => $initiator->get_name(),
'page' => 'users'), 'page' => 'users'),
@ -129,7 +133,7 @@ class ProductPage extends Page {
if($event instanceof Loan) { if($event instanceof Loan) {
$user = $event->get_user(); $user = $event->get_user();
$product = $event->get_product(); $product = $event->get_product();
$itemlink = replace(array('id' => $user->get_id(), $userlink = replace(array('id' => $user->get_id(),
'name' => $user->get_name(), 'name' => $user->get_name(),
'page' => 'users'), 'page' => 'users'),
$this->fragments['item_link']); $this->fragments['item_link']);
@ -142,16 +146,16 @@ class ProductPage extends Page {
} }
} }
$rows .= replace(array('status' => $status, $rows .= replace(array('status' => $status,
'name' => $itemlink, 'name' => $userlink,
'start_date' => format_date($start), 'start_date' => format_date($start),
'end_date' => format_date($end), 'end_date' => format_date($end),
'initiator' => $initiator_name, 'initiator' => $initiator_link,
'hidden' => $hidden,
'notes' => $notes, 'notes' => $notes,
'misc' => $misc), 'misc' => $misc),
$this->fragments['product_loan_table_row']); $this->fragments['product_loan_card']);
} }
return replace(array('rows' => $rows), return $rows;
$this->fragments['product_loan_table']);
} }

103
style.css

@ -10,6 +10,18 @@ ul {
padding-left: 15px; padding-left: 15px;
} }
#contents {
display: grid;
grid-template-columns: [col] auto [col] auto [col];
grid-template-rows: auto [row] repeat(3, auto [row]);
grid-template-areas: "header header"
"first second"
"third fourth";
gap: 1rem;
justify-items: start;
align-items: start;
}
#message { #message {
position: absolute; position: absolute;
top: 5px; top: 5px;
@ -54,7 +66,7 @@ ul {
} }
input[type="text"].narrow { input[type="text"].narrow {
width: 6em; width: 6rem;
} }
.hidden { .hidden {
@ -65,6 +77,40 @@ form {
margin-bottom: 5px; margin-bottom: 5px;
} }
.column {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 1rem;
}
.loan {
display: grid;
grid-template-columns: 5px 1fr;
grid-template-rows: auto auto;
grid-auto-rows: auto;
background-color: #d7e0eb;
margin-bottom: 1rem;
margin-top: 1rem;
}
.loan .status {
grid-row: 1 / -1;
grid-column: 1 / 2;
}
.loan .data {
grid-row: 1;
grid-column: 2;
}
.loan .misc {
grid-row: 2;
grid-column: 2;
justify-self: end;
padding: 2px 5px;
}
table { table {
border-collapse: collapse; border-collapse: collapse;
} }
@ -76,31 +122,35 @@ td, th {
padding: 2px 5px; padding: 2px 5px;
} }
td.available { td.notes {
background-color: #a3a86b; white-space: pre-wrap;
} }
td.status, th.status { .status {
width: 5px; width: 5px;
} }
td.on_loan, td.active_loan { .status.available {
background-color: #a3a86b;
}
.status.on_loan, .status.active_loan {
background-color: #acdee6; background-color: #acdee6;
} }
td.inactive_loan { .status.inactive_loan {
background-color: #cdebf0; background-color: #cdebf0;
} }
td.overdue, td.overdue_loan { .status.overdue, .status.overdue_loan {
background-color: #d95e00; background-color: #d95e00;
} }
td.discarded { .status.discarded {
background-color: #a0a0a0; background-color: #a0a0a0;
} }
td.service, td.active_service, td.inactive_service { .status.service, .status.active_service, .status.inactive_service {
background-color: #e7e08d; background-color: #e7e08d;
} }
@ -142,15 +192,15 @@ input:disabled, textarea:disabled {
input[type="text"].newtag, input[type="text"].newtag,
input[type="text"].newfield { input[type="text"].newfield {
width: 11.5em; width: 10.5rem;
} }
tbody.fixedwidth > tr > td { tbody.fixedwidth > tr > td {
min-width: 11.5em; min-width: 10.5rem;
} }
input[type="text"].newtemplate { input[type="text"].newtemplate {
width: 8em; width: 8rem;
} }
.minibutton { .minibutton {
@ -188,43 +238,14 @@ h1 {
grid-area: header; grid-area: header;
} }
#product-details {
grid-column: col 1 / col 2;
grid-row: row 1 / row 3;
}
#user-select {
grid-area: first;
}
#user-table { #user-table {
grid-area: first; grid-area: first;
} }
#user-details {
grid-area: first;
}
#active-loans {
grid-area: third;
}
#inactive-loans {
grid-area: fourth;
}
#public-message { #public-message {
grid-area: first / first / second / second; grid-area: first / first / second / second;
} }
#product-checkout {
grid-area: third;
}
#product-create {
grid-area: first;
}
#product-table { #product-table {
grid-area: first; grid-area: first;
} }

@ -351,16 +351,3 @@ button:disabled, input[type="submit"]:disabled {
width: calc(100% - 8px); width: calc(100% - 8px);
} }
} }
#contents {
display: grid;
grid-template-columns: [col] auto [col] auto [col];
grid-template-rows: auto [row] repeat(3, auto [row]);
grid-template-areas: "header header"
"first second"
"third fourth"
"fifth sixth";
grid-gap: 1rem;
justify-items: start;
align-items: start;
}