Skip to content

Commit bd161d2

Browse files
authored
chore: Visual update (#933)
- New Solver Logo - Removed dependency on SolverUI Webjar (most of it was already duplicated anyways as there are a lot of tiny nuances, and this also makes it more portable and less "magical"). - Replaced most color usage to take visually impaired into account. - Aligned some of the UI to make it more compact and consistent. - Every quickstart has a short text explaining what it does and quantities of the variables / entities.
1 parent e02c351 commit bd161d2

File tree

57 files changed

+1274
-1248
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1274
-1248
lines changed

README.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<p align="center">
2-
<a href="https://timefold.ai">
3-
<img src="/timefold-logo.png" width="400px" alt="Timefold Solver" />
2+
<a href="https://solver.timefold.ai">
3+
<img src="/timefold-solver-logo.png" width="400px" alt="Timefold Solver" />
44
</a>
55
</p>
66

@@ -11,23 +11,23 @@ It shows different use cases and basic implementations to get you started on you
1111

1212
## Overview
1313

14-
| Use Case <img width="341" height="1"> | Notable Solver Concepts <img width="541" height="1"> |
15-
|-----------------------------------------------------------------------|----------------------------------------------------------|
16-
| 🚚 <a href="#-vehicle-routing">Vehicle Routing</a> | Chained Through Time, Shadow Variables |
17-
| 🧑‍💼 <a href="#-employee-scheduling">Employee Scheduling</a> | Load Balancing |
18-
| 🛠️ <a href="#-maintenance-scheduling">Maintenance Scheduling</a> | TimeGrain, Shadow Variable, Variable Listener |
19-
| 📦 <a href="#-food-packaging">Food Packaging</a> | Chained Through Time, Shadow Variables, Pinning |
20-
| 🛒 <a href="#-order-picking">Order Picking</a> | Chained Planning Variable, Shadow Variables |
21-
| 🏫 <a href="#-school-timetabling">School Timetabling</a> | Timeslot |
22-
| 🏭 <a href="#-facility-location-problem">Facility Location Problem</a> | Shadow Variable |
23-
| 🎤 <a href="#-conference-scheduling">Conference Scheduling</a> | Timeslot, Justifications |
14+
| Use Case | Notable Solver Concepts |
15+
|-------------------------------------------------------------------------|----------------------------------------------------------|
16+
| 🚚 <a href="#-vehicle-routing">Vehicle Routing</a> | Chained Through Time, Shadow Variables |
17+
| 🧑‍💼 <a href="#-employee-scheduling">Employee Scheduling</a> | Load Balancing |
18+
| 🛠️ <a href="#-maintenance-scheduling">Maintenance Scheduling</a> | TimeGrain, Shadow Variable, Variable Listener |
19+
| 📦 <a href="#-food-packaging">Food Packaging</a> | Chained Through Time, Shadow Variables, Pinning |
20+
| 🛒 <a href="#-order-picking">Order Picking</a> | Chained Planning Variable, Shadow Variables |
21+
| 🏫 <a href="#-school-timetabling">School Timetabling</a> | Timeslot |
22+
| 🏭 <a href="#-facility-location-problem">Facility Location Problem</a> | Shadow Variable |
23+
| 🎤 <a href="#-conference-scheduling">Conference Scheduling</a> | Timeslot, Justifications |
2424
| 🛏️ <a href="#-bed-allocation-scheduling">Bed Allocation Scheduling</a> | Allows Unassigned |
25-
| 🛫 <a href="#-flight-crew-scheduling">Flight Crew Scheduling</a> | |
26-
| 👥 <a href="#-meeting-scheduling">Meeting Scheduling</a> | TimeGrain |
27-
| ✅ <a href="#-task-assigning">Task Assigning</a> | Bendable Score, Chained Through Time, Allows Unassigned |
28-
| 📆 <a href="#-project-job-scheduling">Project Job Scheduling</a> | Shadow Variables, Variable Listener, Strenght Comparator |
29-
| 🏆 <a href="#-sports-league-scheduling">Sports League Scheduling</a> | Consecutive Sequences |
30-
| 🏅 <a href="#-tournament-scheduling">Tournament Scheduling</a> | Pinning, Load Balancing |
25+
| 🛫 <a href="#-flight-crew-scheduling">Flight Crew Scheduling</a> | |
26+
| 👥 <a href="#-meeting-scheduling">Meeting Scheduling</a> | TimeGrain |
27+
| ✅ <a href="#-task-assigning">Task Assigning</a> | Bendable Score, Chained Through Time, Allows Unassigned |
28+
| 📆 <a href="#-project-job-scheduling">Project Job Scheduling</a> | Shadow Variables, Variable Listener, Strenght Comparator |
29+
| 🏆 <a href="#-sports-league-scheduling">Sports League Scheduling</a> | Consecutive Sequences |
30+
| 🏅 <a href="#-tournament-scheduling">Tournament Scheduling</a> | Pinning, Load Balancing |
3131

3232
> [!NOTE]
3333
> The implementations in this repository serve as a starting point and/or inspiration when creating your own application.

java/bed-allocation/pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,6 @@
9393
<groupId>io.quarkus</groupId>
9494
<artifactId>quarkus-web-dependency-locator</artifactId>
9595
</dependency>
96-
<dependency>
97-
<groupId>ai.timefold.solver</groupId>
98-
<artifactId>timefold-solver-webui</artifactId>
99-
<scope>runtime</scope>
100-
</dependency>
10196
<dependency>
10297
<groupId>org.webjars</groupId>
10398
<artifactId>bootstrap</artifactId>

java/bed-allocation/src/main/resources/META-INF/resources/app.js

Lines changed: 8 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
var autoRefreshIntervalId = null;
2-
const dateFormatter = JSJoda.DateTimeFormatter.ofPattern('MM-dd');
3-
const roomDateFormatter = JSJoda.DateTimeFormatter.ofPattern('d MMM').withLocale(JSJodaLocale.Locale.ENGLISH);
42

53
const byRoomPanel = document.getElementById("byRoomPanel");
64
const byRoomTimelineOptions = {
@@ -19,8 +17,6 @@ let loadedSchedule = null;
1917
let viewType = "R";
2018

2119
$(document).ready(function () {
22-
replaceQuickstartTimefoldAutoHeaderFooter();
23-
2420
$("#solveButton").click(function () {
2521
solve();
2622
});
@@ -102,6 +98,7 @@ function refreshSchedule() {
10298
function renderSchedule(schedule) {
10399
refreshSolvingButtons(schedule.solverStatus != null && schedule.solverStatus !== "NOT_SOLVING");
104100
$("#score").text("Score: " + (schedule.score == null ? "?" : schedule.score));
101+
$("#info").text(`This dataset has ${schedule.stays.length} stays and ${schedule.departments.flatMap(d => d.rooms).length} beds across ${schedule.departments.length} departments.`);
105102

106103
if (viewType === "R") {
107104
renderScheduleByRoom(schedule);
@@ -120,12 +117,12 @@ function renderScheduleByRoom(schedule) {
120117
if (room.equipments.length > 0) {
121118
let equipments = room.equipments.sort().slice(0, Math.min(2, room.equipments.length));
122119
content += `<div class="d-flex">`;
123-
equipments.forEach(e => content += `<div><span class="badge text-bg-success m-1" style="background-color: ${pickColor(e)}">${e}</span></div>`);
120+
equipments.forEach(e => content += `<div><span class="badge text-bg-success m-1">${e}</span></div>`);
124121
content += "</div>";
125122
if (room.equipments.length > 2) {
126123
let equipments = room.equipments.sort().slice(2, Math.min(4, room.equipments.length));
127124
content += `<div class="d-flex">`;
128-
equipments.forEach(e => content += `<div><span class="badge text-bg-success m-1" style="background-color: ${pickColor(e)}">${e}</span></div>`);
125+
equipments.forEach(e => content += `<div><span class="badge text-bg-success m-1">${e}</span></div>`);
129126
content += "</div>";
130127
}
131128
}
@@ -148,6 +145,9 @@ function renderScheduleByRoom(schedule) {
148145
schedule.departments.flatMap(d => d.rooms).flatMap(r => r.beds).forEach(b => bedMap.set(b.id, b));
149146

150147
$.each(schedule.stays, (_, stay) => {
148+
const bgcolor = stay.patientGender === 'MALE' ? '#729FCF' : '#FCE94F';
149+
const color = stay.patientGender === 'MALE' ? 'white' : 'black';
150+
151151
if (stay.bed == null) {
152152
unassignedJobsCount++;
153153
const unassignedPatientElement = $(`<div class="card-body p-2"/>`)
@@ -177,8 +177,7 @@ function renderScheduleByRoom(schedule) {
177177
unassignedPatientElement.append($("<div />").prop("class", "d-flex justify-content-end").append($(`<small class="ms-2 mt-1 card-text text-muted"/>`)
178178
.text(stay.patientPreferredMaximumRoomCapacity)));
179179

180-
const color = stay.patientGender == 'MALE' ? '#729FCF' : '#FCE94F';
181-
unassignedPatients.append($(`<div class="col"/>`).append($(`<div class="card" style="background-color: ${color}"/>`).append(unassignedPatientElement)));
180+
unassignedPatients.append($(`<div class="col"/>`).append($(`<div class="card" style="background-color: ${bgcolor};color:${color}"/>`).append(unassignedPatientElement)));
182181
byRoomItemData.add({
183182
id: stay.id,
184183
group: stay.id,
@@ -209,15 +208,14 @@ function renderScheduleByRoom(schedule) {
209208
}
210209
byPatientElement.append($("<div />").prop("class", "d-flex justify-content-end").append($(`<small class="ms-2 mt-1 card-text text-muted"/>`)
211210
.text(stay.patientPreferredMaximumRoomCapacity)));
212-
const color = stay.patientGender == 'MALE' ? '#729FCF' : '#FCE94F';
213211

214212
byRoomItemData.add({
215213
id: stay.id,
216214
group: stay.bed,
217215
content: byPatientElement.html(),
218216
start: stay.arrivalDate,
219217
end: stay.departureDate,
220-
style: `background-color: ${color}`
218+
style: `background-color: ${bgcolor}; color: ${color}`
221219
});
222220
}
223221
});
@@ -407,51 +405,3 @@ function compareTimeslots(t1, t2) {
407405
}
408406
return diff;
409407
}
410-
411-
// TODO: move to the webjar
412-
function replaceQuickstartTimefoldAutoHeaderFooter() {
413-
const timefoldHeader = $("header#timefold-auto-header");
414-
if (timefoldHeader != null) {
415-
timefoldHeader.addClass("bg-black")
416-
timefoldHeader.append($(`<div class="container-fluid">
417-
<nav class="navbar sticky-top navbar-expand-lg navbar-dark shadow mb-3">
418-
<a class="navbar-brand" href="https://timefold.ai">
419-
<img src="/webjars/timefold/img/timefold-logo-horizontal-negative.svg" alt="Timefold logo" width="200">
420-
</a>
421-
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
422-
<span class="navbar-toggler-icon"></span>
423-
</button>
424-
<div class="collapse navbar-collapse" id="navbarNav">
425-
<ul class="nav nav-pills">
426-
<li class="nav-item active" id="navUIItem">
427-
<button class="nav-link active" id="navUI" data-bs-toggle="pill" data-bs-target="#demo" type="button">Demo UI</button>
428-
</li>
429-
<li class="nav-item" id="navRestItem">
430-
<button class="nav-link" id="navRest" data-bs-toggle="pill" data-bs-target="#rest" type="button">Guide</button>
431-
</li>
432-
<li class="nav-item" id="navOpenApiItem">
433-
<button class="nav-link" id="navOpenApi" data-bs-toggle="pill" data-bs-target="#openapi" type="button">REST API</button>
434-
</li>
435-
</ul>
436-
</div>
437-
</nav>
438-
</div>`));
439-
}
440-
441-
const timefoldFooter = $("footer#timefold-auto-footer");
442-
if (timefoldFooter != null) {
443-
timefoldFooter.append($(`<footer class="bg-black text-white-50">
444-
<div class="container">
445-
<div class="hstack gap-3 p-4">
446-
<div class="ms-auto"><a class="text-white" href="https://timefold.ai">Timefold</a></div>
447-
<div class="vr"></div>
448-
<div><a class="text-white" href="https://timefold.ai/docs">Documentation</a></div>
449-
<div class="vr"></div>
450-
<div><a class="text-white" href="https://github.com/TimefoldAI/timefold-quickstarts">Code</a></div>
451-
<div class="vr"></div>
452-
<div class="me-auto"><a class="text-white" href="https://timefold.ai/product/support/">Support</a></div>
453-
</div>
454-
</div>
455-
</footer>`));
456-
}
457-
}

java/bed-allocation/src/main/resources/META-INF/resources/index.html

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,18 @@
55
<title>Bed Allocation Scheduling - Timefold Solver on Quarkus</title>
66
<link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.min.css"/>
77
<link rel="stylesheet" href="/webjars/font-awesome/css/all.css"/>
8-
<link rel="stylesheet" href="/webjars/timefold/css/timefold-webui.css"/>
9-
<link rel="icon" href="/webjars/timefold/img/timefold-favicon.svg" type="image/svg+xml">
8+
<link rel="icon" href="https://timefold.ai/uploads/images/Brand-Assets/timefold-solver-logomark.png" type="image/png">
109
<style>
10+
:root {
11+
--ts-violet-1-rgb: #3E00FF;
12+
--ts-violet-2-rgb: #3423A6;
13+
14+
--bs-link-color: var(--ts-violet-1-rgb);
15+
--bs-link-hover-color: var(--ts-violet-2-rgb);
16+
}
17+
.nav-pills {
18+
--bs-nav-pills-link-active-bg: var(--ts-violet-1-rgb);
19+
}
1120
.vis-time-axis .vis-grid.vis-saturday,
1221
.vis-time-axis .vis-grid.vis-sunday {
1322
background: #D3D7CFFF;
@@ -18,16 +27,44 @@
1827
</style>
1928
</head>
2029
<body>
21-
<header id="timefold-auto-header">
22-
<!-- Filled in by app.js -->
30+
<header class="bg-white shadow">
31+
<div class="container-fluid">
32+
<nav class="navbar navbar-expand-lg mb-3">
33+
<a class="navbar-brand" style="width: 200px;overflow: hidden;">
34+
<img src="https://timefold.ai/uploads/images/Brand-Assets/timefold-solver-logo.png" alt="Timefold Solver logo" width="200px" style="margin-top:-20px;margin-bottom:-20px">
35+
</a>
36+
<a class="navbar-brand d-none d-lg-block">Bed Allocation Scheduling Quickstart</a>
37+
<div class="ms-auto">
38+
<div class="d-flex align-items-center" id="navbarNav">
39+
<ul class="nav nav-pills">
40+
<li class="nav-item active" id="navUIItem">
41+
<button class="nav-link active" id="navUI" data-bs-toggle="pill" data-bs-target="#demo" type="button">Demo UI</button>
42+
</li>
43+
<li class="nav-item" id="navRestItem">
44+
<button class="nav-link" id="navRest" data-bs-toggle="pill" data-bs-target="#rest" type="button">Guide</button>
45+
</li>
46+
<li class="nav-item" id="navOpenApiItem">
47+
<button class="nav-link" id="navOpenApi" data-bs-toggle="pill" data-bs-target="#openapi" type="button">REST API</button>
48+
</li>
49+
</ul>
50+
<div class="vr mx-2"></div>
51+
<div class="dropdown ">
52+
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
53+
Data
54+
</button>
55+
<div id="testDataButton" class="dropdown-menu" aria-labelledby="dropdownMenuButton"></div>
56+
</div>
57+
</div>
58+
</div>
59+
</nav>
60+
</div>
2361
</header>
2462
<div class="tab-content">
2563
<div id="demo" class="tab-pane fade show active container">
2664
<div class="sticky-top d-flex justify-content-center align-items-center" aria-live="polite" aria-atomic="true">
2765
<div id="notificationPanel" style="position: absolute; top: .5rem;"></div>
2866
</div>
29-
<h1>Bed Allocation Scheduling Solver</h1>
30-
<p>Generate the optimal schedule for your bed scheduling.</p>
67+
<blockquote>Generate the optimal schedule for your bed scheduling. <span id="info" class="fw-bold"></span></blockquote>
3168

3269
<div class="mb-2">
3370
<button id="solveButton" type="button" class="btn btn-success">
@@ -40,14 +77,6 @@ <h1>Bed Allocation Scheduling Solver</h1>
4077
<button id="analyzeButton" type="button" class="ms-2 btn btn-secondary">
4178
<span class="fas fa-question"></span>
4279
</button>
43-
44-
<div class="btn-group dropstart float-end ms-2">
45-
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton"
46-
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
47-
Data
48-
</button>
49-
<div id="testDataButton" class="dropdown-menu" aria-labelledby="dropdownMenuButton"></div>
50-
</div>
5180
<div class="float-end">
5281
<ul class="nav nav-pills" role="tablist">
5382
<li class="nav-item" role="presentation">
@@ -64,7 +93,7 @@ <h1>Bed Allocation Scheduling Solver</h1>
6493
</div>
6594
</div>
6695

67-
<h2>Unassigned stays</h2>
96+
<h2 class="my-4">Unassigned stays</h2>
6897
<div id="unassignedPatients" class="row row-cols-3 g-3 mb-4"></div>
6998
</div>
7099

@@ -118,14 +147,25 @@ <h3>6. Terminate solving early</h3>
118147
</div>
119148

120149
<div id="openapi" class="tab-pane fade container-fluid">
121-
<h1>REST API Reference</h1>
122150
<div class="ratio ratio-1x1">
123151
<!-- "scrolling" attribute is obsolete, but e.g. Chrome does not support "overflow:hidden" -->
124152
<iframe src="/q/swagger-ui" style="overflow:hidden;" scrolling="no"></iframe>
125153
</div>
126154
</div>
127155
</div>
128-
<footer id="timefold-auto-footer"></footer>
156+
<footer class="bg-white">
157+
<div class="container">
158+
<div class="hstack gap-3 p-4">
159+
<div class="ms-auto"><a class="text-black" href="https://solver.timefold.ai">Website</a></div>
160+
<div class="vr"></div>
161+
<div><a class="text-black" href="https://docs.timefold.ai/timefold-solver/latest/">Documentation</a></div>
162+
<div class="vr"></div>
163+
<div><a class="text-black" href="https://github.com/TimefoldAI/timefold-quickstarts">Code</a></div>
164+
<div class="vr"></div>
165+
<div class="me-auto"><a class="text-black" href="https://github.com/TimefoldAI/timefold-solver/discussions">GitHub Discussions</a></div>
166+
</div>
167+
</div>
168+
</footer>
129169
<div class="modal fadebd-example-modal-lg" id="scoreAnalysisModal" tabindex="-1"
130170
aria-labelledby="scoreAnalysisModalLabel" aria-hidden="true">
131171
<div class="modal-dialog modal-lg modal-dialog-scrollable">
@@ -171,7 +211,6 @@ <h5 class="modal-title" id="uploadModalLabel">Import bed plan from local file</h
171211
<script src="/webjars/jquery/jquery.min.js"></script>
172212
<script src="/webjars/js-joda/dist/js-joda.min.js"></script>
173213
<script src="/webjars/js-joda__locale_en-us/dist/index.js"></script>
174-
<script src="/webjars/timefold/js/timefold-webui.js"></script>
175214
<script src="https://cdn.jsdelivr.net/npm/[email protected]/standalone/umd/vis-timeline-graph2d.min.js"
176215
integrity="sha256-Jy2+UO7rZ2Dgik50z3XrrNpnc5+2PAx9MhL2CicodME=" crossorigin="anonymous"></script>
177216
<script src="/app.js"></script>

java/conference-scheduling/pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,6 @@
9898
<groupId>io.quarkus</groupId>
9999
<artifactId>quarkus-web-dependency-locator</artifactId>
100100
</dependency>
101-
<dependency>
102-
<groupId>ai.timefold.solver</groupId>
103-
<artifactId>timefold-solver-webui</artifactId>
104-
<scope>runtime</scope>
105-
</dependency>
106101
<dependency>
107102
<groupId>org.webjars</groupId>
108103
<artifactId>bootstrap</artifactId>

0 commit comments

Comments
 (0)