386 lines
22 KiB
Plaintext
386 lines
22 KiB
Plaintext
<!-- resources/views/projects/create.blade.php -->
|
|
<x-layouts.app :title="__('Create Project')">
|
|
<x-slot name="header">
|
|
<h2 class="text-xl font-semibold leading-tight text-gray-800">
|
|
Nuevo Proyecto
|
|
</h2>
|
|
</x-slot>
|
|
|
|
<div class="py-6">
|
|
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
|
|
<div class="overflow-hidden bg-white shadow-sm sm:rounded-lg">
|
|
<div class="p-6 bg-white border-b border-gray-200">
|
|
<form method="POST" action="{{ route('projects.store') }}" enctype="multipart/form-data">
|
|
@csrf
|
|
<!-- Sección de Información Básica -->
|
|
<div class="grid gap-6 mb-8 md:grid-cols-2">
|
|
<!-- Columna Izquierda -->
|
|
<div>
|
|
<!-- Nombre -->
|
|
<div class="mb-6">
|
|
<x-label for="name" value="__('Nombre del Proyecto')" />
|
|
<x-input id="name" class="block w-full mt-1"
|
|
type="text" name="name" value="old('name')" required />
|
|
@error('name')
|
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
|
|
<!-- Descripción -->
|
|
<div class="mb-6">
|
|
<x-label for="description" value="__('Descripción')" />
|
|
<x-textarea id="description" class="block w-full mt-1"
|
|
name="description" rows="4" required>
|
|
{{ old('description') }}
|
|
</x-textarea>
|
|
@error('description')
|
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
|
|
<!-- Editor Enriquecido -->
|
|
<div class="mb-6">
|
|
<x-label for="description" value="__('Descripción Detallada')" />
|
|
<div id="editor-container">
|
|
<!-- Barra de herramientas -->
|
|
<div id="toolbar">
|
|
<button class="ql-bold"></button>
|
|
<button class="ql-italic"></button>
|
|
<button class="ql-underline"></button>
|
|
<button class="ql-strike"></button>
|
|
<button class="ql-blockquote"></button>
|
|
<button class="ql-code-block"></button>
|
|
<button class="ql-link"></button>
|
|
<button class="ql-image"></button>
|
|
|
|
</div>
|
|
|
|
<!-- Editor -->
|
|
<div id="rich-editor">{!! old('description') !!}</div>
|
|
</div>
|
|
@error('description')
|
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
|
|
<!-- Estado -->
|
|
<div class="mb-6">
|
|
<x-label for="status" value="__('Estado del Proyecto')" />
|
|
<x-select id="status" name="status" class="block w-full mt-1">
|
|
<option value="active" {{ old('status') == 'active' ? 'selected' : '' }}>Activo</option>
|
|
<option value="inactive" {{ old('status') == 'inactive' ? 'selected' : '' }}>Inactivo</option>
|
|
</x-select>
|
|
@error('status')
|
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
|
|
<!-- Imagen de Referencia -->
|
|
<div class="mb-6">
|
|
<x-label for="project_image" value="__('Imagen de Referencia')" />
|
|
<div class="relative mt-1">
|
|
<input type="file" id="project_image" name="project_image"
|
|
class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4
|
|
file:rounded-md file:border-0
|
|
file:text-sm file:font-semibold
|
|
file:bg-blue-50 file:text-blue-700
|
|
hover:file:bg-blue-100"
|
|
accept="image/*"
|
|
onchange="previewImage(event)">
|
|
<div class="mt-2" id="image-preview-container" style="display:none;">
|
|
<img id="image-preview" class="object-cover h-48 w-full rounded-lg">
|
|
</div>
|
|
</div>
|
|
@error('project_image')
|
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Columna Derecha -->
|
|
<div>
|
|
<!-- Dirección -->
|
|
<div class="mb-6">
|
|
<x-label value="__('Ubicación')" />
|
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
<div>
|
|
<x-input id="address" class="block w-full mt-1"
|
|
type="text" name="address"
|
|
value="old('address')"
|
|
placeholder="Dirección" />
|
|
@error('address')
|
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
|
|
<div>
|
|
<x-input id="postal_code" class="block w-full mt-1"
|
|
type="text" name="postal_code"
|
|
value="old('postal_code')"
|
|
placeholder="Código Postal" />
|
|
@error('postal_code')
|
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
|
|
<div>
|
|
<x-input id="province" class="block w-full mt-1"
|
|
type="text" name="province"
|
|
value="old('province')"
|
|
placeholder="Provincia" />
|
|
@error('province')
|
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
|
|
<div>
|
|
<x-select id="country" name="country" class="block w-full mt-1">
|
|
<option value="">Seleccione País</option>
|
|
@foreach(config('countries') as $code => $name)
|
|
<option value="{{ $code }}" {{ old('country') == $code ? 'selected' : '' }}>
|
|
{{ $name }}
|
|
</option>
|
|
@endforeach
|
|
</x-select>
|
|
@error('country')
|
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mapa para Coordenadas -->
|
|
<div class="mb-6">
|
|
<x-label value="__('Seleccione Ubicación en el Mapa')" />
|
|
<div id="map" class="h-64 rounded-lg border-2 border-gray-200"></div>
|
|
<div class="grid grid-cols-2 gap-4 mt-2">
|
|
<div>
|
|
<x-label for="latitude" value="__('Latitud')" />
|
|
<x-input id="latitude" name="latitude"
|
|
type="number" step="any"
|
|
value="old('latitude')" required />
|
|
</div>
|
|
<div>
|
|
<x-label for="longitude" value="__('Longitud')" />
|
|
<x-input id="longitude" name="longitude"
|
|
type="number" step="any"
|
|
value="old('longitude')" required />
|
|
</div>
|
|
</div>
|
|
@error('latitude')
|
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
@error('longitude')
|
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sección Principal en 2 Columnas -->
|
|
<div class="grid gap-6 mb-8 md:grid-cols-2">
|
|
<!-- Columna Izquierda -->
|
|
<div>
|
|
<!-- Icono y Categorías -->
|
|
<div class="mb-6">
|
|
<x-label value="__('Identificación Visual')" />
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<x-label for="icon" value="__('Icono del Proyecto')" />
|
|
<div class="relative mt-1">
|
|
<select id="icon" name="icon"
|
|
class="w-full rounded-md shadow-sm border-gray-300 focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50">
|
|
<option value="">Seleccionar Icono</option>
|
|
@foreach(config('project.icons') as $icon)
|
|
<option value="{{ $icon }}"
|
|
{{ old('icon') == $icon ? 'selected' : '' }}>
|
|
<i class="fas fa-{{ $icon }} mr-2"></i>
|
|
{{ Str::title($icon) }}
|
|
</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<x-label for="categories" value="__('Categorías')" />
|
|
<x-multiselect
|
|
name="categories[]"
|
|
:options="$categories"
|
|
:selected="old('categories', [])"
|
|
placeholder="Seleccione categorías"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Fechas Importantes -->
|
|
<div class="grid grid-cols-2 gap-4 mb-6">
|
|
<div>
|
|
<x-label for="start_date" value="__('Fecha de Inicio')" />
|
|
<x-input id="start_date" type="date"
|
|
name="start_date" value="old('start_date')" />
|
|
</div>
|
|
<div>
|
|
<x-label for="deadline" value="__('Fecha Límite')" />
|
|
<x-input id="deadline" type="date"
|
|
name="deadline" value="old('deadline')"
|
|
min="{{ now()->format('Y-m-d') }}" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Columna Derecha -->
|
|
<div>
|
|
<!-- Archivos Adjuntos -->
|
|
<div class="mb-6">
|
|
<x-label value="__('Documentos Iniciales')" />
|
|
<div x-data="{ files: [] }" class="mt-1">
|
|
<div class="flex items-center justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md">
|
|
<div class="text-center">
|
|
<input type="file"
|
|
name="documents[]"
|
|
multiple
|
|
class="absolute opacity-0"
|
|
x-ref="fileInput"
|
|
@change="files = Array.from($event.target.files)">
|
|
|
|
<template x-if="files.length === 0">
|
|
<div>
|
|
<x-icons.upload class="mx-auto h-12 w-12 text-gray-400" />
|
|
<p class="mt-1 text-sm text-gray-600">
|
|
Arrastra archivos o haz clic para subir
|
|
</p>
|
|
</div>
|
|
</template>
|
|
|
|
<template x-if="files.length > 0">
|
|
<div class="space-y-2">
|
|
<template x-for="(file, index) in files" :key="index">
|
|
<div class="flex items-center text-sm text-gray-600">
|
|
<x-icons icon="document" class="w-5 h-5 mr-2" />
|
|
<span x-text="file.name"></span>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mapa y Ubicación (Implementación previa) -->
|
|
<!-- ... (Mantener sección de mapa y ubicación anterior) ... -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Resto del formulario -->
|
|
<!-- ... (Mantener secciones anteriores de equipo, estado, etc) ... -->
|
|
|
|
<!-- Miembros del Equipo -->
|
|
<div class="mb-6">
|
|
Miembros del Equipo
|
|
<x-label value="__('Miembros del Equipo')" />
|
|
<div class="grid grid-cols-1 mt-2 gap-y-2 gap-x-4 sm:grid-cols-2">
|
|
@foreach($users as $user)
|
|
<label class="flex items-center space-x-2">
|
|
<input type="checkbox"
|
|
name="team[]"
|
|
value="{{ $user->id }}"
|
|
{{ in_array($user->id, old('team', [])) ? 'checked' : '' }}
|
|
class="rounded border-gray-300 text-blue-600 shadow-sm focus:ring-blue-500">
|
|
<span class="text-sm text-gray-700">{{ $user->name }}</span>
|
|
</label>
|
|
@endforeach
|
|
</div>
|
|
@error('team')
|
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
|
@enderror
|
|
</div>
|
|
|
|
<!-- Botones de Acción -->
|
|
<div class="flex justify-end mt-8 space-x-4">
|
|
<a href="{{ route('projects.index') }}"
|
|
class="px-4 py-2 text-gray-600 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50">
|
|
{{ __('Cancelar') }}
|
|
</a>
|
|
<x-button type="submit" class="bg-blue-600 hover:bg-blue-700">
|
|
<x-icons icon="save" class="w-5 h-5 mr-2" />
|
|
{{ __('Crear Proyecto') }}
|
|
</x-button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@push('styles')
|
|
<link href="https://cdn.jsdelivr.net/npm/@yaireo/tagify/dist/tagify.css" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.snow.css" rel="stylesheet" />
|
|
@endpush
|
|
|
|
@push('scripts')
|
|
<script src="https://cdn.jsdelivr.net/npm/@yaireo/tagify"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.js"></script>
|
|
<script>
|
|
// Editor Quill
|
|
onst toolbarOptions = [
|
|
['bold', 'italic', 'underline', 'strike'], // toggled buttons
|
|
['blockquote', 'code-block'],
|
|
['link', 'image', 'video', 'formula'],
|
|
|
|
[{ 'header': 1 }, { 'header': 2 }], // custom button values
|
|
[{ 'list': 'ordered'}, { 'list': 'bullet' }, { 'list': 'check' }],
|
|
[{ 'script': 'sub'}, { 'script': 'super' }], // superscript/subscript
|
|
[{ 'indent': '-1'}, { 'indent': '+1' }], // outdent/indent
|
|
[{ 'direction': 'rtl' }], // text direction
|
|
|
|
[{ 'size': ['small', false, 'large', 'huge'] }], // custom dropdown
|
|
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
|
|
|
|
[{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme
|
|
[{ 'font': [] }],
|
|
[{ 'align': [] }],
|
|
|
|
['clean'] // remove formatting button
|
|
];
|
|
|
|
const quill = new Quill('#rich-editor', {
|
|
theme: 'snow',
|
|
modules: {
|
|
toolbar: toolbarOptions,
|
|
placeholder: 'Description...',
|
|
}
|
|
});
|
|
|
|
// Tagify para categorías
|
|
new Tagify(document.querySelector('[name="categories[]"]'), {
|
|
enforceWhitelist: true,
|
|
whitelist: @json($categories->pluck('name')),
|
|
dropdown: {
|
|
enabled: 1,
|
|
maxItems: 5
|
|
}
|
|
});
|
|
</script>
|
|
<script>
|
|
function previewImage(event) {
|
|
const reader = new FileReader();
|
|
const preview = document.getElementById('image-preview');
|
|
|
|
reader.onload = function() {
|
|
preview.innerHTML = `
|
|
<img src="${reader.result}"
|
|
class="object-cover w-full h-48 rounded-lg shadow-sm"
|
|
alt="Vista previa de la imagen">
|
|
`;
|
|
}
|
|
|
|
if(event.target.files[0]) {
|
|
reader.readAsDataURL(event.target.files[0]);
|
|
}
|
|
}
|
|
</script>
|
|
@endpush
|
|
</x-layouts.app> |