Compare commits
21 Commits
0.5.1
...
fc50313907
Author | SHA1 | Date | |
---|---|---|---|
fc50313907 | |||
4824e75049 | |||
00a391d08c | |||
744ff5207c | |||
1094ce670e | |||
1ad533a306 | |||
970cdf896c | |||
f72e7b8cec | |||
d87716977a | |||
0ca52d4fe4 | |||
e0285922bd | |||
4eaa1ccbd1 | |||
ccea4dfaa4 | |||
c6cb36c260 | |||
646df25d5b | |||
0e83e31fda | |||
5d1b49c896 | |||
42dc51fe9a | |||
1ca95f8768 | |||
a73f7c2934 | |||
489aa88c4b |
10
.earthignore
10
.earthignore
@ -1,10 +0,0 @@
|
||||
build/
|
||||
.git/
|
||||
docker-compose.yml
|
||||
README.md
|
||||
Earthfile
|
||||
config.example.json
|
||||
.gitignore
|
||||
.vscode/
|
||||
budgeteer
|
||||
budgeteer.exe
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -1,7 +1,11 @@
|
||||
{
|
||||
"files.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/vendor": true
|
||||
"**/vendor": true,
|
||||
"**/*.sql.go": true,
|
||||
".task/": true,
|
||||
"build/": true,
|
||||
"web/dist/": true
|
||||
},
|
||||
"gopls": {
|
||||
"formatting.gofumpt": true,
|
||||
|
@ -1,23 +0,0 @@
|
||||
pipeline:
|
||||
build:
|
||||
name: Taskfile.dev
|
||||
image: hub.javil.eu/budgeteer:dev
|
||||
pull: true
|
||||
commands:
|
||||
- task ci
|
||||
|
||||
docker:
|
||||
image: plugins/docker
|
||||
secrets: [ docker_username, docker_password ]
|
||||
settings:
|
||||
registry: hub.javil.eu
|
||||
repo: hub.javil.eu/budgeteer
|
||||
context: build
|
||||
dockerfile: build/Dockerfile
|
||||
tags:
|
||||
- latest
|
||||
when:
|
||||
event: [push, tag, deployment]
|
||||
|
||||
image_pull_secrets:
|
||||
- hub.javil.eu
|
21
Earthfile
21
Earthfile
@ -1,21 +0,0 @@
|
||||
FROM golang:1.17
|
||||
WORKDIR /src
|
||||
|
||||
build:
|
||||
COPY go.mod go.sum .
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build go build -o build/budgeteer ./cmd/budgeteer
|
||||
SAVE ARTIFACT build/budgeteer /budgeteer AS LOCAL build/budgeteer
|
||||
|
||||
docker:
|
||||
WORKDIR /app
|
||||
COPY +build/budgeteer .
|
||||
ENTRYPOINT ["/app/budgeteer"]
|
||||
SAVE IMAGE hub.javil.eu/budgeteer:latest
|
||||
|
||||
run:
|
||||
LOCALLY
|
||||
WITH DOCKER --load=+docker
|
||||
RUN docker-compose up -d
|
||||
END
|
@ -96,7 +96,7 @@ tasks:
|
||||
- ./docker/build.sh
|
||||
- ./web/package.json
|
||||
cmds:
|
||||
- docker build -t {{.IMAGE_NAME}}:dev . -f docker/Dockerfile
|
||||
- docker build -t {{.IMAGE_NAME}}:dev . -f docker/Dockerfile.dev
|
||||
- docker push {{.IMAGE_NAME}}:dev
|
||||
|
||||
run:
|
||||
|
@ -1,3 +0,0 @@
|
||||
FROM scratch
|
||||
COPY ./budgeteer /app/budgeteer
|
||||
ENTRYPOINT ["/app/budgeteer"]
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"DatabaseHost": "localhost",
|
||||
"DatabaseUser": "user",
|
||||
"DatabasePassword": "thisismypassword",
|
||||
"DatabaseName": "budgeteer"
|
||||
}
|
@ -1,17 +1,3 @@
|
||||
FROM alpine as godeps
|
||||
RUN apk --no-cache add go
|
||||
RUN go install github.com/kyleconroy/sqlc/cmd/sqlc@latest
|
||||
RUN go install github.com/go-task/task/v3/cmd/task@latest
|
||||
RUN go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
|
||||
FROM alpine
|
||||
RUN apk --no-cache add go nodejs yarn bash curl git git-perl tmux
|
||||
ADD docker/build.sh /
|
||||
RUN yarn global add @vue/cli
|
||||
ENV PATH="/root/.yarn/bin/:${PATH}"
|
||||
WORKDIR /src/web
|
||||
ADD web/package.json web/yarn.lock /src/web/
|
||||
RUN yarn
|
||||
WORKDIR /src
|
||||
COPY --from=godeps /root/go/bin/task /root/go/bin/sqlc /root/go/bin/golangci-lint /usr/local/bin/
|
||||
CMD /build.sh
|
||||
FROM scratch
|
||||
COPY ./budgeteer /app/budgeteer
|
||||
ENTRYPOINT ["/app/budgeteer"]
|
||||
|
17
docker/Dockerfile.dev
Normal file
17
docker/Dockerfile.dev
Normal file
@ -0,0 +1,17 @@
|
||||
FROM alpine as godeps
|
||||
RUN apk --no-cache add go
|
||||
RUN go install github.com/kyleconroy/sqlc/cmd/sqlc@latest
|
||||
RUN go install github.com/go-task/task/v3/cmd/task@latest
|
||||
RUN go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
|
||||
FROM alpine
|
||||
RUN apk --no-cache add go nodejs yarn bash curl git git-perl tmux
|
||||
ADD docker/dev.sh /
|
||||
RUN yarn global add @vue/cli
|
||||
ENV PATH="/root/.yarn/bin/:${PATH}"
|
||||
WORKDIR /src/web
|
||||
ADD web/package.json web/yarn.lock /src/web/
|
||||
RUN yarn
|
||||
WORKDIR /src
|
||||
COPY --from=godeps /root/go/bin/task /root/go/bin/sqlc /root/go/bin/golangci-lint /usr/local/bin/
|
||||
CMD /dev.sh
|
2
go.mod
2
go.mod
@ -11,7 +11,7 @@ require (
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa
|
||||
)
|
||||
|
||||
require github.com/DATA-DOG/go-txdb v0.1.5 // indirect
|
||||
require github.com/DATA-DOG/go-txdb v0.1.5
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
|
1
go.sum
1
go.sum
@ -74,6 +74,7 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
|
@ -12,10 +12,11 @@ import (
|
||||
|
||||
// TokenVerifier verifies Tokens.
|
||||
type TokenVerifier struct {
|
||||
secret string
|
||||
Expiration time.Duration
|
||||
secret string
|
||||
}
|
||||
|
||||
var ErrEmptySecret = fmt.Errorf("secret is required")
|
||||
const DefaultExpiration = time.Hour * time.Duration(72)
|
||||
|
||||
func NewTokenVerifier(secret string) (*TokenVerifier, error) {
|
||||
if secret == "" {
|
||||
@ -23,20 +24,16 @@ func NewTokenVerifier(secret string) (*TokenVerifier, error) {
|
||||
}
|
||||
|
||||
return &TokenVerifier{
|
||||
secret: secret,
|
||||
Expiration: DefaultExpiration,
|
||||
secret: secret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Token contains everything to authenticate a user.
|
||||
type Token struct {
|
||||
username string
|
||||
name string
|
||||
expiry float64
|
||||
id uuid.UUID
|
||||
}
|
||||
|
||||
const (
|
||||
expiration = 72
|
||||
var (
|
||||
ErrUnexpectedSigningMethod = fmt.Errorf("unexpected signing method")
|
||||
ErrInvalidToken = fmt.Errorf("token is invalid")
|
||||
ErrTokenExpired = fmt.Errorf("token has expired")
|
||||
ErrEmptySecret = fmt.Errorf("secret is required")
|
||||
)
|
||||
|
||||
// CreateToken creates a new token from username and name.
|
||||
@ -48,7 +45,7 @@ func (tv *TokenVerifier) CreateToken(user *postgres.User) (string, error) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"usr": user.Email,
|
||||
"name": user.Name,
|
||||
"exp": time.Now().Add(time.Hour * expiration).Unix(),
|
||||
"exp": time.Now().Add(tv.Expiration).Unix(),
|
||||
"id": user.ID,
|
||||
})
|
||||
|
||||
@ -61,13 +58,7 @@ func (tv *TokenVerifier) CreateToken(user *postgres.User) (string, error) {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
var (
|
||||
ErrUnexpectedSigningMethod = fmt.Errorf("unexpected signing method")
|
||||
ErrInvalidToken = fmt.Errorf("token is invalid")
|
||||
ErrTokenExpired = fmt.Errorf("token has expired")
|
||||
)
|
||||
|
||||
// VerifyToken verifys a given string-token.
|
||||
// VerifyToken verifies a given string-token.
|
||||
func (tv *TokenVerifier) VerifyToken(tokenString string) (budgeteer.Token, error) { //nolint:ireturn
|
||||
if tv.secret == "" {
|
||||
return nil, ErrEmptySecret
|
||||
@ -96,36 +87,3 @@ func (tv *TokenVerifier) VerifyToken(tokenString string) (budgeteer.Token, error
|
||||
}
|
||||
return tkn, nil
|
||||
}
|
||||
|
||||
func verifyToken(token *jwt.Token) (jwt.MapClaims, error) {
|
||||
if !token.Valid {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
if !claims.VerifyExpiresAt(time.Now().Unix(), true) {
|
||||
return nil, ErrTokenExpired
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func (t *Token) GetName() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *Token) GetUsername() string {
|
||||
return t.username
|
||||
}
|
||||
|
||||
func (t *Token) GetExpiry() float64 {
|
||||
return t.expiry
|
||||
}
|
||||
|
||||
func (t *Token) GetID() uuid.UUID {
|
||||
return t.id
|
||||
}
|
49
jwt/token.go
Normal file
49
jwt/token.go
Normal file
@ -0,0 +1,49 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Token contains everything to authenticate a user.
|
||||
type Token struct {
|
||||
username string
|
||||
name string
|
||||
expiry float64
|
||||
id uuid.UUID
|
||||
}
|
||||
|
||||
func verifyToken(token *jwt.Token) (jwt.MapClaims, error) {
|
||||
if !token.Valid {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
if !claims.VerifyExpiresAt(time.Now().Unix(), true) {
|
||||
return nil, ErrTokenExpired
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func (t *Token) GetName() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *Token) GetUsername() string {
|
||||
return t.username
|
||||
}
|
||||
|
||||
func (t *Token) GetExpiry() float64 {
|
||||
return t.expiry
|
||||
}
|
||||
|
||||
func (t *Token) GetID() uuid.UUID {
|
||||
return t.id
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body class="bg-slate-200 text-slate-800 dark:bg-slate-800 dark:text-slate-200">
|
||||
<body class="bg-slate-200 text-slate-800 dark:bg-slate-800 dark:text-slate-200 box-border w-full">
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
@ -27,39 +27,34 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="box-border w-full">
|
||||
<div class="flex bg-gray-400 dark:bg-gray-600 p-4 m-2 rounded-lg">
|
||||
<span class="flex-1 font-bold text-5xl -my-3 hidden md:inline" @click="toggleMenuSize">≡</span>
|
||||
<span class="flex-1 font-bold text-5xl -my-3 md:hidden" @click="toggleMenu">≡</span>
|
||||
|
||||
<span class="flex-1">{{ CurrentBudgetName }}</span>
|
||||
|
||||
<div class="flex flex-1 flex-row justify-end -mx-4">
|
||||
<router-link class="mx-4" v-if="LoggedIn" to="/dashboard">Dashboard</router-link>
|
||||
<router-link class="mx-4" v-if="!LoggedIn" to="/login">Login</router-link>
|
||||
<a class="mx-4" v-if="LoggedIn" @click="logout">Logout</a>
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-row flex-1">
|
||||
<div
|
||||
:class="[Menu.Expand ? 'md:w-72' : 'md:w-36', Menu.Show ? '' : 'hidden']"
|
||||
class="md:block flex-shrink-0 w-full bg-gray-500 border-r-4 border-black"
|
||||
>
|
||||
<router-view name="sidebar"></router-view>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:flex-row flex-1">
|
||||
<div
|
||||
:class="[Menu.Expand ? 'md:w-72' : 'md:w-36', Menu.Show ? '' : 'hidden']"
|
||||
class="md:block flex-shrink-0 w-full"
|
||||
>
|
||||
<router-view name="sidebar"></router-view>
|
||||
<div class="flex-1">
|
||||
<div class="flex bg-gray-400 dark:bg-gray-600 p-4 fixed md:static top-0 left-0 w-full h-14">
|
||||
<span
|
||||
class="flex-1 font-bold text-5xl -my-3 hidden md:inline"
|
||||
@click="toggleMenuSize"
|
||||
>≡</span>
|
||||
<span class="flex-1 font-bold text-5xl -my-3 md:hidden" @click="toggleMenu">≡</span>
|
||||
|
||||
<span class="flex-1">{{ CurrentBudgetName }}</span>
|
||||
|
||||
<div class="flex flex-1 flex-row justify-end -mx-4">
|
||||
<router-link class="mx-4" v-if="LoggedIn" to="/dashboard">Dashboard</router-link>
|
||||
<router-link class="mx-4" v-if="!LoggedIn" to="/login">Login</router-link>
|
||||
<a class="mx-4" v-if="LoggedIn" @click="logout">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 p-6">
|
||||
<div class="p-3 pl-6">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
</style>
|
||||
</template>
|
11
web/src/components/Checkbox.vue
Normal file
11
web/src/components/Checkbox.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps(["modelValue"]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="modelValue"
|
||||
@change="$emit('update:modelValue', ($event.target as HTMLInputElement)?.checked)"
|
||||
class="dark:bg-slate-900">
|
||||
</template>
|
@ -4,7 +4,7 @@ const props = defineProps(["modelValue"]);
|
||||
|
||||
<template>
|
||||
<input
|
||||
v-model="modelValue"
|
||||
:value="modelValue"
|
||||
@input="$emit('update:modelValue', ($event.target as HTMLInputElement)?.value)"
|
||||
class="dark:bg-slate-900">
|
||||
</template>
|
@ -7,6 +7,7 @@ import TransactionEditRow from "./TransactionEditRow.vue";
|
||||
import { formatDate } from "../date";
|
||||
import { useAccountStore } from "../stores/budget-account";
|
||||
import Input from "./Input.vue";
|
||||
import Checkbox from "./Checkbox.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
transactionid: string,
|
||||
@ -70,7 +71,7 @@ function getStatusSymbol() {
|
||||
{{ TX.GroupID ? "☀" : "" }}
|
||||
{{ getStatusSymbol() }}
|
||||
<a @click="edit = true;">✎</a>
|
||||
<Input v-if="Reconciling && TX.Status != 'Reconciled'" type="checkbox" v-model="TX.Reconciled" />
|
||||
<Checkbox v-if="Reconciling && TX.Status != 'Reconciled'" v-model="TX.Reconciled" />
|
||||
</td>
|
||||
</tr>
|
||||
<TransactionEditRow v-if="edit" :transactionid="TX.ID" @save="edit = false" />
|
||||
|
@ -8,4 +8,10 @@ h1 {
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
@ -49,41 +49,48 @@ function createReconcilationTransaction() {
|
||||
</h1>
|
||||
|
||||
<div class="text-right flex flex-wrap flex-col md:flex-row justify-end gap-2 max-w-sm">
|
||||
<span class="border-2 rounded-lg p-1 whitespace-nowrap flex-1">
|
||||
<span class="rounded-lg p-1 whitespace-nowrap flex-1">
|
||||
Working:
|
||||
<Currency :value="accounts.CurrentAccount?.WorkingBalance" />
|
||||
</span>
|
||||
|
||||
<span class="border-2 rounded-lg p-1 whitespace-nowrap flex-1">
|
||||
<span class="rounded-lg p-1 whitespace-nowrap flex-1">
|
||||
Cleared:
|
||||
<Currency :value="accounts.CurrentAccount?.ClearedBalance" />
|
||||
</span>
|
||||
|
||||
<span
|
||||
class="border-2 border-blue-500 rounded-lg bg-blue-500 p-1 whitespace-nowrap flex-1"
|
||||
class="rounded-lg bg-blue-500 p-1 whitespace-nowrap flex-1"
|
||||
v-if="!transactions.Reconciling"
|
||||
@click="transactions.Reconciling = true"
|
||||
>
|
||||
Reconciled:
|
||||
<Currency :value="accounts.CurrentAccount?.ReconciledBalance" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span v-if="transactions.Reconciling" class="border-2 block bg-gray-200 dark:bg-gray-800 rounded-lg p-2">
|
||||
Is
|
||||
<Currency :value="transactions.ReconcilingBalance" />your current balance?
|
||||
<Button class="bg-blue-500 mx-3 py-2" @click="submitReconcilation">Yes!</Button>
|
||||
<br />No, it's:
|
||||
<Input class="text-right" type="number" v-model="TargetReconcilingBalance" />
|
||||
Difference:
|
||||
<Currency :value="transactions.ReconcilingBalance - TargetReconcilingBalance" />
|
||||
<Button
|
||||
class="bg-orange-500 mx-3 py-2"
|
||||
v-if="Math.abs(transactions.ReconcilingBalance - TargetReconcilingBalance) > 0.01"
|
||||
@click="createReconcilationTransaction"
|
||||
>Create reconciling Transaction</Button>
|
||||
<Button class="bg-red-500 mx-3 py-2" @click="cancelReconcilation">Cancel</Button>
|
||||
</span>
|
||||
<span v-if="transactions.Reconciling" class="contents">
|
||||
<Button @click="submitReconcilation"
|
||||
class="bg-blue-500 p-1 whitespace-nowrap flex-1">
|
||||
My current balance is
|
||||
<Currency :value="transactions.ReconcilingBalance" />
|
||||
</Button>
|
||||
|
||||
<Button @click="createReconcilationTransaction"
|
||||
class="bg-orange-500 p-1 whitespace-nowrap flex-1">
|
||||
No, it's:
|
||||
<Input
|
||||
class="text-right w-20 bg-transparent dark:bg-transparent border-b-2"
|
||||
type="number"
|
||||
v-model="TargetReconcilingBalance"
|
||||
/>
|
||||
(Difference
|
||||
<Currency
|
||||
:value="transactions.ReconcilingBalance - TargetReconcilingBalance"
|
||||
/>)
|
||||
</Button>
|
||||
<Button class="bg-red-500 p-1 flex-1" @click="cancelReconcilation">Cancel</Button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
@ -97,7 +104,11 @@ function createReconcilationTransaction() {
|
||||
<Input v-if="transactions.Reconciling" type="checkbox" @input="setReconciled" />
|
||||
</td>
|
||||
</tr>
|
||||
<TransactionInputRow class="hidden md:table-row" :budgetid="budgetid" :accountid="accountid" />
|
||||
<TransactionInputRow
|
||||
class="hidden md:table-row"
|
||||
:budgetid="budgetid"
|
||||
:accountid="accountid"
|
||||
/>
|
||||
<TransactionRow
|
||||
v-for="(transaction, index) in transactions.TransactionsList"
|
||||
:key="transaction.ID"
|
||||
@ -110,7 +121,11 @@ function createReconcilationTransaction() {
|
||||
<template v-slot:placeholder>
|
||||
<Button class="fixed right-4 bottom-4 font-bold text-lg bg-blue-500 py-2">+</Button>
|
||||
</template>
|
||||
<TransactionInputRow class="grid grid-cols-2" :budgetid="budgetid" :accountid="accountid" />
|
||||
<TransactionInputRow
|
||||
class="grid grid-cols-2"
|
||||
:budgetid="budgetid"
|
||||
:accountid="accountid"
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -20,7 +20,6 @@ const OffBudgetAccountsBalance = computed(() => accountStore.OffBudgetAccountsBa
|
||||
function isRecentlyReconciled(account : Account) {
|
||||
const now = new Date().getTime();
|
||||
const recently = 7 * 24 * 60 * 60 * 1000;
|
||||
console.log(account.Name, account.LastReconciled, now, recently, new Date(now-recently));
|
||||
return new Date(now - recently).getTime() < account.LastReconciled.getTime();
|
||||
}
|
||||
|
||||
@ -31,17 +30,17 @@ function getAccountName(account : Account) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<span class="m-1 p-1 px-3 text-xl">
|
||||
<router-link to="/dashboard">⌂</router-link>
|
||||
<div class="flex flex-col mt-14 md:mt-0">
|
||||
<span class="m-2 p-1 px-3 h-10 overflow-hidden" :class="[ExpandMenu ? 'text-2xl' : 'text-md']">
|
||||
<router-link to="/dashboard" style="font-size:150%">⌂</router-link>
|
||||
{{CurrentBudgetName}}
|
||||
</span>
|
||||
<span class="bg-orange-200 dark:bg-orange-700 rounded-lg m-1 p-1 px-3 flex flex-col">
|
||||
<span class="bg-gray-100 dark:bg-gray-700 p-2 px-3 flex flex-col">
|
||||
<router-link :to="'/budget/'+CurrentBudgetID+'/budgeting'">Budget</router-link><br />
|
||||
<!--<router-link :to="'/budget/'+CurrentBudgetID+'/reports'">Reports</router-link>-->
|
||||
<!--<router-link :to="'/budget/'+CurrentBudgetID+'/all-accounts'">All Accounts</router-link>-->
|
||||
</span>
|
||||
<li class="bg-orange-200 dark:bg-orange-700 rounded-lg m-1 p-1 px-3">
|
||||
<li class="bg-slate-200 dark:bg-slate-700 my-2 p-2 px-3">
|
||||
<div class="flex flex-row justify-between font-bold">
|
||||
<span>On-Budget Accounts</span>
|
||||
<Currency :class="ExpandMenu?'md:inline':'md:hidden'" :value="OnBudgetAccountsBalance" />
|
||||
@ -51,7 +50,7 @@ function getAccountName(account : Account) {
|
||||
<Currency :class="ExpandMenu?'md:inline':'md:hidden'" :value="account.ClearedBalance" />
|
||||
</div>
|
||||
</li>
|
||||
<li class="bg-red-200 dark:bg-red-800 rounded-lg m-1 p-1 px-3">
|
||||
<li class="bg-slate-200 dark:bg-slate-700 my-2 p-2 px-3">
|
||||
<div class="flex flex-row justify-between font-bold">
|
||||
<span>Off-Budget Accounts</span>
|
||||
<Currency :class="ExpandMenu?'md:inline':'md:hidden'" :value="OffBudgetAccountsBalance" />
|
||||
@ -61,18 +60,16 @@ function getAccountName(account : Account) {
|
||||
<Currency :class="ExpandMenu?'md:inline':'md:hidden'" :value="account.ClearedBalance" />
|
||||
</div>
|
||||
</li>
|
||||
<li class="bg-red-200 dark:bg-red-800 rounded-lg m-1 p-1 px-3">
|
||||
<li class="bg-slate-100 dark:bg-slate-800 my-2 p-2 px-3">
|
||||
<div class="flex flex-row justify-between font-bold">
|
||||
<span>Closed Accounts</span>
|
||||
</div>
|
||||
+ Add Account
|
||||
</li>
|
||||
<!--<li>
|
||||
<router-link :to="'/budget/'+CurrentBudgetID+'/accounts'">Edit accounts</router-link>
|
||||
</li>-->
|
||||
<li class="bg-red-200 dark:bg-red-800 rounded-lg m-1 p-1 px-3">
|
||||
+ Add Account
|
||||
</li>
|
||||
<li class="bg-red-200 dark:bg-red-800 rounded-lg m-1 p-1 px-3">
|
||||
<li class="bg-red-100 dark:bg-slate-600 my-2 p-2 px-3">
|
||||
<router-link :to="'/budget/'+CurrentBudgetID+'/settings'">Budget-Settings</router-link>
|
||||
</li>
|
||||
<!--<li><router-link to="/admin">Admin</router-link></li>-->
|
||||
|
@ -57,7 +57,6 @@ onMounted(() => {
|
||||
const expandedGroups = ref<Map<string, boolean>>(new Map<string, boolean>())
|
||||
|
||||
function toggleGroup(group: { Name: string, Expand: boolean }) {
|
||||
console.log(expandedGroups.value);
|
||||
expandedGroups.value.set(group.Name, !(expandedGroups.value.get(group.Name) ?? group.Expand))
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,6 @@ export const useTransactionsStore = defineStore("budget/transactions", {
|
||||
this.AddTransactions([recTrans]);
|
||||
account.Transactions.unshift(recTrans.ID);
|
||||
}
|
||||
console.log("Reconcile: " + response.message);
|
||||
},
|
||||
logout() {
|
||||
this.$reset()
|
||||
|
Reference in New Issue
Block a user