From fdcc9350fdab7ef7f74609c7860806cfe3f63ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=2E=20Nagy=20Gerg=C5=91?= Date: Fri, 22 Dec 2023 22:28:27 +0100 Subject: [PATCH] value metric --- resources/views/resources/index.blade.php | 4 +- .../views/widgets/pending-widget.blade.php | 8 ++ resources/views/widgets/trend.blade.php | 34 ++++-- resources/views/widgets/value.blade.php | 37 ++++++ resources/views/widgets/widget.blade.php | 4 +- src/Actions/Actions.php | 2 +- src/Conversion/Image.php | 4 +- src/Fields/BelongsToMany.php | 2 +- src/Fields/Boolean.php | 2 +- src/Fields/Color.php | 2 +- src/Fields/Date.php | 4 +- src/Fields/Dropdown.php | 2 +- src/Fields/Editor.php | 4 +- src/Fields/Email.php | 2 +- src/Fields/Field.php | 8 +- src/Fields/Fields.php | 2 +- src/Fields/Fieldset.php | 2 +- src/Fields/File.php | 2 +- src/Fields/HasMany.php | 2 +- src/Fields/Hidden.php | 2 +- src/Fields/Meta.php | 32 ++--- src/Fields/Number.php | 2 +- src/Fields/Range.php | 2 +- src/Fields/Relation.php | 2 +- src/Fields/Slug.php | 2 +- src/Fields/Text.php | 2 +- src/Fields/URL.php | 2 +- src/Http/Controllers/MorphToController.php | 2 +- src/Interfaces/Models/Medium.php | 6 +- src/Models/Medium.php | 8 +- src/Navigation/HasItems.php | 2 +- src/RootServiceProvider.php | 4 + src/View/Components/Modal.php | 2 +- src/Widgets/Metric.php | 62 ++++++++++ src/Widgets/Value.php | 113 ++++++++++++++++++ src/Widgets/Widget.php | 9 ++ src/Widgets/Widgets.php | 2 +- 37 files changed, 316 insertions(+), 67 deletions(-) create mode 100644 resources/views/widgets/pending-widget.blade.php create mode 100644 resources/views/widgets/value.blade.php create mode 100644 src/Widgets/Value.php diff --git a/resources/views/resources/index.blade.php b/resources/views/resources/index.blade.php index 69101599e..8a7eb6eea 100644 --- a/resources/views/resources/index.blade.php +++ b/resources/views/resources/index.blade.php @@ -18,7 +18,9 @@ @if(! empty($widgets))
@foreach($widgets as $widget) - @include($widget['template'], $widget) + + @include('root::widgets.pending-widget', $widget) + @endforeach
@endif diff --git a/resources/views/widgets/pending-widget.blade.php b/resources/views/widgets/pending-widget.blade.php new file mode 100644 index 000000000..15c171c64 --- /dev/null +++ b/resources/views/widgets/pending-widget.blade.php @@ -0,0 +1,8 @@ + +
+
+

{{ $name }}

+

{{ __('Loading') }}...

+
+
+
diff --git a/resources/views/widgets/trend.blade.php b/resources/views/widgets/trend.blade.php index e499053ea..66ecfd944 100644 --- a/resources/views/widgets/trend.blade.php +++ b/resources/views/widgets/trend.blade.php @@ -1,13 +1,25 @@ -
-
-

{{ $name }}

-

65

- + diff --git a/resources/views/widgets/value.blade.php b/resources/views/widgets/value.blade.php new file mode 100644 index 000000000..ac285def0 --- /dev/null +++ b/resources/views/widgets/value.blade.php @@ -0,0 +1,37 @@ + +
+ @if(! is_null($icon)) +
+ +
+ @endif +
+

+ {{ $name }} +
+ + +
+

+

{{ $data['current'] }}

+ @if($data['trend'] < 0) + + @elseif($data['trend'] > 0) + + @endif +
+
+
+
+
+
diff --git a/resources/views/widgets/widget.blade.php b/resources/views/widgets/widget.blade.php index dead391ff..22c12fa93 100644 --- a/resources/views/widgets/widget.blade.php +++ b/resources/views/widgets/widget.blade.php @@ -1 +1,3 @@ -
+ +
+
diff --git a/src/Actions/Actions.php b/src/Actions/Actions.php index c74b1ac62..f40e4c8bf 100644 --- a/src/Actions/Actions.php +++ b/src/Actions/Actions.php @@ -26,7 +26,7 @@ public function register(array|Action $actions): static /** * Filter the actions that are available for the current request and model. */ - public function authorized(Request $request, Model $model = null): static + public function authorized(Request $request, ?Model $model = null): static { return $this->filter->authorized($request, $model)->values(); } diff --git a/src/Conversion/Image.php b/src/Conversion/Image.php index c1e15e160..27afc11c9 100644 --- a/src/Conversion/Image.php +++ b/src/Conversion/Image.php @@ -96,7 +96,7 @@ public function setQuality(int $quality): static * * @return $this */ - public function crop(int $width = null, int $height = null): static + public function crop(?int $width = null, ?int $height = null): static { $this->resize($width, $height, true); @@ -106,7 +106,7 @@ public function crop(int $width = null, int $height = null): static /** * Resize the image. */ - public function resize(int $width = null, int $height = null, bool $crop = false): static + public function resize(?int $width = null, ?int $height = null, bool $crop = false): static { $x = $y = 0; [$originalWidth, $originalHeight] = getimagesize($this->medium->getAbsolutePath()); diff --git a/src/Fields/BelongsToMany.php b/src/Fields/BelongsToMany.php index 7fe3f61c8..44e50faa9 100644 --- a/src/Fields/BelongsToMany.php +++ b/src/Fields/BelongsToMany.php @@ -33,7 +33,7 @@ class BelongsToMany extends Relation /** * Create a new relation field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null, Closure|string $relation = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null, Closure|string|null $relation = null) { parent::__construct($label, $modelAttribute, $relation); diff --git a/src/Fields/Boolean.php b/src/Fields/Boolean.php index 8cca35f6b..367edf7f0 100644 --- a/src/Fields/Boolean.php +++ b/src/Fields/Boolean.php @@ -16,7 +16,7 @@ class Boolean extends Field /** * Create a new file field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null) { parent::__construct($label, $modelAttribute); diff --git a/src/Fields/Color.php b/src/Fields/Color.php index f62382e32..22e1050ef 100644 --- a/src/Fields/Color.php +++ b/src/Fields/Color.php @@ -9,7 +9,7 @@ class Color extends Field /** * Create a new field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null) { parent::__construct($label, $modelAttribute); diff --git a/src/Fields/Date.php b/src/Fields/Date.php index cef661c07..4a2b778eb 100644 --- a/src/Fields/Date.php +++ b/src/Fields/Date.php @@ -25,7 +25,7 @@ class Date extends Field /** * Create a new field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null) { parent::__construct($label, $modelAttribute); @@ -73,7 +73,7 @@ public function withTime(bool $value = true): static /** * Set the timezone. */ - public function timezone(string $value = null): static + public function timezone(?string $value = null): static { $this->timezone = $value; diff --git a/src/Fields/Dropdown.php b/src/Fields/Dropdown.php index d72f4d842..68ff73953 100644 --- a/src/Fields/Dropdown.php +++ b/src/Fields/Dropdown.php @@ -18,7 +18,7 @@ class Dropdown extends Select /** * Create a new field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null) { parent::__construct($label, $modelAttribute); diff --git a/src/Fields/Editor.php b/src/Fields/Editor.php index 5522bac95..3c8b1be55 100644 --- a/src/Fields/Editor.php +++ b/src/Fields/Editor.php @@ -37,7 +37,7 @@ class Editor extends Field /** * Create a new field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null) { parent::__construct($label, $modelAttribute); @@ -83,7 +83,7 @@ public function getConfig(): array /** * Configure the media field. */ - public function withMedia(Closure $callback = null): static + public function withMedia(?Closure $callback = null): static { if (is_null($this->fields)) { $this->fields = new Fields(); diff --git a/src/Fields/Email.php b/src/Fields/Email.php index d2ea40536..01ef93ff0 100644 --- a/src/Fields/Email.php +++ b/src/Fields/Email.php @@ -9,7 +9,7 @@ class Email extends Text /** * Create a new field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null) { parent::__construct($label, $modelAttribute); diff --git a/src/Fields/Field.php b/src/Fields/Field.php index 314ca17e5..69e62a203 100644 --- a/src/Fields/Field.php +++ b/src/Fields/Field.php @@ -123,7 +123,7 @@ abstract class Field implements Arrayable, JsonSerializable /** * Create a new field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null) { $this->computed = $modelAttribute instanceof Closure; @@ -235,7 +235,7 @@ public function placeholder(string|Closure $value): static /** * Set the help attribute. */ - public function help(string $value = null): static + public function help(?string $value = null): static { $this->help = $value; @@ -329,7 +329,7 @@ public function resolveSearchQuery(Request $request, Builder $query, mixed $valu /** * Set the value resolver. */ - public function value(Closure $callback = null): static + public function value(?Closure $callback = null): static { $this->valueResolver = $callback; @@ -391,7 +391,7 @@ public function getValue(Model $model): mixed /** * Set the format resolver. */ - public function format(Closure $callback = null): static + public function format(?Closure $callback = null): static { $this->formatResolver = $callback; diff --git a/src/Fields/Fields.php b/src/Fields/Fields.php index c3cd6e9e3..2c6e9912d 100644 --- a/src/Fields/Fields.php +++ b/src/Fields/Fields.php @@ -38,7 +38,7 @@ public function persist(Request $request, Model $model): void /** * Filter the fields that are available for the current request and model. */ - public function authorized(Request $request, Model $model = null): static + public function authorized(Request $request, ?Model $model = null): static { return $this->filter->authorized($request, $model)->values(); } diff --git a/src/Fields/Fieldset.php b/src/Fields/Fieldset.php index 215cb23d2..f39264e14 100644 --- a/src/Fields/Fieldset.php +++ b/src/Fields/Fieldset.php @@ -22,7 +22,7 @@ class Fieldset extends Field /** * Create a new field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null) { parent::__construct($label, $modelAttribute); diff --git a/src/Fields/File.php b/src/Fields/File.php index b52d20a65..a29b455e3 100644 --- a/src/Fields/File.php +++ b/src/Fields/File.php @@ -44,7 +44,7 @@ class File extends MorphToMany /** * Create a new field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null, Closure|string $relation = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null, Closure|string|null $relation = null) { parent::__construct($label, $modelAttribute, $relation); diff --git a/src/Fields/HasMany.php b/src/Fields/HasMany.php index 7a0186d73..964b06ea4 100644 --- a/src/Fields/HasMany.php +++ b/src/Fields/HasMany.php @@ -11,7 +11,7 @@ class HasMany extends HasOneOrMany /** * Create a new relation field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null, Closure|string $relation = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null, Closure|string|null $relation = null) { parent::__construct($label, $modelAttribute, $relation); diff --git a/src/Fields/Hidden.php b/src/Fields/Hidden.php index 5e454f5ad..40518d029 100644 --- a/src/Fields/Hidden.php +++ b/src/Fields/Hidden.php @@ -14,7 +14,7 @@ class Hidden extends Field /** * Create a new field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null) { parent::__construct($label, $modelAttribute); diff --git a/src/Fields/Meta.php b/src/Fields/Meta.php index c4f2be584..05378eb8e 100644 --- a/src/Fields/Meta.php +++ b/src/Fields/Meta.php @@ -18,7 +18,7 @@ class Meta extends MorphOne /** * Create a new relation field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null, Closure|string $relation = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null, Closure|string|null $relation = null) { $relation ??= function (Model $model): EloquentRelation { $related = $model->metaData()->make(); @@ -51,7 +51,7 @@ public function getRelationName(): string /** * Set the field class. */ - public function as(string $field, Closure $callback = null): static + public function as(string $field, ?Closure $callback = null): static { $this->field = new $field($this->label, $this->getModelAttribute()); @@ -69,7 +69,7 @@ public function as(string $field, Closure $callback = null): static /** * Set the meta field as boolean. */ - public function asBoolean(Closure $callback = null): static + public function asBoolean(?Closure $callback = null): static { return $this->as(Boolean::class, $callback); } @@ -77,7 +77,7 @@ public function asBoolean(Closure $callback = null): static /** * Set the meta field as checkbox. */ - public function asCheckbox(Closure $callback = null): static + public function asCheckbox(?Closure $callback = null): static { return $this->as(Checkbox::class, $callback); } @@ -85,7 +85,7 @@ public function asCheckbox(Closure $callback = null): static /** * Set the meta field as color. */ - public function asColor(Closure $callback = null): static + public function asColor(?Closure $callback = null): static { return $this->as(Color::class, $callback); } @@ -93,7 +93,7 @@ public function asColor(Closure $callback = null): static /** * Set the meta field as date. */ - public function asDate(Closure $callback = null): static + public function asDate(?Closure $callback = null): static { return $this->as(Date::class, $callback); } @@ -101,7 +101,7 @@ public function asDate(Closure $callback = null): static /** * Set the meta field as editor. */ - public function asEditor(Closure $callback = null): static + public function asEditor(?Closure $callback = null): static { return $this->as(Editor::class, $callback); } @@ -109,7 +109,7 @@ public function asEditor(Closure $callback = null): static /** * Set the meta field as hidden. */ - public function asHidden(Closure $callback = null): static + public function asHidden(?Closure $callback = null): static { return $this->as(Hidden::class, $callback); } @@ -117,7 +117,7 @@ public function asHidden(Closure $callback = null): static /** * Set the meta field as number. */ - public function asNumber(Closure $callback = null): static + public function asNumber(?Closure $callback = null): static { return $this->as(Number::class, $callback); } @@ -125,7 +125,7 @@ public function asNumber(Closure $callback = null): static /** * Set the meta field as radio. */ - public function asRadio(Closure $callback = null): static + public function asRadio(?Closure $callback = null): static { return $this->as(Radio::class, $callback); } @@ -133,7 +133,7 @@ public function asRadio(Closure $callback = null): static /** * Set the meta field as range. */ - public function asRange(Closure $callback = null): static + public function asRange(?Closure $callback = null): static { return $this->as(Range::class, $callback); } @@ -141,7 +141,7 @@ public function asRange(Closure $callback = null): static /** * Set the meta field as select. */ - public function asSelect(Closure $callback = null): static + public function asSelect(?Closure $callback = null): static { return $this->as(Select::class, $callback); } @@ -149,7 +149,7 @@ public function asSelect(Closure $callback = null): static /** * Set the meta field as tag. */ - public function asTag(Closure $callback = null): static + public function asTag(?Closure $callback = null): static { return $this->as(Tag::class, $callback); } @@ -157,7 +157,7 @@ public function asTag(Closure $callback = null): static /** * Set the meta field as text. */ - public function asText(Closure $callback = null): static + public function asText(?Closure $callback = null): static { return $this->as(Text::class, $callback); } @@ -165,7 +165,7 @@ public function asText(Closure $callback = null): static /** * Set the meta field as textarea. */ - public function asTextarea(Closure $callback = null): static + public function asTextarea(?Closure $callback = null): static { return $this->as(Textarea::class, $callback); } @@ -173,7 +173,7 @@ public function asTextarea(Closure $callback = null): static /** * Set the meta field as URL. */ - public function asUrl(Closure $callback = null): static + public function asUrl(?Closure $callback = null): static { return $this->as(URL::class, $callback); } diff --git a/src/Fields/Number.php b/src/Fields/Number.php index b2eacd2b4..36b23f5e9 100644 --- a/src/Fields/Number.php +++ b/src/Fields/Number.php @@ -9,7 +9,7 @@ class Number extends Field /** * Create a new field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null) { parent::__construct($label, $modelAttribute); diff --git a/src/Fields/Range.php b/src/Fields/Range.php index a63983cf8..79ecae420 100644 --- a/src/Fields/Range.php +++ b/src/Fields/Range.php @@ -14,7 +14,7 @@ class Range extends Number /** * Create a new field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null) { parent::__construct($label, $modelAttribute); diff --git a/src/Fields/Relation.php b/src/Fields/Relation.php index 40f3c5057..beaf0cd44 100644 --- a/src/Fields/Relation.php +++ b/src/Fields/Relation.php @@ -98,7 +98,7 @@ abstract class Relation extends Field implements Form /** * Create a new relation field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null, Closure|string $relation = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null, Closure|string|null $relation = null) { parent::__construct($label, $modelAttribute); diff --git a/src/Fields/Slug.php b/src/Fields/Slug.php index f2864ca33..53b9fecef 100644 --- a/src/Fields/Slug.php +++ b/src/Fields/Slug.php @@ -45,7 +45,7 @@ class Slug extends Text /** * Create a new field instance. */ - public function __construct(string $label = null, Closure|string $modelAttribute = null) + public function __construct(?string $label = null, Closure|string|null $modelAttribute = null) { parent::__construct($label ?: __('Slug'), $modelAttribute ?: 'slug'); diff --git a/src/Fields/Text.php b/src/Fields/Text.php index 3be29f1ce..552840437 100644 --- a/src/Fields/Text.php +++ b/src/Fields/Text.php @@ -9,7 +9,7 @@ class Text extends Field /** * Create a new field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null) { parent::__construct($label, $modelAttribute); diff --git a/src/Fields/URL.php b/src/Fields/URL.php index 50b1cfc87..e265c8e3a 100644 --- a/src/Fields/URL.php +++ b/src/Fields/URL.php @@ -17,7 +17,7 @@ class URL extends Text /** * Create a new field instance. */ - public function __construct(string $label, Closure|string $modelAttribute = null) + public function __construct(string $label, Closure|string|null $modelAttribute = null) { parent::__construct($label, $modelAttribute); diff --git a/src/Http/Controllers/MorphToController.php b/src/Http/Controllers/MorphToController.php index 95a3af741..f912bd66f 100644 --- a/src/Http/Controllers/MorphToController.php +++ b/src/Http/Controllers/MorphToController.php @@ -13,7 +13,7 @@ class MorphToController extends Controller /** * Handle the incoming request. */ - public function __invoke(Request $request, Model $model, Model $related = null): Response + public function __invoke(Request $request, Model $model, ?Model $related = null): Response { $field = $request->route('field'); diff --git a/src/Interfaces/Models/Medium.php b/src/Interfaces/Models/Medium.php index b5457855d..241710969 100644 --- a/src/Interfaces/Models/Medium.php +++ b/src/Interfaces/Models/Medium.php @@ -12,15 +12,15 @@ public function convert(): static; /** * Get the path to the conversion. */ - public function getPath(string $conversion = null, bool $absolute = false): string; + public function getPath(?string $conversion = null, bool $absolute = false): string; /** * Get the full path to the conversion. */ - public function getAbsolutePath(string $conversion = null): string; + public function getAbsolutePath(?string $conversion = null): string; /** * Get the url to the conversion. */ - public function getUrl(string $conversion = null): string; + public function getUrl(?string $conversion = null): string; } diff --git a/src/Models/Medium.php b/src/Models/Medium.php index a324a7cad..4c7c2f351 100644 --- a/src/Models/Medium.php +++ b/src/Models/Medium.php @@ -235,7 +235,7 @@ public function convert(): static /** * Get the path to the conversion. */ - public function getPath(string $conversion = null, bool $absolute = false): string + public function getPath(?string $conversion = null, bool $absolute = false): string { $path = sprintf('%s/%s', $this->uuid, $this->file_name); @@ -251,7 +251,7 @@ public function getPath(string $conversion = null, bool $absolute = false): stri /** * Get the full path to the conversion. */ - public function getAbsolutePath(string $conversion = null): string + public function getAbsolutePath(?string $conversion = null): string { return $this->getPath($conversion, true); } @@ -259,7 +259,7 @@ public function getAbsolutePath(string $conversion = null): string /** * Get the url to the conversion. */ - public function getUrl(string $conversion = null): string + public function getUrl(?string $conversion = null): string { return URL::to(Storage::disk($this->disk)->url($this->getPath($conversion))); } @@ -275,7 +275,7 @@ public function hasConversion(string $conversion): bool /** * Scope the query only to the given search term. */ - public function scopeSearch(Builder $query, string $value = null): Builder + public function scopeSearch(Builder $query, ?string $value = null): Builder { if (is_null($value)) { return $query; diff --git a/src/Navigation/HasItems.php b/src/Navigation/HasItems.php index 928c615e9..3310794ab 100644 --- a/src/Navigation/HasItems.php +++ b/src/Navigation/HasItems.php @@ -15,7 +15,7 @@ trait HasItems /** * Make a new item. */ - public function new(string $url, string $label, array $attributes = [], Closure $callback = null): static + public function new(string $url, string $label, array $attributes = [], ?Closure $callback = null): static { $item = new Item($url, $label, $attributes); diff --git a/src/RootServiceProvider.php b/src/RootServiceProvider.php index 6ec376578..4c77a2de3 100644 --- a/src/RootServiceProvider.php +++ b/src/RootServiceProvider.php @@ -2,6 +2,7 @@ namespace Cone\Root; +use Carbon\CarbonImmutable; use Cone\Root\Resources\Resource; use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Contracts\Foundation\Application; @@ -12,6 +13,7 @@ use Illuminate\Routing\Route; use Illuminate\Routing\Router; use Illuminate\Support\Facades\Blade; +use Illuminate\Support\Facades\Date; use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\ServiceProvider; @@ -71,6 +73,8 @@ public function boot(): void $this->registerViews(); $this->registerRoutes(); + + Date::use(CarbonImmutable::class); } /** diff --git a/src/View/Components/Modal.php b/src/View/Components/Modal.php index f86e90d7e..b43302cdb 100644 --- a/src/View/Components/Modal.php +++ b/src/View/Components/Modal.php @@ -31,7 +31,7 @@ class Modal extends Component /** * Create a new component instance. */ - public function __construct(string $title, string $subtitle = null, string $key = null, bool $open = false) + public function __construct(string $title, ?string $subtitle = null, ?string $key = null, bool $open = false) { $this->title = $title; $this->subtitle = $subtitle; diff --git a/src/Widgets/Metric.php b/src/Widgets/Metric.php index 96f374837..8d8fb904d 100644 --- a/src/Widgets/Metric.php +++ b/src/Widgets/Metric.php @@ -4,8 +4,10 @@ use Closure; use Cone\Root\Exceptions\QueryResolutionException; +use DateTimeInterface; use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Date; abstract class Metric extends Widget { @@ -58,12 +60,72 @@ public function resolveQuery(Request $request): Builder : call_user_func_array($this->queryResolver, [$request, $this->query]); } + /** + * Get the to date. + */ + public function to(Request $request): DateTimeInterface + { + return Date::now(); + } + + /** + * Get the from date. + */ + public function from(Request $request): DateTimeInterface + { + $to = $this->to($request); + + $range = empty($this->ranges()) ? 'ALL' : $this->getCurrentRange($request); + + return $this->range($to, $range); + } + + /** + * Get the current range. + */ + public function getCurrentRange(Request $request): string + { + return $request->input('range', 'MONTH'); + } + + /** + * Create a new method. + */ + protected function range(DateTimeInterface $date, string $range): mixed + { + return match ($range) { + 'TODAY' => $date->startOfDay(), + 'WEEK' => $date->subWeek(), + 'MONTH' => $date->subMonth(), + 'QUARTER' => $date->subQuarter(), + 'YEAR' => $date->subYear(), + 'ALL' => Date::parse('0000-01-01'), + default => $date->subDays((int) $range), + }; + } + + /** + * Get the available ranges. + */ + public function ranges(): array + { + return [ + 'TODAY' => __('Today'), + 'WEEK' => __('Week to today'), + 'MONTH' => __('Month to today'), + 'QUARTER' => __('Quarter to today'), + 'YEAR' => __('Year to today'), + 'ALL' => __('All time'), + ]; + } + /** * Get the data. */ public function data(Request $request): array { return array_merge(parent::data($request), [ + 'ranges' => $this->ranges(), 'data' => $this->calculate($request), ]); } diff --git a/src/Widgets/Value.php b/src/Widgets/Value.php new file mode 100644 index 000000000..3c5ae40a8 --- /dev/null +++ b/src/Widgets/Value.php @@ -0,0 +1,113 @@ +setAttribute('class', 'app-widget app-widget--summary'); + } + + /** + * Set the icon. + */ + public function icon(string $value): static + { + $this->icon = $value; + + return $this; + } + + /** + * Get the previous period. + */ + public function previous(Request $request): DateTimeInterface + { + $from = $this->from($request); + + return $this->range($from, $this->getCurrentRange($request)); + } + + /** + * {@inheritdoc} + */ + public function resolveQuery(Request $request): Builder + { + return parent::resolveQuery($request) + ->when(! empty($this->ranges()) && $this->getCurrentRange($request) !== 'ALL', function (Builder $query) use ($request): Builder { + $from = $this->from($request); + + $to = $this->to($request); + + $previous = $this->previous($request); + + $column = $query->getModel()->getQualifiedCreatedAtColumn(); + + return $query->whereBetween($column, [$previous, $to])->groupByRaw(sprintf( + "(case when %s between '%s' and '%s' then 0 else 1 end)", + $query->getQuery()->getGrammar()->wrap($column), + (string) $previous, + (string) $from + )); + }); + } + + /** + * Count values. + */ + public function count(Request $request, string $column = '*'): array + { + return $this->resolveQuery($request) + ->getQuery() + ->selectRaw(sprintf('count(%s) as `total`', $column)) + ->get() + ->pluck('total') + ->toArray(); + } + + /** + * Calculate the metric data. + */ + public function calculate(Request $request): array + { + $data = $this->count($request); + + $previous = count($data) === 1 ? 0 : $data[0]; + + $current = count($data) === 1 ? $data[0] : $data[1]; + + return [ + 'previous' => $previous, + 'current' => $current, + 'trend' => $previous === 0 ? 0 : round(($current - $previous) / (($current + $previous) / 2) * 100, 1), + ]; + } + + /** + * Convert the widget to an array. + */ + public function toArray(): array + { + return array_merge(parent::toArray(), [ + 'icon' => $this->icon, + ]); + } +} diff --git a/src/Widgets/Widget.php b/src/Widgets/Widget.php index 8791f931e..f31bb2d59 100644 --- a/src/Widgets/Widget.php +++ b/src/Widgets/Widget.php @@ -30,6 +30,14 @@ abstract class Widget implements Arrayable, Responsable */ protected string $template = 'root::widgets.widget'; + /** + * Create a new widget instance. + */ + public function __construct() + { + $this->setAttribute('class', 'app-widget'); + } + /** * Get the key. */ @@ -88,6 +96,7 @@ public function toArray(): array 'key' => $this->getKey(), 'name' => $this->getName(), 'template' => $this->template, + 'url' => $this->getUri(), ]; } diff --git a/src/Widgets/Widgets.php b/src/Widgets/Widgets.php index 6e15c67df..7a7e03cdd 100644 --- a/src/Widgets/Widgets.php +++ b/src/Widgets/Widgets.php @@ -25,7 +25,7 @@ public function register(array|Widget $widgets): static /** * Filter the fields that are available for the current request and model. */ - public function authorized(Request $request, Model $model = null): static + public function authorized(Request $request, ?Model $model = null): static { return $this->filter->authorized($request, $model)->values(); }