From 3cc2b91ed4379737a584243e2f0806456c38a24f Mon Sep 17 00:00:00 2001
From: Erik Thuning <boooink@gmail.com>
Date: Mon, 28 Oct 2019 16:55:08 +0100
Subject: [PATCH] Changed the layout to use two columns Preparing to implement
 attachments

---
 html/fragments.html     | 718 ++++++++++++++++++++++------------------
 include/HistoryPage.php |   6 +-
 include/Page.php        |  76 ++---
 include/Product.php     |   4 +
 include/ProductPage.php |  93 ++++--
 include/SearchPage.php  |  72 ++--
 script.js               |  12 +-
 style.css               |  78 ++++-
 template.css            |   9 +-
 9 files changed, 639 insertions(+), 429 deletions(-)

diff --git a/html/fragments.html b/html/fragments.html
index d61d7a0..5da87eb 100644
--- a/html/fragments.html
+++ b/html/fragments.html
@@ -21,7 +21,7 @@
          name="action"
          value="checkout" />
 </form>
-<table>
+<table id="user-table">
   <thead>
     <tr>
       <th>
@@ -62,6 +62,24 @@
   </td>
 </tr>
 
+¤¤ product_page ¤¤
+<div id="product-table">
+  <form id="product-create"
+        action="./"
+        method="get">
+    <input type="hidden"
+           name="page"
+           value="products" />
+    <input type="hidden"
+           name="action"
+           value="new" />
+    <button>
+      Ny artikel
+    </button>
+    ¤product_table¤
+  </form>
+</div>
+
 ¤¤ product_table ¤¤
 <table>
   <thead>
@@ -99,183 +117,216 @@
   </td>
 </tr>
 
-¤¤ create_product ¤¤
-<form action="./"
-      method="get">
-  <input type="hidden"
-         name="page"
-         value="products" />
-  <input type="hidden"
-         name="action"
-         value="new" />
-  <button>
-    Ny artikel
-  </button>
-</form>
-
 ¤¤ template_management ¤¤
-<h2>Mallar</h2>
-<form class="dark templates"
-      onSubmit="JavaScript:loadTemplate(event)">
-  <datalist id="template_suggest"></datalist>
-  <input type="hidden"
-         name="page"
-         value="products" />
-  <input type="hidden"
-         name="action"
-         value="new" />
-  <input onFocus="JavaScript:suggest(this, 'template')"
-         list="template_suggest"
-         autocomplete="off"
-         type="text"
-         name="template"
-         value="¤template¤"
-         placeholder="Namn" />
-  <button>
-    Ladda
-  </button>
-  <button onClick="JavaScript:saveTemplate(event)">
-    Spara
-  </button>
-  <button onClick="JavaScript:deleteTemplate(event)">
-    Ta bort
-  </button>
-</form>
+<div>
+  <h2>Mallar</h2>
+  <form class="dark templates"
+        onSubmit="JavaScript:loadTemplate(event)">
+    <datalist id="template_suggest"></datalist>
+    <input type="hidden"
+           name="page"
+           value="products" />
+    <input type="hidden"
+           name="action"
+           value="new" />
+    <input onFocus="JavaScript:suggest(this, 'template')"
+           list="template_suggest"
+           autocomplete="off"
+           type="text"
+           name="template"
+           value="¤template¤"
+           placeholder="Namn" />
+    <button>
+      Ladda
+    </button>
+    <button onClick="JavaScript:saveTemplate(event)">
+      Spara
+    </button>
+    <button onClick="JavaScript:deleteTemplate(event)">
+      Ta bort
+    </button>
+  </form>
+</div>
 
 ¤¤ product_details ¤¤
