Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
259 lines (244 loc) · 11.7 KB
/
Copy pathindex.html
File metadata and controls
259 lines (244 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>FieldOpt — open-source dispatch</title>
<meta name="description" content="Open-source field service dispatch. FieldOpt assigns, routes and runs a technician's day, and the whole system lives inside Postgres.">
<meta name="theme-color" content="#07090d">
<meta property="og:title" content="FieldOpt — open-source dispatch">
<meta property="og:description" content="Assign, route and run a field team's day. The whole system lives inside Postgres.">
<meta property="og:url" content="https://fieldopt.dev">
<meta property="og:type" content="website">
<meta property="og:site_name" content="FieldOpt">
<meta property="og:image" content="https://fieldopt.dev/assets/og.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="FieldOpt: dispatch that routes itself, over a map of a day's jobs in New York">
<meta name="twitter:card" content="summary_large_image">
<link rel="icon" type="image/svg+xml" href="/assets/favicon.svg">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32x32.png">
<link rel="apple-touch-icon" href="/assets/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="stylesheet" href="/vendor/leaflet.css">
<link rel="stylesheet" href="/css/site.css">
</head>
<body>
<nav class="nav" id="nav">
<a class="brand" href="#top"><span class="brand-mark">F</span>FieldOpt</a>
<div class="nav-links">
<a href="#how">How it works</a>
<a href="#demo">Demo</a>
<a href="#self-host">Self-host</a>
<a href="#enterprise">Enterprise</a>
<a class="nav-gh" href="https://github.com/sys-ae/fieldopt" aria-label="FieldOpt on GitHub">
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor" aria-hidden="true"><path d="M12 .5a12 12 0 0 0-3.8 23.4c.6.1.8-.3.8-.6v-2.1c-3.3.7-4-1.4-4-1.4-.6-1.4-1.4-1.8-1.4-1.8-1.1-.7.1-.7.1-.7 1.2.1 1.9 1.2 1.9 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.8-1.6-2.7-.3-5.5-1.3-5.5-5.9 0-1.3.5-2.4 1.2-3.2-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.3 1.2a11.5 11.5 0 0 1 6 0C17.3 4.6 18.3 5 18.3 5c.7 1.7.2 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.6-2.8 5.6-5.5 5.9.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0 0 12 .5z"/></svg>
<span>GitHub</span>
</a>
</div>
</nav>
<!-- The scroll story: the map stays pinned while the chapters scroll over it. -->
<main class="story" id="top">
<div class="stage" aria-hidden="true">
<div id="map"></div>
<div class="vignette"></div>
<div class="hud" id="hud">
<div class="hud-clock" id="hud-clock">07:59</div>
<div class="hud-counts">
<span class="c-p"><b id="n-p">0</b> unassigned</span>
<span class="c-a"><b id="n-a">0</b> assigned</span>
<span class="c-i"><b id="n-i">0</b> in progress</span>
<span class="c-c"><b id="n-c">0</b> done</span>
</div>
</div>
</div>
<div class="steps">
<section class="step step-hero" data-step="hero">
<div class="hero">
<p class="eyebrow">Open-source field service dispatch</p>
<h1>Dispatch that<br>routes itself.</h1>
<p class="lede">FieldOpt assigns, routes and runs a field team's day — and the whole system lives inside Postgres. Free and open source.</p>
<div class="cta">
<a class="btn btn-primary" href="https://demo.fieldopt.dev">Try the live demo</a>
<a class="btn btn-ghost" href="https://github.com/sys-ae/fieldopt">View on GitHub</a>
</div>
<p class="scroll-hint">Scroll to watch a day <span aria-hidden="true">↓</span></p>
</div>
</section>
<section class="step" data-step="morning" id="how">
<div class="card">
<p class="card-time">07:59</p>
<h2>Fifty jobs. Twenty-five techs. Nothing assigned.</h2>
<p>This is a real demo day. Every pin and every move you are about to see was recorded from FieldOpt's engine — not drawn by hand.</p>
</div>
</section>
<section class="step" data-step="rules">
<div class="card">
<p class="card-time">The rules</p>
<h2>Not everyone can take every job.</h2>
<p>Skills, management areas and shift hours decide who qualifies. <span data-fill="showcase"></span></p>
</div>
</section>
<section class="step step-route" data-step="route">
<div class="card card-sticky">
<p class="card-time">08:00</p>
<h2>Routed in <span data-fill="routeMs"></span> ms.</h2>
<p>Each job goes to the closest tech who qualifies for it, highest priority first.</p>
<p class="meter"><b id="routed">0</b> / <span data-fill="morningJobs"></span> assigned</p>
</div>
</section>
<section class="step step-day" data-step="day">
<div class="card card-sticky">
<p class="card-time" id="day-time">08:00</p>
<h2 id="day-title">Then the day happens.</h2>
<p id="day-text">Techs leave when they need to — not all at eight.</p>
</div>
</section>
<section class="step" data-step="end">
<div class="card card-wide">
<p class="card-time" data-fill="endTime"></p>
<h2>End of day.</h2>
<div class="stats">
<div><b data-fill="done"></b><span>jobs completed</span></div>
<div><b data-fill="onTime"></b><span>started inside the window</span></div>
<div><b data-fill="miles"></b><span>miles driven</span></div>
<div><b><span data-fill="routeMs"></span><small>ms</small></b><span>to route the morning</span></div>
</div>
</div>
</section>
</div>
</main>
<section class="section demo" id="demo">
<div class="wrap">
<p class="eyebrow">Live demo</p>
<h2>That was a recording.<br>This is the real thing.</h2>
<p class="lede">The full dispatch console, running in your browser: Postgres compiled to WebAssembly, executing the same SQL as a server install. Nothing to install, nothing sent anywhere.</p>
<a class="device" href="https://demo.fieldopt.dev" aria-label="Open the live demo">
<div class="device-bar"><i></i><i></i><i></i><span>demo.fieldopt.dev</span></div>
<img src="/assets/console.jpg" width="2000" height="1077" loading="lazy" alt="The FieldOpt dispatch console mid-day: technicians, jobs and the timeline">
</a>
<div class="cta cta-center">
<a class="btn btn-primary btn-lg" href="https://demo.fieldopt.dev">Open the live demo</a>
</div>
<p class="muted small">About 5 MB the first time, cached after that. Works on a phone.</p>
</div>
</section>
<section class="section" id="platform">
<div class="wrap">
<p class="eyebrow">Architecture</p>
<h2>The database is the app.</h2>
<p class="lede">No application server. Postgres holds the routing, the simulation engine and every screen of the console as PL/pgSQL. PostgREST turns those functions into HTTP; nginx serves the static files.</p>
<div class="arch">
<div class="arch-box arch-main">
<b>Postgres</b>
<span>the application — routing, sim engine, every screen</span>
</div>
<div class="arch-arrow" aria-hidden="true"></div>
<div class="arch-box"><b>PostgREST</b><span>functions → HTTP</span></div>
<div class="arch-arrow" aria-hidden="true"></div>
<div class="arch-box"><b>nginx</b><span>static files</span></div>
</div>
<ul class="proof">
<li><b>Three containers, one command.</b> <code>make up</code> and the database installs itself on first boot.</li>
<li><b>Ported, then proven.</b> Diffed against the stack it replaced before that stack was retired: 11 of 11 screens byte-identical, 50 of 50 routing decisions identical.</li>
<li><b>No supply chain to babysit.</b> No npm, no bundler; a handful of pinned single-file libraries.</li>
</ul>
</div>
</section>
<section class="section" id="features">
<div class="wrap">
<p class="eyebrow">The console</p>
<h2>Built by someone who does the work.</h2>
<div class="grid">
<div class="tile">
<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M2 3h8M2 8h5M2 13h8M12 6l2 2-2 2"/></svg>
<h3>Drag, drop, done</h3>
<p>Drag a job onto a tech. Multi-select to move five at once. On a phone, tap a job and pick from the qualified list.</p>
</div>
<div class="tile">
<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M3 8.5l3 3 7-7"/></svg>
<h3>Checks before it's wrong</h3>
<p>Skill, management area and shift time are checked on every assignment. Override when you know better — FieldOpt tells you what you're overriding.</p>
</div>
<div class="tile">
<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M1 4h14M1 8h14M1 12h14"/></svg>
<h3>A timeline per tech</h3>
<p>Every tech's day as blocks against the clock, with a live now-line. Select techs to narrow it.</p>
</div>
<div class="tile">
<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M1 3.5l4.5-2 5 2.5 4.5-2v11l-4.5 2-5-2.5-4.5 2z"/><path d="M5.5 1.5v11M10.5 4v11"/></svg>
<h3>A map on the second screen</h3>
<p>Jobs, techs and each tech's route, in its own window for the second monitor.</p>
</div>
<div class="tile">
<svg viewBox="0 0 16 16" aria-hidden="true"><rect x="4.5" y="1.5" width="7" height="13" rx="1.5"/><path d="M7 12.5h2"/></svg>
<h3>Works on a phone</h3>
<p>Tabs, cards and bottom-sheet menus below tablet width. Nothing scrolls sideways.</p>
</div>
<div class="tile">
<svg viewBox="0 0 16 16" aria-hidden="true"><rect x="1.5" y="4" width="13" height="8" rx="1.5"/><path d="M4 7h1M7 7h2M11 7h1M5 9.5h6"/></svg>
<h3>Keyboard-first</h3>
<p><kbd>D</kbd> <kbd>S</kbd> <kbd>C</kbd> <kbd>H</kbd> <kbd>U</kbd> act on the selection; <kbd>/</kbd> searches, <kbd>F</kbd> filters, <kbd>P</kbd> opens personnel.</p>
</div>
</div>
</div>
</section>
<section class="section" id="self-host">
<div class="wrap split">
<div>
<p class="eyebrow">Self-host</p>
<h2>Your server. Your data.</h2>
<p class="lede">Docker brings up Postgres, PostgREST and nginx, and a fresh database installs the app and a demo day on first boot. Prefer no Docker? The SQL, the PostgREST binary and an nginx config are all it needs.</p>
</div>
<pre class="code"><code><span class="dim">$</span> git clone https://github.com/sys-ae/fieldopt
<span class="dim">$</span> cd fieldopt && make up
<span class="dim"># → http://localhost:8081</span></code></pre>
</div>
</section>
<section class="section" id="enterprise">
<div class="wrap">
<p class="eyebrow">Pricing</p>
<h2>Free. Every feature.</h2>
<div class="offer">
<div class="offer-card">
<h3>Open source</h3>
<p class="price">$0</p>
<ul>
<li>Every feature, self-hosted</li>
<li>No seat or technician limits</li>
<li>AGPL-3.0 licensed</li>
<li>Community support on GitHub</li>
</ul>
<a class="btn btn-ghost" href="https://github.com/sys-ae/fieldopt">Get the code</a>
</div>
<div class="offer-card offer-ent">
<h3>Enterprise help</h3>
<p class="price">Let's talk</p>
<ul>
<li>Installation and deployment</li>
<li>Support and maintenance</li>
<li>Integrations with your systems</li>
<li>Training for dispatchers</li>
</ul>
<a class="btn btn-primary" id="contact" href="mailto:contact@fieldopt.dev?subject=FieldOpt%20enterprise">Get in touch</a>
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="wrap footer-row">
<a class="brand" href="#top"><span class="brand-mark">F</span>FieldOpt</a>
<nav>
<a href="#demo">Demo</a>
<a href="https://github.com/sys-ae/fieldopt">GitHub</a>
<a href="https://github.com/sys-ae/fieldopt/issues">Issues</a>
<a href="https://github.com/sys-ae/fieldopt/discussions">Discussions</a>
<a href="https://github.com/sys-ae/fieldopt/blob/main/LICENSE">AGPL-3.0</a>
</nav>
<p class="muted small">Built for the field, by the field. A Systems Architecture Engineering project.</p>
</div>
</footer>
<script src="/vendor/leaflet.js"></script>
<script src="/js/story.js"></script>
</body>
</html>