diff --git a/includes/controller/public_dashboard_controller.php b/includes/controller/public_dashboard_controller.php
index dc81ba2c..bf909bed 100644
--- a/includes/controller/public_dashboard_controller.php
+++ b/includes/controller/public_dashboard_controller.php
@@ -1,70 +1,22 @@
stats_angels_needed_three_hours(),
+ 'needed-night' => stats_angels_needed_for_nightshifts(),
+ 'angels-working' => stats_currently_working(),
+ 'hours-to-work' => stats_hours_to_work()
+ ];
- $now = time();
- $in3hours = $now + 3 * 60 * 60;
- $result = Db::selectOne("
- SELECT SUM(
- (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
- - (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
- ) as `count`
- FROM `Shifts`
- WHERE ((`end` > ? AND `end` < ?) OR (`start` > ? AND `start` < ?))", [
- $now,
- $in3hours,
- $now,
- $in3hours
- ]);
- $stats['needed-3-hours'] = $result['count'];
-
- $night_start = parse_date('Y-m-d H:i', date('Y-m-d', time() + 12 * 60 * 60) . ' 02:00');
- $night_end = $night_start + 6 * 60 * 60;
- $result = Db::selectOne("
- SELECT SUM(
- (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
- - (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
- ) as `count`
- FROM `Shifts`
- WHERE ((`end` > ? AND `end` < ?) OR (`start` > ? AND `start` < ?))", [
- $night_start,
- $night_end,
- $night_start,
- $night_end
- ]);
- $stats['needed-night'] = $result['count'];
-
- $result = Db::selectOne("
- SELECT SUM(
- (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
- ) as `count`
- FROM `Shifts`
- WHERE (`end` >= ? AND `start` <= ?)", [
- time(),
- time()
- ]);
- $stats['angels-working'] = $result['count'];
-
- $result = Db::selectOne("
- SELECT ROUND(SUM(
- (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
- * (`Shifts`.`end` - `Shifts`.`start`)/3600
- )) as `count`
- FROM `Shifts`
- WHERE `end` >= ?", [
- time()
- ]);
- $stats['hours-to-work'] = $result['count'];
+ $free_shifts = Shifts_free(time(), time() + 12 * 60 * 60);
return [
- 'Engelsystem Public Dashboard',
- public_dashboard_view($stats)
+ _('Engelsystem Public Dashboard'),
+ public_dashboard_view($stats, $free_shifts)
];
}
?>
\ No newline at end of file
diff --git a/includes/includes.php b/includes/includes.php
index c533cfe8..b6bd41cf 100644
--- a/includes/includes.php
+++ b/includes/includes.php
@@ -22,6 +22,7 @@ $includeFiles = [
__DIR__ . '/../includes/model/ShiftsFilter.php',
__DIR__ . '/../includes/model/ShiftSignupState.php',
__DIR__ . '/../includes/model/ShiftTypes_model.php',
+ __DIR__ . '/../includes/model/Stats.php',
__DIR__ . '/../includes/model/UserAngelTypes_model.php',
__DIR__ . '/../includes/model/UserDriverLicenses_model.php',
__DIR__ . '/../includes/model/UserGroups_model.php',
diff --git a/includes/model/Shifts_model.php b/includes/model/Shifts_model.php
index 1fc7fe89..b0d82a5b 100644
--- a/includes/model/Shifts_model.php
+++ b/includes/model/Shifts_model.php
@@ -26,6 +26,29 @@ function Shifts_by_angeltype($angeltype) {
', [$angeltype['id'], $angeltype['id']]);
}
+/**
+ * Returns every shift with needed angels in the given time range.
+ */
+function Shifts_free($start, $end)
+{
+ $shifts = Db::select("
+ SELECT *
+ FROM `Shifts`
+ WHERE (`end` > ? AND `start` < ?)
+ AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
+ > (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
+ ORDER BY `start`
+ ", [
+ $start,
+ $end
+ ]);
+ $free_shifts = [];
+ foreach ($shifts as $shift) {
+ $free_shifts[] = Shift($shift['SID']);
+ }
+ return $free_shifts;
+}
+
/**
* Returns all shifts with a PSID (from frab import)
*/
diff --git a/includes/model/Stats.php b/includes/model/Stats.php
new file mode 100644
index 00000000..5618cdc6
--- /dev/null
+++ b/includes/model/Stats.php
@@ -0,0 +1,94 @@
+= ? AND `start` <= ?)", [
+ time(),
+ time()
+ ]);
+ if (empty($result['count'])) {
+ return '-';
+ }
+ return $result['count'];
+}
+
+/**
+ * Return the number of hours still to work.
+ */
+function stats_hours_to_work()
+{
+ $result = Db::selectOne("
+ SELECT ROUND(SUM(
+ (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
+ * (`Shifts`.`end` - `Shifts`.`start`)/3600
+ )) as `count`
+ FROM `Shifts`
+ WHERE `end` >= ?", [
+ time()
+ ]);
+ if (empty($result['count'])) {
+ return '-';
+ }
+ return $result['count'];
+}
+
+/**
+ * Returns the number of needed angels in the next 3 hours
+ */
+function stats_angels_needed_three_hours()
+{
+ $now = time();
+ $in3hours = $now + 3 * 60 * 60;
+ $result = Db::selectOne("
+ SELECT SUM(
+ (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
+ - (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
+ ) as `count`
+ FROM `Shifts`
+ WHERE ((`end` > ? AND `end` < ?) OR (`start` > ? AND `start` < ?))", [
+ $now,
+ $in3hours,
+ $now,
+ $in3hours
+ ]);
+ if (empty($result['count'])) {
+ return '-';
+ }
+ return $result['count'];
+}
+
+/**
+ * Returns the number of needed angels for nightshifts (between 2 and 8)
+ */
+function stats_angels_needed_for_nightshifts()
+{
+ $night_start = parse_date('Y-m-d H:i', date('Y-m-d', time() + 12 * 60 * 60) . ' 02:00');
+ $night_end = $night_start + 6 * 60 * 60;
+ $result = Db::selectOne("
+ SELECT SUM(
+ (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
+ - (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
+ ) as `count`
+ FROM `Shifts`
+ WHERE ((`end` > ? AND `end` < ?) OR (`start` > ? AND `start` < ?))", [
+ $night_start,
+ $night_end,
+ $night_start,
+ $night_end
+ ]);
+ if (empty($result['count'])) {
+ return '-';
+ }
+ return $result['count'];
+}
+
+?>
\ No newline at end of file
diff --git a/includes/sys_template.php b/includes/sys_template.php
index 819b7d1c..b5846e9b 100644
--- a/includes/sys_template.php
+++ b/includes/sys_template.php
@@ -1,5 +1,30 @@
0, and success if number == 0.
+ *
+ * @param string $label
+ * @param string $number
+ * @param string $style default, warning, danger or success. Optional.
+ */
+function stats($label, $number, $style = null)
+{
+ if(empty($style)) {
+ if($number > 0) {
+ $style = 'danger';
+ } else {
+ $style = 'success';
+ }
+ }
+ return div('stats stats-' . $style, [
+ $label,
+ div('number', [
+ $number
+ ])
+ ]);
+}
+
/**
* Renders tabs from the array. Array key is tab name, array value is tab content.
*
diff --git a/includes/view/PublicDashboard_view.php b/includes/view/PublicDashboard_view.php
index 6fa40ed4..7b15c7dd 100644
--- a/includes/view/PublicDashboard_view.php
+++ b/includes/view/PublicDashboard_view.php
@@ -3,29 +3,62 @@
/**
* Public dashboard (formerly known as angel news hub)
*/
-function public_dashboard_view($stats)
+function public_dashboard_view($stats, $free_shifts)
{
+ $shift_panels = [];
+ foreach ($free_shifts as $shift) {
+ $shift_panels[] = public_dashborad_shift_render($shift);
+ }
return page([
- div('first', [
- div('col-xs-3 text-center', [
- _('Angels needed in the next 3 hrs'),
- heading($stats['needed-3-hours'], 1)
- ]),
- div('col-xs-3 text-center', [
- _('Angels needed for nightshifts'),
- heading($stats['needed-night'], 1)
- ]),
- div('col-xs-3 text-center', [
- _('Angels currently working'),
- heading($stats['angels-working'], 1)
- ]),
- div('col-xs-3 text-center', [
- _('Hours to be worked'),
- heading($stats['hours-to-work'], 1)
- ]),
+ div('first container-fluid', [
+ stats(_('Angels needed in the next 3 hrs'), $stats['needed-3-hours']),
+ stats(_('Angels needed for nightshifts'), $stats['needed-night']),
+ stats(_('Angels currently working'), $stats['angels-working'], 'default'),
+ stats(_('Hours to be worked'), $stats['hours-to-work'], 'default'),
''
+ ]),
+ div('container-fluid first', [
+ heading(_('Needed angels:'), 1),
+ join($shift_panels)
])
]);
}
+/**
+ * Renders a single shift panel for a dashboard shift with needed angels
+ */
+function public_dashborad_shift_render($shift)
+{
+ $style = 'default';
+ if (time() + 3 * 60 * 60 > $shift['start']) {
+ $style = 'warning';
+ }
+ if (time() > $shift['start']) {
+ $style = 'danger';
+ }
+
+ $panel_body = glyph('time') . date('H:i', $shift['start']) . ' - ' . date('H:i', $shift['end']);
+
+ $panel_body .= '
' . glyph('tasks') . ShiftType($shift['shifttype_id'])['name'];
+ if (! empty($shift['title'])) {
+ $panel_body .= ' (' . $shift['title'] . ')';
+ }
+
+ $panel_body .= '
' . glyph('map-marker') . Room($shift['RID'])['Name'];
+
+ foreach ($shift['NeedAngels'] as $needed_angels) {
+ $need = $needed_angels['count'] - $needed_angels['taken'];
+ if ($need > 0) {
+ $panel_body .= '
' . glyph('user') . $need . ' × ' . AngelType($needed_angels['TID'])['name'];
+ }
+ }
+
+ $panel_body = '' . $panel_body . '';
+
+ return div('panel panel-' . $style . ' col-xs-3', [
+ div('panel-body', [
+ heading($panel_body, 4)
+ ])
+ ]);
+}
?>
\ No newline at end of file
diff --git a/public/css/theme0.css b/public/css/theme0.css
index 050f689e..35a35f9e 100644
--- a/public/css/theme0.css
+++ b/public/css/theme0.css
@@ -6736,6 +6736,28 @@ body {
line-height: 30px;
margin: 0px;
}
+.stats {
+ position: relative;
+ float: left;
+ width: 25%;
+ min-height: 1px;
+ padding-left: 15px;
+ padding-right: 15px;
+ text-align: center;
+}
+.stats .number {
+ font-size: 80px;
+ font-weight: 200;
+}
+.stats-danger {
+ color: #d9534f;
+}
+.stats-warning {
+ color: #f0ad4e;
+}
+.stats-success {
+ color: #5cb85c;
+}
.panel-primary .panel-heading a {
color: #ffffff;
}
diff --git a/public/css/theme1.css b/public/css/theme1.css
index 06ce9907..06979c3e 100644
--- a/public/css/theme1.css
+++ b/public/css/theme1.css
@@ -6759,6 +6759,28 @@ body {
line-height: 30px;
margin: 0px;
}
+.stats {
+ position: relative;
+ float: left;
+ width: 25%;
+ min-height: 1px;
+ padding-left: 15px;
+ padding-right: 15px;
+ text-align: center;
+}
+.stats .number {
+ font-size: 80px;
+ font-weight: 200;
+}
+.stats-danger {
+ color: #d9534f;
+}
+.stats-warning {
+ color: #f0ad4e;
+}
+.stats-success {
+ color: #5cb85c;
+}
.panel-primary .panel-heading a {
color: #ffffff;
}
diff --git a/public/css/theme2.css b/public/css/theme2.css
index 9ca9babb..ad7770a4 100644
--- a/public/css/theme2.css
+++ b/public/css/theme2.css
@@ -6736,6 +6736,28 @@ body {
line-height: 30px;
margin: 0px;
}
+.stats {
+ position: relative;
+ float: left;
+ width: 25%;
+ min-height: 1px;
+ padding-left: 15px;
+ padding-right: 15px;
+ text-align: center;
+}
+.stats .number {
+ font-size: 80px;
+ font-weight: 200;
+}
+.stats-danger {
+ color: #7f528b;
+}
+.stats-warning {
+ color: #e3a14d;
+}
+.stats-success {
+ color: #7b9c41;
+}
.panel-primary .panel-heading a {
color: #ffffff;
}
diff --git a/public/css/theme3.css b/public/css/theme3.css
index abb90f23..f5dcce0e 100644
--- a/public/css/theme3.css
+++ b/public/css/theme3.css
@@ -6745,6 +6745,28 @@ body {
line-height: 30px;
margin: 0px;
}
+.stats {
+ position: relative;
+ float: left;
+ width: 25%;
+ min-height: 1px;
+ padding-left: 15px;
+ padding-right: 15px;
+ text-align: center;
+}
+.stats .number {
+ font-size: 80px;
+ font-weight: 200;
+}
+.stats-danger {
+ color: #da1639;
+}
+.stats-warning {
+ color: #dad216;
+}
+.stats-success {
+ color: #39ab50;
+}
.panel-primary .panel-heading a {
color: #ffffff;
}
diff --git a/public/css/theme4.css b/public/css/theme4.css
index 24771afd..554cf166 100644
--- a/public/css/theme4.css
+++ b/public/css/theme4.css
@@ -6759,6 +6759,28 @@ body {
line-height: 30px;
margin: 0px;
}
+.stats {
+ position: relative;
+ float: left;
+ width: 25%;
+ min-height: 1px;
+ padding-left: 15px;
+ padding-right: 15px;
+ text-align: center;
+}
+.stats .number {
+ font-size: 80px;
+ font-weight: 200;
+}
+.stats-danger {
+ color: #ff263c;
+}
+.stats-warning {
+ color: #fff45f;
+}
+.stats-success {
+ color: #8dc123;
+}
.panel-primary .panel-heading a {
color: #ffffff;
}
diff --git a/public/css/theme5.css b/public/css/theme5.css
index 98922778..f3c345a2 100644
--- a/public/css/theme5.css
+++ b/public/css/theme5.css
@@ -6739,6 +6739,28 @@ body {
line-height: 30px;
margin: 0px;
}
+.stats {
+ position: relative;
+ float: left;
+ width: 25%;
+ min-height: 1px;
+ padding-left: 15px;
+ padding-right: 15px;
+ text-align: center;
+}
+.stats .number {
+ font-size: 80px;
+ font-weight: 200;
+}
+.stats-danger {
+ color: #ff6600;
+}
+.stats-warning {
+ color: #ffff33;
+}
+.stats-success {
+ color: #99cc00;
+}
.panel-primary .panel-heading a {
color: #ffffff;
}
diff --git a/public/css/theme6.css b/public/css/theme6.css
index c30379fb..e8601992 100644
--- a/public/css/theme6.css
+++ b/public/css/theme6.css
@@ -6762,6 +6762,28 @@ body {
line-height: 30px;
margin: 0px;
}
+.stats {
+ position: relative;
+ float: left;
+ width: 25%;
+ min-height: 1px;
+ padding-left: 15px;
+ padding-right: 15px;
+ text-align: center;
+}
+.stats .number {
+ font-size: 80px;
+ font-weight: 200;
+}
+.stats-danger {
+ color: #ff6600;
+}
+.stats-warning {
+ color: #ffff33;
+}
+.stats-success {
+ color: #99cc00;
+}
.panel-primary .panel-heading a {
color: #ffffff;
}
diff --git a/themes/base.less b/themes/base.less
index 677f3200..35473860 100644
--- a/themes/base.less
+++ b/themes/base.less
@@ -21,6 +21,28 @@ body {
margin: 0px;
}
+.stats {
+ .make-xs-column(3);
+ text-align: center;
+
+ .number {
+ font-size: 80px;
+ font-weight: 200;
+ }
+}
+
+.stats-danger {
+ color: @brand-danger;
+}
+
+.stats-warning {
+ color: @brand-warning;
+}
+
+.stats-success {
+ color: @brand-success;
+}
+
.panel-primary .panel-heading a {
color: @panel-primary-text;
}