-<h2>Artikeldata</h2>
-<form id="productdata"
-      onSubmit="JavaScript:saveProduct(event)"
-      class="left">
-  <input type="hidden"
-         name="id"
-         value="¤id¤" />
-  <datalist id="field_suggest"></datalist>
-  <datalist id="tag_suggest"></datalist>
-  <table>
-    <tfoot>
-      <tr>
-        <td>
-        </td>
-        <td>
-          <button id="save"
-                  class="right">
-            Spara
-          </button>
-          <button id="reset"
-                  class="right">
-            Återställ
-          </button>
-        </td>
-      </tr>
-    </tfoot>
-    <tbody>
-      <tr>
-        <td>
-          Namn:
-        </td>
-        <td>
-          <input type="text"
-                 name="name"
-                 value="¤name¤"
-                 onFocus="JavaScript:suggestContent(this)"
-                 list="name_suggest"
-                 autocomplete="off" />
-          <datalist id="name_suggest"></datalist>
-        </td>
-      </tr>
-      <tr>
-        <td>
-          Tillverkare:
-        </td>
-        <td>
-          <input type="text"
-                 name="brand"
-                 value="¤brand¤"
-                 onFocus="JavaScript:suggestContent(this)"
-                 list="brand_suggest"
-                 autocomplete="off" />
-          <datalist id="brand_suggest"></datalist>
-        </td>
-      </tr>
-      <tr>
-        <td>
-          Fakturanummer:
-        </td>
-        <td>
-          <input type="text"
-                 name="invoice"
-                 value="¤invoice¤" />
-        </td>
-      </tr>
-      <tr id="before_info">
-        <td>
-          Serienummer:
-        </td>
-        <td>
-          <input type="text"
-                 name="serial"
-                 value="¤serial¤" />
-        </td>
-      </tr>
-      ¤info¤
-      <tr>
-        <td>
-          <input onKeyPress="JavaScript:addField(event)"
-                 onFocus="JavaScript:suggest(this, 'field')"
-                 list="field_suggest"
-                 autocomplete="off"
-                 class="newfield"
-                 type="text"
-                 name="new_key"
-                 placeholder="Nytt fält" />
-          <button class="minibutton"
-                  onClick="addField(event)">
-            +
-          </button>
-        </td>
-        <td>
-        </td>
-      </tr>
-      <tr>
-        <td>
-          Taggar:
-        </td>
-        <td id="tags">
-          ¤tags¤
-          <input onKeyPress="JavaScript:addTag(event)"
-                 onFocus="JavaScript:suggest(this, 'tag')"
-                 list="tag_suggest"
-                 autocomplete="off"
-                 class="newtag"
-                 type="text"
-                 name="new_tag"
-                 placeholder="Ny tagg" />
-          <button class="minibutton"
-                  onClick="JavaScript:addTag(event)">
-            +
-          </button>
-        </td>
-      </tr>
-    </tbody>
-  </table>
-</form>
-¤label¤
-<div class="clear"></div>
-<form class="¤hidden¤">
-  <input type="hidden"
-         name="id"
-         value="¤id¤" />
-  <button onClick="JavaScript:discardProduct(event)">
-    Skrota artikel
-  </button>
-  <button onClick="JavaScript:toggleService(event)">
-    ¤service¤
-  </button>
-</form>
+<div id="product-details">
+  <h2>Artikeldata</h2>
+  <form id="product-data"
+        onSubmit="JavaScript:saveProduct(event)"
+        class="data">
+    <input type="hidden"
+           name="id"
+           value="¤id¤" />
+    <datalist id="field_suggest"></datalist>
+    <datalist id="tag_suggest"></datalist>
+    <table>
+      <tfoot>
+        <tr>
+          <td>
+          </td>
+          <td>
+            <button id="save"
+                    class="right">
+              Spara
+            </button>
+            <button id="reset"
+                    class="right">
+              Återställ
+            </button>
+          </td>
+        </tr>
+      </tfoot>
+      <tbody>
+        <tr>
+          <td>
+            Namn:
+          </td>
+          <td>
+            <input type="text"
+                   name="name"
+                   value="¤name¤"
+                   onFocus="JavaScript:suggestContent(this)"
+                   list="name_suggest"
+                   autocomplete="off" />
+            <datalist id="name_suggest"></datalist>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            Tillverkare:
+          </td>
+          <td>
+            <input type="text"
+                   name="brand"
+                   value="¤brand¤"
+                   onFocus="JavaScript:suggestContent(this)"
+                   list="brand_suggest"
+                   autocomplete="off" />
+            <datalist id="brand_suggest"></datalist>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            Fakturanummer:
+          </td>
+          <td>
+            <input type="text"
+                   name="invoice"
+                   value="¤invoice¤" />
+          </td>
+        </tr>
+        <tr id="before_info">
+          <td>
+            Serienummer:
+          </td>
+          <td>
+            <input type="text"
+                   name="serial"
+                   value="¤serial¤" />
+          </td>
+        </tr>
+        ¤info¤
+        <tr>
+          <td>
+            <input onKeyPress="JavaScript:addField(event)"
+                   onFocus="JavaScript:suggest(this, 'field')"
+                   list="field_suggest"
+                   autocomplete="off"
+                   class="newfield"
+                   type="text"
+                   name="new_key"
+                   placeholder="Nytt fält" />
+            <button class="minibutton"
+                    onClick="addField(event)">
+              +
+            </button>
+          </td>
+          <td>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            Taggar:
+          </td>
+          <td id="tags">
+            ¤tags¤
+            <input onKeyPress="JavaScript:addTag(event)"
+                   onFocus="JavaScript:suggest(this, 'tag')"
+                   list="tag_suggest"
+                   autocomplete="off"
+                   class="newtag"
+                   type="text"
+                   name="new_tag"
+                   placeholder="Ny tagg" />
+            <button class="minibutton"
+                    onClick="JavaScript:addTag(event)">
+              +
+            </button>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </form>
+  <form id="product-actions"
+        class="¤hidden¤">
+    <input type="hidden"
+           name="id"
+           value="¤id¤" />
+    <button onClick="JavaScript:discardProduct(event)">
+      Skrota artikel
+    </button>
+    <button onClick="JavaScript:toggleService(event)">
+      ¤service¤
+    </button>
+  </form>
+</div>
+<div id="product-history"
+     class="¤hidden¤">
+  <h2>Artikelhistorik</h2>
+  ¤history¤
+</div>
+<!--
+<div id="product-attachments"
+     class="¤hidden¤">
+  <h2>Bilagor</h2>
+  ¤attachments¤
+  <form id="attachment_upload"
+        onSubmit="JavaScript:uploadAttachment(event)">
+    <input type="hidden"
+           name="id"
+           value="¤id¤" />
+    <input id="uploadfile"
+           name="uploadfile"
+           type="file"
+           onchange="show_file(event)"/>
+    <input id="filename"
+           name="filename"
+           type="text"
+           placeholder="Välj en fil..."
+           onclick="select_file(event)"
+           readonly />
+    <button>Ladda upp</button>
+  </form>
+</div>
+-->
+<div id="product-label"
+     class="¤hidden¤">
+  <h2>Etikett</h2>
+  ¤label¤
+</div>
+
+¤¤ attachment_list ¤¤
+<ul>
+  ¤attachments¤
+</ul>
+
+¤¤ attachment ¤¤
+<li>
+  <strong>¤name¤</strong>: <a href="./?page=dl&id=¤id¤">Ladda ner</a>
+</li>
 
 ¤¤ product_label ¤¤
