feat(features): edición de elementos + catálogo de tipos de elemento

- Migración: tabla feature_types + columnas feature_type_id (FK) e is_active en features.
  Modelo FeatureType; Feature gana featureType() + casts is_active.
- Catálogo de tipos (FeatureTypeManager, ruta /feature-types): CRUD con modal
  (nombre, descripción, color), contador de usos; gateado por edit layers.
- Editor de elementos del proyecto (FeatureManager, ruta projects.features):
  tabla Rappasoft ProjectFeaturesTable (filtros nombre/capa/tipo en cabecera),
  toggle activo/inactivo y borrado individual; modal para editar nombre/tipo/activo.
- Acceso: botón "Elementos" en el dashboard del proyecto (edit layers) y enlace a
  "Tipos de elemento" desde el editor.

Tests: FeatureManagementTest (6). Suite 87 passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-25 16:12:47 +02:00
co-authored by Claude Opus 4.8
parent 1decb19bba
commit 3ee70cf48d
11 changed files with 640 additions and 2 deletions
@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('feature_types', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->string('description')->nullable();
$table->string('color', 7)->default('#6b7280');
$table->timestamps();
$table->softDeletes();
});
Schema::table('features', function (Blueprint $table) {
$table->foreignId('feature_type_id')->nullable()->after('template_id')
->constrained('feature_types')->nullOnDelete();
$table->boolean('is_active')->default(true)->after('status');
});
}
public function down(): void
{
Schema::table('features', function (Blueprint $table) {
$table->dropConstrainedForeignId('feature_type_id');
$table->dropColumn('is_active');
});
Schema::dropIfExists('feature_types');
}
};