From 0b165bc24cbe0de525188a4d03bb888f3fdc8327 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Wed, 25 Oct 2023 02:42:11 +0200 Subject: [PATCH] Recreated shift type admin backend --- config/routes.php | 12 + includes/controller/shifttypes_controller.php | 163 ------------- includes/includes.php | 2 - includes/pages/admin_shifts.php | 2 +- includes/sys_menu.php | 2 +- includes/view/ShiftTypes_view.php | 140 ----------- includes/view/Shifts_view.php | 2 +- resources/lang/de_DE/additional.po | 6 + resources/lang/de_DE/default.po | 39 +-- resources/lang/en_US/additional.po | 6 + resources/lang/en_US/default.po | 12 + resources/views/admin/shifttypes/edit.twig | 38 +++ resources/views/admin/shifttypes/index.twig | 66 ++++++ resources/views/admin/shifttypes/view.twig | 11 + .../Admin/ShiftTypesController.php | 141 +++++++++++ src/Middleware/LegacyMiddleware.php | 3 - .../Admin/ShiftTypeControllerTest.php | 223 ++++++++++++++++++ 17 files changed, 530 insertions(+), 338 deletions(-) delete mode 100644 includes/controller/shifttypes_controller.php delete mode 100644 includes/view/ShiftTypes_view.php create mode 100644 resources/views/admin/shifttypes/edit.twig create mode 100644 resources/views/admin/shifttypes/index.twig create mode 100644 resources/views/admin/shifttypes/view.twig create mode 100644 src/Controllers/Admin/ShiftTypesController.php create mode 100644 tests/Unit/Controllers/Admin/ShiftTypeControllerTest.php diff --git a/config/routes.php b/config/routes.php index 90030002..389338d1 100644 --- a/config/routes.php +++ b/config/routes.php @@ -162,6 +162,18 @@ $route->addGroup( } ); + // Shift types + $route->addGroup( + '/shifttypes', + function (RouteCollector $route): void { + $route->get('', 'Admin\\ShiftTypesController@index'); + $route->post('', 'Admin\\ShiftTypesController@delete'); + $route->get('/{shift_type_id:\d+}', 'Admin\\ShiftTypesController@view'); + $route->get('/edit[/{shift_type_id:\d+}]', 'Admin\\ShiftTypesController@edit'); + $route->post('/edit[/{shift_type_id:\d+}]', 'Admin\\ShiftTypesController@save'); + } + ); + // Questions $route->addGroup( '/questions', diff --git a/includes/controller/shifttypes_controller.php b/includes/controller/shifttypes_controller.php deleted file mode 100644 index 3ade5864..00000000 --- a/includes/controller/shifttypes_controller.php +++ /dev/null @@ -1,163 +0,0 @@ - 'view', 'shifttype_id' => $shifttype->id]); -} - -/** - * Delete a shifttype. - * - * @return array - */ -function shifttype_delete_controller() -{ - $request = request(); - if (!$request->has('shifttype_id')) { - throw_redirect(url('/shifttypes')); - } - - $shifttype = ShiftType::findOrFail($request->input('shifttype_id')); - if ($request->hasPostData('delete')) { - engelsystem_log('Deleted shifttype ' . $shifttype->name); - success(sprintf(__('Shifttype %s deleted.'), $shifttype->name)); - - $shifttype->delete(); - throw_redirect(url('/shifttypes')); - } - - return [ - sprintf(__('Delete shifttype %s'), $shifttype->name), - ShiftType_delete_view($shifttype), - ]; -} - -/** - * Edit or create shift type. - * - * @return array - */ -function shifttype_edit_controller() -{ - $shifttype_id = null; - $name = ''; - $description = ''; - - $request = request(); - - if ($request->has('shifttype_id')) { - $shifttype = ShiftType::findOrFail($request->input('shifttype_id')); - $shifttype_id = $shifttype->id; - $name = $shifttype->name; - $description = $shifttype->description; - } - - if ($request->hasPostData('submit')) { - $valid = true; - - if ($request->has('name') && $request->input('name') != '') { - $name = strip_request_item('name'); - } else { - $valid = false; - error(__('Please enter a name.')); - } - - if ($request->has('description')) { - $description = strip_request_item_nl('description'); - } - - if ($valid) { - $shiftType = ShiftType::findOrNew($shifttype_id); - $shiftType->name = $name; - $shiftType->description = $description; - $shiftType->save(); - - if ($shifttype_id) { - engelsystem_log('Updated shifttype ' . $name); - success(__('Updated shifttype.')); - } else { - $shifttype_id = $shiftType->id; - - engelsystem_log('Created shifttype ' . $name); - success(__('Created shifttype.')); - } - - throw_redirect(url('/shifttypes', ['action' => 'view', 'shifttype_id' => $shifttype_id])); - } - } - - return [ - shifttypes_title(), - ShiftType_edit_view($name, $description, $shifttype_id), - ]; -} - -/** - * @return array - */ -function shifttype_controller() -{ - $request = request(); - if (!$request->has('shifttype_id')) { - throw_redirect(url('/shifttypes')); - } - $shifttype = ShiftType::findOrFail($request->input('shifttype_id')); - - return [ - $shifttype->name, - ShiftType_view($shifttype), - ]; -} - -/** - * List all shift types. - * - * @return array - */ -function shifttypes_list_controller() -{ - $shifttypes = ShiftType::all()->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE); - - return [ - shifttypes_title(), - ShiftTypes_list_view($shifttypes), - ]; -} - -/** - * Text for shift type related links. - * - * @return string - */ -function shifttypes_title() -{ - return __('Shifttypes'); -} - -/** - * Route shift type actions - * - * @return array - */ -function shifttypes_controller() -{ - $request = request(); - $action = 'list'; - if ($request->has('action')) { - $action = $request->input('action'); - } - - return match ($action) { - 'view' => shifttype_controller(), - 'edit' => shifttype_edit_controller(), - 'delete' => shifttype_delete_controller(), - 'list' => shifttypes_list_controller(), - default => shifttypes_list_controller(), - }; -} diff --git a/includes/includes.php b/includes/includes.php index 465f7525..afdb1d81 100644 --- a/includes/includes.php +++ b/includes/includes.php @@ -31,7 +31,6 @@ $includeFiles = [ __DIR__ . '/../includes/view/ShiftsFilterRenderer.php', __DIR__ . '/../includes/view/Shifts_view.php', __DIR__ . '/../includes/view/ShiftEntry_view.php', - __DIR__ . '/../includes/view/ShiftTypes_view.php', __DIR__ . '/../includes/view/UserAngelTypes_view.php', __DIR__ . '/../includes/view/UserHintsRenderer.php', __DIR__ . '/../includes/view/User_view.php', @@ -42,7 +41,6 @@ $includeFiles = [ __DIR__ . '/../includes/controller/locations_controller.php', __DIR__ . '/../includes/controller/shift_entries_controller.php', __DIR__ . '/../includes/controller/shifts_controller.php', - __DIR__ . '/../includes/controller/shifttypes_controller.php', __DIR__ . '/../includes/controller/users_controller.php', __DIR__ . '/../includes/controller/user_angeltypes_controller.php', diff --git a/includes/pages/admin_shifts.php b/includes/pages/admin_shifts.php index 5254109f..da9fd2f8 100644 --- a/includes/pages/admin_shifts.php +++ b/includes/pages/admin_shifts.php @@ -328,7 +328,7 @@ function admin_shifts() . '
' . location_name_render(Location::find($shift['location_id'])), 'title' => - ShiftType_name_render(ShiftType::find($shifttype_id)) + ShiftType::find($shifttype_id)->name . ($shift['title'] ? '
' . $shift['title'] : ''), 'needed_angels' => '', ]; diff --git a/includes/sys_menu.php b/includes/sys_menu.php index 1f09f1ed..eb33f89e 100644 --- a/includes/sys_menu.php +++ b/includes/sys_menu.php @@ -83,7 +83,7 @@ function make_navigation() 'users' => ['All Angels', 'admin_user'], 'admin_free' => 'Free angels', 'admin/questions' => ['Answer questions', 'question.edit'], - 'shifttypes' => 'Shifttypes', + 'admin/shifttypes' => ['shifttype.shifttypes', 'shifttypes'], 'admin_shifts' => 'Create shifts', 'admin/locations' => ['location.locations', 'admin_locations'], 'admin_groups' => 'Grouprights', diff --git a/includes/view/ShiftTypes_view.php b/includes/view/ShiftTypes_view.php deleted file mode 100644 index 7603571e..00000000 --- a/includes/view/ShiftTypes_view.php +++ /dev/null @@ -1,140 +0,0 @@ -can('shifttypes')) { - return '' . $shifttype->name . ''; - } - return $shifttype->name; -} - -/** - * @param ShiftType $shifttype - * @return string - */ -function ShiftType_delete_view(ShiftType $shifttype) -{ - return page_with_title(sprintf(__('Delete shifttype %s'), $shifttype->name), [ - info(sprintf(__('Do you want to delete shifttype %s?'), $shifttype->name), true), - form([ - buttons([ - button(url('/shifttypes'), icon('x-lg') . __('form.cancel')), - form_submit( - 'delete', - icon('trash') . __('delete'), - 'btn-danger', - false - ), - ]), - ]), - ], true); -} - -/** - * @param string $name - * @param string $description - * @param int $shifttype_id - * @return string - */ -function ShiftType_edit_view($name, $description, $shifttype_id) -{ - return page_with_title( - $shifttype_id - ? (button( - url('/shifttypes', ['action' => 'view', 'shifttype_id' => $shifttype_id]), - icon('chevron-left'), - 'btn-sm' - ) . ' ' . __('Edit shifttype')) - : (button(url('/shifttypes'), icon('chevron-left'), 'btn-sm') . ' ' . __('Create shifttype')), - [ - msg(), - buttons([ - button(url('/shifttypes'), shifttypes_title(), 'back'), - ]), - form([ - form_text('name', __('general.name'), $name), - form_textarea('description', __('general.description'), $description), - form_info('', __('Please use markdown for the description.')), - form_submit('submit', __('form.save')), - ]), - ], - true - ); -} - -/** - * @param ShiftType $shifttype - * @return string - */ -function ShiftType_view(ShiftType $shifttype) -{ - $parsedown = new Parsedown(); - $title = $shifttype->name; - $link = button(url('/shifttypes'), icon('chevron-left'), 'btn-sm'); - return page_with_title($link . ' ' . $title, [ - msg(), - buttons([ - button( - url('/shifttypes', ['action' => 'edit', 'shifttype_id' => $shifttype->id]), - icon('pencil') . __('edit') - ), - button( - url('/shifttypes', ['action' => 'delete', 'shifttype_id' => $shifttype->id]), - icon('trash') . __('delete'), - ), - ]), - heading(__('general.description'), 2), - $parsedown->parse($shifttype->description), - ], true); -} - -/** - * @param ShiftType[]|array[]|Collection $shifttypes - * @return string - */ -function ShiftTypes_list_view($shifttypes) -{ - foreach ($shifttypes as $shifttype) { - $shifttype->name = '' - . $shifttype->name - . ''; - $shifttype->actions = table_buttons([ - button( - url( - '/shifttypes', - ['action' => 'edit', 'shifttype_id' => $shifttype->id] - ), - icon('pencil') . __('edit'), - 'btn-sm' - ), - button( - url('/shifttypes', ['action' => 'delete', 'shifttype_id' => $shifttype->id]), - icon('trash') . __('delete'), - 'btn-sm' - ), - ]); - } - - $link = button(url('/shifttypes', ['action' => 'edit']), icon('plus-lg'), 'add'); - return page_with_title( - shifttypes_title() . ' ' . $link, - [ - msg(), - - table([ - 'name' => __('general.name'), - 'actions' => '', - ], $shifttypes), - ], - true, - ); -} diff --git a/includes/view/Shifts_view.php b/includes/view/Shifts_view.php index 006fd9c5..d944fe9c 100644 --- a/includes/view/Shifts_view.php +++ b/includes/view/Shifts_view.php @@ -175,7 +175,7 @@ function Shift_view( $buttons = [ $shift_admin ? button(shift_edit_link($shift), icon('pencil') . __('edit')) : '', $shift_admin ? button(shift_delete_link($shift), icon('trash') . __('delete')) : '', - $admin_shifttypes ? button(shifttype_link($shifttype), $shifttype->name) : '', + $admin_shifttypes ? button(url('/admin/shifttypes/' . $shifttype->id), $shifttype->name) : '', $admin_locations ? button(location_link($location), icon('pin-map-fill') . $location->name) : '', ]; } diff --git a/resources/lang/de_DE/additional.po b/resources/lang/de_DE/additional.po index 9d36a254..9950bb83 100644 --- a/resources/lang/de_DE/additional.po +++ b/resources/lang/de_DE/additional.po @@ -272,6 +272,12 @@ msgstr "Ort erfolgreich bearbeitet." msgid "location.delete.success" msgstr "Ort erfolgreich gelöscht." +msgid "shifttype.edit.success" +msgstr "Schichttyp erfolgreich bearbeitet." + +msgid "shifttype.delete.success" +msgstr "Schichttyp erfolgreich gelöscht." + msgid "validation.name.exists" msgstr "Der Name wird bereits verwendet." diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index a5ecf1fb..fd0476f9 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -323,24 +323,6 @@ msgstr "Möchtest Du die Schicht %s von %s bis %s löschen?" msgid "Shift could not be found." msgstr "Schicht konnte nicht gefunden werden." -msgid "Shifttype %s deleted." -msgstr "Schichttyp %s gelöscht." - -msgid "Delete shifttype %s" -msgstr "Lösche Schichttyp %s" - -msgid "Please enter a name." -msgstr "Gib bitte einen Namen an." - -msgid "Updated shifttype." -msgstr "Schichttyp geändert." - -msgid "Created shifttype." -msgstr "Schichttyp erstellt." - -msgid "Shifttypes" -msgstr "Schichttypen" - msgid "There is %d unconfirmed angeltype." msgid_plural "There are %d unconfirmed angeltypes." msgstr[0] "Es gibt %d nicht freigeschalteten Engeltypen!" @@ -1127,15 +1109,6 @@ msgstr "Titel:" msgid "Type:" msgstr "Typ:" -msgid "Do you want to delete shifttype %s?" -msgstr "Möchtest Du den Schichttypen %s löschen?" - -msgid "Edit shifttype" -msgstr "Schichttyp bearbeiten" - -msgid "Create shifttype" -msgstr "Schichttyp erstellen" - msgid "Location" msgstr "Ort" @@ -1960,6 +1933,18 @@ msgstr "Ort bearbeiten" msgid "location.delete.title" msgstr "Ort \"%s\" löschen" +msgid "shifttype.shifttypes" +msgstr "Schichttypen" + +msgid "shifttype.edit.title" +msgstr "Schichttyp bearbeiten" + +msgid "shifttype.create.title" +msgstr "Schichttyp erstellen" + +msgid "shifttype.delete.title" +msgstr "Schichttyp \"%s\" löschen" + msgid "event.day" msgstr "Tag %1$d" diff --git a/resources/lang/en_US/additional.po b/resources/lang/en_US/additional.po index 8d1c8a46..fa89cf32 100644 --- a/resources/lang/en_US/additional.po +++ b/resources/lang/en_US/additional.po @@ -271,6 +271,12 @@ msgstr "Location edited successfully." msgid "location.delete.success" msgstr "Location successfully deleted." +msgid "shifttype.edit.success" +msgstr "Shift type edited successfully." + +msgid "shifttype.delete.success" +msgstr "Shift type successfully deleted." + msgid "validation.name.exists" msgstr "The name is already used." diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index ba21df58..9c8c8e27 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -616,6 +616,18 @@ msgstr "Edit location" msgid "location.delete.title" msgstr "Delete location \"%s\"" +msgid "shifttype.shifttypes" +msgstr "Shift types" + +msgid "shifttype.edit.title" +msgstr "Edit shift type" + +msgid "shifttype.create.title" +msgstr "Create shift type" + +msgid "shifttype.delete.title" +msgstr "Delete shift type \"%s\"" + msgid "event.day" msgstr "Day %1$d" diff --git a/resources/views/admin/shifttypes/edit.twig b/resources/views/admin/shifttypes/edit.twig new file mode 100644 index 00000000..b470564c --- /dev/null +++ b/resources/views/admin/shifttypes/edit.twig @@ -0,0 +1,38 @@ +{% extends 'admin/shifttypes/index.twig' %} +{% import 'macros/base.twig' as m %} +{% import 'macros/form.twig' as f %} + +{% block title %}{{ shifttype ? __('shifttype.edit.title') : __('shifttype.create.title') }}{% endblock %} + +{% block row_content %} +
+ {{ csrf() }} + {{ f.hidden('id', shifttype ? shifttype.id : '') }} + +
+
+ {{ f.input('name', __('general.name'), { + 'required': true, + 'required_icon': true, + 'value': f.formData('shifttype', shifttype ? shifttype.name : ''), + }) }} + + {{ f.textarea('description', __('general.description'), { + 'value': f.formData('description', shifttype ? shifttype.description : ''), + 'rows': 5, + 'info': __('form.markdown') + }) }} +
+
+ +
+
+ {{ f.submit(__('form.save'), {'icon_left': 'save'}) }} + {% if shifttype %} + {{ f.delete(__('form.delete'), {'confirm_title': __('shifttype.delete.title', [shifttype.name|e])}) }} + {% endif %} +
+
+ +
+{% endblock %} diff --git a/resources/views/admin/shifttypes/index.twig b/resources/views/admin/shifttypes/index.twig new file mode 100644 index 00000000..990a96d8 --- /dev/null +++ b/resources/views/admin/shifttypes/index.twig @@ -0,0 +1,66 @@ +{% extends 'layouts/app.twig' %} +{% import 'macros/base.twig' as m %} +{% import 'macros/form.twig' as f %} + +{% block title %}{{ __('shifttype.shifttypes') }}{% endblock %} + +{% block content %} +
+

