new functionality: Add project coding configuration feature for projects
This commit is contained in:
377
resources/views/project-settings/index.blade.php
Normal file
377
resources/views/project-settings/index.blade.php
Normal file
@@ -0,0 +1,377 @@
|
||||
|
||||
<x-layouts.app title="{{ 'Configuración del Proyecto - ' . $project->name }}"
|
||||
:showSidebar={{ $showSidebar }}>
|
||||
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-gray-900">Configuración del Proyecto</h2>
|
||||
<p class="text-gray-600">{{ $project->reference }} - {{ $project->name }}</p>
|
||||
</div>
|
||||
|
||||
<flux:button
|
||||
href="{{ route('projects.show', $project) }}"
|
||||
icon:trailing="arrow-uturn-left"
|
||||
variant="ghost"
|
||||
>
|
||||
Volver al Proyecto
|
||||
</flux:button>
|
||||
</div>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="mb-4 p-4 bg-green-100 text-green-700 rounded-lg">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(session('error'))
|
||||
<div class="mb-4 p-4 bg-red-100 text-red-700 rounded-lg">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Tabs de navegación -->
|
||||
<div x-data="{ activeTab: 'coding' }" class="bg-white rounded-lg shadow-md border-1">
|
||||
<div class="border-b border-gray-200 mb-6">
|
||||
<nav class="-mb-px flex space-x-8">
|
||||
<button @click="activeTab = 'coding'"
|
||||
:class="activeTab === 'coding' ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700'"
|
||||
class="py-4 px-1 border-b-2 font-medium">
|
||||
Codificación de Documentos
|
||||
</button>
|
||||
|
||||
<button @click="activeTab = 'statuses'"
|
||||
:class="activeTab === 'statuses' ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700'"
|
||||
class="py-4 px-1 border-b-2 font-medium">
|
||||
Estados de Documentos
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="p-6">
|
||||
<!-- Sección de Codificación -->
|
||||
<div x-show="activeTab === 'coding'">
|
||||
<div id="coding" class="mb-12">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900">Codificación de Documentos</h3>
|
||||
<div class="text-sm text-gray-500">
|
||||
Última actualización: {{ $project->codingConfig->updated_at->format('d/m/Y H:i') ?? 'No configurado' }}
|
||||
</div>
|
||||
</div>
|
||||
<livewire:project-name-coder
|
||||
:project="$project"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sección de Estados -->
|
||||
<div x-show="activeTab === 'statuses'">
|
||||
<div id="statuses">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900">Estados de Documentos</h3>
|
||||
<button type="button"
|
||||
onclick="openStatusModal()"
|
||||
class="btn btn-primary">
|
||||
<x-icons icon="plus" class="w-4 h-4 mr-1" /> Nuevo Estado
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow overflow-hidden">
|
||||
<!-- Lista de estados existentes -->
|
||||
<div id="statuses-list" class="divide-y divide-gray-200">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal para crear/editar estado -->
|
||||
<div id="status-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50" style="display: none;">
|
||||
<div class="bg-white rounded-lg shadow-xl w-full max-w-md">
|
||||
<div class="border-b px-6 py-4">
|
||||
<h3 class="text-lg font-medium text-gray-900" id="modal-title">Nuevo Estado</h3>
|
||||
</div>
|
||||
|
||||
<form id="status-form" method="POST" class="p-6">
|
||||
@csrf
|
||||
<div id="method-field"></div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- Nombre -->
|
||||
<div>
|
||||
<label for="modal-name" class="block text-sm font-medium text-gray-700">
|
||||
Nombre del Estado *
|
||||
</label>
|
||||
<input type="text"
|
||||
id="modal-name"
|
||||
name="name"
|
||||
required
|
||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
</div>
|
||||
|
||||
<!-- Color -->
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label for="modal-color" class="block text-sm font-medium text-gray-700">
|
||||
Color de fondo
|
||||
</label>
|
||||
<div class="mt-1 flex items-center">
|
||||
<input type="color"
|
||||
id="modal-color"
|
||||
name="color"
|
||||
value="#6b7280"
|
||||
class="h-10 w-16 rounded border">
|
||||
<input type="text"
|
||||
id="modal-color-text"
|
||||
name="color_text"
|
||||
value="#6b7280"
|
||||
class="ml-2 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="modal-text-color" class="block text-sm font-medium text-gray-700">
|
||||
Color del texto
|
||||
</label>
|
||||
<div class="mt-1 flex items-center">
|
||||
<input type="color"
|
||||
id="modal-text-color"
|
||||
name="text_color"
|
||||
value="#ffffff"
|
||||
class="h-10 w-16 rounded border">
|
||||
<input type="text"
|
||||
id="modal-text-color-text"
|
||||
name="text_color_text"
|
||||
value="#ffffff"
|
||||
class="ml-2 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Descripción -->
|
||||
<div>
|
||||
<label for="modal-description" class="block text-sm font-medium text-gray-700">
|
||||
Descripción
|
||||
</label>
|
||||
<textarea id="modal-description"
|
||||
name="description"
|
||||
rows="3"
|
||||
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Permisos -->
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox"
|
||||
name="allow_upload"
|
||||
value="1"
|
||||
class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
|
||||
<span class="ml-2 text-sm text-gray-700">Permitir subida</span>
|
||||
</label>
|
||||
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox"
|
||||
name="allow_edit"
|
||||
value="1"
|
||||
class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
|
||||
<span class="ml-2 text-sm text-gray-700">Permitir edición</span>
|
||||
</label>
|
||||
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox"
|
||||
name="allow_delete"
|
||||
value="1"
|
||||
class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-indigo-200 focus:ring-opacity-50">
|
||||
<span class="ml-2 text-sm text-gray-700">Permitir eliminación</span>
|
||||
</label>
|
||||
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox"
|
||||
name="requires_approval"
|
||||
value="1"
|
||||
class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
|
||||
<span class="ml-2 text-sm text-gray-700">Requiere aprobación</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Estado por defecto -->
|
||||
<div>
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox"
|
||||
name="is_default"
|
||||
value="1"
|
||||
class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
|
||||
<span class="ml-2 text-sm text-gray-700">Estado por defecto para nuevos documentos</span>
|
||||
</label>
|
||||
<p class="mt-1 text-xs text-gray-500">
|
||||
Solo puede haber un estado por defecto
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-end space-x-3">
|
||||
<button type="button" onclick="closeStatusModal()" class="btn btn-secondary">
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Guardar Estado
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal de confirmación para eliminar -->
|
||||
<div id="delete-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50" style="display: none;">
|
||||
<div class="bg-white rounded-lg shadow-xl w-full max-w-md">
|
||||
<div class="border-b px-6 py-4">
|
||||
<h3 class="text-lg font-medium text-gray-900">Confirmar eliminación</h3>
|
||||
</div>
|
||||
|
||||
<div class="p-6">
|
||||
<p class="text-gray-700" id="delete-message">¿Estás seguro de que deseas eliminar este estado?</p>
|
||||
|
||||
<form id="delete-form" method="POST" class="mt-6">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
|
||||
<div class="flex justify-end space-x-3">
|
||||
<button type="button" onclick="closeDeleteModal()" class="btn btn-secondary">
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="submit" class="btn btn-danger">
|
||||
Eliminar Estado
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
// Variables globales
|
||||
let currentStatusId = null;
|
||||
const statusModal = document.getElementById('status-modal');
|
||||
const deleteModal = document.getElementById('delete-modal');
|
||||
|
||||
// Funciones para el modal de estados
|
||||
function openStatusModal(status = null) {
|
||||
const form = document.getElementById('status-form');
|
||||
const title = document.getElementById('modal-title');
|
||||
const methodField = document.getElementById('method-field');
|
||||
|
||||
if (status) {
|
||||
// Modo edición
|
||||
title.textContent = 'Editar Estado';
|
||||
form.action = `/projects/{{ $project->id }}/settings/statuses/${status.id}`;
|
||||
methodField.innerHTML = '<input type="hidden" name="_method" value="PUT">';
|
||||
|
||||
// Llenar los campos con los datos del estado
|
||||
document.getElementById('modal-name').value = status.name;
|
||||
document.getElementById('modal-color').value = status.color;
|
||||
document.getElementById('modal-color-text').value = status.color;
|
||||
document.getElementById('modal-text-color').value = status.text_color;
|
||||
document.getElementById('modal-text-color-text').value = status.text_color;
|
||||
document.getElementById('modal-description').value = status.description || '';
|
||||
|
||||
// Checkboxes
|
||||
document.querySelector('input[name="allow_upload"]').checked = status.allow_upload;
|
||||
document.querySelector('input[name="allow_edit"]').checked = status.allow_edit;
|
||||
document.querySelector('input[name="allow_delete"]').checked = status.allow_delete;
|
||||
document.querySelector('input[name="requires_approval"]').checked = status.requires_approval;
|
||||
document.querySelector('input[name="is_default"]').checked = status.is_default;
|
||||
|
||||
currentStatusId = status.id;
|
||||
} else {
|
||||
// Modo creación
|
||||
title.textContent = 'Nuevo Estado';
|
||||
form.action = `/projects/{{ $project->id }}/settings/statuses`;
|
||||
methodField.innerHTML = '';
|
||||
|
||||
// Resetear campos
|
||||
form.reset();
|
||||
document.getElementById('modal-color').value = '#6b7280';
|
||||
document.getElementById('modal-color-text').value = '#6b7280';
|
||||
document.getElementById('modal-text-color').value = '#ffffff';
|
||||
document.getElementById('modal-text-color-text').value = '#ffffff';
|
||||
|
||||
currentStatusId = null;
|
||||
}
|
||||
|
||||
statusModal.style.display = 'flex';
|
||||
}
|
||||
|
||||
function closeStatusModal() {
|
||||
statusModal.style.display = 'none';
|
||||
}
|
||||
|
||||
// Funciones para el modal de eliminación
|
||||
function openDeleteModal(status) {
|
||||
const message = document.getElementById('delete-message');
|
||||
const form = document.getElementById('delete-form');
|
||||
|
||||
message.textContent = `¿Estás seguro de que deseas eliminar el estado "${status.name}"?`;
|
||||
form.action = `/projects/{{ $project->id }}/settings/statuses/${status.id}`;
|
||||
|
||||
currentStatusId = status.id;
|
||||
deleteModal.style.display = 'flex';
|
||||
}
|
||||
|
||||
function closeDeleteModal() {
|
||||
deleteModal.style.display = 'none';
|
||||
}
|
||||
|
||||
// Sincronizar inputs de color
|
||||
document.getElementById('modal-color').addEventListener('input', function(e) {
|
||||
document.getElementById('modal-color-text').value = e.target.value;
|
||||
});
|
||||
|
||||
document.getElementById('modal-color-text').addEventListener('input', function(e) {
|
||||
document.getElementById('modal-color').value = e.target.value;
|
||||
});
|
||||
|
||||
document.getElementById('modal-text-color').addEventListener('input', function(e) {
|
||||
document.getElementById('modal-text-color-text').value = e.target.value;
|
||||
});
|
||||
|
||||
document.getElementById('modal-text-color-text').addEventListener('input', function(e) {
|
||||
document.getElementById('modal-text-color').value = e.target.value;
|
||||
});
|
||||
|
||||
// Cerrar modales al hacer clic fuera
|
||||
window.addEventListener('click', function(e) {
|
||||
if (e.target === statusModal) {
|
||||
closeStatusModal();
|
||||
}
|
||||
if (e.target === deleteModal) {
|
||||
closeDeleteModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Funcionalidad de drag & drop para reordenar estados
|
||||
new Sortable(document.getElementById('statuses-list'), {
|
||||
animation: 150,
|
||||
handle: '.drag-handle',
|
||||
onEnd: function() {
|
||||
// Obtener el nuevo orden
|
||||
const order = Array.from(document.querySelectorAll('.status-item')).map(item => {
|
||||
return item.dataset.statusId;
|
||||
});
|
||||
|
||||
// Enviar el nuevo orden al servidor
|
||||
fetch(`/projects/{{ $project->id }}/settings/statuses/reorder`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
},
|
||||
body: JSON.stringify({ order: order })
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
</x-layouts.app>
|
||||
@@ -0,0 +1,68 @@
|
||||
<div class="status-item p-4 flex items-center hover:bg-gray-50 transition-colors"
|
||||
data-status-id="{{ $status->id }}">
|
||||
<!-- Handle para drag & drop -->
|
||||
<div class="drag-handle cursor-move mr-3 text-gray-400 hover:text-gray-600">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8h16M4 16h16" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Badge de color -->
|
||||
<div class="w-8 h-8 rounded-full mr-3 border"
|
||||
style="background-color: {{ $status->color }}; color: {{ $status->text_color }}"
|
||||
title="{{ $status->name }}">
|
||||
<div class="flex items-center justify-center h-full text-xs font-bold">
|
||||
{{ substr($status->name, 0, 2) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Información del estado -->
|
||||
<div class="flex-grow">
|
||||
<div class="flex items-center">
|
||||
<span class="font-medium text-gray-900">{{ $status->name }}</span>
|
||||
@if($status->is_default)
|
||||
<span class="ml-2 px-2 py-1 text-xs bg-blue-100 text-blue-800 rounded-full">
|
||||
Por defecto
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
@if($status->description)
|
||||
<p class="text-sm text-gray-600 mt-1">{{ $status->description }}</p>
|
||||
@endif
|
||||
|
||||
<!-- Permisos -->
|
||||
<div class="flex items-center space-x-3 mt-2">
|
||||
@if($status->allow_upload)
|
||||
<span class="text-xs text-green-600">✓ Subir</span>
|
||||
@endif
|
||||
@if($status->allow_edit)
|
||||
<span class="text-xs text-blue-600">✓ Editar</span>
|
||||
@endif
|
||||
@if($status->allow_delete)
|
||||
<span class="text-xs text-red-600">✓ Eliminar</span>
|
||||
@endif
|
||||
@if($status->requires_approval)
|
||||
<span class="text-xs text-yellow-600">⚠ Aprobación</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Acciones -->
|
||||
<div class="flex items-center space-x-2">
|
||||
<button type="button"
|
||||
onclick="openStatusModal({{ json_encode($status) }})"
|
||||
class="text-yellow-600 hover:text-yellow-900"
|
||||
title="Editar">
|
||||
<x-icons.pencil class="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
@if(!$status->is_default && $status->documents_count == 0)
|
||||
<button type="button"
|
||||
onclick="openDeleteModal({{ json_encode($status) }})"
|
||||
class="text-red-600 hover:text-red-900"
|
||||
title="Eliminar">
|
||||
<x-icons.trash class="w-5 h-5" />
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user