Compare commits

..

3 commits

Author SHA1 Message Date
1cc14d0a0b improve navigation 2024-09-22 19:01:16 -03:00
8ed967209f arreglar types 2024-09-22 18:55:47 -03:00
0be2ff8911 loading state
fixes #60
2024-09-22 18:54:29 -03:00
9 changed files with 117 additions and 41 deletions

View file

@ -0,0 +1,17 @@
<script lang="ts">
// Parent has to be position relative
import { LoaderCircle } from 'lucide-svelte';
export let loading = false;
</script>
{#if loading}
<div class="absolute inset-0 flex items-center justify-center">
<LoaderCircle class="h-4 w-4 animate-spin" />
</div>
<div class="invisible">
<slot />
</div>
{:else}
<slot />
{/if}

View file

@ -1,17 +1,25 @@
<script lang="ts">
import { Input } from '$lib/components/ui/input';
import { Button } from '$lib/components/ui/button';
import { goto } from '$app/navigation';
import { afterNavigate, beforeNavigate, goto } from '$app/navigation';
import { page } from '$app/stores';
let search = $page.params.query ?? '';
let loading = false;
beforeNavigate(() => {
loading = true;
});
afterNavigate(() => {
loading = false;
});
function handleSubmit() {
goto(`/search/${encodeURIComponent(search)}`);
}
</script>
<form class="flex gap-2" on:submit|preventDefault={handleSubmit}>
<Input placeholder="Buscar productos" bind:value={search} />
<Button type="submit">Buscar</Button>
<Input placeholder="Buscar productos" bind:value={search} disabled={loading} />
<Button type="submit" {loading}>Buscar</Button>
</form>

View file

@ -2,6 +2,7 @@
import { Button as ButtonPrimitive } from 'bits-ui';
import { type Events, type Props, buttonVariants } from './index.js';
import { cn } from '$lib/utils.js';
import Loading from '$lib/components/Loading.svelte';
type $$Props = Props;
type $$Events = Events;
@ -10,16 +11,19 @@
export let variant: $$Props['variant'] = 'default';
export let size: $$Props['size'] = 'default';
export let builders: $$Props['builders'] = [];
export let loading = false;
export { className as class };
</script>
<ButtonPrimitive.Root
{builders}
class={cn(buttonVariants({ variant, size, className }))}
class={cn(buttonVariants({ variant, size, className }), 'relative')}
type="button"
{...$$restProps}
on:click
on:keydown
>
<Loading {loading}>
<slot />
</Loading>
</ButtonPrimitive.Root>

View file

@ -32,6 +32,7 @@ type Size = VariantProps<typeof buttonVariants>['size'];
type Props = ButtonPrimitive.Props & {
variant?: Variant;
size?: Size;
loading?: boolean;
};
type Events = ButtonPrimitive.Events;

View file

@ -28,3 +28,14 @@ export const pesosFormatter = new Intl.NumberFormat('es-AR', {
style: 'currency',
currency: 'ARS'
});
export function parseMarcas(marcas: readonly string[]) {
const x = marcas
.map((m) => m.trim().replaceAll(/['`´]/g, ''))
.filter((m) => !['sin marca', 'VARIOS'].includes(m))
.filter((m) => m.length > 0);
if (x.length === 0) {
return ['n/a'];
}
return Array.from(new Set(x));
}

View file

@ -3,10 +3,13 @@
import { ArrowLeft } from 'lucide-svelte';
import Map from '$lib/components/Map.svelte';
import Badge from '$lib/components/ui/badge/badge.svelte';
import {} from '$app/navigation';
import { goto } from '$app/navigation';
import { generateGoogleMapsLink, pesosFormatter, processBanderaNombre } from '$lib/sepa-utils';
import { page } from '$app/stores';
export let data: PageData;
const query = $page.url.searchParams.get('query');
</script>
<svelte:head>
@ -15,7 +18,12 @@
<div class="flex min-h-screen flex-col">
<div class="max-w-screen flex items-stretch gap-3 overflow-hidden px-2">
<button on:click={() => window.history.back()}>
<button
on:click={() =>
goto(
`/search/${encodeURIComponent(query ?? data.precios[0].productos_descripcion ?? $page.params.id)}`
)}
>
<ArrowLeft class="size-8 flex-shrink-0" />
</button>
<div class="flex flex-wrap items-center gap-x-2 overflow-hidden p-1">

View file

@ -8,6 +8,8 @@
import { MapPin } from 'lucide-svelte';
import Button from '$lib/components/ui/button/button.svelte';
import { es } from 'date-fns/locale';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
export let data: PageData;
@ -23,7 +25,15 @@
<div>
<div class="max-w-screen flex items-stretch gap-3 overflow-hidden px-2">
<button on:click={() => window.history.back()}>
<button
on:click={() => {
if (history.length > 1) {
history.back();
} else {
goto(`/id_producto/${$page.params.id}`);
}
}}
>
<ArrowLeft class="size-8 flex-shrink-0" />
</button>
<div class="flex flex-wrap items-center gap-x-2 overflow-hidden p-1">

View file

@ -1,24 +1,12 @@
<script lang="ts">
import SearchBar from '$lib/components/SearchBar.svelte';
import Badge from '$lib/components/ui/badge/badge.svelte';
import Button from '$lib/components/ui/button/button.svelte';
import * as Card from '$lib/components/ui/card/index.js';
import { ArrowLeft } from 'lucide-svelte';
import type { PageData } from './$types';
import { goto } from '$app/navigation';
import ProductCard from './ProductCard.svelte';
export let data: PageData;
function parseMarcas(marcas: readonly string[]) {
const x = marcas
.map((m) => m.trim().replaceAll(/['`´]/g, ''))
.filter((m) => !['sin marca', 'VARIOS'].includes(m))
.filter((m) => m.length > 0);
if (x.length === 0) {
return ['n/a'];
}
return Array.from(new Set(x));
}
</script>
<svelte:head>
@ -39,26 +27,7 @@
</p>
{:else}
{#each data.collapsedProductos as producto}
<a href={`/id_producto/${producto.id_producto}`} class="my-2 block">
<Card.Root class="transition-colors duration-200 hover:bg-gray-100">
<Card.Header class="block px-3 py-2 pb-0">
<Badge>{parseMarcas(Array.from(producto.marcas)).join('/')}</Badge>
<Badge variant="outline"
>en
{producto.in_datasets_count} cadena{#if producto.in_datasets_count > 1}s{/if}
</Badge>
<Badge variant="outline">EAN {producto.id_producto}</Badge>
</Card.Header>
<Card.Content class="px-3 py-2">
{#each producto.descriptions as description}
<span>{description}</span>
{#if description !== producto.descriptions[producto.descriptions.length - 1]}
<span class="text-gray-500"></span>{' '}
{/if}
{/each}
</Card.Content>
</Card.Root>
</a>
<ProductCard {producto} query={data.query} />
{/each}
{/if}
</div>

View file

@ -0,0 +1,48 @@
<script lang="ts">
import * as Card from '$lib/components/ui/card';
import { Badge } from '$lib/components/ui/badge';
import { parseMarcas } from '$lib/sepa-utils';
import { beforeNavigate } from '$app/navigation';
import Loading from '$lib/components/Loading.svelte';
export let producto: {
id_producto: string;
marcas: Set<string>;
in_datasets_count: number;
descriptions: string[];
};
export let query: string | undefined;
let loading = false;
beforeNavigate((x) => {
if (x.to?.params?.id === producto.id_producto) {
loading = true;
}
});
</script>
<a
href={`/id_producto/${producto.id_producto}?query=${encodeURIComponent(query ?? producto.descriptions[0])}`}
class="my-2 block"
>
<Card.Root class="relative transition-colors duration-200 hover:bg-gray-100">
<Loading {loading}>
<Card.Header class="block px-3 py-2 pb-0">
<Badge>{parseMarcas(Array.from(producto.marcas)).join('/')}</Badge>
<Badge variant="outline"
>en
{producto.in_datasets_count} cadena{#if producto.in_datasets_count > 1}s{/if}
</Badge>
<Badge variant="outline">EAN {producto.id_producto}</Badge>
</Card.Header>
<Card.Content class="px-3 py-2">
{#each producto.descriptions as description}
<span>{description}</span>
{#if description !== producto.descriptions[producto.descriptions.length - 1]}
<span class="text-gray-500"></span>{' '}
{/if}
{/each}
</Card.Content>
</Loading>
</Card.Root>
</a>