Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update + un/archive groups #15

Merged
merged 5 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions my-app/src/lib/client/alerts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export async function confirmArchiveGroup(group: Group) {
const action = group.is_archived ? 'desarchivar' : 'archivar';
const message = `¿Querés ${action} el grupo ${group.name}?`;
if (!confirm(message)) return;
await fetch(`/api/groups?groupId=${group.id}&archive=${!group.is_archived}`, { method: 'PUT' });
location.reload();
}
12 changes: 9 additions & 3 deletions my-app/src/lib/server/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,16 @@ export const userService = {
export const groupService = {
save: (data: Group, cookies: Cookies) =>
data.id > 0
? put(`group/${data.id}`, data, getAuthHeader(cookies))
? put('group', data, getAuthHeader(cookies))
: post('group', data, getAuthHeader(cookies)),
list: (cookies: Cookies) => get('group', getAuthHeader(cookies)),
get: (id: Id, cookies: Cookies) => get(`group/${id}`, getAuthHeader(cookies))
list: (cookies: Cookies) =>
groupService.listAll(cookies).then((data: Group[]) => data.filter((g) => !g.is_archived)),
listAll: (cookies: Cookies) => get('group', getAuthHeader(cookies)),
get: (id: Id, cookies: Cookies) => get(`group/${id}`, getAuthHeader(cookies)),
archive: (id: Id, cookies: Cookies) =>
put(`group/${id}/archive`, undefined!, getAuthHeader(cookies)),
unarchive: (id: Id, cookies: Cookies) =>
put(`group/${id}/unarchive`, undefined!, getAuthHeader(cookies))
};
export const spendingService = {
save: (data: Spending, cookies: Cookies) =>
Expand Down
3 changes: 3 additions & 0 deletions my-app/src/lib/svgs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Useful SVGs
export const PENCIL_SVG =
'<svg fill="#FFF" width="25px" height="25px" viewBox="0 0 528.899 528.899"><g><path d="M328.883,89.125l107.59,107.589l-272.34,272.34L56.604,361.465L328.883,89.125z M518.113,63.177l-47.981-47.981 c-18.543-18.543-48.653-18.543-67.259,0l-45.961,45.961l107.59,107.59l53.611-53.611 C532.495,100.753,532.495,77.559,518.113,63.177z M0.3,512.69c-1.958,8.812,5.998,16.708,14.811,14.565l119.891-29.069 L27.473,390.597L0.3,512.69z"/></g></svg>';
11 changes: 11 additions & 0 deletions my-app/src/routes/api/groups/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { groupService } from '$lib/server/api';
import type { RequestHandler } from './$types';

export const PUT: RequestHandler = async ({ url, cookies }) => {
const groupId = Number(url.searchParams.get('groupId'));
const archive = url.searchParams.get('archive') === 'true';
const body = archive
? await groupService.archive(groupId, cookies)
: await groupService.unarchive(groupId, cookies);
return new Response(JSON.stringify(body));
};
24 changes: 12 additions & 12 deletions my-app/src/routes/budgets/details/[[id=integer]]/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ import { fixDateString } from '$lib/formatter';
export const load: PageServerLoad = async ({ params, url, cookies }) => {
const group_id = Number(url.searchParams.get('group_id'));
const id = Number(params.id) || 0;
let budget: Budget = {
id,
amount: 0,
description: '',
start_date: '',
end_date: '',
category_id: 0,
group_id
};
if (id) {
budget = await budgetService.get(id, cookies);
}
const now = new Date();
const budget: Budget = id
? await await budgetService.get(id, cookies)
: {
id,
amount: 0,
description: '',
start_date: now.toJSON(),
end_date: now.toJSON(),
category_id: 0,
group_id
};
const groups: Group[] = await groupService.list(cookies);
return { budget, groups };
};
Expand Down
23 changes: 12 additions & 11 deletions my-app/src/routes/budgets/details/[[id=integer]]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,26 @@
<title>{title} - Nuevo Presupuesto</title>
</svelte:head>

<nav aria-label="breadcrumb">
<ul>
<li><a href="/groups">Grupos</a></li>
<li>Presupuestos</li>
</ul>
</nav>

<h2>
{#if edit}Editando{:else}Creando{/if} Presupuesto
</h2>
<form method="POST" autocomplete="off">
<fieldset>
<input type="hidden" name="timezoneOffset" bind:value={timezoneOffset} required />
<input type="hidden" name="timezoneOffset" value={timezoneOffset} required />
<label>
Ingrese el grupo al que pertenece el presupuesto
<select
name="groupId"
required
aria-readonly={edit}
bind:value={data.budget.group_id}
value={data.budget.group_id}
on:change={(e) => onGroupUpdate(+e.currentTarget.value)}
>
{#each data.groups as group}
Expand All @@ -88,7 +95,7 @@
name="description"
placeholder="Descripción"
list="description-list"
bind:value={data.budget.description}
value={data.budget.description}
on:change={(e) => autocomplete(e.currentTarget.value)}
/>
<datalist id="description-list">
Expand All @@ -99,17 +106,11 @@
</label>
<label>
Ingrese un monto
<input
type="number"
name="amount"
placeholder="Monto"
required
bind:value={data.budget.amount}
/>
<input type="number" name="amount" placeholder="Monto" required value={data.budget.amount} />
</label>
<label>
Ingrese la categoría a la que pertenece el presupuesto
<select name="categoryId" required bind:value={data.budget.category_id}>
<select name="categoryId" required value={data.budget.category_id}>
{#each categories as category, i}
<option value={i + 1}>{category.name}</option>
{/each}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { error } from '@sveltejs/kit';
import { error, redirect } from '@sveltejs/kit';
import type { Actions } from './$types';
import { categoryService, groupService } from '$lib/server/api';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ params, url, cookies }) => {
const group_id = url.searchParams.get('group_id') || '';
const group_id = url.searchParams.get('groupId') || '';
const id = Number(params.id) || 0;
let category: Category = { id: 0, group_id, name: '', description: '', strategy: '' };
if (id) {
category = await categoryService.get(id, cookies);
}
const category: Category = id
? await categoryService.get(id, cookies)
: { id: 0, group_id, name: '', description: '', strategy: '' };
const groups: Group[] = await groupService.list(cookies);
return { category, groups };
};
Expand All @@ -35,6 +34,7 @@ export const actions: Actions = {

const category: Category = { id, group_id, name, description, strategy };
await categoryService.save(category, cookies);
return { success: true };

redirect(302, `/groups/movements/${group_id}`);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,19 @@
<title>{title} - Nueva Categoría</title>
</svelte:head>

<nav aria-label="breadcrumb">
<ul>
<li><a href="/groups">Grupos</a></li>
<li>Categorías</li>
</ul>
</nav>

<h2>Nueva Categoría</h2>
<form method="POST">
<fieldset>
<label>
Ingrese un grupo para la categoría
<select name="groupId" required bind:value={data.category.group_id}>
<select name="groupId" required value={data.category.group_id}>
{#each data.groups as group}
<option value={String(group.id)}>{group.name}</option>
{/each}
Expand Down
2 changes: 1 addition & 1 deletion my-app/src/routes/groups/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import { groupService } from '$lib/server/api';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ cookies }) => {
const groups: Group[] = await groupService.list(cookies);
const groups: Group[] = await groupService.listAll(cookies);
return { groups };
};
55 changes: 46 additions & 9 deletions my-app/src/routes/groups/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,37 +1,74 @@
<script lang="ts">
import { title } from '$lib';
import { confirmArchiveGroup } from '$lib/client/alerts';
import { PENCIL_SVG } from '$lib/svgs';
import type { PageData } from './$types';

export let data: PageData;
const activeGroups = data.groups.filter((g) => !g.is_archived);
const archivedGroups = data.groups.filter((g) => g.is_archived);
</script>

<svelte:head>
<title>{title} - Grupos</title>
</svelte:head>

<header class="row">
<h2>Grupos</h2>
<div>
<a href="/groups/details" role="button">Nuevo grupo</a>
<a href="/spendings/details" class="outline" role="button">Nuevo gasto</a>
<h2>Grupos</h2>
<p>Ingrese a un grupo para poder ver sus movimientos y agregar nuevos</p>
</div>
<div>
<details class="dropdown">
<!-- svelte-ignore a11y-no-redundant-roles -->
<summary role="button">Opciones</summary>
<ul>
<li><a href="/groups/details">Añadir grupo</a></li>
<li><a href="/spendings/details">Añadir gasto</a></li>
<li><a href="/budgets/details">Añadir presupuesto</a></li>
<li><a href="/categories/details">Añadir categoría</a></li>
</ul>
</details>
</div>
</header>
<main>
{#if !data?.groups.length}
{#if !data.groups.length}
<article class="centered">
<p>Todavía no pertenece a ningún grupo. ¿Por qué no crea uno?</p>
<a href="/groups/details">Crear un nuevo grupo</a>
</article>
{/if}
{#each data?.groups as group}
{#each activeGroups as group}
<article>
<header>{group.name}</header>
<header class="row">
<b style="padding: 15px">{group.name}</b>
<a class="secondary" href="/groups/details/{group.id}" role="button">{@html PENCIL_SVG}</a>
</header>
<p>{group.description}</p>
<!-- <a href="/groups/details/{group.id}" role="button" class="">Editar</a> -->
<a href="/groups/movements/{group.id}" role="button" class="outline">Moovimientos</a>
<a href="/groups/budgets/{group.id}" role="button" class="outline">Presupuestos</a>
<footer class="grid">
<a href="/groups/movements/{group.id}" role="button" class="outline">Ver moovimientos</a>
<a href="/groups/details/{group.id}" role="button" class="outline secondary">
Editar grupo
</a>
<button class="outline contrast" on:click={() => confirmArchiveGroup(group)}>
Archivar
</button>
</footer>
</article>
{/each}

{#if archivedGroups.length}
<article>
<details>
<summary> Archivados </summary>
{#each archivedGroups as group}
<button class="outline contrast" on:click={() => confirmArchiveGroup(group)}>
Desarchivar {group.name}
</button>
{/each}
</details>
</article>
{/if}
</main>

<style>
Expand Down
16 changes: 11 additions & 5 deletions my-app/src/routes/groups/budgets/[[id=integer]]/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
<script lang="ts">
import { title } from '$lib';
import { formatDateString, formatMoney } from '$lib/formatter';
import { PENCIL_SVG } from '$lib/svgs';
import type { PageServerData } from './$types';

export let data: PageServerData;

export const PENCIL_SVG =
'<svg fill="#FFF" width="25px" height="25px" viewBox="0 0 528.899 528.899"><g><path d="M328.883,89.125l107.59,107.589l-272.34,272.34L56.604,361.465L328.883,89.125z M518.113,63.177l-47.981-47.981 c-18.543-18.543-48.653-18.543-67.259,0l-45.961,45.961l107.59,107.59l53.611-53.611 C532.495,100.753,532.495,77.559,518.113,63.177z M0.3,512.69c-1.958,8.812,5.998,16.708,14.811,14.565l119.891-29.069 L27.473,390.597L0.3,512.69z"/></g></svg>';
</script>

<svelte:head>
<title>{title} - {data.group.name}</title>
</svelte:head>

<nav aria-label="breadcrumb">
<ul>
<li><a href="/groups">Grupos</a></li>
<li><a href="/groups/movements/{data.group.id}">{data.group.name}</a></li>
<li>Presupuestos</li>
</ul>
</nav>

<header class="row">
<div>
<h2>{data.group.name}</h2>
<h2>Presupuestos</h2>
<p>{data.group.description}</p>
</div>
<div>
<a href="/budgets/details?group_id={data.group.id}" role="button">Nuevo presupuesto</a>
<a href="/budgets/details?groupId={data.group.id}" role="button">Nuevo presupuesto</a>
</div>
</header>

Expand Down
13 changes: 6 additions & 7 deletions my-app/src/routes/groups/details/[[id=integer]]/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import type { Actions } from './$types';
import { groupService } from '$lib/server/api';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ params }) => {
const group: Group = { name: '', description: '', id: 0, owner_id: 0 };
const id = params.id;
if (id) {
// TODO: load real group
}
export const load: PageServerLoad = async ({ params, cookies }) => {
const id = Number(params.id) || 0;
const group: Group = id
? await groupService.get(id, cookies)
: { name: '', description: '', id: 0, owner_id: 0, is_archived: false };
return { group };
};

Expand All @@ -26,7 +25,7 @@ export const actions: Actions = {
throw error(400, 'Description is required');
}

const group: Group = { id, name, description, owner_id: 0 };
const group: Group = { id, name, description, owner_id: 0, is_archived: false };
const body = await groupService.save(group, cookies);
id = body.id;
redirect(302, `/groups/movements/${id}`);
Expand Down
26 changes: 21 additions & 5 deletions my-app/src/routes/groups/details/[[id=integer]]/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
<script lang="ts">
import { title } from '$lib';
import { confirmArchiveGroup } from '$lib/client/alerts';
import type { PageData } from './$types';

export let data: PageData;
const edit = data.group.id !== 0;

const pageTitle = `${edit ? 'Editando' : 'Creando'} Grupo`;
</script>

<svelte:head>
<title>{title} - Nuevo Grupo</title>
<title>{title} - {pageTitle}</title>
</svelte:head>

<h2>Nuevo Grupo</h2>
<nav aria-label="breadcrumb">
<ul>
<li><a href="/groups">Grupos</a></li>
<li>Detalles</li>
</ul>
</nav>

<h2>{pageTitle}</h2>
<form method="POST">
<fieldset>
<label>
Ingrese un nombre para el grupo
<input type="text" name="name" value={data.group.name} placeholder="Nombre" required />
<input type="text" name="name" placeholder="Nombre" required value={data.group.name} />
</label>
<label>
Ingrese una descripción para el grupo
<input
type="text"
name="description"
value={data.group.description}
placeholder="Descripción"
required
value={data.group.description}
/>
</label>
<button>Crear</button>
<button>Guardar</button>
{#if edit}
<button type="button" class="contrast" on:click={() => confirmArchiveGroup(data.group)}>
Archivar
</button>
{/if}
<button type="button" class="outline" on:click={() => history.back()}>Cancelar</button>
</fieldset>
</form>
Loading