diff --git a/app/Helpers/DocumentIdentifier.php b/app/Helpers/DocumentIdentifier.php deleted file mode 100644 index 6d6c6e1..0000000 --- a/app/Helpers/DocumentIdentifier.php +++ /dev/null @@ -1,134 +0,0 @@ - 'Ingeniería', - 'ARC' => 'Arquitectura', - 'CIV' => 'Civil', - 'MEC' => 'Mecánica', - 'ELC' => 'Eléctrica', - 'INS' => 'Instrumentación', - 'PIP' => 'Piping', - 'STR' => 'Estructural' - ]; - - private $tiposDocumentoValidos = [ - 'DRW' => 'Dibujo', - 'ESP' => 'Especificación', - 'LST' => 'Lista de materiales', - 'PRO' => 'Procedimiento', - 'INF' => 'Informe', - 'MAN' => 'Manual', - 'CAL' => 'Cálculo', - 'REP' => 'Reporte' - ]; - - public function analizarDocumento($codigoCompleto) { - // Validar formato básico - if (strpos($codigoCompleto, ' - ') === false) { - return $this->crearResultadoError("Formato inválido: falta separador ' - '"); - } - - list($codigo, $nombre) = explode(' - ', $codigoCompleto, 2); - $segmentos = explode('-', $codigo); - - // Validar número de segmentos - if (count($segmentos) != 5) { - return $this->crearResultadoError("Número incorrecto de segmentos"); - } - - // Extraer y validar cada parte - $codigoProyecto = $segmentos[0]; - $codigoMaker = $segmentos[1]; - $disciplina = $segmentos[2]; - $tipoDocumento = $segmentos[3]; - $revisionCompleta = $segmentos[4]; - - // Validar formato de revisión - if (strpos($revisionCompleta, 'REV.') !== 0) { - return $this->crearResultadoError("Formato de revisión inválido"); - } - - $numeroRevision = substr($revisionCompleta, 4); // Remover "REV." - - // Validar número de revisión - if (!ctype_digit($numeroRevision) || strlen($numeroRevision) != 2) { - return $this->crearResultadoError("Número de revisión inválido"); - } - - // Validar disciplinas y tipos - $disciplinaValida = $this->validarDisciplina($disciplina); - $tipoValido = $this->validarTipoDocumento($tipoDocumento); - - return [ - 'codigo_completo' => $codigoCompleto, - 'codigo_proyecto' => $codigoProyecto, - 'codigo_maker' => $codigoMaker, - 'disciplina' => $disciplina, - 'disciplina_desc' => $disciplinaValida, - 'tipo_documento' => $tipoDocumento, - 'tipo_documento_desc' => $tipoValido, - 'revision' => $numeroRevision, - 'nombre_documento' => $nombre, - 'estructura_valida' => true, - 'errores' => [] - ]; - } - - private function validarDisciplina($codigo) { - return isset($this->disciplinasValidas[$codigo]) - ? $this->disciplinasValidas[$codigo] - : "Desconocida"; - } - - private function validarTipoDocumento($codigo) { - return isset($this->tiposDocumentoValidos[$codigo]) - ? $this->tiposDocumentoValidos[$codigo] - : "Desconocido"; - } - - private function crearResultadoError($mensaje) { - return [ - 'estructura_valida' => false, - 'errores' => [$mensaje] - ]; - } - - // Método para generar un nuevo código - public function generarCodigo($proyecto, $maker, $disciplina, $tipo, $revision, $nombre = '') { - $revisionFormateada = str_pad($revision, 2, '0', STR_PAD_LEFT); - $codigo = "{$proyecto}-{$maker}-{$disciplina}-{$tipo}-REV.{$revisionFormateada}"; - - if (!empty($nombre)) { - $codigo .= " - {$nombre}"; - } - - return $codigo; - } -} - -// EJEMPLOS DE USO -/* -$analizador = new DocumentIdentifier(); - -// Analizar un código existente -$codigo1 = "MP00002-SOGOS-ENG-DRW-REV.01 - Plano principal"; -$resultado1 = $analizador->analizarDocumento($codigo1); - -echo "Análisis del documento:\n"; -print_r($resultado1); - -// Generar un nuevo código -$nuevoCodigo = $analizador->generarCodigo( - 'MP00002', - 'SOGOS', - 'CIV', - 'ESP', - '03', - 'Especificación técnica de cimientos' -); - -echo "\nNuevo código generado: " . $nuevoCodigo . "\n";*/ - -?> \ No newline at end of file diff --git a/app/Helpers/ProjectNamingSchema.php b/app/Helpers/ProjectNamingSchema.php deleted file mode 100644 index 322a51b..0000000 --- a/app/Helpers/ProjectNamingSchema.php +++ /dev/null @@ -1,47 +0,0 @@ - null, + 'name' => '', + 'color' => '#6b7280', + 'text_color' => '#ffffff', + 'description' => '', + 'allow_upload' => true, + 'allow_edit' => true, + 'allow_delete' => false, + 'requires_approval' => false, + 'is_default' => false, + ]; + + public $showDeleteModal = false; + public $statusToDelete = null; + public $orderedStatusIds = []; + + protected $rules = [ + 'formData.name' => 'required|string|min:2|max:100', + 'formData.color' => 'required|string|max:7', + 'formData.text_color' => 'nullable|string|max:7', + 'formData.description' => 'nullable|string|max:500', + 'formData.allow_upload' => 'boolean', + 'formData.allow_edit' => 'boolean', + 'formData.allow_delete' => 'boolean', + 'formData.requires_approval' => 'boolean', + 'formData.is_default' => 'boolean', + ]; + + public function mount(Project $project) + { + $this->project = $project; + $this->loadStatuses(); + } + + public function loadStatuses() + { + $this->statuses = $this->project->documentStatuses() + ->orderBy('order') + ->get() + ->toArray(); + + $this->statuses = []; + + $this->orderedStatusIds = collect($this->statuses)->pluck('id')->toArray(); + } + + public function openForm($statusId = null) + { + $this->resetForm(); + + if ($statusId) { + $status = ProjectDocumentStatus::find($statusId); + if ($status && $status->project_id === $this->project->id) { + $this->formData = [ + 'id' => $status->id, + 'name' => $status->name, + 'color' => $status->color, + 'text_color' => $status->text_color, + 'description' => $status->description, + 'allow_upload' => $status->allow_upload, + 'allow_edit' => $status->allow_edit, + 'allow_delete' => $status->allow_delete, + 'requires_approval' => $status->requires_approval, + 'is_default' => $status->is_default, + ]; + } + } + + $this->showForm = true; + } + + public function closeForm() + { + $this->showForm = false; + $this->resetForm(); + } + + public function resetForm() + { + $this->formData = [ + 'id' => null, + 'name' => '', + 'color' => '#6b7280', + 'text_color' => '#ffffff', + 'description' => '', + 'allow_upload' => true, + 'allow_edit' => true, + 'allow_delete' => false, + 'requires_approval' => false, + 'is_default' => false, + ]; + $this->resetErrorBag(); + } + + public function saveStatus() + { + $this->validate(); + + try { + DB::beginTransaction(); + + // Si se marca como default, quitar el default de otros estados + if ($this->formData['is_default']) { + $this->project->documentStatuses()->update(['is_default' => false]); + } + + $data = [ + 'name' => $this->formData['name'], + 'color' => $this->formData['color'], + 'text_color' => $this->formData['text_color'] ?: $this->calculateTextColor($this->formData['color']), + 'description' => $this->formData['description'], + 'allow_upload' => $this->formData['allow_upload'], + 'allow_edit' => $this->formData['allow_edit'], + 'allow_delete' => $this->formData['allow_delete'], + 'requires_approval' => $this->formData['requires_approval'], + 'is_default' => $this->formData['is_default'], + ]; + + if ($this->formData['id']) { + // Editar estado existente + $status = ProjectDocumentStatus::find($this->formData['id']); + if ($status && $status->project_id === $this->project->id) { + $status->update($data); + } + } else { + // Crear nuevo estado + $data['project_id'] = $this->project->id; + $data['slug'] = $this->generateUniqueSlug($this->formData['name']); + $data['order'] = $this->project->documentStatuses()->count(); + + ProjectDocumentStatus::create($data); + } + + DB::commit(); + + $this->loadStatuses(); + $this->closeForm(); + + $this->dispatch('show-message', [ + 'type' => 'success', + 'message' => $this->formData['id'] ? 'Estado actualizado correctamente.' : 'Estado creado correctamente.' + ]); + + } catch (\Exception $e) { + DB::rollBack(); + $this->dispatch('show-message', [ + 'type' => 'error', + 'message' => 'Error al guardar: ' . $e->getMessage() + ]); + } + } + + private function generateUniqueSlug($name) + { + $slug = Str::slug($name); + $originalSlug = $slug; + $counter = 1; + + while ($this->project->documentStatuses()->where('slug', $slug)->exists()) { + $slug = $originalSlug . '-' . $counter; + $counter++; + } + + return $slug; + } + + private function calculateTextColor($backgroundColor) + { + // Convertir hex a RGB + $hex = str_replace('#', '', $backgroundColor); + if (strlen($hex) == 3) { + $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2]; + } + + $r = hexdec(substr($hex, 0, 2)); + $g = hexdec(substr($hex, 2, 2)); + $b = hexdec(substr($hex, 4, 2)); + + // Calcular luminosidad + $luminosity = (0.299 * $r + 0.587 * $g + 0.114 * $b) / 255; + + return $luminosity > 0.5 ? '#000000' : '#ffffff'; + } + + public function confirmDelete($statusId) + { + $this->statusToDelete = ProjectDocumentStatus::find($statusId); + if ($this->statusToDelete && $this->statusToDelete->project_id === $this->project->id) { + $this->showDeleteModal = true; + } + } + + public function closeDeleteModal() + { + $this->showDeleteModal = false; + $this->statusToDelete = null; + } + + public function deleteStatus() + { + if (!$this->statusToDelete) { + return; + } + + try { + // Verificar que no haya documentos con este estado + if ($this->statusToDelete->documents()->exists()) { + $this->dispatch('show-message', [ + 'type' => 'error', + 'message' => 'No se puede eliminar el estado porque hay documentos asociados.' + ]); + $this->closeDeleteModal(); + return; + } + + // Si es el estado por defecto, establecer otro como default + if ($this->statusToDelete->is_default) { + $newDefault = $this->project->documentStatuses() + ->where('id', '!=', $this->statusToDelete->id) + ->first(); + + if ($newDefault) { + $newDefault->update(['is_default' => true]); + } + } + + $this->statusToDelete->delete(); + $this->loadStatuses(); + + $this->dispatch('show-message', [ + 'type' => 'success', + 'message' => 'Estado eliminado correctamente.' + ]); + + } catch (\Exception $e) { + $this->dispatch('show-message', [ + 'type' => 'error', + 'message' => 'Error al eliminar: ' . $e->getMessage() + ]); + } + + $this->closeDeleteModal(); + } + + public function updateOrder() + { + try { + foreach ($this->orderedStatusIds as $index => $statusId) { + $status = ProjectDocumentStatus::find($statusId); + if ($status && $status->project_id === $this->project->id) { + $status->update(['order' => $index]); + } + } + + $this->loadStatuses(); + + $this->dispatch('show-message', [ + 'type' => 'success', + 'message' => 'Orden actualizado correctamente.' + ]); + + } catch (\Exception $e) { + $this->dispatch('show-message', [ + 'type' => 'error', + 'message' => 'Error al actualizar el orden: ' . $e->getMessage() + ]); + } + } + + public function moveUp($statusId) + { + $index = array_search($statusId, $this->orderedStatusIds); + + if ($index > 0) { + $temp = $this->orderedStatusIds[$index]; + $this->orderedStatusIds[$index] = $this->orderedStatusIds[$index - 1]; + $this->orderedStatusIds[$index - 1] = $temp; + + $this->updateOrder(); + } + } + + public function moveDown($statusId) + { + $index = array_search($statusId, $this->orderedStatusIds); + + if ($index < count($this->orderedStatusIds) - 1) { + $temp = $this->orderedStatusIds[$index]; + $this->orderedStatusIds[$index] = $this->orderedStatusIds[$index + 1]; + $this->orderedStatusIds[$index + 1] = $temp; + + $this->updateOrder(); + } + } + + public function render() + { + return view('livewire.project-document-status-manager'); + } +} \ No newline at end of file diff --git a/app/Livewire/ProjectNameCoder.php b/app/Livewire/ProjectNameCoder.php index 4f8610a..d02bf04 100644 --- a/app/Livewire/ProjectNameCoder.php +++ b/app/Livewire/ProjectNameCoder.php @@ -23,11 +23,9 @@ class ProjectNameCoder extends Component // Inicializar con un componente vacío $this->project = $project; - // Si hay configuración inicial, cargarla if ($project->codingConfig) { $this->loadDatabaseConfiguration(); } else { - // Inicializar con un componente vacío $this->addComponent(); } } @@ -180,7 +178,7 @@ class ProjectNameCoder extends Component // Preparar la configuración completa $configData = [ 'components' => $this->components, - //'total_components' => $this->componentsCount, + 'total_components' => $this->componentsCount, //'total_document_types' => $this->totalDocumentTypes, //'generated_format' => $this->generateFormatString(), //'last_updated' => now()->toDateTimeString(), diff --git a/app/Livewire/ProjectShow.php b/app/Livewire/ProjectShow.php index adda360..15b9072 100644 --- a/app/Livewire/ProjectShow.php +++ b/app/Livewire/ProjectShow.php @@ -11,7 +11,7 @@ use App\Models\Document; use Illuminate\Validation\Rule; use Illuminate\Support\Facades\Auth; -use App\Helpers\DocumentIdentifier; +use App\Services\ProjectCodeService; class ProjectShow extends Component { @@ -35,10 +35,12 @@ class ProjectShow extends Component protected $listeners = ['documents-updated' => '$refresh']; + protected ProjectCodeService $codeService; public function mount(Project $project) { - $this->project = $project->load('rootFolders'); + $this->project = $project; + $this->project['javi'] = 'braña'; $this->currentFolder = $this->project->rootFolders->first() ?? null; } @@ -129,6 +131,13 @@ class ProjectShow extends Component $this->validate([ 'selectedFiles.*' => 'file|max:10240|mimes:pdf,docx,xlsx,jpg,png' ]); + + $isValid = app(ProjectCodeService::class)->validate($this->project, $files[0]->getClientOriginalName()); + if ($isValid) { + echo "✅ Código válido\n"; + } else { + echo "❌ Código inválido\n"; + } $this->selectedFiles = array_merge($this->selectedFiles, $files); } @@ -142,10 +151,16 @@ class ProjectShow extends Component public function uploadFiles(): void { + $validator = new ProjectCodeValidator($project->codingConfig); foreach ($this->selectedFiles as $file) { - //$analizador = analizarDocumento(); //print_r($analizador); //$resultado1 = $analizador->analizarDocumento($file->getClientOriginalName()); + if ($validator->validate($file->getClientOriginalName())) { + echo "✅ Código válido\n"; + } else { + echo "❌ Código inválido\n"; + return; + } $code = $this->project->reference; diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index f7e8e44..717be5a 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -8,6 +8,9 @@ use App\Livewire\PdfViewer; use App\Livewire\ProjectShow; use App\Livewire\Toolbar; use App\View\Components\Multiselect; +use App\Services\ProjectCodeService; +use App\Services\ProjectCodeValidator; + use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\Validator; @@ -21,7 +24,16 @@ class AppServiceProvider extends ServiceProvider */ public function register(): void { - // + // Registrar el Service + $this->app->singleton(ProjectCodeService::class, function ($app) { + return new ProjectCodeService(new ProjectCodeValidator([])); + }); + + // O si prefieres, registrar el validador por separado + $this->app->bind(ProjectCodeValidator::class, function ($app, $params) { + // $params[0] contendría los datos del proyecto + return new ProjectCodeValidator($params[0] ?? []); + }); } /** diff --git a/app/Services/ProjectCodeService.php b/app/Services/ProjectCodeService.php new file mode 100644 index 0000000..c2dcc7a --- /dev/null +++ b/app/Services/ProjectCodeService.php @@ -0,0 +1,80 @@ +validator = $validator; + } + + /** + * Crea un validador para un proyecto específico + */ + public function forProject(Project $project): ProjectCodeValidator + { + // Si es un modelo Eloquent, convertir a array + //$projectData = $project instanceof Project ? $project->toArray() : $project; + + return new ProjectCodeValidator($project); + } + + /** + * Valida un código rápidamente + */ + public function validate(Project $project, string $code): bool + { + $validator = $this->forProject($project); + return $validator->validate($code); + } + + /** + * Analiza un código y devuelve detalles + */ + public function analyze(Project $project, string $code): array + { + $validator = $this->forProject($project); + return $validator->analyze($code); + } + + /** + * Genera un nuevo código para el proyecto + */ + public function generate( + Project $project, + array $components, + string $documentName + ): string + { + $validator = $this->forProject($project); + return $validator->generateCode($components, $documentName); + } + + /** + * Obtiene la configuración de codificación + */ + public function getConfiguration(Project $project): array + { + $validator = $this->forProject($project); + + return [ + 'format' => $validator->getFormat(), + 'reference' => $validator->getReference(), + 'separator' => $validator->getSeparator(), + 'sequence_length' => $validator->getSequenceLength(), + 'components' => [ + 'maker' => $validator->getAllowedCodesForComponent('maker'), + 'system' => $validator->getAllowedCodesForComponent('system'), + 'discipline' => $validator->getAllowedCodesForComponent('discipline'), + 'document_type' => $validator->getAllowedCodesForComponent('document_type'), + ] + ]; + } +} \ No newline at end of file diff --git a/app/Services/ProjectCodeValidator.php b/app/Services/ProjectCodeValidator.php new file mode 100644 index 0000000..519cadb --- /dev/null +++ b/app/Services/ProjectCodeValidator.php @@ -0,0 +1,543 @@ +projectData = $project; + // Validar que exista la configuración de codificación + /*if (!isset($this->projectData['codingConfig'])) { + throw new InvalidArgumentException('El proyecto no tiene configuración de codificación'); + }*/ + + $this->initializeConfiguration(); + } + + /** + * Inicializa la configuración a partir de los datos del proyecto + */ + private function initializeConfiguration(): void + { + $codingConfig = $this->projectData['codingConfig']; + + // Obtener componentes ordenados por 'order' + $this->components = $codingConfig['elements']['components'] ?? []; + usort($this->components, fn($a, $b) => $a['order'] <=> $b['order']); + + $this->separator = $codingConfig['separator'] ?? '-'; + $this->reference = $this->projectData['reference'] ?? ''; + $this->sequenceLength = $codingConfig['sequence_length'] ?? 4; + } + + /** + * Valida si un código cumple con la configuración de codificación + * + * @param string $code El código a validar + * @return bool True si el código es válido + */ + public function validate(string $code): bool + { + // Validación básica: número de partes + $parts = explode($this->separator, $code); + if (count($parts) !== 7) { + return false; + } + + // Validar referencia (primera parte) + if ($parts[0] !== $this->reference) { + return false; + } + + // Validar cada componente (partes 2-6) + for ($i = 1; $i <= 5; $i++) { + if (!$this->validateComponent($i - 1, $parts[$i])) { + return false; + } + } + + // La parte 7 (DOCUMENT_NAME) no tiene validación específica + return true; + } + + /** + * Valida un componente específico del código + * + * @param int $componentIndex Índice del componente (0-4) + * @param string $value Valor a validar + * @return bool True si el valor es válido para el componente + */ + private function validateComponent(int $componentIndex, string $value): bool + { + if (!isset($this->components[$componentIndex])) { + return false; + } + + $component = $this->components[$componentIndex]; + $headerLabel = $component['headerLabel'] ?? ''; + + // Validar longitud máxima + $maxLength = $component['data']['maxLength'] ?? null; + if ($maxLength && strlen($value) > $maxLength) { + return false; + } + + // Validar según el tipo de componente + if ($headerLabel === 'Version') { + return $this->validateVersionComponent($value); + } + + // Validar contra códigos permitidos + return $this->validateAgainstAllowedCodes($component, $value); + } + + /** + * Valida el componente de versión + * + * @param string $value Valor de la versión + * @return bool True si es válido + */ + private function validateVersionComponent(string $value): bool + { + // La versión debe ser numérica + if (!is_numeric($value)) { + return false; + } + + // La versión debe tener la longitud especificada + if (strlen($value) !== $this->sequenceLength) { + return false; + } + + return true; + } + + /** + * Valida un valor contra los códigos permitidos de un componente + * + * @param array $component Configuración del componente + * @param string $value Valor a validar + * @return bool True si el valor está en los códigos permitidos + */ + private function validateAgainstAllowedCodes(array $component, string $value): bool + { + $documentTypes = $component['data']['documentTypes'] ?? []; + + if (empty($documentTypes)) { + return true; // No hay restricciones de códigos + } + + $allowedCodes = array_column($documentTypes, 'code'); + return in_array($value, $allowedCodes, true); + } + + /** + * Realiza un análisis detallado de un código + * + * @param string $code El código a analizar + * @return array Array con información detallada de la validación + */ + public function analyze(string $code): array + { + $result = [ + 'is_valid' => false, + 'code' => $code, + 'parts' => [], + 'errors' => [], + 'warnings' => [] + ]; + + // Dividir en partes + $parts = explode($this->separator, $code); + + // Verificar número de partes + $expectedParts = 7; + if (count($parts) !== $expectedParts) { + $result['errors'][] = sprintf( + 'El código debe tener %d partes separadas por "%s", tiene %d', + $expectedParts, + $this->separator, + count($parts) + ); + return $result; + } + + // Nombres de los componentes según el orden esperado + $componentNames = [ + 'reference', + 'maker', + 'system', + 'discipline', + 'document_type', + 'version', + 'document_name' + ]; + + // Analizar cada parte + foreach ($parts as $index => $value) { + $analysis = $this->analyzePart($index, $value); + $analysis['name'] = $componentNames[$index] ?? 'unknown'; + + $result['parts'][] = $analysis; + + if (!$analysis['is_valid']) { + $result['errors'][] = sprintf( + 'Parte %d (%s): %s', + $index + 1, + $componentNames[$index], + $analysis['error'] ?? 'Error desconocido' + ); + } + } + + $result['is_valid'] = empty($result['errors']); + return $result; + } + + /** + * Analiza una parte específica del código + * + * @param int $partIndex Índice de la parte (0-6) + * @param string $value Valor de la parte + * @return array Análisis detallado de la parte + */ + private function analyzePart(int $partIndex, string $value): array + { + $analysis = [ + 'index' => $partIndex, + 'value' => $value, + 'is_valid' => true, + 'expected' => '', + 'error' => '', + 'notes' => [] + ]; + + switch ($partIndex) { + case 0: // Referencia + $analysis['expected'] = $this->reference; + if ($value !== $this->reference) { + $analysis['is_valid'] = false; + $analysis['error'] = sprintf( + 'Referencia incorrecta. Esperado: %s', + $this->reference + ); + } + break; + + case 1: // Maker (componente 0) + case 2: // System (componente 1) + case 3: // Discipline (componente 2) + case 4: // Document Type (componente 3) + $componentIndex = $partIndex - 1; + if (isset($this->components[$componentIndex])) { + $component = $this->components[$componentIndex]; + $analysis = array_merge($analysis, $this->analyzeComponent($component, $value)); + } + break; + + case 5: // Version (componente 4) + if (isset($this->components[4])) { + $component = $this->components[4]; + $analysis = array_merge($analysis, $this->analyzeComponent($component, $value)); + + // Información adicional para la versión + $analysis['notes'][] = sprintf( + 'Longitud de secuencia: %d', + $this->sequenceLength + ); + } + break; + + case 6: // Document Name + $analysis['notes'][] = 'Nombre del documento (sin restricciones específicas)'; + break; + + default: + $analysis['is_valid'] = false; + $analysis['error'] = 'Índice de parte no válido'; + break; + } + + return $analysis; + } + + /** + * Analiza un componente específico + * + * @param array $component Configuración del componente + * @param string $value Valor a analizar + * @return array Análisis del componente + */ + private function analyzeComponent(array $component, string $value): array + { + $result = [ + 'is_valid' => true, + 'expected' => '', + 'error' => '', + 'notes' => [] + ]; + + $headerLabel = $component['headerLabel'] ?? ''; + $maxLength = $component['data']['maxLength'] ?? null; + $documentTypes = $component['data']['documentTypes'] ?? []; + + // Validar longitud + if ($maxLength && strlen($value) > $maxLength) { + $result['is_valid'] = false; + $result['error'] = sprintf( + 'Longitud máxima excedida: %d (actual: %d)', + $maxLength, + strlen($value) + ); + } + + // Validar tipo de componente + if ($headerLabel === 'Version') { + if (!is_numeric($value)) { + $result['is_valid'] = false; + $result['error'] = 'Debe ser numérico'; + } elseif (strlen($value) !== $this->sequenceLength) { + $result['is_valid'] = false; + $result['error'] = sprintf( + 'Longitud incorrecta. Esperado: %d, Actual: %d', + $this->sequenceLength, + strlen($value) + ); + } + + $result['expected'] = sprintf('Número de %d dígitos', $this->sequenceLength); + } else { + // Obtener códigos permitidos + $allowedCodes = array_column($documentTypes, 'code'); + if (!empty($allowedCodes) && !in_array($value, $allowedCodes, true)) { + $result['is_valid'] = false; + $result['error'] = sprintf( + 'Código no permitido. Permitidos: %s', + implode(', ', $allowedCodes) + ); + } + + $result['expected'] = $allowedCodes ? implode('|', $allowedCodes) : 'Sin restricciones'; + $result['notes'][] = sprintf('Componente: %s', $headerLabel); + + // Agregar etiquetas de los códigos permitidos + $labels = []; + foreach ($documentTypes as $type) { + if (isset($type['label'])) { + $labels[] = sprintf('%s = %s', $type['code'], $type['label']); + } + } + if (!empty($labels)) { + $result['notes'][] = 'Significados: ' . implode(', ', $labels); + } + } + + return $result; + } + + /** + * Genera un código válido basado en la configuración + * + * @param array $componentsValues Valores para cada componente + * @param string $documentName Nombre del documento + * @return string Código generado + * @throws InvalidArgumentException Si los valores no son válidos + */ + public function generateCode(array $componentsValues, string $documentName): string + { + // Validar que tengamos valores para todos los componentes + $requiredComponents = 5; // Maker, System, Discipline, Document Type, Version + + if (count($componentsValues) < $requiredComponents) { + throw new InvalidArgumentException( + sprintf('Se requieren valores para %d componentes', $requiredComponents) + ); + } + + // Construir las partes del código + $parts = [$this->reference]; + + // Agregar componentes validados + for ($i = 0; $i < $requiredComponents; $i++) { + $value = $componentsValues[$i]; + + if (!$this->validateComponent($i, $value)) { + throw new InvalidArgumentException( + sprintf('Valor inválido para componente %d: %s', $i, $value) + ); + } + + $parts[] = $value; + } + + // Agregar nombre del documento + $parts[] = $this->sanitizeDocumentName($documentName); + + return implode($this->separator, $parts); + } + + /** + * Sanitiza el nombre del documento para usarlo en el código + * + * @param string $documentName Nombre del documento + * @return string Nombre sanitizado + */ + private function sanitizeDocumentName(string $documentName): string + { + // Reemplazar caracteres no deseados + $sanitized = preg_replace('/[^a-zA-Z0-9_-]/', '_', $documentName); + + // Limitar longitud si es necesario + return substr($sanitized, 0, 50); + } + + /** + * Obtiene los códigos permitidos para un componente específico + * + * @param string $componentName Nombre del componente (maker, system, discipline, document_type) + * @return array Array de códigos permitidos + */ + public function getAllowedCodesForComponent(string $componentName): array + { + $componentMap = [ + 'maker' => 0, + 'system' => 1, + 'discipline' => 2, + 'document_type' => 3 + ]; + + if (!isset($componentMap[$componentName])) { + return []; + } + + $index = $componentMap[$componentName]; + if (!isset($this->components[$index])) { + return []; + } + + $component = $this->components[$index]; + $documentTypes = $component['data']['documentTypes'] ?? []; + + $result = []; + foreach ($documentTypes as $type) { + $result[] = [ + 'code' => $type['code'], + 'label' => $type['label'] ?? $type['code'], + 'max_length' => $type['max_length'] ?? $component['data']['maxLength'] ?? null + ]; + } + + return $result; + } + + /** + * Obtiene el formato de codificación + * + * @return string Formato de codificación + */ + public function getFormat(): string + { + return $this->projectData['coding_config']['format'] ?? ''; + } + + /** + * Obtiene la referencia del proyecto + * + * @return string Referencia del proyecto + */ + public function getReference(): string + { + return $this->reference; + } + + /** + * Obtiene el separador usado en la codificación + * + * @return string Separador + */ + public function getSeparator(): string + { + return $this->separator; + } + + /** + * Obtiene la longitud de secuencia para la versión + * + * @return int Longitud de secuencia + */ + public function getSequenceLength(): int + { + return $this->sequenceLength; + } +} + +// Ejemplo de uso: +// Suponiendo que $projectDataVariable es la variable que contiene tus datos del proyecto +/* +$validator = new ProjectCodeValidator($projectDataVariable); + +// Validar un código +$codigo = "SOGOS0001-SOGOS-PV0-CV-DWG-0001-InformeTecnico"; +if ($validator->validate($codigo)) { + echo "✅ Código válido\n"; +} else { + echo "❌ Código inválido\n"; +} + +// Analizar un código detalladamente +$analisis = $validator->analyze($codigo); +echo "Código analizado: " . $analisis['code'] . "\n"; +echo "Válido: " . ($analisis['is_valid'] ? 'Sí' : 'No') . "\n"; + +if (!$analisis['is_valid']) { + echo "Errores:\n"; + foreach ($analisis['errors'] as $error) { + echo "- $error\n"; + } +} + +// Generar un código nuevo +try { + $componentes = ['SOGOS', 'PV0', 'CV', 'DWG', '0014']; + $nuevoCodigo = $validator->generateCode($componentes, 'PlanosGenerales'); + echo "Código generado: $nuevoCodigo\n"; +} catch (InvalidArgumentException $e) { + echo "Error al generar código: " . $e->getMessage() . "\n"; +} + +// Obtener códigos permitidos para un componente +$systemCodes = $validator->getAllowedCodesForComponent('system'); +echo "Códigos permitidos para System:\n"; +foreach ($systemCodes as $code) { + echo "- {$code['code']}: {$code['label']}\n"; +} +*/ \ No newline at end of file diff --git a/resources/views/livewire/project-document-status-manager.blade.php b/resources/views/livewire/project-document-status-manager.blade.php new file mode 100644 index 0000000..3ab6361 --- /dev/null +++ b/resources/views/livewire/project-document-status-manager.blade.php @@ -0,0 +1,348 @@ +
+ +
+
+

Estados de Documentos

+

Configura los estados disponibles para los documentos de este proyecto

+
+ + +
+ + +
+ @if(false) + @if(count($statuses) > 0) +
+ @foreach($statuses as $status) +
+
+ +
+ +
+
+ + {{ substr($status['name'], 0, 2) }} + +
+
+ + +
+
+ {{ $status['name'] }} + @if($status['is_default']) + + Por defecto + + @endif +
+ + @if($status['description']) +

{{ $status['description'] }}

+ @endif + + +
+ @if($status['allow_upload']) + + Subir + + @endif + + @if($status['allow_edit']) + + Editar + + @endif + + @if($status['allow_delete']) + + Eliminar + + @endif + + @if($status['requires_approval']) + + Aprobación + + @endif +
+
+
+ + +
+ +
+ + + +
+ + + + + + @if(!$status['is_default']) + + @endif +
+
+
+ @endforeach +
+ @else +
+ +

No hay estados configurados para este proyecto.

+ +
+ @endif +
+ @endif + + +
+ +
+ +
+

+ {{ $formData['id'] ? 'Editar Estado' : 'Crear Nuevo Estado' }} +

+
+ + +
+ +
+ + + @error('formData.name') +

{{ $message }}

+ @enderror +
+ + +
+
+ +
+ + +
+ @error('formData.color') +

{{ $message }}

+ @enderror +
+ +
+ +
+ + +
+ @error('formData.text_color') +

{{ $message }}

+ @enderror +
+
+ + +
+

Previsualización:

+
+ + {{ $formData['name'] ?: 'Nombre del estado' }} + + (Esto es cómo se verá) +
+
+ + +
+ + + @error('formData.description') +

{{ $message }}

+ @enderror +
+ + +
+

Permisos:

+
+ + + + + + + +
+
+ + +
+ +

+ Solo puede haber un estado por defecto. Si marcas este, se desmarcará automáticamente el anterior. +

+
+ + +
+ + +
+
+
+
+ + +
+ +
+
+

Confirmar eliminación

+
+ +
+

+ ¿Estás seguro de que deseas eliminar el estado + "{{ $statusToDelete->name ?? '' }}"? +

+ + @if($statusToDelete && $statusToDelete->documents()->exists()) +
+

+ + No se puede eliminar porque hay documentos asociados a este estado. +

+
+ @endif + +
+ + + @if(!($statusToDelete && $statusToDelete->documents()->exists())) + + @endif +
+
+
+
+
+ diff --git a/resources/views/livewire/project/show.blade.php b/resources/views/livewire/project/show.blade.php index 1e4275e..370d8d5 100644 --- a/resources/views/livewire/project/show.blade.php +++ b/resources/views/livewire/project/show.blade.php @@ -18,6 +18,9 @@

{{ $project->reference }}

+

+ {{ $project['codingConfig'] }} +

@if($project->phone) diff --git a/resources/views/project-settings/index.blade.php b/resources/views/project-settings/index.blade.php index 2460ec0..7bf8e0e 100644 --- a/resources/views/project-settings/index.blade.php +++ b/resources/views/project-settings/index.blade.php @@ -69,11 +69,6 @@

Estados de Documentos

-
@@ -82,296 +77,9 @@
+ - - - - - - - - @push('scripts') - - @endpush \ No newline at end of file