Subir todos los cambios recientes a la rama principal

This commit is contained in:
arielit3
2026-01-16 13:39:55 -07:00
parent de5b6feef4
commit 99427cd48c
38 changed files with 15383 additions and 66 deletions

View File

@@ -29,12 +29,13 @@ const initialState: AppState = {
// API helper function
async function apiCall(endpoint: string, options: RequestInit = {}) {
const authState = get(auth);
const response = await fetch(`/api/v1${endpoint}`, {
...options,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${authState.token}`,
'X-Tenant-ID': authState.tenantId, // Added tenant header
...options.headers
}
});
@@ -60,8 +61,10 @@ function createAppStore() {
try {
const categories = await apiCall('/categories/');
console.log('Loaded categories:', categories); // Debugging
update(state => ({ ...state, categories, isLoading: false }));
} catch (error) {
console.error('Error loading categories:', error); // Debugging
update(state => ({
...state,
isLoading: false,

View File

@@ -107,6 +107,7 @@ function createAuthStore() {
isLoading: false
});
} catch (error) {
console.error('Login error:', error); // Debugging the error
update(state => ({ ...state, isLoading: false }));
throw error;
}

View File

@@ -0,0 +1,44 @@
import { writable } from 'svelte/store';
export const clients = writable([]);
export async function fetchClients() {
const response = await fetch('/api/v1/clients');
const data = await response.json();
clients.set(data);
}
export async function createClient(client) {
const response = await fetch('/api/v1/clients', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(client),
});
if (response.ok) {
fetchClients();
}
}
export async function updateClient(clientId, client) {
const response = await fetch(`/api/v1/clients/${clientId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(client),
});
if (response.ok) {
fetchClients();
}
}
export async function deleteClient(clientId) {
const response = await fetch(`/api/v1/clients/${clientId}`, {
method: 'DELETE',
});
if (response.ok) {
fetchClients();
}
}

View File

@@ -0,0 +1,53 @@
<script lang="ts">
import { onMount } from 'svelte';
import { clients } from '$lib/stores/clients';
import { toast } from '$lib/stores/toast';
let clientList = [];
let isLoading = true;
onMount(async () => {
try {
clientList = await clients.loadClients();
} catch (error) {
toast.error('Error al cargar los clientes');
} finally {
isLoading = false;
}
});
</script>
<svelte:head>
<title>Clientes - ServiceManager</title>
</svelte:head>
<div class="container mx-auto py-8">
<h1 class="text-2xl font-bold mb-4">Listado de Clientes</h1>
{#if isLoading}
<p>Cargando clientes...</p>
{:else if clientList.length === 0}
<p>No hay clientes registrados.</p>
{:else}
<table class="table-auto w-full border-collapse border border-gray-300">
<thead>
<tr>
<th class="border border-gray-300 px-4 py-2">Nombre</th>
<th class="border border-gray-300 px-4 py-2">Email</th>
<th class="border border-gray-300 px-4 py-2">Acciones</th>
</tr>
</thead>
<tbody>
{#each clientList as client}
<tr>
<td class="border border-gray-300 px-4 py-2">{client.name}</td>
<td class="border border-gray-300 px-4 py-2">{client.email}</td>
<td class="border border-gray-300 px-4 py-2">
<a href={`/clients/${client.id}/edit`} class="text-blue-500 hover:underline">Editar</a>
</td>
</tr>
{/each}
</tbody>
</table>
{/if}
</div>

View File

@@ -0,0 +1,83 @@
<script lang="ts">
import { onMount } from 'svelte';
import { clients } from '$lib/stores/clients';
import { toast } from '$lib/stores/toast';
import { goto, page } from '$app/navigation';
let clientId = $page.params.id;
let name = '';
let email = '';
let isSubmitting = false;
let errors: Record<string, string> = {};
onMount(async () => {
try {
const client = await clients.loadClient(clientId);
name = client.name;
email = client.email;
} catch (error) {
toast.error('Error al cargar el cliente');
goto('/clients');
}
});
function validateForm() {
errors = {};
if (!name.trim()) {
errors.name = 'El nombre es requerido';
}
if (!email.trim()) {
errors.email = 'El email es requerido';
}
return Object.keys(errors).length === 0;
}
async function handleSubmit() {
if (!validateForm()) return;
isSubmitting = true;
try {
await clients.updateClient(clientId, { name, email });
toast.success('Cliente actualizado exitosamente');
goto('/clients');
} catch (error) {
toast.error('Error al actualizar el cliente');
} finally {
isSubmitting = false;
}
}
</script>
<svelte:head>
<title>Editar Cliente - ServiceManager</title>
</svelte:head>
<div class="container mx-auto py-8">
<h1 class="text-2xl font-bold mb-4">Editar Cliente</h1>
<form on:submit|preventDefault={handleSubmit} class="space-y-4">
<div>
<label for="name" class="block text-sm font-medium text-gray-700">Nombre</label>
<input id="name" type="text" bind:value={name} class="mt-1 block w-full border-gray-300 rounded-md shadow-sm" />
{#if errors.name}
<p class="text-red-500 text-sm">{errors.name}</p>
{/if}
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
<input id="email" type="email" bind:value={email} class="mt-1 block w-full border-gray-300 rounded-md shadow-sm" />
{#if errors.email}
<p class="text-red-500 text-sm">{errors.email}</p>
{/if}
</div>
<button type="submit" class="btn btn-primary" disabled={isSubmitting}>
{isSubmitting ? 'Guardando...' : 'Guardar Cambios'}
</button>
</form>
</div>

View File

@@ -0,0 +1,71 @@
<script lang="ts">
import { onMount } from 'svelte';
import { clients } from '$lib/stores/clients';
import { toast } from '$lib/stores/toast';
import { goto } from '$app/navigation';
let name = '';
let email = '';
let isSubmitting = false;
let errors: Record<string, string> = {};
function validateForm() {
errors = {};
if (!name.trim()) {
errors.name = 'El nombre es requerido';
}
if (!email.trim()) {
errors.email = 'El email es requerido';
}
return Object.keys(errors).length === 0;
}
async function handleSubmit() {
if (!validateForm()) return;
isSubmitting = true;
try {
await clients.createClient({ name, email });
toast.success('Cliente creado exitosamente');
goto('/clients');
} catch (error) {
toast.error('Error al crear el cliente');
} finally {
isSubmitting = false;
}
}
</script>
<svelte:head>
<title>Crear Cliente - ServiceManager</title>
</svelte:head>
<div class="container mx-auto py-8">
<h1 class="text-2xl font-bold mb-4">Crear Nuevo Cliente</h1>
<form on:submit|preventDefault={handleSubmit} class="space-y-4">
<div>
<label for="name" class="block text-sm font-medium text-gray-700">Nombre</label>
<input id="name" type="text" bind:value={name} class="mt-1 block w-full border-gray-300 rounded-md shadow-sm" />
{#if errors.name}
<p class="text-red-500 text-sm">{errors.name}</p>
{/if}
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
<input id="email" type="email" bind:value={email} class="mt-1 block w-full border-gray-300 rounded-md shadow-sm" />
{#if errors.email}
<p class="text-red-500 text-sm">{errors.email}</p>
{/if}
</div>
<button type="submit" class="btn btn-primary" disabled={isSubmitting}>
{isSubmitting ? 'Creando...' : 'Crear Cliente'}
</button>
</form>
</div>