-<div class="qr left">
+<div class="qr">
   <a href="./?page=print&id=¤id¤"
      title="Skriv ut">
     <span>¤name¤</span>
@@ -332,75 +383,81 @@
 </p>
 
 ¤¤ user_details ¤¤
-<form onSubmit="JavaScript:updateUser(event)">
-  <input type="hidden"
-         name="id"
-         value="¤id¤" />
-  <table>
-    <tfoot>
-      <tr>
-        <td>
-        </td>
-        <td>
-          <button class="right"
-                  id="save">
-            Spara
-          </button>
-          <button class="right"
-                  id="reset">
-            Återställ
-          </button>
-        </td>
-      </tr>
-    </tfoot>
-    <tbody>
-      <tr>
-        <td>
-          Namn:
-        </td>
-        <td>
+<div id="user-details">
+  <form onSubmit="JavaScript:updateUser(event)">
+    <input type="hidden"
+           name="id"
+           value="¤id¤" />
+    <table>
+      <tfoot>
+        <tr>
+          <td>
+          </td>
+          <td>
+            <button class="right"
+                    id="save">
+              Spara
+            </button>
+            <button class="right"
+                    id="reset">
+              Återställ
+            </button>
+          </td>
+        </tr>
+      </tfoot>
+      <tbody>
+        <tr>
+          <td>
+            Namn:
+          </td>
+          <td>
           <input type="text"
                  value="¤displayname¤"
                  disabled />
-        </td>
-      </tr>
-      <tr>
-        <td>
-          Användarnamn:
-        </td>
-        <td>
-          <input type="text"
-                 name="name"
-                 value="¤name¤" />
-        </td>
-      </tr>
-      <tr>
-        <td>
-          Anteckningar:
-        </td>
-        <td>
-          <textarea name="notes">¤notes¤</textarea>
-        </td>
-      </tr>
-    </tbody>
-  </table>
-</form>
-<form method="GET">
-  <input type="hidden"
-         name="action"
-         value="checkout" />
-  <button name="user"
-          value="¤name¤">
-    Nytt lån
-  </button>
-</form>
-<h2>Aktuella lån</h2>
-¤active_loans¤
-<h2>Gamla lån</h2>
-¤inactive_loans¤
+          </td>
+        </tr>
+        <tr>
+          <td>
+            Användarnamn:
+          </td>
+          <td>
+            <input type="text"
+                   name="name"
+                   value="¤name¤" />
+          </td>
+        </tr>
+        <tr>
+          <td>
+            Anteckningar:
+          </td>
+          <td>
+            <textarea name="notes">¤notes¤</textarea>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </form>
+  <form method="GET">
+    <input type="hidden"
+           name="action"
+           value="checkout" />
+    <button name="user"
+            value="¤name¤">
+      Nytt lån
+    </button>
+  </form>
+</div>
+<div id="active-loans">
+  <h2>Aktuella lån</h2>
+  ¤active_loans¤
+</div>
+<div id="inactive-loans">
+  <h2>Gamla lån</h2>
+  ¤inactive_loans¤
+</div>
 
 ¤¤ history_table ¤¤
