Compare commits
4 Commits
24f504dc87
...
6d66648363
Author | SHA1 | Date |
---|---|---|
Anantha Kumaran | 6d66648363 | |
Anantha Kumaran | 7f1731f244 | |
Anantha Kumaran | aea5f0a3fe | |
Anantha Kumaran | e0d58caa0f |
|
@ -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
|
||||
|
|
|
@ -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 |
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
13
src/app.scss
13
src/app.scss
|
@ -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%,
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue