From 480bda2fe1dadf5af8d9e46825781ab633434986 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sun, 7 Apr 2024 17:31:42 +0000 Subject: [PATCH] ui fixes --- Admin/Install/Navigation.install.json | 15 ++ Admin/Install/db.json | 21 +++ Admin/Routes/Web/Backend.php | 18 ++ Controller/ApiController.php | 237 ++++++++++++++++++++++++- Controller/BackendController.php | 6 +- Models/Report.php | 77 ++++++++ Models/ReportMapper.php | 141 +++++++++++++++ Theme/Backend/Lang/de.lang.php | 71 ++++++++ Theme/Backend/Lang/en.lang.php | 71 ++++++++ Theme/Backend/report-dashboard.tpl.php | 119 ++++++++++++- info.json | 3 +- 11 files changed, 774 insertions(+), 5 deletions(-) create mode 100644 Admin/Install/db.json create mode 100644 Models/Report.php create mode 100644 Models/ReportMapper.php create mode 100644 Theme/Backend/Lang/de.lang.php create mode 100644 Theme/Backend/Lang/en.lang.php diff --git a/Admin/Install/Navigation.install.json b/Admin/Install/Navigation.install.json index 3fdeb6b..371d379 100644 --- a/Admin/Install/Navigation.install.json +++ b/Admin/Install/Navigation.install.json @@ -41,6 +41,21 @@ "permission": { "permission": 2, "category": null, "element": null }, "parent": 1008502001, "children": [] + }, + { + "id": 1008502003, + "pid": "/qualitymanagement", + "type": 3, + "subtype": 1, + "name": "Create", + "uri": "{/base}/qualitymanagement/report/create?{?}", + "target": "self", + "icon": null, + "order": 5, + "from": "QualityManagement", + "permission": { "permission": 2, "category": null, "element": null }, + "parent": 1008502001, + "children": [] } ] } diff --git a/Admin/Install/db.json b/Admin/Install/db.json new file mode 100644 index 0000000..eb29b65 --- /dev/null +++ b/Admin/Install/db.json @@ -0,0 +1,21 @@ +{ + "qualitymgmt_report": { + "name": "qualitymgmt_report", + "fields": { + "qualitymgmt_report_id": { + "name": "qualitymgmt_report_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "qualitymgmt_report_task": { + "name": "qualitymgmt_report_task", + "type": "INT", + "null": false, + "foreignTable": "task", + "foreignKey": "task_id" + } + } + } +} \ No newline at end of file diff --git a/Admin/Routes/Web/Backend.php b/Admin/Routes/Web/Backend.php index e6ca92b..ea370d6 100644 --- a/Admin/Routes/Web/Backend.php +++ b/Admin/Routes/Web/Backend.php @@ -22,6 +22,7 @@ return [ [ 'dest' => '\Modules\QualityManagement\Controller\BackendController:viewReportDashboard', 'verb' => RouteVerb::GET, + 'active' => true, 'permission' => [ 'module' => BackendController::NAME, 'type' => PermissionType::READ, @@ -33,6 +34,7 @@ return [ [ 'dest' => '\Modules\QualityManagement\Controller\BackendController:viewQualityReport', 'verb' => RouteVerb::GET, + 'active' => true, 'permission' => [ 'module' => BackendController::NAME, 'type' => PermissionType::READ, @@ -40,10 +42,23 @@ return [ ], ], ], + '^/qualitymanagement/report/create(\?.*$|$)' => [ + [ + 'dest' => '\Modules\QualityManagement\Controller\BackendController:viewQualityReportCreate', + 'verb' => RouteVerb::GET, + 'active' => true, + 'permission' => [ + 'module' => BackendController::NAME, + 'type' => PermissionType::CREATE, + 'state' => PermissionCategory::QUALITY_REPORT, + ], + ], + ], '^/qualitymanagement/audit/list(\?.*$|$)' => [ [ 'dest' => '\Modules\QualityManagement\Controller\BackendController:viewAuditList', 'verb' => RouteVerb::GET, + 'active' => true, 'permission' => [ 'module' => BackendController::NAME, 'type' => PermissionType::READ, @@ -55,6 +70,7 @@ return [ [ 'dest' => '\Modules\QualityManagement\Controller\BackendController:viewQuality', 'verb' => RouteVerb::GET, + 'active' => true, 'permission' => [ 'module' => BackendController::NAME, 'type' => PermissionType::READ, @@ -67,6 +83,7 @@ return [ [ 'dest' => '\Modules\QualityManagement\Controller\BackendController:viewPrivateReportDashboard', 'verb' => RouteVerb::GET, + 'active' => true, 'permission' => [ 'module' => BackendController::NAME, 'type' => PermissionType::READ, @@ -78,6 +95,7 @@ return [ [ 'dest' => '\Modules\QualityManagement\Controller\BackendController:viewPrivateReport', 'verb' => RouteVerb::GET, + 'active' => true, 'permission' => [ 'module' => BackendController::NAME, 'type' => PermissionType::READ, diff --git a/Controller/ApiController.php b/Controller/ApiController.php index 810e2aa..cd2d608 100644 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -14,8 +14,22 @@ declare(strict_types=1); namespace Modules\QualityManagement\Controller; +use Modules\Admin\Models\AccountMapper; +use Modules\Admin\Models\ContactType; +use Modules\Messages\Models\EmailMapper; +use Modules\QualityManagement\Models\Report; +use Modules\QualityManagement\Models\ReportMapper; +use Modules\QualityManagement\Models\SettingsEnum; +use Modules\Tasks\Models\TaskElementMapper; +use Modules\Tasks\Models\TaskMapper; +use Modules\Tasks\Models\TaskStatus; +use Modules\Tasks\Models\TaskType; +use phpOMS\Message\Http\RequestStatusCode; +use phpOMS\Message\RequestAbstract; +use phpOMS\Message\ResponseAbstract; + /** - * QualityManagement api controller class. + * Api controller for the QualityManagement module. * * @package Modules\QualityManagement * @license OMS License 2.0 @@ -24,4 +38,225 @@ namespace Modules\QualityManagement\Controller; */ final class ApiController extends Controller { + /** + * Validate report create request + * + * @param RequestAbstract $request Request + * + * @return array Returns the validation array of the request + * + * @since 1.0.0 + */ + private function validateReportCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['title'] = !$request->hasData('title')) + || ($val['plain'] = !$request->hasData('plain')) + ) { + return $val; + } + + return []; + } + + /** + * Api method to create a report + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param array $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiReportCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void + { + if (!empty($val = $this->validateReportCreate($request))) { + $response->header->status = RequestStatusCode::R_400; + $this->createInvalidCreateResponse($request, $response, $val); + + return; + } + + $report = $this->createReportFromRequest($request); + $this->createModel($request->header->account, $report, ReportMapper::class, 'report', $request->getOrigin()); + + $this->createStandardCreateResponse($request, $response, $report); + } + + /** + * Method to create report from request. + * + * @param RequestAbstract $request Request + * + * @return Report Returns the created report from the request + * + * @since 1.0.0 + */ + private function createReportFromRequest(RequestAbstract $request) : Report + { + $request->setData('redirect', 'qualitymanagement/report/view?for={$id}'); + $task = $this->app->moduleManager->get('Tasks')->createTaskFromRequest($request); + $task->type = TaskType::HIDDEN; + $task->unit ??= $this->app->unitId; + + $report = new Report($task); + + return $report; + } + + /** + * Api method to get a report + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param array $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiReportGet(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void + { + /** @var \Modules\QualityManagement\Models\Report $report */ + $report = ReportMapper::get()->where('id', (int) $request->getData('id'))->execute(); + $this->createStandardReturnResponse($request, $response, $report); + } + + /** + * Api method to update a report + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param array $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiReportSet(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void + { + /** @var \Modules\QualityManagement\Models\Report $old */ + $old = ReportMapper::get() + ->with('task') + ->where('id', (int) $request->getData('id')) + ->execute(); + + $new = $this->updateReportFromRequest($request, clone $old); + + $this->updateModel($request->header->account, $old, $new, ReportMapper::class, 'report', $request->getOrigin()); + $this->createStandardUpdateResponse($request, $response, $new); + } + + /** + * Method to update an report from a request + * + * @param RequestAbstract $request Request + * + * @return Report Returns the updated report from the request + * + * @since 1.0.0 + */ + private function updateReportFromRequest(RequestAbstract $request, Report $new) : Report + { + return $new; + } + + /** + * Validate report element create request + * + * @param RequestAbstract $request Request + * + * @return array Returns the validation array of the request + * + * @since 1.0.0 + */ + private function validateReportElementCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['status'] = !TaskStatus::isValidValue((int) $request->getData('status'))) + || ($val['due'] = !((bool) \strtotime((string) $request->getData('due')))) + || ($val['report'] = !(\is_numeric($request->getData('report')))) + || ($val['forward'] = !(\is_numeric($request->hasData('forward') ? $request->getData('forward') : $request->header->account))) + ) { + return $val; + } + + return []; + } + + /** + * Api method to create a report element + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param array $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiReportElementCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void + { + if (!empty($val = $this->validateReportElementCreate($request))) { + $response->header->status = RequestStatusCode::R_400; + $this->createInvalidCreateResponse($request, $response, $val); + + return; + } + + // @question Consider to use the apiTaskElementCreate() function + + /** @var \Modules\QualityManagement\Models\Report $report */ + $report = ReportMapper::get() + ->with('task') + ->where('id', (int) ($request->getData('report'))) + ->execute(); + + $element = $this->app->moduleManager->get('Tasks')->createTaskElementFromRequest($request, $report->task); + + $old = clone $report->task; + + $report->task->status = $element->status; + $report->task->priority = $element->priority; + $report->task->due = $element->due; + + $this->createModel($request->header->account, $element, TaskElementMapper::class, 'report_element', $request->getOrigin()); + $this->updateModel($request->header->account, $old, $report->task, TaskMapper::class, 'report', $request->getOrigin()); + + $report->task->taskElements[] = $element; + + $this->createStandardCreateResponse($request, $response, $element); + } + + /** + * Api method to update a report + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param array $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiReportElementSet(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void + { + $this->app->moduleManager->get('Tasks')->apiTaskElementSet($request, $response); + $new = $response->getData($request->uri->__toString())['response']; + + //$this->updateModel($request->header->account, $report, $report, ReportMapper::class, 'report', $request->getOrigin()); + $this->createStandardUpdateResponse($request, $response, $new); + } } diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 156be05..cf1df06 100644 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -66,7 +66,11 @@ final class BackendController extends Controller { $view = new View($this->app->l11nManager, $request, $response); $view->setTemplate('/Modules/QualityManagement/Theme/Backend/report-dashboard'); - $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1002901101, $request, $response); + $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1008502001, $request, $response); + + $view->data['reports'] = []; + $view->data['open'] = []; + $view->data['stats'] = []; return $view; } diff --git a/Models/Report.php b/Models/Report.php new file mode 100644 index 0000000..6f09fca --- /dev/null +++ b/Models/Report.php @@ -0,0 +1,77 @@ +task = $task ?? new Task(); + $this->task->type = TaskType::HIDDEN; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return [ + 'id' => $this->id, + 'task' => $this->task, + ]; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() : mixed + { + return $this->toArray(); + } +} diff --git a/Models/ReportMapper.php b/Models/ReportMapper.php new file mode 100644 index 0000000..8111df1 --- /dev/null +++ b/Models/ReportMapper.php @@ -0,0 +1,141 @@ + + */ +final class ReportMapper extends DataMapperFactory +{ + /** + * Columns. + * + * @var array + * @since 1.0.0 + */ + public const COLUMNS = [ + 'qualitymgmt_report_id' => ['name' => 'qualitymgmt_report_id', 'type' => 'int', 'internal' => 'id'], + 'qualitymgmt_report_task' => ['name' => 'qualitymgmt_report_task', 'type' => 'int', 'internal' => 'task'], + ]; + + /** + * Has one relation. + * + * @var array + * @since 1.0.0 + */ + public const OWNS_ONE = [ + 'task' => [ + 'mapper' => TaskMapper::class, + 'external' => 'qualitymgmt_report_task', + ], + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + public const TABLE = 'qualitymgmt_report'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + public const PRIMARYFIELD = 'qualitymgmt_report_id'; + + /** + * Get general ticket stats + * + * @return array{total:int, unassigned:int, open:int, closed:int, inprogress:int} + * + * @since 1.0.0 + */ + public static function getStatOverview() : array + { + $start = SmartDateTime::startOfMonth(); + + return [ + 'total' => self::count()->with('task')->where('task/createdAt', $start, '>=')->executeCount(), + 'unassigned' => self::count()->with('task')->where('for', null)->executeCount(), + 'open' => self::count()->with('task')->where('task/status', TaskStatus::OPEN)->executeCount(), + 'closed' => self::count()->with('task')->where('task/createdAt', $start, '>=')->where('task/status', TaskStatus::DONE)->where('task/status', TaskStatus::CANCELED, '=', 'OR')->where('task/status', TaskStatus::SUSPENDED, '=', 'OR')->executeCount(), + 'inprogress' => self::count()->with('task')->where('task/status', TaskStatus::WORKING)->executeCount(), + ]; + } + + /** + * Get tasks that have something to do with the user + * + * @param int $user User + * + * @return ReadMapper + * + * @since 1.0.0 + */ + public static function getAnyRelatedToUser(int $user) : ReadMapper + { + $query = new Builder(self::$db, true); + $query->innerJoin(TaskMapper::TABLE, TaskMapper::TABLE . '_d2_task') + ->on(self::TABLE . '_d1.qualitymgmt_report_task', '=', TaskMapper::TABLE . '_d2_task.task_id') + ->innerJoin(TaskElementMapper::TABLE) + ->on(TaskMapper::TABLE . '_d2_task.task_id', '=', TaskElementMapper::TABLE . '.task_element_task') + ->on(TaskMapper::TABLE . '_d2_task.task_type', '!=', TaskType::TEMPLATE) + ->innerJoin(AccountRelationMapper::TABLE) + ->on(TaskElementMapper::TABLE . '.task_element_id', '=', AccountRelationMapper::TABLE . '.task_account_task_element') + ->where(AccountRelationMapper::TABLE . '.task_account_account', '=', $user) + ->orWhere(TaskMapper::TABLE . '_d2_task.task_created_by', '=', $user) + ->groupBy(self::PRIMARYFIELD); + + // @todo Improving query performance by using raw queries and result arrays for large responses like this + $sql = <<query($query); + } +} diff --git a/Theme/Backend/Lang/de.lang.php b/Theme/Backend/Lang/de.lang.php new file mode 100644 index 0000000..bd25c53 --- /dev/null +++ b/Theme/Backend/Lang/de.lang.php @@ -0,0 +1,71 @@ + [ + 'Account' => 'Konto', + 'All' => 'Alle', + 'Assigned' => 'Zugewiesen', + 'AverageAmount' => 'Durschnittliche Menge', + 'AverageProcessTime' => 'Durchschn. Prozess Zeit', + 'Closed' => 'Geschlossen', + 'Completion' => 'Abgeschlossen', + 'Advanced' => 'Fortgeschritten', + 'Created' => 'Erstellt', + 'Creator' => 'Ersteller', + 'Day' => 'Tag', + 'Department' => 'Abteilung', + 'Description' => 'Beschreibung', + 'Due' => 'Fällig', + 'Files' => 'Dateien', + 'For' => 'Für', + 'Forwarded' => 'Weitergeleitet', + 'From' => 'Von', + 'Group' => 'Gruppe', + 'InTime' => 'Rechtzeitig', + 'Interval' => 'Intervall', + 'Media' => 'Medien', + 'Message' => 'Nachricht', + 'Month' => 'Monat', + 'Name' => 'Name', + 'New' => 'Neu', + 'Open' => 'Offen', + 'Person' => 'Person', + 'Priority' => 'Priorität', + 'Received' => 'Erhalten', + 'Receiver' => 'Empfänger', + 'Redirected' => 'Umgeleitet', + 'Responsible' => 'Verantwortlich', + 'Select' => 'Wählen', + 'Settings' => 'Einstellungen', + 'Size' => 'Größe', + 'Statistics' => 'Statistiken', + 'Stats' => 'Stats', + 'Status' => 'Status', + 'Support' => 'Unterstützung', + 'Report' => 'Bericht', + 'Reports' => 'Berichte', + 'Title' => 'Titel', + 'To' => 'Zu', + 'Item' => 'Artikel', + 'Today' => 'Heute', + 'Topic' => 'Thema', + 'Type' => 'Typ', + 'Unassigned' => 'Unzugewiesen', + 'Unsolved' => 'Ungelöst', + 'Upload' => 'Hochladen', + 'Week' => 'Woche', + 'Year' => 'Jahr', + 'Total' => 'Gesamt', + 'InProgress' => 'In Bearbeitung', +]]; diff --git a/Theme/Backend/Lang/en.lang.php b/Theme/Backend/Lang/en.lang.php new file mode 100644 index 0000000..237128a --- /dev/null +++ b/Theme/Backend/Lang/en.lang.php @@ -0,0 +1,71 @@ + [ + 'Account' => 'Account', + 'All' => 'All', + 'Assigned' => 'Assigned', + 'AverageAmount' => 'Average Amount', + 'AverageProcessTime' => 'Avg. Process Time', + 'Closed' => 'Closed', + 'Completion' => 'Completion', + 'Advanced' => 'Advanced', + 'Created' => 'Created', + 'Creator' => 'Creator', + 'Day' => 'Day', + 'Department' => 'Department', + 'Description' => 'Description', + 'Due' => 'Due', + 'Files' => 'Files', + 'For' => 'For', + 'Forwarded' => 'Forwarded', + 'From' => 'From', + 'Group' => 'Group', + 'InTime' => 'In Time', + 'Interval' => 'Interval', + 'Media' => 'Media', + 'Message' => 'Message', + 'Month' => 'Month', + 'Name' => 'Name', + 'New' => 'New', + 'Open' => 'Open', + 'Person' => 'Person', + 'Priority' => 'Priority', + 'Received' => 'Received', + 'Receiver' => 'Receiver', + 'Redirected' => 'Redirected', + 'Responsible' => 'Responsible', + 'Select' => 'Select', + 'Settings' => 'Settings', + 'Size' => 'Size', + 'Statistics' => 'Statistics', + 'Stats' => 'Stats', + 'Status' => 'Status', + 'Support' => 'Support', + 'Report' => 'Report', + 'Reports' => 'Reports', + 'Title' => 'Title', + 'To' => 'To', + 'Item' => 'Item', + 'Today' => 'Today', + 'Topic' => 'Topic', + 'Type' => 'Type', + 'Unassigned' => 'Unassigned', + 'Unsolved' => 'Unsolved', + 'Upload' => 'Upload', + 'Week' => 'Week', + 'Year' => 'Year', + 'Total' => 'Total', + 'InProgress' => 'In Progress', +]]; diff --git a/Theme/Backend/report-dashboard.tpl.php b/Theme/Backend/report-dashboard.tpl.php index a075e76..3e022ff 100644 --- a/Theme/Backend/report-dashboard.tpl.php +++ b/Theme/Backend/report-dashboard.tpl.php @@ -1,8 +1,123 @@ +data['reports']; + +echo $this->data['nav']->render(); ?> +
-
+
+
getHtml('Open'); ?>download
+
+ + + + data['open'] as $key => $report) : ++$c; + $url = UriFactory::build('{/base}/qualitymanagement/report/view?{?}&id=' . $report->id); + ?> + +
getHtml('Status'); ?> + getHtml('Title'); ?> + getHtml('Creator'); ?> + getHtml('Assigned'); ?> + getHtml('For'); ?> + getHtml('Item'); ?> + getHtml('Created'); ?> +
+ + getHtml('S' . $report->task->status, 'Tasks'); ?> + + printHtml($report->task->title); ?> + printHtml($report->task->createdBy->name1); ?> printHtml($report->task->createdBy->name2); ?> + task->getResponsible(); + foreach ($responsibles as $responsible) : ?> + + printHtml($responsible->name1); ?> printHtml($responsible->name2); ?> + + + printHtml($report->task->for->name1); ?> printHtml($report->task->for->name2); ?> + getHtml('P' . $report->task->priority, 'Tasks'); ?> + printHtml($report->task->createdAt->format('Y-m-d H:i')); ?> + +
getHtml('Empty', '0', '0'); ?> + +
+
+
+ +
+
getHtml('Reports'); ?>download
+
+ + + + data['reports'] as $key => $report) : ++$c; + $url = UriFactory::build('{/base}/qualitymanagement/report/view?{?}&id=' . $report->id); + ?> + +
getHtml('Status'); ?> + getHtml('Title'); ?> + getHtml('Creator'); ?> + getHtml('Assigned'); ?> + getHtml('For'); ?> + getHtml('Item'); ?> + getHtml('Created'); ?> +
+ + getHtml('S' . $report->task->status, 'Tasks'); ?> + + printHtml($report->task->title); ?> + printHtml($report->task->createdBy->name1); ?> printHtml($report->task->createdBy->name2); ?> + task->getResponsible(); + foreach ($responsibles as $responsible) : ?> + + printHtml($responsible->name1); ?> printHtml($responsible->name2); ?> + + + printHtml($report->task->for->name1); ?> printHtml($report->task->for->name2); ?> + getHtml('P' . $report->task->priority, 'Tasks'); ?> + printHtml($report->task->createdAt->format('Y-m-d H:i')); ?> + +
getHtml('Empty', '0', '0'); ?> + +
+
+
+
+ +
+
+
getHtml('Stats'); ?>
- + +
getHtml('Unassigned'); ?>data['stats']['unassigned'] ?? 0; ?> +
getHtml('Open'); ?>data['stats']['open'] ?? 0; ?> +
getHtml('InProgress'); ?>data['stats']['inprogress'] ?? 0; ?> +
getHtml('Closed'); ?>data['stats']['closed'] ?? 0; ?> +
getHtml('Total'); ?>data['stats']['total'] ?? 0; ?> +
diff --git a/info.json b/info.json index 8db65ca..9dffd8d 100644 --- a/info.json +++ b/info.json @@ -18,6 +18,7 @@ "dependencies": { "Admin": "1.0.0", "Tasks": "1.0.0", + "ItemManagement": "1.0.0", "Tools": "1.0.0" }, "providing": { @@ -29,7 +30,7 @@ "/qualitymanagement" ], "type": 4, - "for": "Content", + "for": 0, "file": "QualityManagement", "from": "QualityManagement" },