ThreadCount Community edition

Uniform stock management for healthcare linen rooms. Licensed under the GNU AGPL v3.
This commit is contained in:
ThreadCount
2026-09-13 08:54:35 +10:00
commit 344b1701dd
505 changed files with 56231 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>No connection — ThreadCount</title>
<style>
/* Bundled with the app, so it has to stand on its own: no web fonts, no network, no images.
Archivo is the brand face; the system stack is what actually renders here. */
:root { --paper:#f3f2f2; --ink:#201e1d; --accent:#ec3013; --n600:#7a7573; --n700:#57534f; }
* { box-sizing:border-box; margin:0; padding:0; }
body {
font-family:"Archivo",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
background:var(--paper); color:var(--ink);
min-height:100dvh; display:flex; flex-direction:column;
padding-top:env(safe-area-inset-top); padding-bottom:env(safe-area-inset-bottom);
}
header { height:56px; flex:0 0 56px; background:var(--ink); color:var(--paper);
display:flex; align-items:center; gap:10px; padding:0 16px; }
.sq { width:15px; height:15px; background:var(--accent); flex:none; }
.wm { font-weight:800; font-size:17px; letter-spacing:-.01em; }
.rule { height:4px; background:var(--accent); }
main { flex:1; padding:36px 20px; }
h1 { font-weight:800; font-size:30px; line-height:1.05; letter-spacing:-.03em; }
.mark { width:56px; height:4px; background:var(--accent); margin:18px 0 0; }
p { font-size:15.5px; line-height:1.65; color:var(--n700); margin-top:20px; max-width:44ch; }
ul { margin-top:18px; padding:0; list-style:none; border-top:2px solid var(--ink); }
li { display:flex; gap:11px; padding:12px 0; border-bottom:1px solid #cfcccb;
font-size:14.5px; line-height:1.5; color:var(--n700); }
li i { width:7px; height:7px; background:var(--accent); flex:none; margin-top:7px; }
button {
width:100%; min-height:64px; border:0; background:var(--accent); color:#fff;
font-family:inherit; font-weight:800; font-size:14px; letter-spacing:.08em;
text-transform:uppercase; text-align:left; padding:0 20px; cursor:pointer;
display:flex; align-items:center; gap:12px;
}
button:active { background:#b8240e; }
.foot { font-size:13px; color:var(--n600); padding:0 20px 22px; }
</style>
</head>
<body>
<header><span class="sq"></span><span class="wm">ThreadCount</span></header>
<div class="rule"></div>
<main>
<h1>No connection.</h1>
<div class="mark"></div>
<p>ThreadCount cant reach the linen rooms records, so it wont let you issue or count
anything right now — nothing is being saved up in the background.</p>
<ul>
<li><i></i>Check the wards wifi, or step somewhere with signal.</li>
<li><i></i>Nothing you had already recorded is lost. A count you hadnt filed is still
on this phone and will be there when you get back.</li>
<li><i></i>If the wifi is fine and this keeps happening, its our end — the contact form
on threadcount.tech reaches the person who wrote it.</li>
</ul>
</main>
<button id="again">Try again <span aria-hidden="true"></span></button>
<div class="foot" id="stamp"></div>
<script>
/* "Try again" has to leave this page. Capacitor serves it from the app's own origin
(https://localhost/error.html), so location.reload() re-requests a local file that always
loads — the same screen comes back no matter how good the signal is, and the only way out is
to force-quit the app. index.html records where it was headed under the same origin, so this
goes back there instead; if there is nothing recorded, the app's home screen will do. */
var SITE = "https://threadcount.tech";
var HOME = "/m";
document.getElementById('again').addEventListener('click', function () {
var path = HOME;
try { path = localStorage.getItem('tc.lastpath') || HOME; } catch (e) { /* private mode is fine */ }
// A stored value only ever came from this app, but it is still concatenated onto a URL, so it
// is checked: one leading slash, and no second one that would make it another host entirely.
if (path.charAt(0) !== '/' || path.charAt(1) === '/') path = HOME;
location.replace(SITE + path);
});
// A timestamp turns "it says no connection" into something answerable over the phone.
try {
document.getElementById('stamp').textContent =
'Last tried ' + new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
} catch (e) { /* nothing to show is fine */ }
</script>
</body>
</html>