diff --git a/extensions/Servers/Proxmox/Http/Controllers/ServerTagController.php b/extensions/Servers/Proxmox/Http/Controllers/ServerTagController.php index b10ee7496..8ca3f03a8 100644 --- a/extensions/Servers/Proxmox/Http/Controllers/ServerTagController.php +++ b/extensions/Servers/Proxmox/Http/Controllers/ServerTagController.php @@ -8,58 +8,6 @@ use Paymenter\Extensions\Servers\Proxmox\Models\Server; class ServerTagController { - /** - * Proxmox-style tag colors - */ - private static $proxmoxTagColors = [ - '#e74c3c', // Red - '#e67e22', // Orange - '#f1c40f', // Yellow - '#2ecc71', // Green - '#1abc9c', // Teal - '#3498db', // Blue - '#9b59b6', // Purple - '#e91e63', // Pink - '#00bcd4', // Cyan - '#8bc34a', // Light Green - '#ff5722', // Deep Orange - '#607d8b', // Blue Grey - '#795548', // Brown - '#9e9e9e', // Grey - '#673ab7', // Deep Purple - '#4caf50', // Green Material - ]; - - /** - * Generate a consistent color for a tag based on its text - */ - public static function generateTagColor(string $tagText): string - { - $hash = 0; - for ($i = 0; $i < strlen($tagText); $i++) { - $hash = ord($tagText[$i]) + (($hash << 5) - $hash); - $hash = $hash & $hash; - } - - $index = abs($hash) % count(self::$proxmoxTagColors); - return self::$proxmoxTagColors[$index]; - } - - /** - * Get tags for a server - */ - public function show($serverId) - { - $server = Server::where('id', $serverId) - ->where('user_id', Auth::id()) - ->firstOrFail(); - - return response()->json([ - 'success' => true, - 'tags' => $server->tags ?? [], - ]); - } - /** * Update tags for a server */ @@ -70,44 +18,15 @@ class ServerTagController ->where('user_id', Auth::id()) ->firstOrFail(); - $tags = $request->input('tags', []); - - if (!is_array($tags)) { - $tags = []; - } - - $tags = array_slice($tags, 0, 3); - - $processedTags = []; - foreach ($tags as $tag) { - if (!is_array($tag) || !isset($tag['text'])) continue; - - $text = trim($tag['text'] ?? ''); - if (empty($text)) continue; - - $text = strtolower(preg_replace('/[^a-zA-Z0-9_-]/', '', $text)); - if (empty($text)) continue; - - $text = substr($text, 0, 32); - - $processedTags[] = [ - 'text' => $text, - 'color' => $tag['color'] ?? self::generateTagColor($text), - ]; - } - - $server->tag1 = $processedTags[0]['text'] ?? null; - $server->tag1color = $processedTags[0]['color'] ?? null; - $server->tag2 = $processedTags[1]['text'] ?? null; - $server->tag2color = $processedTags[1]['color'] ?? null; - $server->tag3 = $processedTags[2]['text'] ?? null; - $server->tag3color = $processedTags[2]['color'] ?? null; + $server->tag1 = $request->input('tag1'); + $server->tag1color = $request->input('tag1color'); + $server->tag2 = $request->input('tag2'); + $server->tag2color = $request->input('tag2color'); + $server->tag3 = $request->input('tag3'); + $server->tag3color = $request->input('tag3color'); $server->save(); - return response()->json([ - 'success' => true, - 'tags' => $processedTags, - ]); + return response()->json(['success' => true]); } catch (\Exception $e) { return response()->json([ 'success' => false, @@ -115,15 +34,4 @@ class ServerTagController ], 500); } } - - /** - * Get available tag colors - */ - public function colors() - { - return response()->json([ - 'success' => true, - 'colors' => self::$proxmoxTagColors, - ]); - } } diff --git a/extensions/Servers/Proxmox/Models/Server.php b/extensions/Servers/Proxmox/Models/Server.php index 8581adc17..7d4d49dcf 100644 --- a/extensions/Servers/Proxmox/Models/Server.php +++ b/extensions/Servers/Proxmox/Models/Server.php @@ -31,26 +31,6 @@ class Server extends Model 'tag3color', ]; - /** - * Get tags as an array of objects - */ - public function getTagsAttribute(): array - { - $tags = []; - - if (!empty($this->tag1)) { - $tags[] = ['text' => $this->tag1, 'color' => $this->tag1color ?? '#3498db']; - } - if (!empty($this->tag2)) { - $tags[] = ['text' => $this->tag2, 'color' => $this->tag2color ?? '#3498db']; - } - if (!empty($this->tag3)) { - $tags[] = ['text' => $this->tag3, 'color' => $this->tag3color ?? '#3498db']; - } - - return $tags; - } - public function node() { return $this->belongsTo(Node::class); diff --git a/extensions/Servers/Proxmox/database/migrations/2025_12_18_000000_add_tags_to_ext_proxmox_servers.php b/extensions/Servers/Proxmox/database/migrations/2025_12_18_000000_add_tags_to_ext_proxmox_servers.php new file mode 100644 index 000000000..4505284d0 --- /dev/null +++ b/extensions/Servers/Proxmox/database/migrations/2025_12_18_000000_add_tags_to_ext_proxmox_servers.php @@ -0,0 +1,39 @@ +longText('tag1')->nullable(); + } + if (!Schema::hasColumn('ext_proxmox_servers', 'tag1color')) { + $table->longText('tag1color')->nullable(); + } + if (!Schema::hasColumn('ext_proxmox_servers', 'tag2')) { + $table->longText('tag2')->nullable(); + } + if (!Schema::hasColumn('ext_proxmox_servers', 'tag2color')) { + $table->longText('tag2color')->nullable(); + } + if (!Schema::hasColumn('ext_proxmox_servers', 'tag3')) { + $table->longText('tag3')->nullable(); + } + if (!Schema::hasColumn('ext_proxmox_servers', 'tag3color')) { + $table->longText('tag3color')->nullable(); + } + }); + } + + public function down(): void + { + Schema::table('ext_proxmox_servers', function (Blueprint $table) { + $table->dropColumn(['tag1', 'tag1color', 'tag2', 'tag2color', 'tag3', 'tag3color']); + }); + } +}; diff --git a/extensions/Servers/Proxmox/routes.php b/extensions/Servers/Proxmox/routes.php index 3cc7be50e..9c7cf5143 100644 --- a/extensions/Servers/Proxmox/routes.php +++ b/extensions/Servers/Proxmox/routes.php @@ -42,13 +42,11 @@ Route::group([ Route::get('{server}/help', Help::class)->name('server.help'); }); -// Server Tag Routes - separate prefix to match frontend URL +// Server Tag Routes Route::group([ 'prefix' => 'proxmox/server', 'as' => 'proxmox.server.', 'middleware' => ['web', 'auth'], ], function () { - Route::get('{server}/tags', [ServerTagController::class, 'show'])->name('tags.show'); Route::post('{server}/tags', [ServerTagController::class, 'update'])->name('tags.update'); - Route::get('tags/colors', [ServerTagController::class, 'colors'])->name('tags.colors'); }); diff --git a/extensions/Servers/Proxmox/views/components/layout.blade.php b/extensions/Servers/Proxmox/views/components/layout.blade.php index b429fe84a..0c98266b2 100644 --- a/extensions/Servers/Proxmox/views/components/layout.blade.php +++ b/extensions/Servers/Proxmox/views/components/layout.blade.php @@ -1225,18 +1225,21 @@ } } - // Server Tags Manager - Proxmox-style tags with color generation - function serverTagsManager({serverId, initialTags}) { + // Server Tags Manager - Simple tags stored in database + function serverTagsManager({serverId, tag1, tag1color, tag2, tag2color, tag3, tag3color}) { return { serverId: serverId, show: false, saving: false, - tags: initialTags || [], - editTags: [], - colorPickerIndex: null, + tag1: tag1 || '', + tag1color: tag1color || '#3498db', + tag2: tag2 || '', + tag2color: tag2color || '#2ecc71', + tag3: tag3 || '', + tag3color: tag3color || '#e74c3c', + colorPickerFor: null, message: '', messageType: 'success', - // Proxmox-style tag colors tagColors: [ '#e74c3c', '#e67e22', '#f1c40f', '#2ecc71', '#1abc9c', '#3498db', '#9b59b6', '#e91e63', '#00bcd4', '#8bc34a', '#ff5722', '#607d8b', @@ -1245,74 +1248,55 @@ openModal() { this.show = true; this.message = ''; - this.colorPickerIndex = null; - // Clone tags for editing - this.editTags = JSON.parse(JSON.stringify(this.tags)); + this.colorPickerFor = null; }, closeModal() { this.show = false; - this.colorPickerIndex = null; + this.colorPickerFor = null; }, - addTag() { - if (this.editTags.length < 3) { - this.editTags.push({ - text: '', - color: this.generateTagColor('') - }); - } + selectColorFor(tagNum) { + this.colorPickerFor = this.colorPickerFor === tagNum ? null : tagNum; }, - removeTag(index) { - this.editTags.splice(index, 1); - if (this.colorPickerIndex === index) { - this.colorPickerIndex = null; - } else if (this.colorPickerIndex > index) { - this.colorPickerIndex--; - } + setColor(tagNum, color) { + if (tagNum === 1) this.tag1color = color; + else if (tagNum === 2) this.tag2color = color; + else if (tagNum === 3) this.tag3color = color; + this.colorPickerFor = null; }, - selectColorFor(index) { - this.colorPickerIndex = this.colorPickerIndex === index ? null : index; - }, - setTagColor(index, color) { - if (this.editTags[index]) { - this.editTags[index].color = color; - } - this.colorPickerIndex = null; - }, - generateTagColor(text) { - // Same algorithm as backend for consistency - let hash = 0; - for (let i = 0; i < text.length; i++) { - hash = text.charCodeAt(i) + ((hash << 5) - hash); - hash = hash & hash; - } - const index = Math.abs(hash) % this.tagColors.length; - return this.tagColors[index]; + getColor(tagNum) { + if (tagNum === 1) return this.tag1color; + if (tagNum === 2) return this.tag2color; + if (tagNum === 3) return this.tag3color; + return '#3498db'; }, saveTags() { this.saving = true; this.message = ''; - // Filter out empty tags - const validTags = this.editTags.filter(tag => tag.text && tag.text.trim()); - fetch(`/proxmox/server/${this.serverId}/tags`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content }, - body: JSON.stringify({ tags: validTags }) + body: JSON.stringify({ + tag1: this.tag1.trim(), + tag1color: this.tag1color, + tag2: this.tag2.trim(), + tag2color: this.tag2color, + tag3: this.tag3.trim(), + tag3color: this.tag3color + }) }) .then(r => r.json()) .then(data => { this.saving = false; if (data.success) { - this.tags = data.tags; this.message = 'Tags saved!'; this.messageType = 'success'; setTimeout(() => { this.closeModal(); location.reload(); }, 1000); } else { - this.message = 'Failed to save tags'; + this.message = data.error || 'Failed to save tags'; this.messageType = 'error'; } }) @@ -1330,15 +1314,34 @@
-