diff --git a/application/AssetManagement/AssetManagementController.php b/application/AssetManagement/AssetManagementController.php index af710d615..ff7d00cc6 100644 --- a/application/AssetManagement/AssetManagementController.php +++ b/application/AssetManagement/AssetManagementController.php @@ -8,6 +8,7 @@ class AssetManagementController extends TTCrud // Simplified columns for better layout, details are in the 'assetDetails' slot protected array $columns = [ ['key' => 'assetDetails', 'text' => 'Gerät', 'modal' => false, 'table' => ['filter' => 'search', 'sortable' => true]], + ['key' => 'category', 'text' => 'Kategorie', 'modal' => false, 'table' => ['filter' => 'select', 'filterOptions' => []]], ['key' => 'currentUser', 'text' => 'Status', 'modal' => false, 'table' => ['sortable' => false, 'filter' => false]], ['key' => 'location', 'text' => 'Lagerort', 'required' => true, 'modal' => ['type' => 'text'], 'table' => ['filter' => 'search']], ['key' => 'serviceDueDate', 'text' => 'Service fällig', 'required' => false, 'modal' => ['type' => 'date'], 'table' => ['filter' => 'date']], @@ -22,6 +23,15 @@ class AssetManagementController extends TTCrud $this->additionalJSVariables['ASSET_ADMIN'] = '0'; $this->columns = array_filter($this->columns, fn($col) => $col['key'] !== 'actions'); } + + $categories = AssetManagementModel::getDistinctCategories(); + $categoryOptions = array_map(fn($cat) => ['value' => $cat, 'text' => $cat], $categories); + foreach ($this->columns as &$column) { + if ($column['key'] === 'category') { + $column['table']['filterOptions'] = $categoryOptions; + break; + } + } } /** @@ -282,6 +292,18 @@ class AssetManagementController extends TTCrud self::returnJson(['success' => true, 'message' => 'Reservierung gelöscht.']); } + protected function getCategoriesAction() + { + $searchTerm = $this->request->q ?? ''; + $categories = AssetManagementModel::getDistinctCategories($searchTerm); + + $result = array_map(function($category) { + return ['value' => $category, 'text' => $category]; + }, $categories); + + self::returnJson($result); + } + protected function printLabelAction() { if (!$this->user->can('AssetAdmin')) { self::sendError("Permission denied", 403); diff --git a/application/AssetManagement/AssetManagementModel.php b/application/AssetManagement/AssetManagementModel.php index 9cef32f94..793e54f10 100644 --- a/application/AssetManagement/AssetManagementModel.php +++ b/application/AssetManagement/AssetManagementModel.php @@ -4,6 +4,7 @@ class AssetManagementModel extends TTCrudBaseModel { public int $id; public string $name; public ?string $description; + public ?string $category; public ?int $mainImageId; // Renamed from imageId public ?string $imageIds; // Changed to JSON (will be a string in PHP) public string $assetNumber; @@ -35,8 +36,7 @@ class AssetManagementModel extends TTCrudBaseModel { foreach ($searchTerms as $term) { if (empty(trim($term))) continue; $escapedTerm = $db->real_escape_string($term); - // For each term, search in name, assetNumber, and description. - $searchConditions[] = "(`name` LIKE '%$escapedTerm%' OR `assetNumber` LIKE '%$escapedTerm%' OR `description` LIKE '%$escapedTerm%')"; + $searchConditions[] = "(`name` LIKE '%$escapedTerm%' OR `assetNumber` LIKE '%$escapedTerm%' OR `description` LIKE '%$escapedTerm%' OR `category` LIKE '%$escapedTerm%')"; } if (!empty($searchConditions)) { @@ -99,8 +99,8 @@ class AssetManagementModel extends TTCrudBaseModel { foreach ($searchTerms as $term) { if (empty(trim($term))) continue; $escapedTerm = $db->real_escape_string($term); - // For each term, search in name, assetNumber, and description. - $searchConditions[] = "(`name` LIKE '%$escapedTerm%' OR `assetNumber` LIKE '%$escapedTerm%' OR `description` LIKE '%$escapedTerm%')"; + // For each term, search in name, assetNumber, description, and category. + $searchConditions[] = "(`name` LIKE '%$escapedTerm%' OR `assetNumber` LIKE '%$escapedTerm%' OR `description` LIKE '%$escapedTerm%' OR `category` LIKE '%$escapedTerm%')"; } if (!empty($searchConditions)) { @@ -128,4 +128,26 @@ class AssetManagementModel extends TTCrudBaseModel { return $result->fetch_assoc()['count']; } + + public static function getDistinctCategories(string $searchTerm = ''): array { + $db = self::getDB(); + $table = self::getFullyQualifiedTable(); + + $sql = "SELECT DISTINCT `category` FROM $table WHERE `category` IS NOT NULL AND `category` != ''"; + + if (!empty($searchTerm)) { + $escapedTerm = $db->real_escape_string($searchTerm); + $sql .= " AND `category` LIKE '%$escapedTerm%'"; + } + + $sql .= " ORDER BY `category` ASC LIMIT 20"; + + $result = $db->query($sql); + $categories = []; + while ($row = $result->fetch_assoc()) { + $categories[] = $row['category']; + } + + return $categories; + } } \ No newline at end of file diff --git a/db/migrations/20260119140000_asset_management_add_category.php b/db/migrations/20260119140000_asset_management_add_category.php new file mode 100644 index 000000000..47dac3c34 --- /dev/null +++ b/db/migrations/20260119140000_asset_management_add_category.php @@ -0,0 +1,33 @@ +getEnvironment() == "thetool") { + $table = $this->table("AssetManagement"); + $table->addColumn('category', 'string', [ + 'limit' => 255, + 'null' => true, + 'default' => null, + 'after' => 'description', + 'comment' => 'Free text category for the asset with autocomplete support', + ]); + $table->addIndex(['category'], ['name' => 'idx_category']); + $table->update(); + } + } + + public function down(): void + { + if ($this->getEnvironment() == "thetool") { + $table = $this->table("AssetManagement"); + $table->removeIndex(['category']); + $table->removeColumn('category'); + $table->update(); + } + } +} diff --git a/public/js/pages/AssetManagement/AssetManagement.js b/public/js/pages/AssetManagement/AssetManagement.js index a26bd81d1..64ab588d0 100644 --- a/public/js/pages/AssetManagement/AssetManagement.js +++ b/public/js/pages/AssetManagement/AssetManagement.js @@ -454,6 +454,14 @@ Vue.component('asset-management-modal', { + @@ -488,9 +496,11 @@ Vue.component('asset-management-modal', { `, data(){ return { + categoryAutoCompleteUrl: window.TT_CONFIG.BASE_PATH + '/AssetManagement/getCategories', asset: { name: '', description: '', + category: '', assetNumber: '', location: 'Liftkammer', serviceDueDate: null,