From b66edef213d40bdff024e27f64a23dd4c896df6d Mon Sep 17 00:00:00 2001 From: Kamil Piech Date: Tue, 26 Mar 2024 09:33:49 +0100 Subject: [PATCH] #270 - refresh indicator (#410) * #270 - refresh inidicator added * #270 - added last update component * #270 - update * #270 - configurable refresh time * #270 -c csf * #270 - added calendar to refreshable pages * #270 - fix connected with previous PR --- .env.example | 3 + .../Providers/EventServiceProvider.php | 2 + .../WaitForAdminApprovalAction.php | 1 + .../Listeners/UpdateLastUpdateCache.php | 21 ++++ .../Controllers/Api/LastUpdateController.php | 19 ++++ .../Http/Middleware/HandleInertiaRequests.php | 4 + .../Resources/VacationRequestResource.php | 2 +- docker-compose.yml | 2 - environment/prod/app/Dockerfile | 1 + environment/prod/app/vite.env | 1 + public/images/icon-alert.png | Bin 0 -> 12983 bytes resources/js/Shared/LastUpdate.vue | 28 +++++ resources/js/Shared/Layout/AppLayout.vue | 22 +++- resources/js/Shared/MainMenu.vue | 97 +++++++++++------- resources/js/Shared/updateFavicon.js | 7 ++ routes/api.php | 2 + 16 files changed, 172 insertions(+), 40 deletions(-) create mode 100644 app/Domain/Listeners/UpdateLastUpdateCache.php create mode 100644 app/Infrastructure/Http/Controllers/Api/LastUpdateController.php create mode 100644 environment/prod/app/vite.env create mode 100644 public/images/icon-alert.png create mode 100644 resources/js/Shared/LastUpdate.vue create mode 100644 resources/js/Shared/updateFavicon.js diff --git a/.env.example b/.env.example index 3e3af021..ababebfd 100644 --- a/.env.example +++ b/.env.example @@ -68,3 +68,6 @@ SANCTUM_STATEFUL_DOMAINS=toby.blumilk.localhost SOPS_AGE_BETA_SECRET_KEY= SOPS_AGE_PROD_SECRET_KEY= + +# 5 minutes (in miliseconds) +VITE_LAST_UPDATE_TIMEOUT=300000 diff --git a/app/Architecture/Providers/EventServiceProvider.php b/app/Architecture/Providers/EventServiceProvider.php index a1e9f46b..63beae0d 100644 --- a/app/Architecture/Providers/EventServiceProvider.php +++ b/app/Architecture/Providers/EventServiceProvider.php @@ -7,12 +7,14 @@ use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Toby\Domain\Events\VacationRequestChanged; use Toby\Domain\Listeners\UpdateDailySummaries; +use Toby\Domain\Listeners\UpdateLastUpdateCache; class EventServiceProvider extends ServiceProvider { protected $listen = [ VacationRequestChanged::class => [ UpdateDailySummaries::class, + UpdateLastUpdateCache::class, ], ]; } diff --git a/app/Domain/Actions/VacationRequest/WaitForAdminApprovalAction.php b/app/Domain/Actions/VacationRequest/WaitForAdminApprovalAction.php index 32bc3932..3afa7e97 100644 --- a/app/Domain/Actions/VacationRequest/WaitForAdminApprovalAction.php +++ b/app/Domain/Actions/VacationRequest/WaitForAdminApprovalAction.php @@ -35,6 +35,7 @@ protected function notifyAuthorizedUsers(VacationRequest $vacationRequest): void { $users = Permission::findByName("receiveVacationRequestWaitsForApprovalNotification") ->users() + ->with("permissions") ->get(); $users = $users->filter(fn(User $user): bool => $user->can("acceptAsAdminApprover", $vacationRequest)); diff --git a/app/Domain/Listeners/UpdateLastUpdateCache.php b/app/Domain/Listeners/UpdateLastUpdateCache.php new file mode 100644 index 00000000..8581087c --- /dev/null +++ b/app/Domain/Listeners/UpdateLastUpdateCache.php @@ -0,0 +1,21 @@ +cacheManager->set("last_update", Carbon::now()->toIso8601String()); + } +} diff --git a/app/Infrastructure/Http/Controllers/Api/LastUpdateController.php b/app/Infrastructure/Http/Controllers/Api/LastUpdateController.php new file mode 100644 index 00000000..c31483c2 --- /dev/null +++ b/app/Infrastructure/Http/Controllers/Api/LastUpdateController.php @@ -0,0 +1,19 @@ + $cache->get("last_update", Carbon::now()->toIso8601String()), + ]); + } +} diff --git a/app/Infrastructure/Http/Middleware/HandleInertiaRequests.php b/app/Infrastructure/Http/Middleware/HandleInertiaRequests.php index a3ba9cd3..09ff3f22 100644 --- a/app/Infrastructure/Http/Middleware/HandleInertiaRequests.php +++ b/app/Infrastructure/Http/Middleware/HandleInertiaRequests.php @@ -4,7 +4,9 @@ namespace Toby\Infrastructure\Http\Middleware; +use Carbon\Carbon; use Closure; +use Illuminate\Cache\CacheManager; use Illuminate\Http\Request; use Inertia\Middleware; use Spatie\Permission\Models\Permission; @@ -17,6 +19,7 @@ class HandleInertiaRequests extends Middleware { public function __construct( protected YearPeriodRetriever $yearPeriodRetriever, + protected CacheManager $cache, ) {} public function share(Request $request): array @@ -27,6 +30,7 @@ public function share(Request $request): array "years" => $this->getYearsData($request), "vacationRequestsCount" => $this->getVacationRequestsCount($request), "deployInformation" => $this->getDeployInformation(), + "lastUpdate" => $this->cache->rememberForever("last_update", fn(): string => Carbon::now()->toIso8601String()), ]); } diff --git a/app/Infrastructure/Http/Resources/VacationRequestResource.php b/app/Infrastructure/Http/Resources/VacationRequestResource.php index a1414b52..a5a785a3 100644 --- a/app/Infrastructure/Http/Resources/VacationRequestResource.php +++ b/app/Infrastructure/Http/Resources/VacationRequestResource.php @@ -30,7 +30,7 @@ public function toArray($request): array return [ "id" => $this->id, "name" => $this->name, - "user" => new SimpleUserResource($user), + "user" => new SimpleUserResource($this->user), "type" => $this->type, "isVacation" => $this->configRetriever->isVacation($this->type), "state" => $this->state, diff --git a/docker-compose.yml b/docker-compose.yml index 5456e4a4..df62a59e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: app: build: diff --git a/environment/prod/app/Dockerfile b/environment/prod/app/Dockerfile index 83e2c0b9..38a0d2e2 100644 --- a/environment/prod/app/Dockerfile +++ b/environment/prod/app/Dockerfile @@ -24,6 +24,7 @@ FROM node:21.6.2-bullseye-slim as frontend WORKDIR /app_frontend_dependencies COPY package.json package-lock.json postcss.config.js tailwind.config.js vite.config.js ./ +COPY ./environment/prod/app/vite.env .env RUN npm clean-install diff --git a/environment/prod/app/vite.env b/environment/prod/app/vite.env new file mode 100644 index 00000000..bd58f70c --- /dev/null +++ b/environment/prod/app/vite.env @@ -0,0 +1 @@ +VITE_LAST_UPDATE_TIMEOUT=300000 diff --git a/public/images/icon-alert.png b/public/images/icon-alert.png new file mode 100644 index 0000000000000000000000000000000000000000..d8dab065cdfa9388523e578a2d9cae3fc333dc0f GIT binary patch literal 12983 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 G!T$xi`wo-< literal 0 HcmV?d00001 diff --git a/resources/js/Shared/LastUpdate.vue b/resources/js/Shared/LastUpdate.vue new file mode 100644 index 00000000..82377be0 --- /dev/null +++ b/resources/js/Shared/LastUpdate.vue @@ -0,0 +1,28 @@ + diff --git a/resources/js/Shared/Layout/AppLayout.vue b/resources/js/Shared/Layout/AppLayout.vue index fbd506b1..b17e3095 100644 --- a/resources/js/Shared/Layout/AppLayout.vue +++ b/resources/js/Shared/Layout/AppLayout.vue @@ -1,8 +1,10 @@