+ {% if not is_index|default(false) %} + {{ m.button(m.icon('chevron-left'), url('/admin/shifttypes'), 'secondary', 'sm') }} + {% endif %} + + {{ block('title') }} + + {% if is_index|default(false) %} + {{ m.button(m.icon('plus-lg'), url('/admin/shifttypes/edit'), 'secondary') }} + {% endif %} +

+ + {% include 'layouts/parts/messages.twig' %} + +
+ + {% block row_content %} +
+
+ + + + + + + + + + {% for shifttype in shifttypes %} + + + + + + {% endfor %} + +
{{ __('general.name') }}
+ {{ shifttype.name }} + +
+ + {{ m.button(m.icon('pencil'), url('admin/shifttypes/edit/' ~ shifttype.id), null, 'sm', __('form.edit')) }} + +
+ {{ csrf() }} + {{ f.hidden('id', shifttype.id) }} + {{ f.delete(null, {'size': 'sm', 'confirm_title': __('shifttype.delete.title', [shifttype.name|e])}) }} +
+ +
+
+
+
+ {% endblock %} + +
+
+{% endblock %} diff --git a/resources/views/admin/shifttypes/view.twig b/resources/views/admin/shifttypes/view.twig new file mode 100644 index 00000000..ab5eb26f --- /dev/null +++ b/resources/views/admin/shifttypes/view.twig @@ -0,0 +1,11 @@ +{% extends 'admin/shifttypes/index.twig' %} +{% import 'macros/base.twig' as m %} +{% import 'macros/form.twig' as f %} + +{% block title %}{{ shifttype.name }}{% endblock %} + +{% block row_content %} +
+ {{ shifttype.description|md }} +
+{% endblock %} diff --git a/src/Controllers/Admin/ShiftTypesController.php b/src/Controllers/Admin/ShiftTypesController.php new file mode 100644 index 00000000..6b49dadf --- /dev/null +++ b/src/Controllers/Admin/ShiftTypesController.php @@ -0,0 +1,141 @@ + */ + protected array $permissions = [ + 'shifttypes', + ]; + + public function __construct( + protected LoggerInterface $log, + protected ShiftType $shiftType, + protected Redirector $redirect, + protected Response $response + ) { + } + + public function index(): Response + { + $shiftTypes = $this->shiftType + ->get() + ->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE); + + return $this->response->withView( + 'admin/shifttypes/index', + ['shifttypes' => $shiftTypes, 'is_index' => true] + ); + } + + public function edit(Request $request): Response + { + $shiftTypeId = (int) $request->getAttribute('shift_type_id'); + + $shiftType = $this->shiftType->find($shiftTypeId); + + return $this->response->withView( + 'admin/shifttypes/edit', + ['shifttype' => $shiftType] + ); + } + + public function view(Request $request): Response + { + $shiftTypeId = (int) $request->getAttribute('shift_type_id'); + $shiftType = $this->shiftType->findOrFail($shiftTypeId); + + return $this->response->withView( + 'admin/shifttypes/view', + ['shifttype' => $shiftType] + ); + } + + public function save(Request $request): Response + { + $shiftTypeId = (int) $request->getAttribute('shift_type_id'); + + /** @var ShiftType $shiftType */ + $shiftType = $this->shiftType->findOrNew($shiftTypeId); + + if ($request->request->has('delete')) { + return $this->delete($request); + } + + $data = $this->validate( + $request, + [ + 'name' => 'required', + 'description' => 'required|optional', + ] + ); + + if (ShiftType::whereName($data['name'])->where('id', '!=', $shiftType->id)->exists()) { + throw new ValidationException((new Validator())->addErrors(['name' => ['validation.name.exists']])); + } + + $shiftType->name = $data['name']; + $shiftType->description = $data['description']; + + $shiftType->save(); + + $this->log->info( + 'Updated shift type "{name}": {description}', + [ + 'name' => $shiftType->name, + 'description' => $shiftType->description, + ] + ); + + $this->addNotification('shifttype.edit.success'); + + return $this->redirect->to('/admin/shifttypes'); + } + + public function delete(Request $request): Response + { + $data = $this->validate($request, [ + 'id' => 'required|int', + 'delete' => 'checked', + ]); + + $shiftType = $this->shiftType->findOrFail($data['id']); + + $shifts = $shiftType->shifts; + foreach ($shifts as $shift) { + foreach ($shift->shiftEntries as $entry) { + event('shift.entry.deleting', [ + 'user' => $entry->user, + 'start' => $shift->start, + 'end' => $shift->end, + 'name' => $shift->shiftType->name, + 'title' => $shift->title, + 'type' => $entry->angelType->name, + 'location' => $shift->location, + 'freeloaded' => $entry->freeloaded, + ]); + } + } + $shiftType->delete(); + + $this->log->info('Deleted shift type {name}', ['name' => $shiftType->name]); + $this->addNotification('shifttype.delete.success'); + + return $this->redirect->to('/admin/shifttypes'); + } +} diff --git a/src/Middleware/LegacyMiddleware.php b/src/Middleware/LegacyMiddleware.php index 0f583a2f..2f837249 100644 --- a/src/Middleware/LegacyMiddleware.php +++ b/src/Middleware/LegacyMiddleware.php @@ -88,9 +88,6 @@ class LegacyMiddleware implements MiddlewareInterface return users_controller(); case 'user_angeltypes': return user_angeltypes_controller(); - case 'shifttypes': - list($title, $content) = shifttypes_controller(); - return [$title, $content]; case 'admin_event_config': list($title, $content) = event_config_edit_controller(); return [$title, $content]; diff --git a/tests/Unit/Controllers/Admin/ShiftTypeControllerTest.php b/tests/Unit/Controllers/Admin/ShiftTypeControllerTest.php new file mode 100644 index 00000000..ebaedf8a --- /dev/null +++ b/tests/Unit/Controllers/Admin/ShiftTypeControllerTest.php @@ -0,0 +1,223 @@ +app->make(ShiftTypesController::class); + ShiftType::factory(5)->create(); + + $this->response->expects($this->once()) + ->method('withView') + ->willReturnCallback(function (string $view, array $data) { + $this->assertEquals('admin/shifttypes/index', $view); + $this->assertTrue($data['is_index'] ?? false); + $this->assertCount(5, $data['shifttypes'] ?? []); + return $this->response; + }); + + $controller->index(); + } + + /** + * @covers \Engelsystem\Controllers\Admin\ShiftTypesController::view + */ + public function testView(): void + { + /** @var ShiftTypesController $controller */ + $controller = $this->app->make(ShiftTypesController::class); + $shiftType = ShiftType::factory()->create(); + + $this->response->expects($this->once()) + ->method('withView') + ->willReturnCallback(function (string $view, array $data) use ($shiftType) { + $this->assertEquals('admin/shifttypes/view', $view); + $this->assertArrayHasKey('shifttype', $data); + $this->assertEquals($shiftType->id, $data['shifttype']['id']); + return $this->response; + }); + + $controller->view(new Request([], [], ['shift_type_id' => $shiftType->id])); + } + + /** + * @covers \Engelsystem\Controllers\Admin\ShiftTypesController::edit + */ + public function testEdit(): void + { + /** @var ShiftTypesController $controller */ + $controller = $this->app->make(ShiftTypesController::class); + /** @var ShiftType $shifttype */ + $shifttype = ShiftType::factory()->create(); + + $this->response->expects($this->once()) + ->method('withView') + ->willReturnCallback(function (string $view, array $data) use ($shifttype) { + $this->assertEquals('admin/shifttypes/edit', $view); + $this->assertEquals($shifttype->id, $data['shifttype']?->id); + $this->assertNotEmpty($data['shifttype']?->name); + $this->assertNotEmpty($data['shifttype']?->description); + return $this->response; + }); + + $this->request = $this->request->withAttribute('shift_type_id', 1); + + $controller->edit($this->request); + } + + /** + * @covers \Engelsystem\Controllers\Admin\ShiftTypesController::edit + */ + public function testEditNew(): void + { + /** @var ShiftTypesController $controller */ + $controller = $this->app->make(ShiftTypesController::class); + + $this->response->expects($this->once()) + ->method('withView') + ->willReturnCallback(function (string $view, array $data) { + $this->assertEquals('admin/shifttypes/edit', $view); + $this->assertArrayHasKey('shifttype', $data); + $this->assertNull($data['shifttype']); + return $this->response; + }); + + $controller->edit($this->request); + } + + /** + * @covers \Engelsystem\Controllers\Admin\ShiftTypesController::save + */ + public function testSave(): void + { + /** @var ShiftTypesController $controller */ + $controller = $this->app->make(ShiftTypesController::class); + $controller->setValidator(new Validator()); + + $this->setExpects($this->redirect, 'to', ['/admin/shifttypes']); + + $this->request = $this->request->withParsedBody([ + 'name' => 'Test shift type', + 'description' => 'Something', + ]); + + $controller->save($this->request); + + $this->assertTrue($this->log->hasInfoThatContains('Updated shift type')); + $this->assertHasNotification('shifttype.edit.success'); + $this->assertCount(1, ShiftType::whereName('Test shift type')->get()); + } + + /** + * @covers \Engelsystem\Controllers\Admin\ShiftTypesController::save + */ + public function testSaveUniqueName(): void + { + /** @var ShiftTypesController $controller */ + $controller = $this->app->make(ShiftTypesController::class); + $controller->setValidator(new Validator()); + ShiftType::factory()->create(['name' => 'Test shift type']); + + $this->request = $this->request->withParsedBody([ + 'name' => 'Test shift type', + ]); + + $this->expectException(ValidationException::class); + $controller->save($this->request); + } + + /** + * @covers \Engelsystem\Controllers\Admin\ShiftTypesController::save + * @covers \Engelsystem\Controllers\Admin\ShiftTypesController::delete + */ + public function testSaveDelete(): void + { + /** @var ShiftTypesController $controller */ + $controller = $this->app->make(ShiftTypesController::class); + $controller->setValidator(new Validator()); + /** @var ShiftType $shifttype */ + $shifttype = ShiftType::factory()->create(); + + $this->request = $this->request->withParsedBody([ + 'id' => '1', + 'delete' => '1', + ]); + + $controller->save($this->request); + $this->assertEmpty(ShiftType::find($shifttype->id)); + } + + /** + * @covers \Engelsystem\Controllers\Admin\ShiftTypesController::delete + */ + public function testDelete(): void + { + /** @var EventDispatcher|MockObject $dispatcher */ + $dispatcher = $this->createMock(EventDispatcher::class); + $this->app->instance('events.dispatcher', $dispatcher); + /** @var ShiftTypesController $controller */ + $controller = $this->app->make(ShiftTypesController::class); + $controller->setValidator(new Validator()); + /** @var ShiftType $shifttype */ + $shifttype = ShiftType::factory()->create(); + /** @var Shift $shift */ + $shift = Shift::factory()->create(['shift_type_id' => $shifttype->id, 'start' => Carbon::create()->subHour()]); + /** @var User $user */ + $user = User::factory()->create(['name' => 'foo', 'email' => 'lorem@ipsum']); + /** @var ShiftEntry $shiftEntry */ + ShiftEntry::factory()->create(['shift_id' => $shift->id, 'user_id' => $user->id]); + + $this->setExpects($this->redirect, 'to', ['/admin/shifttypes'], $this->response); + + $dispatcher->expects($this->once()) + ->method('dispatch') + ->willReturnCallback(function (string $event, array $data) use ($shifttype, $user) { + $this->assertEquals('shift.entry.deleting', $event); + $this->assertEquals($shifttype->name, $data['name']); + $this->assertEquals($user->id, $data['user']->id); + + return []; + }); + + $this->request = $this->request->withParsedBody(['id' => 1, 'delete' => '1']); + + $controller->delete($this->request); + + $this->assertNull(ShiftType::find($shifttype->id)); + $this->assertTrue($this->log->hasInfoThatContains('Deleted shift type')); + $this->assertHasNotification('shifttype.delete.success'); + } + + public function setUp(): void + { + parent::setUp(); + + $this->redirect = $this->createMock(Redirector::class); + $this->app->instance(Redirector::class, $this->redirect); + } +}