Merge pull request 'fix(multimoneda): formato fijo DD/MM/YYYY en campo de fecha' (#476) from fix/catalogos-multimoneda into development
Reviewed-on: ADUANASOFT/anexo76#476
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
import type { CurrencyType } from '$lib/api/dashboard/reference_data/currency_types';
|
import type { CurrencyType } from '$lib/api/dashboard/reference_data/currency_types';
|
||||||
import type { Country } from '$lib/api/dashboard/reference_data/countries';
|
import type { Country } from '$lib/api/dashboard/reference_data/countries';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
|
import DateInputDMY from '$lib/components/ui/date-input-dmy.svelte';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
open = $bindable(false),
|
open = $bindable(false),
|
||||||
@@ -205,14 +206,12 @@
|
|||||||
{m.multi_currency_date_label()} <span class="text-destructive">*</span>
|
{m.multi_currency_date_label()} <span class="text-destructive">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<div class="col-span-3">
|
<div class="col-span-3">
|
||||||
<Input
|
<DateInputDMY
|
||||||
id="pub_date"
|
id="pub_date"
|
||||||
type="date"
|
|
||||||
bind:value={formData.date_str}
|
bind:value={formData.date_str}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<p class="mt-1 text-[10px] text-muted-foreground">{m.multi_currency_date_help()}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
129
frontend/src/lib/components/ui/date-input-dmy.svelte
Normal file
129
frontend/src/lib/components/ui/date-input-dmy.svelte
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Calendar } from 'lucide-svelte';
|
||||||
|
import { cn } from '$lib/utils.js';
|
||||||
|
|
||||||
|
let {
|
||||||
|
value = $bindable(''),
|
||||||
|
id = '',
|
||||||
|
disabled = false,
|
||||||
|
required = false,
|
||||||
|
class: className = ''
|
||||||
|
}: {
|
||||||
|
value: string;
|
||||||
|
id?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
required?: boolean;
|
||||||
|
class?: string;
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
// YYYY-MM-DD → DD/MM/YYYY
|
||||||
|
function toDisplay(iso: string): string {
|
||||||
|
if (!iso || iso.length !== 10) return '';
|
||||||
|
const [y, m, d] = iso.split('-');
|
||||||
|
return `${d}/${m}/${y}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DD/MM/YYYY → YYYY-MM-DD
|
||||||
|
function toIso(dmy: string): string {
|
||||||
|
if (!dmy || dmy.length !== 10) return '';
|
||||||
|
const [d, m, y] = dmy.split('/');
|
||||||
|
if (!d || !m || !y || y.length !== 4) return '';
|
||||||
|
return `${y}-${m}-${d}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let display = $state(toDisplay(value));
|
||||||
|
let lastSetByInput = value;
|
||||||
|
let hiddenRef = $state<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (value !== lastSetByInput) {
|
||||||
|
display = toDisplay(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleInput(e: Event) {
|
||||||
|
const input = e.currentTarget as HTMLInputElement;
|
||||||
|
const digits = input.value.replace(/\D/g, '');
|
||||||
|
|
||||||
|
let formatted = digits;
|
||||||
|
if (digits.length > 4) {
|
||||||
|
formatted = digits.slice(0, 2) + '/' + digits.slice(2, 4) + '/' + digits.slice(4, 8);
|
||||||
|
} else if (digits.length > 2) {
|
||||||
|
formatted = digits.slice(0, 2) + '/' + digits.slice(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
display = formatted;
|
||||||
|
input.value = formatted;
|
||||||
|
|
||||||
|
const iso = formatted.length === 10 ? toIso(formatted) : '';
|
||||||
|
lastSetByInput = iso;
|
||||||
|
value = iso;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeydown(e: KeyboardEvent) {
|
||||||
|
const allowed = ['Backspace', 'Delete', 'Tab', 'ArrowLeft', 'ArrowRight', 'Home', 'End'];
|
||||||
|
if (allowed.includes(e.key)) return;
|
||||||
|
if (!/^\d$/.test(e.key)) e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cuando el usuario elige una fecha desde el picker nativo, sincroniza al texto
|
||||||
|
function handleHiddenChange(e: Event) {
|
||||||
|
const iso = (e.currentTarget as HTMLInputElement).value;
|
||||||
|
lastSetByInput = iso;
|
||||||
|
value = iso;
|
||||||
|
display = toDisplay(iso);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openPicker() {
|
||||||
|
if (!hiddenRef || disabled) return;
|
||||||
|
// Sincroniza el valor actual antes de abrir
|
||||||
|
hiddenRef.value = value;
|
||||||
|
if ('showPicker' in hiddenRef && typeof hiddenRef.showPicker === 'function') {
|
||||||
|
hiddenRef.showPicker();
|
||||||
|
} else {
|
||||||
|
hiddenRef.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="relative w-full">
|
||||||
|
<input
|
||||||
|
{id}
|
||||||
|
type="text"
|
||||||
|
inputmode="numeric"
|
||||||
|
placeholder="DD/MM/YYYY"
|
||||||
|
value={display}
|
||||||
|
oninput={handleInput}
|
||||||
|
onkeydown={handleKeydown}
|
||||||
|
{disabled}
|
||||||
|
{required}
|
||||||
|
maxlength={10}
|
||||||
|
autocomplete="off"
|
||||||
|
class={cn(
|
||||||
|
'border-input bg-background selection:bg-primary dark:bg-input/30 selection:text-primary-foreground ring-offset-background placeholder:text-muted-foreground shadow-xs flex h-9 w-full min-w-0 rounded-md border px-3 py-1 pr-11 text-base text-foreground outline-none transition-[color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||||
|
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
||||||
|
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Input nativo oculto — solo sirve para abrir el calendar picker del navegador -->
|
||||||
|
<input
|
||||||
|
bind:this={hiddenRef}
|
||||||
|
type="date"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-hidden="true"
|
||||||
|
onchange={handleHiddenChange}
|
||||||
|
class="pointer-events-none absolute inset-0 h-full w-full opacity-0"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="Abrir selector de fecha"
|
||||||
|
onclick={openPicker}
|
||||||
|
{disabled}
|
||||||
|
class="absolute top-1/2 right-1 flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-md text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
>
|
||||||
|
<Calendar aria-hidden="true" class="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user