Skip to content

Commit

Permalink
Starting with UI impl for add transaction (#244)
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto authored Dec 14, 2023
1 parent ac84245 commit 73b49f9
Show file tree
Hide file tree
Showing 16 changed files with 318 additions and 44 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ mgo
coverage.cov
money.db
.DS_Store
__debug*
moneyd
16 changes: 14 additions & 2 deletions gen/portfolio.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package portfoliov1

import (
"hash/fnv"
"log/slog"
"strconv"
"time"
"log/slog"
)

func (p *Portfolio) EventMap() (m map[string][]*PortfolioEvent) {
Expand Down Expand Up @@ -50,10 +50,22 @@ func (tx *PortfolioEvent) MakeUniqueName() {
tx.Name = strconv.FormatUint(h.Sum64(), 16)
}

// LogValue implements slog.LogValuer.
func (tx *PortfolioEvent) LogValue() slog.Value {
return slog.GroupValue(
slog.String("name", tx.Name),
slog.String("security.name", tx.SecurityName),
slog.Float64("amount", float64(tx.Amount)),
slog.Float64("price", float64(tx.Price)),
slog.Float64("fees", float64(tx.Fees)),
slog.Float64("taxes", float64(tx.Taxes)),
)
}

// LogValue implements slog.LogValuer.
func (ls *ListedSecurity) LogValue() slog.Value {
return slog.GroupValue(
slog.String("name", ls.SecurityName),
slog.String("ticker", ls.Ticker),
)
}
}
1 change: 1 addition & 0 deletions money-gopher.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"portfoliov",
"protobuf",
"protoreflect",
"rgossiaux",
"secmap",
"secres",
"steeze",
Expand Down
3 changes: 3 additions & 0 deletions service/portfolio/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package portfolio
import (
"bytes"
"context"
"log/slog"

portfoliov1 "github.com/oxisto/money-gopher/gen"
"github.com/oxisto/money-gopher/import/csv"
Expand All @@ -36,6 +37,8 @@ func (svc *service) CreatePortfolioTransaction(ctx context.Context, req *connect
// Create a unique name for the transaction
req.Msg.Transaction.MakeUniqueName()

slog.Info("Creating transaction", "transaction", req.Msg.Transaction)

return crud.Create(
req.Msg.Transaction,
svc.events,
Expand Down
41 changes: 20 additions & 21 deletions ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"format": "prettier --plugin-search-dir . --write ."
},
"devDependencies": {
"@bufbuild/connect-web": "^0.13.0",
"@connectrpc/connect": "^1.1.3",
"@connectrpc/connect-web": "^1.1.3",
"@steeze-ui/heroicons": "^2.2.3",
"@steeze-ui/svelte-icon": "^1.5.0",
"@sveltejs/adapter-auto": "^2.0.0",
Expand Down
6 changes: 3 additions & 3 deletions ui/src/lib/api/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PortfolioService, SecuritiesService } from '$lib/gen/mgo_connect';
import { createPromiseClient } from '@bufbuild/connect';
import { createConnectTransport } from '@bufbuild/connect-web';
import type { PromiseClient } from '@bufbuild/connect';
import { createPromiseClient } from '@connectrpc/connect';
import { createConnectTransport } from '@connectrpc/connect-web';
import type { PromiseClient } from '@connectrpc/connect';

export function portfolioClient(fetch = window.fetch): PromiseClient<typeof PortfolioService> {
return createPromiseClient(
Expand Down
23 changes: 23 additions & 0 deletions ui/src/lib/components/Button.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script lang="ts">
import type { HTMLButtonAttributes } from 'svelte/elements';
let clazz: string | undefined | null = null;
export { clazz as class };
interface $$Props extends HTMLButtonAttributes {
class?: string | undefined | null;
}
</script>

<button
type="button"
class="mt-5 rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500
focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600
{clazz ?? ''}
"
on:click
{...$$restProps}
>
<slot />
</button>
30 changes: 30 additions & 0 deletions ui/src/lib/components/CurrencyInput.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script lang="ts">
export let value: number;
export let currency: string = 'EUR';
export let symbol: string = '';
export let name: string;
</script>

<div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:py-6">
<label for={name} class="block text-sm font-medium leading-6 text-gray-900 sm:pt-1.5">
<slot />
</label>
<div class="relative mt-2 rounded-md shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500 sm:text-sm">{symbol}</span>
</div>
<input
type="number"
{name}
id={name}
bind:value
class="block w-full rounded-md border-0 py-1.5 pl-7 pr-12 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400
focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
placeholder="0.00"
aria-describedby="price-currency"
/>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
<span class="text-gray-500 sm:text-sm" id="price-currency">{currency}</span>
</div>
</div>
</div>
75 changes: 75 additions & 0 deletions ui/src/lib/components/SecurityComboBox.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<script lang="ts">
import type { Security } from '$lib/gen/mgo_pb';
import { Check, ChevronUpDown } from '@steeze-ui/heroicons';
import { Icon } from '@steeze-ui/svelte-icon';
import { createListbox } from 'svelte-headlessui';
import { Transition } from 'svelte-transition';
const listbox = createListbox({ label: 'Securities', selected: null });
export let securities: Security[];
export let securityName: string | undefined;
$: securityName = $listbox.selected?.name ?? undefined;
</script>

<div class="relative mt-2">
<button
use:listbox.button
class="
relative w-full cursor-default rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1
ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 sm:text-sm sm:leading-6"
>
<span class="inline-flex w-full truncate">
{#if $listbox.selected}
<span class="truncate">{$listbox.selected.displayName}</span>
{:else}
<span class="truncate">Please select a security</span>
{/if}
</span>
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<Icon src={ChevronUpDown} class="h-5 w-5 text-gray-400" aria-hidden="true" />
</span>
</button>

<Transition
show={$listbox.expanded}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<ul
use:listbox.items
class="
absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1
ring-black ring-opacity-5 focus:outline-none sm:text-sm"
>
{#each securities as value (value.name)}
{@const active = $listbox.active === value}
{@const selected = $listbox.selected === value}
<li
use:listbox.item={{ value }}
class="{active
? 'bg-indigo-600 text-white'
: 'text-gray-900'} relative cursor-default select-none py-2 pl-3 pr-9"
>
<div class="flex">
<span class="{selected ? 'font-semibold' : 'font-normal'} truncate">
{value.displayName}
</span>
</div>

{#if selected}
<span
class="{active
? 'text-white'
: 'text-indigo-600'} absolute inset-y-0 right-0 flex items-center pr-4"
>
<Icon src={Check} class="h-5 w-5" aria-hidden="true" />
</span>
{/if}
</li>
{/each}
</ul>
</Transition>
</div>
10 changes: 10 additions & 0 deletions ui/src/routes/+layout.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import 'inter-ui/inter.css';
import '../app.css';
import { securitiesClient } from '$lib/api/client';
import type { LayoutLoad } from './$types';

export const ssr = false;

export const load = (async ({ fetch }) => {
const client = securitiesClient(fetch);

const securities = (await client.listSecurities({})).securities;

return { securities };
}) satisfies LayoutLoad;
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import type { PageLoad } from './$types';
import type { LayoutData } from './$types';
import { error } from '@sveltejs/kit';
import type { Portfolio } from '$lib/gen/mgo_pb';
import { portfolioClient } from '$lib/api/client';

export const load = (async ({ fetch, params }) => {
if (params.name == undefined) {
if (params.portfolioName == undefined) {
throw error(405, 'Required parameter missing');
}

const client = portfolioClient(fetch);
console.log(params.name);
console.log(params.portfolioName);

const portfolio = client.getPortfolio({ name: params.name });
const snapshot = client.getPortfolioSnapshot({ portfolioName: params.name });
const portfolio = client.getPortfolio({ name: params.portfolioName });
const snapshot = client.getPortfolioSnapshot({ portfolioName: params.portfolioName });

return { portfolio, snapshot };
}) as PageLoad;
}) as LayoutData;
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@

<PortfolioBreadcrumb portfolio={data.portfolio} snapshot={data.snapshot} />
<PortfolioPositionsTable snapshot={data.snapshot} />

<a href="/portfolios/{data.portfolio.name}/transactions/add">Add transaction</a>
Loading

0 comments on commit 73b49f9

Please sign in to comment.