|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 357 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 21 KiB |
@ -0,0 +1,249 @@
|
||||
<script lang="ts">
|
||||
import { appState } from '../data/AppState.svelte';
|
||||
import { dataConfig } from '../data/DataConfig';
|
||||
import type { OptionGroup } from '../data/DataConfigTypes';
|
||||
import {formatPrice} from "../utils/formats";
|
||||
|
||||
interface Props {
|
||||
group: OptionGroup;
|
||||
viewId: string;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
let { group, viewId, isOpen, onClose }: Props = $props();
|
||||
|
||||
// Obtener la opción actual seleccionada
|
||||
const currentOptionId = $derived(appState.getCurrentOption(viewId, group.id));
|
||||
const currentOption = $derived(currentOptionId ? appState.getOptionValue(currentOptionId) : null);
|
||||
|
||||
// Obtener información de la opción desde el grupo
|
||||
const optionRef = $derived(
|
||||
group.values.find(v => v.id === currentOptionId)
|
||||
);
|
||||
|
||||
const hasPrice = $derived(
|
||||
optionRef?.amount !== undefined &&
|
||||
optionRef.amount !== 'consultar' &&
|
||||
typeof optionRef.amount === 'number' &&
|
||||
optionRef.amount > 0
|
||||
);
|
||||
|
||||
// Manejar click en el overlay (cerrar)
|
||||
function handleOverlayClick(e: MouseEvent) {
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
|
||||
// Cerrar con tecla ESC
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape' && isOpen) {
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={handleKeydown} />
|
||||
|
||||
{#if isOpen}
|
||||
<!-- Overlay con opacidad muy baja -->
|
||||
<div
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4 animate-fade-in"
|
||||
style="background-color: rgba(0, 0, 0, 0.2); backdrop-filter: blur(2px);"
|
||||
onclick={handleOverlayClick}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-title"
|
||||
>
|
||||
<!-- Modal -->
|
||||
<div class="bg-white rounded-2xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-hidden animate-scale-in">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="bg-gradient-to-r from-green-800 to-green-700 text-white p-3 relative">
|
||||
<button
|
||||
class="absolute top-4 right-4 text-white hover:text-gray-200 transition-colors"
|
||||
onclick={onClose}
|
||||
aria-label="Cerrar"
|
||||
>
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<h2 id="modal-title" class="text-2xl font-bold mb-1">
|
||||
{group.name || group.id}
|
||||
</h2>
|
||||
|
||||
{#if group.text}
|
||||
<p class="text-green-100 text-sm">
|
||||
{group.text}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="p-6 overflow-y-auto max-h-[calc(90vh-200px)]">
|
||||
|
||||
{#if currentOption}
|
||||
<!-- Imagen del material -->
|
||||
{#if currentOption.image}
|
||||
<div class="mb-6 rounded-lg overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={dataConfig.sources.imgOptionsHi + currentOption.image}
|
||||
alt={currentOption.name}
|
||||
class="w-full h-64 object-cover"
|
||||
onerror={(e) => {
|
||||
console.log('error', e);
|
||||
console.log(dataConfig.sources.imgOptionsHi + currentOption.image)
|
||||
const target = e.currentTarget as HTMLImageElement;
|
||||
target.src = dataConfig.sources.images + 'placeholder.jpg';
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Información del material -->
|
||||
<div class="space-y-4">
|
||||
<!-- Nombre del material -->
|
||||
<div class="border-l-4 border-green-500 pl-4">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-1">
|
||||
Material seleccionado
|
||||
</h3>
|
||||
<p class="text-xl font-bold text-green-700">
|
||||
{currentOption.name}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Descripción del material -->
|
||||
{#if currentOption.text}
|
||||
<div class="bg-gray-50 rounded-lg p-4">
|
||||
<h4 class="text-sm font-semibold text-gray-600 mb-2 uppercase tracking-wide">
|
||||
Descripción
|
||||
</h4>
|
||||
<p class="text-gray-700 leading-relaxed">
|
||||
{currentOption.text}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Texto adicional del grupo -->
|
||||
{#if group.help}
|
||||
<div class="bg-blue-50 rounded-lg p-4 border border-blue-200">
|
||||
<h4 class="text-sm font-semibold text-blue-800 mb-2 flex items-center gap-2">
|
||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
Información adicional
|
||||
</h4>
|
||||
<p class="text-blue-900 text-sm leading-relaxed">
|
||||
{group.help}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Precio -->
|
||||
{#if hasPrice}
|
||||
<div class="bg-green-50 rounded-lg p-4 border-2 border-green-200">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm font-semibold text-gray-700">
|
||||
Coste adicional:
|
||||
</span>
|
||||
<span class="text-2xl font-bold text-green-700">
|
||||
+ {formatPrice(optionRef?.amount as number)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Badge Consultar -->
|
||||
{#if optionRef?.amount === 'consultar'}
|
||||
<div class="bg-yellow-50 rounded-lg p-4 border-2 border-yellow-200">
|
||||
<div class="flex items-start gap-3">
|
||||
<span class="text-2xl">⚠️</span>
|
||||
<div class="flex-1">
|
||||
<h4 class="font-semibold text-yellow-800 mb-1">
|
||||
Requiere consulta
|
||||
</h4>
|
||||
{#if optionRef.warn}
|
||||
<p class="text-sm text-yellow-700">
|
||||
{optionRef.warn}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
<!-- Miniatura adicional -->
|
||||
<!--
|
||||
{#if currentOption.thumb && currentOption.thumb !== currentOption.image}
|
||||
<div class="flex items-center gap-3 p-3 bg-gray-100 rounded-lg">
|
||||
<img
|
||||
src={dataConfig.sources.imgValues + currentOption.thumb}
|
||||
alt="Muestra de {currentOption.name}"
|
||||
class="w-20 h-20 object-cover rounded shadow"
|
||||
onerror={(e) => {
|
||||
const target = e.currentTarget as HTMLImageElement;
|
||||
target.style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
<span class="text-sm text-gray-600">Muestra del material</span>
|
||||
</div>
|
||||
{/if}
|
||||
-->
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Sin opción seleccionada -->
|
||||
<div class="text-center py-8">
|
||||
<div class="text-4xl mb-4">🔍</div>
|
||||
<p class="text-gray-500">No hay ninguna opción seleccionada para este elemento</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<!--
|
||||
<div class="bg-gray-50 px-6 py-4 flex justify-end gap-3 border-t">
|
||||
<button
|
||||
class="px-6 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors font-medium shadow-sm"
|
||||
onclick={onClose}
|
||||
>
|
||||
Cerrar
|
||||
</button>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scale-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fade-in 0.2s ease-out;
|
||||
}
|
||||
|
||||
.animate-scale-in {
|
||||
animation: scale-in 0.3s ease-out;
|
||||
}
|
||||
</style>
|
||||