diff --git a/package-lock.json b/package-lock.json index 8b2e3012..6966958e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,10 @@ "@alpinejs/collapse": "^3.13.3", "@alpinejs/focus": "^3.13.3", "@alpinejs/ui": "^3.13.3-beta.4", - "alpinejs": "^3.13.3" + "alpinejs": "^3.13.3", + "chart.js": "^4.4.2", + "chartjs-adapter-moment": "^1.0.1", + "moment": "^2.30.1" }, "devDependencies": { "@alpinejs/anchor": "^3.13.3", @@ -304,6 +307,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1058,6 +1066,26 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chart.js": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.2.tgz", + "integrity": "sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/chartjs-adapter-moment": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz", + "integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==", + "peerDependencies": { + "chart.js": ">=3.0.0", + "moment": "^2.10.2" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -2590,6 +2618,14 @@ "node": "*" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4151,6 +4187,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4776,6 +4817,20 @@ "supports-color": "^7.1.0" } }, + "chart.js": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.2.tgz", + "integrity": "sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==", + "requires": { + "@kurkle/color": "^0.3.0" + } + }, + "chartjs-adapter-moment": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz", + "integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==", + "requires": {} + }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -5831,6 +5886,11 @@ "brace-expansion": "^1.1.7" } }, + "moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/package.json b/package.json index 5e227ea2..61a028e5 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,9 @@ "@alpinejs/collapse": "^3.13.3", "@alpinejs/focus": "^3.13.3", "@alpinejs/ui": "^3.13.3-beta.4", - "alpinejs": "^3.13.3" + "alpinejs": "^3.13.3", + "chart.js": "^4.4.2", + "chartjs-adapter-moment": "^1.0.1", + "moment": "^2.30.1" } } diff --git a/resources/js/cachet.js b/resources/js/cachet.js index 50005015..31e976f5 100644 --- a/resources/js/cachet.js +++ b/resources/js/cachet.js @@ -1,3 +1,7 @@ +import 'moment'; +import Chart from 'chart.js/auto' +import 'chartjs-adapter-moment'; + import Alpine from 'alpinejs' import Anchor from '@alpinejs/anchor' @@ -5,9 +9,13 @@ import Collapse from '@alpinejs/collapse' import Focus from '@alpinejs/focus' import Ui from '@alpinejs/ui' +Chart.defaults.color = '#fff'; +window.Chart = Chart + Alpine.plugin(Anchor) Alpine.plugin(Collapse) Alpine.plugin(Focus) Alpine.plugin(Ui) -Alpine.start() +window.Alpine = Alpine +Alpine.start() \ No newline at end of file diff --git a/resources/views/components/metric.blade.php b/resources/views/components/metric.blade.php new file mode 100644 index 00000000..1e5ea548 --- /dev/null +++ b/resources/views/components/metric.blade.php @@ -0,0 +1,48 @@ +@props([ +'metric', +]) + +@use('\Cachet\Enums\MetricViewEnum') + +
+
+
+
{{ $metric->name }}
+ +
+ +
+ +

{{ $metric->description }}

+
+
+ + + +
+ +
+
+ + diff --git a/resources/views/components/metrics.blade.php b/resources/views/components/metrics.blade.php new file mode 100644 index 00000000..be47d812 --- /dev/null +++ b/resources/views/components/metrics.blade.php @@ -0,0 +1,56 @@ + + +
+ @foreach($metrics as $metric) + + + @endforeach +
\ No newline at end of file diff --git a/resources/views/status-page/index.blade.php b/resources/views/status-page/index.blade.php index 5fef4e98..ca96ff1f 100644 --- a/resources/views/status-page/index.blade.php +++ b/resources/views/status-page/index.blade.php @@ -14,6 +14,8 @@ @endforeach + + @if($schedules->isNotEmpty()) @endif diff --git a/src/View/Components/Metrics.php b/src/View/Components/Metrics.php new file mode 100644 index 00000000..d12b52fc --- /dev/null +++ b/src/View/Components/Metrics.php @@ -0,0 +1,53 @@ +subDays(30); + + $metrics = $this->metrics($startDate); + + // Convert each metric point to Chart.js format (x, y) + $metrics->each(function ($metric) { + $metric->metricPoints->transform(fn ($point) => [ + 'x' => $point->created_at->toIso8601String(), + 'y' => $point->value, + ]); + }); + + return view('cachet::components.metrics', [ + 'metrics' => $metrics + ]); + } + + /** + * Fetch the available metrics and their points. + */ + private function metrics(Carbon $startDate): Collection + { + return Metric::query() + ->with([ + 'metricPoints' => fn ($query) => $query->orderBy('created_at'), + ]) + ->where('visible', '>=', !auth()->check()) + ->whereHas('metricPoints', fn (Builder $query) => $query->where('created_at', '>=', $startDate)) + ->orderBy('places', 'asc') + ->get(); + } +}