Compare commits

...

4 Commits

Author SHA1 Message Date
Anantha Kumaran 6d66648363 add tzdata package
refer #162
2024-01-28 23:31:08 +05:30
Anantha Kumaran 7f1731f244 [goals] add drag handler to goal card 2024-01-28 23:30:46 +05:30
Anantha Kumaran aea5f0a3fe [docs] add logo 2024-01-28 16:11:12 +05:30
Anantha Kumaran e0d58caa0f [docs] credit card account 2024-01-28 11:14:10 +05:30
10 changed files with 118 additions and 30 deletions

View File

@ -15,7 +15,7 @@ COPY --from=web /usr/src/paisa/web/static ./web/static
RUN CGO_ENABLED=1 go build
FROM alpine:3.18
RUN apk --no-cache add ca-certificates ledger
RUN apk --no-cache add ca-certificates ledger tzdata
WORKDIR /root/
COPY --from=go /usr/src/paisa/paisa /usr/bin
EXPOSE 7500

1
docs/images/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><g><g><g transform="rotate(360, 12, 12)"><path d="M12 12Z" fill="none" stroke="#b388ff" stroke-width="4" stroke-linecap="round"></path></g></g><g><g transform="rotate(0, 12, 12)"><path d="M 21.510565162951536 8.909830056250526 A 10 10 0 0 0 12 6" fill="none" stroke="#aeea00" stroke-width="4"></path></g><g transform="rotate(72, 12, 12)"><path d="M 21.510565162951536 8.909830056250526 A 10 10 0 0 0 12 6" fill="none" stroke="#ff1744" stroke-width="4"></path></g><g transform="rotate(144, 12, 12)"><path d="M 21.510565162951536 8.909830056250526 A 10 10 0 0 0 12 6" fill="none" stroke="#d500f9" stroke-width="4"></path></g><g transform="rotate(216, 12, 12)"><path d="M 21.510565162951536 8.909830056250526 A 10 10 0 0 0 12 6" fill="none" stroke="#ffab00" stroke-width="4"></path></g><g transform="rotate(288, 12, 12)"><path d="M 21.510565162951536 8.909830056250526 A 10 10 0 0 0 12 6" fill="none" stroke="#00b0ff" stroke-width="4"></path></g><g transform="rotate(72, 12, 12)"><path d="M12.59 5.88Z" fill="none" stroke="#aeea00" stroke-width="4.2" stroke-linecap="round"></path></g><g transform="rotate(144, 12, 12)"><path d="M12.59 5.88Z" fill="none" stroke="#ff1744" stroke-width="4.2" stroke-linecap="round"></path></g><g transform="rotate(216, 12, 12)"><path d="M12.59 5.88Z" fill="none" stroke="#d500f9" stroke-width="4.2" stroke-linecap="round"></path></g><g transform="rotate(288, 12, 12)"><path d="M12.59 5.88Z" fill="none" stroke="#ffab00" stroke-width="4.2" stroke-linecap="round"></path></g><g transform="rotate(360, 12, 12)"><path d="M12.59 5.88Z" fill="none" stroke="#00b0ff" stroke-width="4.2" stroke-linecap="round"></path></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -6,8 +6,9 @@ hide:
---
<div class="hero" markdown>
![Logo](./images/logo.svg)
# Paisa
<p>Personal Finance Manager</p>
<p class="subtitle">Personal Finance Manager</p>
</div>
<div class="home" markdown>
@ -84,7 +85,7 @@ hide:
- :material-microsoft-excel: **Convert** CSV, Excel and PDF files to Ledger journal.
- :fontawesome-solid-calculator: **Calculate** your taxes, emis, etc using **[sheets](./reference/sheets.md)**.
- :octicons-goal-16: Track your **goals**.
- :material-timer-sync: View your **recurring** transactions.
- :material-timer-sync: View your **recurring** transactions and **credit card** bills.
- :material-beach: Plan your **retirement**.
- :material-chart-bar: And many more **visualizations** to help you make any financial
decisions.
@ -222,7 +223,7 @@ you need to save each month to achieve your goal.
# Bills
Paisa can help you track your [recurring](./reference/recurring.md) transactions like rent, emi,
credit card bills, etc. The calendar view will show you all the bills
[credit card](./reference/credit-cards.md) bills, etc. The calendar view will show you all the bills
that are due in the current month. You can set the recurring period of
the transaction, which is flexible enough to handle any kind of
schedule like weekly, monthly, quarterly, last day of the month, last