-<table>
+<table class="history">
   <thead>
     <tr>
       <th class="status">
@@ -464,76 +521,84 @@
 
 
 ¤¤ checkout_page ¤¤
-<h2>Välj låntagare</h2>
-<form class="dark"
-      action="./"
-      method="GET">
-  <datalist id="user_suggest"></datalist>
-  <input type="hidden"
-         name="page"
-         value="checkout" />
-  <div>
-    <span class="label">
-      Användarnamn:
-    </span>
-    <input onFocus="JavaScript:suggest(this, 'user')"
-           type="text"
+<div id="user-select">
+  <h2>Välj låntagare</h2>
+  <form class="dark"
+        action="./"
+        method="GET">
+    <datalist id="user_suggest"></datalist>
+    <input type="hidden"
+           name="page"
+           value="checkout" />
+    <div>
+      <label for="user">
+        Användarnamn:
+      </label>
+      <input onFocus="JavaScript:suggest(this, 'user')"
+             type="text"
+             name="user"
+             id="user"
+             list="user_suggest"
+             autocomplete="off"
+             placeholder="Användarnamn"
+             value="¤user¤"
+             required />
+      <button type="submit" >
+        Välj
+      </button>
+    </div>
+    <div>
+      <label for="displayname">
+        Namn:
+      </label>
+      <input type="text"
+             name="displayname"
+             id="displayname"
+             value="¤displayname¤"
+             disabled />
+    </div>
+    <div>
+      <label for="notes">
+        Anteckningar:
+      </label>
+      <textarea name="notes"
+                id="notes"
+                disabled>¤notes¤</textarea>
+    </div>
+  </form>
+</div>
+<div id="product-checkout">
+  <h2>Låna ut artikel</h2>
+  <form class="light"
+        onSubmit="JavaScript:checkoutProduct(event)">
+    <input type="hidden"
+           name="page"
+           value="checkout" />
+    <input type="hidden"
            name="user"
-           list="user_suggest"
-           autocomplete="off"
-           placeholder="Användarnamn"
-           value="¤user¤"
-           required />
-    <button type="submit" >
-      Välj
-    </button>
-  </div>
-  <div>
-    <span class="label">
-      Namn:
-    </span>
+           value="¤user¤">
+    <label for="product">Artikel:</label>
     <input type="text"
-           name="displayname"
-           value="¤displayname¤"
-           disabled />
-  </div>
-  <div>
-    <span class="label">
-      Anteckningar:
-    </span>
-    <textarea name="notes"
-              disabled>¤notes¤</textarea>
-  </div>
-</form>
-<h2>Låna ut artikel</h2>
-<form class="light"
-      onSubmit="JavaScript:checkoutProduct(event)">
-  <input type="hidden"
-         name="page"
-         value="checkout" />
-  <input type="hidden"
-         name="user"
-         value="¤user¤">
-  <span class="label">Artikel:</span>
-  <input type="text"
-         name="product"
-         placeholder="Serienummer"
-         required
-         ¤disabled¤ />
-  <button>
+           id="product"
+           name="product"
+           placeholder="Serienummer"
+           required
+           ¤disabled¤ />
+    <button>
       Låna ut
-  </button>
-  <br/>
-  <span class="label">Slutdatum:</span>
-  <input type="text"
-         id="end"
-         onClick="JavaScript:calendar(event)"
-         name="end"
-         value="¤end¤"
-         ¤disabled¤ />
-</form>
-¤subtitle¤
-¤loan_table¤
+    </button>
+    <br/>
+    <label for="end">Slutdatum:</label>
+    <input type="text"
+           id="end"
+           onClick="JavaScript:calendar(event)"
+           name="end"
+           value="¤end¤"
+           ¤disabled¤ />
+  </form>
+  ¤subtitle¤
+  ¤loan_table¤
+</div>
 
 ¤¤ inventory_start ¤¤
 <form class="dark"
@@ -544,7 +609,9 @@
 </form>
 
 ¤¤ inventory_do ¤¤
-<p class="dark">
+
+<div id="inventory-overview"
+     class="dark">
   <span class="label">
     Startdatum:
   </span>
@@ -567,28 +634,39 @@
       Avsluta inventering
     </button>
   </form>
-</p>
-<form class="light ¤hide¤"
+</div>
+<form id="inventory-register"
+      class="light ¤hide¤"
       onSubmit="JavaScript:inventoryProduct(event)">
-  <span class="label">
+  <label for="serial">
     Artikel:
-  </span>
+  </label>
   <input type="text"
          name="serial"
+         id="serial"
          placeholder="Serienummer" />
   <button>
     Registrera
   </button>
 </form>
+<div id="unseen-products">
+  <h2>¤unseen_title¤</h2>
+  ¤unseen¤
+</div>
+<div id="seen-products">
+  <h2>Inventerade artiklar</h2>
+  ¤seen¤
+</div>
 
 ¤¤ return_page ¤¤
 <form class="dark"
       onSubmit="JavaScript:returnProduct(event)">
-  <span class="label">
+  <label for="serial">
     Artikel:
-  </span>
+  </label>
   <input type="text"
          name="serial"
+         id="serial"
          placeholder="Serienummer"
          required />
   <button>
@@ -597,7 +675,7 @@
 </form>
 
 ¤¤ inventory_table ¤¤
-<table>
+<table id="inventory-history">
   <thead>
     <tr>
       <th>
@@ -646,17 +724,18 @@
 </tr>
 
 ¤¤ search_form ¤¤
-
 <form onSubmit="JavaScript:doSearch(event)"
       id="search"
       class="dark">
   <p>
+    <label for="q">Sökterm:</label>
     <input type="hidden"
            name="page"
            value="search" />
     <input type="text"
            onKeyPress="JavaScript:searchInput(event)"
            name="q"
+           id="q"
            placeholder="Vad letar du efter?"
            value=""
            autofocus />
@@ -669,10 +748,19 @@
   </div>
   <div class="clear"></div>
 </form>
-<p>
+<p id="hints">
   Du kan göra mer avancerade sökningar genom att börja med ett nyckelord avslutat med kolon, ex: <strong>serienummer:123456</strong> eller <strong>tag:dator</strong>
 </p>
-
+<div id="found-products"
+     class="¤hide¤">
+  <h2>Artiklar</h2>
+  ¤product_results¤
+</div>
+<div id="found-users"
+     class="¤hide¤">
+  <h2>Användare</h2>
+  ¤user_results¤
+</div>
 
 ¤¤ search_term ¤¤
 
diff --git a/include/HistoryPage.php b/include/HistoryPage.php
index c9f02db..7d0f830 100644
--- a/include/HistoryPage.php
+++ b/include/HistoryPage.php
@@ -22,7 +22,7 @@ class HistoryPage extends Page {
                 $this->subtitle = 'Inventeringsdetaljer';
                 break;
             case 'list':
-                $this->subtitle = 'Genomförda inventeringar';
+                $this->subtitle = 'Historik';
                 break;
         }
     }
