se agrego Componente formulario
This commit is contained in:
176
resources/views/components/form/basic-contact.blade.php
Normal file
176
resources/views/components/form/basic-contact.blade.php
Normal file
@ -0,0 +1,176 @@
|
||||
@props([
|
||||
'id' => 'contact-form',
|
||||
'fields' => [],
|
||||
'buttonLabel' => 'Enviar',
|
||||
'successMessage' => '¡Gracias! Te contactaremos muy pronto.',
|
||||
'action' => config('app.contact_form_action', request()->url()), {{-- Ajusta si usas una ruta específica --}}
|
||||
'method' => 'POST',
|
||||
'buttonClass' => 'btn btn-primary custom-btn text-center text-uppercase text-decoration-none border-0 py-0 px-5 font-weight-semibold',
|
||||
'class' => 'row g-3',
|
||||
'showHoneypot' => true,
|
||||
])
|
||||
|
||||
@php
|
||||
// Normaliza campos y valores por defecto
|
||||
$normalized = collect($fields)->map(function ($f) {
|
||||
$f = collect($f);
|
||||
return [
|
||||
'type' => $f->get('type', 'text'),
|
||||
'name' => $f->get('name', ''),
|
||||
'label' => $f->get('label', ''),
|
||||
'placeholder' => $f->get('placeholder', ''),
|
||||
'value' => old($f->get('name'), $f->get('value', '')),
|
||||
'required' => (bool) $f->get('required', false),
|
||||
'rows' => (int) $f->get('rows', 6),
|
||||
'options' => $f->get('options', []), // para selects/radios si en el futuro lo usas
|
||||
'col' => $f->get('col', 'col-12'), // grid responsive opcional
|
||||
'help' => $f->get('help', ''),
|
||||
'attrs' => $f->get('attrs', []), // atributos HTML extra
|
||||
];
|
||||
});
|
||||
@endphp
|
||||
|
||||
<form id="{{ $id }}"
|
||||
action="{{ $action }}"
|
||||
method="{{ strtoupper($method) === 'GET' ? 'GET' : 'POST' }}"
|
||||
class="contact-form custom-form-style-1 {{ $class }}"
|
||||
novalidate
|
||||
>
|
||||
@csrf
|
||||
@if(!in_array(strtoupper($method), ['GET','POST']))
|
||||
@method($method)
|
||||
@endif
|
||||
|
||||
{{-- Porto: alertas de éxito / error --}}
|
||||
@if (session('contact_success'))
|
||||
<div class="contact-form-success alert alert-success mt-2" role="status">
|
||||
<strong>¡Éxito!</strong> {{ session('contact_success') ?: $successMessage }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="contact-form-error alert alert-danger mt-2" role="alert">
|
||||
<strong>¡Error!</strong> Verifica los datos del formulario.
|
||||
<ul class="mb-0 mt-2">
|
||||
@foreach ($errors->all() as $e)
|
||||
<li>{{ $e }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Honeypot antispam (oculto a usuarios reales) --}}
|
||||
@if($showHoneypot)
|
||||
<div aria-hidden="true" class="d-none">
|
||||
<label for="{{ $id }}-website">Sitio web</label>
|
||||
<input type="text" name="website" id="{{ $id }}-website" tabindex="-1" autocomplete="off">
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Render dinámico de campos --}}
|
||||
@foreach($normalized as $field)
|
||||
@php
|
||||
$inputId = $id . '-' . $field['name'];
|
||||
$required = $field['required'] ? 'required' : '';
|
||||
$isInvalid = $errors->has($field['name']) ? 'is-invalid' : '';
|
||||
$attrs = collect($field['attrs'])->map(fn($v, $k) => $k.'="'.e($v).'"')->implode(' ');
|
||||
@endphp
|
||||
|
||||
@if($field['type'] === 'hidden')
|
||||
<input type="hidden" name="{{ $field['name'] }}" value="{{ $field['value'] }}">
|
||||
@continue
|
||||
@endif
|
||||
|
||||
<div class="form-group {{ $field['col'] }}">
|
||||
@if($field['label'])
|
||||
<label class="form-label mb-1 font-weight-bold text-dark" for="{{ $inputId }}">
|
||||
{{ $field['label'] }}
|
||||
@if($field['required']) <span class="text-danger" aria-hidden="true">*</span>@endif
|
||||
</label>
|
||||
@endif
|
||||
|
||||
@switch($field['type'])
|
||||
@case('textarea')
|
||||
<textarea
|
||||
id="{{ $inputId }}"
|
||||
name="{{ $field['name'] }}"
|
||||
rows="{{ $field['rows'] }}"
|
||||
class="form-control {{ $isInvalid }}"
|
||||
placeholder="{{ $field['placeholder'] ?: $field['label'] }}"
|
||||
{{ $required }}
|
||||
aria-required="{{ $field['required'] ? 'true' : 'false' }}"
|
||||
aria-invalid="{{ $errors->has($field['name']) ? 'true' : 'false' }}"
|
||||
{!! $attrs !!}
|
||||
>{{ $field['value'] }}</textarea>
|
||||
@break
|
||||
|
||||
@case('select')
|
||||
<select
|
||||
id="{{ $inputId }}"
|
||||
name="{{ $field['name'] }}"
|
||||
class="form-select {{ $isInvalid }}"
|
||||
{{ $required }}
|
||||
aria-required="{{ $field['required'] ? 'true' : 'false' }}"
|
||||
aria-invalid="{{ $errors->has($field['name']) ? 'true' : 'false' }}"
|
||||
{!! $attrs !!}
|
||||
>
|
||||
<option value="" disabled {{ $field['value'] === '' ? 'selected' : '' }}>
|
||||
{{ $field['placeholder'] ?: 'Selecciona una opción' }}
|
||||
</option>
|
||||
@foreach($field['options'] as $optValue => $optLabel)
|
||||
<option value="{{ $optValue }}" {{ (string)$optValue === (string)$field['value'] ? 'selected' : '' }}>
|
||||
{{ $optLabel }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@break
|
||||
|
||||
@default
|
||||
<input
|
||||
id="{{ $inputId }}"
|
||||
type="{{ $field['type'] }}"
|
||||
name="{{ $field['name'] }}"
|
||||
value="{{ $field['value'] }}"
|
||||
class="form-control {{ $isInvalid }}"
|
||||
placeholder="{{ $field['placeholder'] ?: $field['label'] }}"
|
||||
{{ $required }}
|
||||
aria-required="{{ $field['required'] ? 'true' : 'false' }}"
|
||||
aria-invalid="{{ $errors->has($field['name']) ? 'true' : 'false' }}"
|
||||
{!! $attrs !!}
|
||||
/>
|
||||
@endswitch
|
||||
|
||||
@if($field['help'])
|
||||
<div class="form-text">{{ $field['help'] }}</div>
|
||||
@endif
|
||||
|
||||
@error($field['name'])
|
||||
<div class="invalid-feedback d-block">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
{{-- Botón enviar al estilo Porto --}}
|
||||
<div class="form-group col-12 mt-2">
|
||||
<button type="submit" class="{{ $buttonClass }}">
|
||||
<span class="me-2">{{ $buttonLabel }}</span>
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{-- Mejora UX: evita reenvío al volver / scroll a errores --}}
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Evita reenvío en back/forward cache
|
||||
if ('scrollRestoration' in history) history.scrollRestoration = 'manual';
|
||||
|
||||
const errorBox = document.querySelector('.contact-form-error');
|
||||
if (errorBox) {
|
||||
const y = errorBox.getBoundingClientRect().top + window.pageYOffset - 120;
|
||||
window.scrollTo({ top: y, behavior: 'smooth' });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
Reference in New Issue
Block a user