View File

@ -128,6 +128,15 @@ your Savings Rate. If you want to track different types of taxes, you
can use sub accounts as well, for example `#!ledger Expenses:Tax:Income`
and `#!ledger Expenses:Tax:GST`.
## Liabilities
### Credit Card
Credit card accounts should be named `#!ledger
Liabilities:CreditCard:{name}`, for example `#!ledger
Liabilities:CreditCard:Freedom` and `#!ledger
Liabilities:CreditCard:AmazonPay`
## Equity
Equity is used in rare cases where you want to balance the

View File

@ -58,12 +58,18 @@
margin-bottom: 3rem;
}
.hero img {
height: 90px;
margin-top: -60px;
margin-bottom: 15px;
}
.hero h1 {
text-align: center;
color: inherit;
font-weight: 700;
color: var(--logo-color);
margin-bottom: 0;
margin-bottom: -7px;
margin-top: -30px;
font-size: 3rem;
}
@ -82,7 +88,11 @@
margin: auto;
line-height: 1.28;
color: var(--md-default-fg-color--light);
font-size: 0.72rem;
font-size: 0.9rem;
}
.hero p.subtitle {
opacity: 0.7;
}
.home .tabbed-set {

View File

@ -759,6 +759,10 @@ nav.level.grid-2 {
.sheet-editor .cm-editor {
}
.preview-editor {
min-height: 35px;
}
.search-query-editor {
border: 1px solid $grey-lighter;
border-radius: $radius;
@ -1106,9 +1110,12 @@ div.is-hoverable:hover {
display: flex;
border: 1px solid $grey-lightest;
box-shadow:
3px 3px 3px 0 rgba(0, 0, 0, 0.05),
10px 10px 10px 0 rgba(0, 0, 0, 0.15),
20px 20px 20px 0 rgba(0, 0, 0, 0.15) !important;
5px 5px 5px 0 rgba(0, 0, 0, 0.2),
15px 15px 15px 0 rgba(0, 0, 0, 0.1),
30px 30px 30px 0 rgba(0, 0, 0, 0.075),
-3px -3px 3px 0 rgba(255, 255, 255, 1),
-7px -7px 7px 0 rgba(255, 255, 255, 1),
-15px -15px 15px 0 rgba(255, 255, 255, 1) !important;
background: linear-gradient(
345deg,
$grey-lightest 0%,

View File

@ -9,10 +9,10 @@
}
</script>
<div class="du-tabs du-tabs-boxed">
<div class="du-tabs du-tabs-boxed du-tabs-sm">
{#each options as option}
<a
class="du-tab du-tab-sm {option.value === value ? 'du-tab-active' : ''}"
class="du-tab {option.value === value ? 'du-tab-active' : ''}"
on:click={() => (value = option.value)}
>
{option.label}

View File

@ -6,9 +6,11 @@
import Progress from "$lib/components/Progress.svelte";
import COLORS from "$lib/colors";
import dayjs from "dayjs";
import type { Action } from "svelte/action";
export let goal: GoalSummary;
export let small = false;
export let action: Action = null;
function formatDate(date: string) {
const d = dayjs(date, "YYYY-MM-DD", true);
@ -31,12 +33,19 @@
<div class="box p-3 goal-summary-card" class:mb-3={small}>
<div class="flex justify-between mb-4">
<a
class="secondary-link has-text-grey"
href="/more/goals/{goal.type}/{encodeURIComponent(goal.name)}"
>
<h4 class="is-size-4 has-text-grey">{goal.name}</h4>
</a>
<div class="flex">
{#if action}
<span use:action class="icon is-size-4 mr-1 mt-1 has-text-grey">
<i class="fas fa-grip-vertical" />
</span>
{/if}
<a
class="secondary-link has-text-grey"
href="/more/goals/{goal.type}/{encodeURIComponent(goal.name)}"
>
<h4 class="is-size-4 has-text-grey">{goal.name}</h4>
</a>
</div>
{#if !_.isEmpty(goal.icon)}
<span class="{small ? 'is-size-3' : 'is-size-2'} custom-icon">{iconGlyph(goal.icon)}</span>
{/if}

View File

@ -251,14 +251,14 @@
<p class="control">
<span data-tippy-content="Create" data-tippy-followCursor="false">
<button class="button" on:click={(_e) => openTemplateCreateModal()}>
<span class="icon is-small">
<span class="icon">
<i class="fas fa-file-circle-plus" />
</span>
</button>
</span>
<span
class="du-tooltip ml-4"
class="ml-4"
data-tippy-followCursor="false"
data-tippy-content={$templateEditorState.hasUnsavedChanges == false
? "No Unsaved Chagnes"
@ -270,7 +270,7 @@
disabled={$templateEditorState.hasUnsavedChanges == false ||
selectedTemplate?.template_type == "builtin"}
>
<span class="icon is-small">
<span class="icon">
<i class="fas fa-floppy-disk" />
</span>
</button>
@ -285,7 +285,7 @@
on:click={(_e) => remove()}
disabled={selectedTemplate?.template_type == "builtin"}
>
<span class="icon is-small">
<span class="icon">
<i class="fas fa-trash-can" />
</span>
</button>
@ -331,22 +331,24 @@
<div class="field">
<div class="control">
<button
title="copy to clipboard"
class="button is-small clipboard"
data-tippy-followCursor="false"
data-tippy-content="Copy to Clipboard"
class="button clipboard"
disabled={_.isEmpty(preview)}
on:click={copyToClipboard}
>
<span class="icon is-small">
<i class="fas fa-clipboard" />
<span class="icon">
<i class="fas fa-copy" />
</span>
</button>
<button
title="save"
class="button is-small save"
data-tippy-followCursor="false"
data-tippy-content="Save"
class="button save"
disabled={_.isEmpty(preview)}
on:click={openSaveModal}
>
<span class="icon is-small">
<span class="icon">
<i class="fas fa-floppy-disk" />
</span>
</button>

View File

@ -7,16 +7,21 @@
import _ from "lodash";
import { onMount } from "svelte";
import * as toast from "bulma-toast";
import { writable } from "svelte/store";
import type { Action } from "svelte/action";
let isEmpty = false;
let config: UserConfig;
let goals: GoalSummary[] = [];
const dragDisabled = writable(true);
function handleConsider(event: CustomEvent<DndEvent<GoalSummary>>) {
goals = event.detail.items;
}
async function handleFinalize(event: CustomEvent<DndEvent<GoalSummary>>) {
dragDisabled.set(true);
goals = event.detail.items;
for (let i = 0; i < goals.length; i++) {
const g = goals[i];
@ -59,19 +64,63 @@
isEmpty = true;
}
});
const dragHandle: Action<HTMLElement, {}> = (node: HTMLElement) => {
function startDrag(e: Event) {
e.preventDefault();
dragDisabled.set(false);
}
function stopDrag(_e: KeyboardEvent) {
dragDisabled.set(true);
}
function handleKeyDown(e: KeyboardEvent) {
if (e.key === "Enter" || e.key === " ") {
dragDisabled.set(false);
}
}
dragDisabled.subscribe((disabled) => {
node.tabIndex = disabled ? 0 : -1;
node.style.cursor = disabled ? "grab" : "grabbing";
});
node.addEventListener("mousedown", startDrag);
node.addEventListener("touchstart", startDrag);
node.addEventListener("keydown", handleKeyDown);
node.addEventListener("mouseup", stopDrag);
node.addEventListener("touchend", stopDrag);
return {
update: () => {},
destroy: () => {
node.removeEventListener("mousedown", startDrag);
node.removeEventListener("touchstart", startDrag);
node.removeEventListener("keydown", handleKeyDown);
node.removeEventListener("mouseup", stopDrag);
node.removeEventListener("touchend", stopDrag);
}
};
};
</script>
<section class="section">
<div class="container is-fluid">
<div
class="columns flex-wrap"
use:dndzone={{ items: goals, dropTargetStyle: {}, flipDurationMs: 300 }}
use:dndzone={{
items: goals,
dropTargetStyle: {},
flipDurationMs: 300,
dragDisabled: $dragDisabled
}}
on:consider={handleConsider}
on:finalize={handleFinalize}
>
{#each goals as goal (goal.id)}
<div animate:flip={{ duration: 300 }} class="column is-6 is-one-third-widescreen">
<GoalSummaryCard {goal} />
<GoalSummaryCard action={dragHandle} {goal} />
</div>
{/each}
</div>