@@ -30,9 +30,11 @@ class HistoryPage extends Page {
     protected function render_body() {
         switch($this->action) {
             case 'list':
+                print(replace(array('title' => 'Genomförda inventeringar'),
+                              $this->fragments['subtitle']));
                 print($this->build_inventory_table());
                 print(replace(array('title' => 'Skrotade artiklar'),
-                              $this->fragments['title']));
+                              $this->fragments['subtitle']));
                 $discards = get_items('product_discarded');
                 if($discards) {
                     print($this->build_product_table($discards));
diff --git a/include/Page.php b/include/Page.php
index b6f169b..11149aa 100644
--- a/include/Page.php
+++ b/include/Page.php
@@ -150,15 +150,17 @@ abstract class Page extends Responder {
                 case 'overdue':
                     $loan = $product->get_active_loan();
                     $user = $loan->get_user();
-                    $userlink = replace(array('name' => $user->get_displayname(),
-                                              'id' => $user->get_id(),
-                                              'page' => 'users'),
+                    $replacements = array('name' => $user->get_displayname(),
+                                          'id'   => $user->get_id(),
+                                          'page' => 'users');
+                    $userlink = replace($replacements,
                                         $this->fragments['item_link']);
                     $note = 'Utlånad till '.$userlink;
                     if($loan->is_overdue()) {
                         $note .= ', försenad';
                     } else {
-                        $note .= ', slutdatum '.format_date($loan->get_endtime());
+                        $note .= ', slutdatum '
+                                .format_date($loan->get_endtime());
                     }
                     break;
             }
@@ -205,42 +207,6 @@ abstract class Page extends Responder {
                        $this->fragments['history_table']);
     }
 
-    final protected function build_product_history_table($history) {
-        $rows = '';
-        foreach($history as $event) {
-            $status = $event->get_status();
-            $itemlink = 'Service';
-            $start = $event->get_starttime();
-            $end = $event->get_returntime();
-            $note = '';
-            if($event instanceof Loan) {
-                $user = $event->get_user();
-                $product = $event->get_product();
-                $itemlink = replace(array('id' => $user->get_id(),
-                                          'name' => $user->get_name(),
-                                          'page' => 'users'),
-                                    $this->fragments['item_link']);
-                if(!$end) {
-                    $end = $event->get_endtime();
-                    $extend = format_date(default_loan_end(time()));
-                    $note = replace(array('id' => $product->get_id(),
-                                      'end_new' => $extend),
-                                $this->fragments['loan_extend_form']);
-                }
-            }
-            $rows .= replace(array('status' => $status,
-                                   'item_link' => $itemlink,
-                                   'start_date' => format_date($start),
-                                   'end_date' => format_date($end),
-                                   'note' => $note),
-                             $this->fragments['history_row']);
-            
-        }
-        return replace(array('rows' => $rows,
-                             'item' => 'Låntagare'),
-                       $this->fragments['history_table']);
-    }
-
     final protected function build_seen_table($products, $inventory) {
         $rows = '';
         foreach($products as $product) {
@@ -315,26 +281,22 @@ abstract class Page extends Responder {
             $missing = 'Kvarvarande artiklar';
             $hidden = '';
         }
-        $out = replace(array('start_date' => $startdate,
+        $unseen_table = 'Inga artiklar saknas.';
+        if($unseen) {
+            $unseen_table = $this->build_product_table($unseen);
+        }
+        $seen_table = 'Inga artiklar inventerade.';
+        if($seen) {
+            $seen_table = $this->build_seen_table($seen, $inventory);
+        }
+        return replace(array('start_date' => $startdate,
                              'total_count' => count($all_products),
                              'seen_count' => count($seen),
-                             'hide' => $hidden),
+                             'hide' => $hidden,
+                             'unseen_title' => $missing,
+                             'unseen' => $unseen_table,
+                             'seen' => $seen_table),
                        $this->fragments['inventory_do']);
-        $out .= replace(array('title' => $missing),
-                        $this->fragments['subtitle']);
-        if($unseen) {
-            $out .= $this->build_product_table($unseen);
-        } else {
-            $out .= 'Inga artiklar saknas.';
-        }
-        $out .= replace(array('title' => 'Inventerade artiklar'),
-                        $this->fragments['subtitle']);
-        if($seen) {
-            $out .= $this->build_seen_table($seen, $inventory);
-        } else {
-            $out .= 'Inga artiklar inventerade.';
-        }
-        return $out;
     }
 }
 ?>
diff --git a/include/Product.php b/include/Product.php
index c432a05..8725079 100644
--- a/include/Product.php
+++ b/include/Product.php
@@ -401,5 +401,9 @@ class Product {
         }
         return $out;
     }
+
+    public function get_attachments() {
+        return array();
+    }
 }
 ?>
diff --git a/include/ProductPage.php b/include/ProductPage.php
index e339e42..c5fe86f 100644
--- a/include/ProductPage.php
+++ b/include/ProductPage.php
@@ -48,8 +48,9 @@ class ProductPage extends Page {
     protected function render_body() {
         switch($this->action) {
             case 'list':
-                print($this->fragments['create_product']);
-                print($this->build_product_table(get_items('product')));
+                $products = $this->build_product_table(get_items('product'));
+                print(replace(array('product_table' => $products),
+                              $this->fragments['product_page']));
                 break;
             case 'show':
                 print($this->build_product_details());
@@ -73,16 +74,22 @@ class ProductPage extends Page {
             $tags .= replace(array('tag' => ucfirst($tag)),
                              $this->fragments['tag']);
         }
-        $fields = array('id' => $this->product->get_id(),
-                        'brand' => $this->product->get_brand(),
-                        'name' => $this->product->get_name(),
-                        'serial' => $this->product->get_serial(),
-                        'invoice' => $this->product->get_invoice(),
-                        'tags' => $tags,
-                        'info' => $info,
-                        'label' => '',
-                        'hidden' => 'hidden',
-                        'service' => 'Starta service');
+        $history = $this->build_history_table($this->product->get_history());
+        $attachments = $this->build_attachment_list(
+            $this->product->get_attachments());
+        $fields = array('id'          => $this->product->get_id(),
+                        'brand'       => $this->product->get_brand(),
+                        'name'        => $this->product->get_name(),
+                        'serial'      => $this->product->get_serial(),
+                        'invoice'     => $this->product->get_invoice(),
+                        'tags'        => $tags,
+                        'info'        => $info,
+                        'label'       => '',
+                        'hidden'      => 'hidden',
+                        'service'     => 'Starta service',
+                        'history'     => $history,
+                        'attachments' => $attachments);
+        $attachments = $this->product->get_attachments();
         if(class_exists('QRcode')) {
             $fields['label'] = replace($fields,
                                        $this->fragments['product_label']);
@@ -93,16 +100,60 @@ class ProductPage extends Page {
                 $fields['service'] = 'Avsluta service';
             }
         }
-        $out = replace($fields, $this->fragments['product_details']);
-        $out .= replace(array('title' => 'Artikelhistorik'),
-                        $this->fragments['subtitle']);
-        $history_table = 'Ingen historik att visa.';
-        $history = $this->product->get_history();
-        if($history) {
-            $history_table = $this->build_product_history_table($history);
+        return replace($fields, $this->fragments['product_details']);
+    }
+
+    private function build_history_table($history) {
+        if(!$history) {
+            return 'Ingen historik att visa.';
         }
-        $out .= $history_table;
-        return $out;
+        $rows = '';
+        foreach($history as $event) {
+            $status = $event->get_status();
+            $itemlink = 'Service';
+            $start = $event->get_starttime();
+            $end = $event->get_returntime();
+            $note = '';
+            if($event instanceof Loan) {
+                $user = $event->get_user();
+                $product = $event->get_product();
+                $itemlink = replace(array('id' => $user->get_id(),
+                                          'name' => $user->get_name(),
+                                          'page' => 'users'),
+                                    $this->fragments['item_link']);
+                if(!$end) {
+                    $end = $event->get_endtime();
+                    $extend = format_date(default_loan_end(time()));
+                    $note = replace(array('id' => $product->get_id(),
+                                      'end_new' => $extend),
+                                $this->fragments['loan_extend_form']);
+                }
+            }
+            $rows .= replace(array('status' => $status,
+                                   'item_link' => $itemlink,
+                                   'start_date' => format_date($start),
+                                   'end_date' => format_date($end),
+                                   'note' => $note),
+                             $this->fragments['history_row']);
+        }
+        return replace(array('rows' => $rows,
+                             'item' => 'Låntagare'),
+                       $this->fragments['history_table']);
+    }
+
+
+    private function build_attachment_list($attachments) {
+        if(!$attachments) {
+            return 'Inga bilagor.';
+        }
+        $items = '';
+        foreach($attachments as $attachment) {
+            $items .= replace(array('name' => $attachment->get_name(),
+                                    'id'   => $attachments->get_id()),
+                              $this->fragments['attachment']);
+        }
+        return replace(array('attachments' => $items),
+                       $this->fragments['attachment_list']);
     }
 
     private function build_new_page() {
diff --git a/include/SearchPage.php b/include/SearchPage.php
index ac2748d..e89111f 100644
--- a/include/SearchPage.php
+++ b/include/SearchPage.php
@@ -1,6 +1,8 @@
 <?php
 class SearchPage extends Page {
     private $terms = array();
+    private $product_hits = array();
+    private $user_hits = array();
     
     public function __construct() {
         parent::__construct();
@@ -8,7 +10,17 @@ class SearchPage extends Page {
         if(isset($_GET['q']) && !$_GET['q']) {
             unset($_GET['q']);
         }
-        $this->terms = $_GET;
+        $this->terms = $this->translate_terms($_GET);
+        if($this->terms) {
+            $this->subtitle = 'Sökresultat';
+            $hits = $this->do_search();
+            if(isset($hits['product'])) {
+                $this->product_hits = $hits['product'];
+            }
+            if(isset($hits['user'])) {
+                $this->user_hits = $hits['user'];
+            }
+        }
     }
     
     private function do_search() {
@@ -17,7 +29,7 @@ class SearchPage extends Page {
             return $out;
         }
         foreach(array('user', 'product') as $type) {
-            $result = $this->search($type, $this->translate_keys($this->terms));
+            $result = $this->search($type, $this->terms);
             if($result) {
                 $out[$type] = $result;
             }
@@ -25,7 +37,14 @@ class SearchPage extends Page {
         return $out;
     }
 
-    private function translate_keys($terms) {
+    private function translate_terms($terms) {
+        $matches = array();
+        if(isset($terms['q']) && preg_match('/([^:]+):(.*)/',
+                                            $terms['q'],
+                                            $matches)) {
+            unset($terms['q']);
+            $terms[$matches[1]] = $matches[2];
+        }
         $translated = array();
         foreach($terms as $key => $value) {
             $newkey = $key;
@@ -121,14 +140,14 @@ class SearchPage extends Page {
     }
     
     protected function render_body() {
+        $hidden = 'hidden';
         $terms = '';
-        foreach($this->terms as $key => $value) {
-            if(!is_array($value)) {
-                $terms .= replace(array('term' => ucfirst($key).": $value",
-                                        'key' => $key,
-                                        'value' => $value),
-                                  $this->fragments['search_term']);
-            } else {
+        if($this->terms) {
+            $hidden = '';
+            foreach($this->terms as $key => $value) {
+                if(!is_array($value)) {
+                    $value = array($value);
+                }
                 foreach($value as $item) {
                     $terms .= replace(array('term' => ucfirst($key).": $item",
                                             'key' => $key,
@@ -137,28 +156,19 @@ class SearchPage extends Page {
                 }
             }
         }
-        print(replace(array('terms' => $terms),
-                      $this->fragments['search_form']));
-        if($this->terms) {
-            $hits = $this->do_search();
-            print(replace(array('title' => 'Sökresultat'),
-                          $this->fragments['title']));
-            $result = '';
-            if(isset($hits['user'])) {
-                $result = replace(array('title' => 'Låntagare'),
-                                  $this->fragments['subtitle']);
-                $result .= $this->build_user_table($hits['user']);
-            }
-            if(isset($hits['product'])) {
-                $result .= replace(array('title' => 'Artiklar'),
-                                   $this->fragments['subtitle']);
-                $result .= $this->build_product_table($hits['product']);
-            }
-            if(!$result) {
-                $result = 'Inga träffar.';
-            }
-            print($result);
+        $products = 'Inga artiklar hittade.';
+        if($this->product_hits) {
+            $products = $this->build_product_table($this->product_hits);
         }
+        $users = 'Inga användare hittade.';
+        if($this->user_hits) {
+            $users = $this->build_user_table($this->user_hits);
+        }
+        print(replace(array('terms' => $terms,
+                            'hidden' => $hidden,
+                            'product_results' => $products,
+                            'user_results' => $users),
+                      $this->fragments['search_form']));
     }
 }
 ?>
diff --git a/script.js b/script.js
index 7352165..cb77bb2 100644
--- a/script.js
+++ b/script.js
@@ -357,7 +357,7 @@ function loadTemplate(event) {
 
 function saveTemplate(event) {
     event.preventDefault()
-    var datalist = productDataList(document.querySelector('#productdata'))
+    var datalist = productDataList(document.querySelector('#product-data'))
     datalist.push(['template', event.currentTarget.form.template.value])
     ajaxRequest('savetemplate', datalist, showResult)
 }
@@ -511,3 +511,13 @@ function removeTerm(event) {
     var parent = term.parentNode
     parent.remove(term)
 }
+
+function selectFile(event) {
+    var fileinput = document.getElementById("uploadfile")
+    fileinput.click()
+}
+
+function showFile(event) {
+    var filefield = document.getElementById("filename")
+    filefield.value = event.currentTarget.files[0].name
+}
diff --git a/style.css b/style.css
index 23c0e6b..2baa6f2 100644
--- a/style.css
+++ b/style.css
@@ -28,7 +28,7 @@ textarea {
     padding-left: 2px;
 }
 
-.label {
+.label, label {
     display: inline-block;
     min-width: 100px;
 }
@@ -153,6 +153,82 @@ input[type="text"].newtemplate {
     padding-left: 2px;
 }
 
+#uploadfile {
+    display: none;
+}
+
+h1 {
+    grid-area: header;
+}
+
+#product-details {
+    grid-column: col 1 / col 2;
+    grid-row: row 1 / row 3;
+}
+
+#user-select {
+    grid-area: first;
+}
+
+#user-table {
+    grid-area: first;
+}
+
+#user-details {
+    grid-area: first;
+}
+
+#active-loans {
+    grid-area: third;
+}
+
+#inactive-loans {
+    grid-area: fourth;
+}
+
+#product-checkout {
+    grid-area: third;
+}
+
+#product-create {
+    grid-area: first;
+}
+
+#product-table {
+    grid-area: first;
+}
+
+#unseen-products {
+    grid-area: third;
+}
+
+#seen-products {
+    grid-area: fourth;
+}
+
+#inventory-register {
+    grid-area: second;
+}
+
+#inventory-overview {
+    grid-area: first;
+}
+
+#inventory-history {
+    grid-area: third;
+}
+
+#search {
+    grid-column: col 1 / col 3;
+    grid-row: row 1 / row 2;
+    justify-self: stretch;
+}
+
+#hints {
+    grid-column: col 1 / col 3;
+    grid-row: row 2 / row 3;
+}
+
 .qr {
     margin: 0 10px;
     padding: 5px;
diff --git a/template.css b/template.css
index 28dd8a9..2afbaf7 100644
--- a/template.css
+++ b/template.css
@@ -354,6 +354,13 @@ button:disabled, input[type="submit"]:disabled {
 
 #contents {
     display: grid;
-    grid-template-columns: minmax(min-content, max-content);
+    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;
 }