Compare commits
18 Commits
1.0.1
...
282f504bcb
Author | SHA1 | Date | |
---|---|---|---|
282f504bcb | |||
f38482757e | |||
7e976d903d | |||
737d7f76d4 | |||
cf0e42ff0e | |||
d2fa9bf9ee | |||
fb5a85311c | |||
288d334615 | |||
87f017fc42 | |||
43fb8dcda6 | |||
a3b9c7cdb1 | |||
c42b8b1bda | |||
bdcbe94c97 | |||
caeff985c2 | |||
bed538793d | |||
602f20ad18 | |||
457c96b37e | |||
a3f2b77548 |
10
.drone.yml
10
.drone.yml
@ -38,6 +38,7 @@ steps:
|
|||||||
- cd moneymgr_backend
|
- cd moneymgr_backend
|
||||||
- rustup component add clippy
|
- rustup component add clippy
|
||||||
- cargo clippy -- -D warnings
|
- cargo clippy -- -D warnings
|
||||||
|
- cargo clippy --example api_curl -- -D warnings
|
||||||
|
|
||||||
- name: backend_test
|
- name: backend_test
|
||||||
image: rust
|
image: rust
|
||||||
@ -51,7 +52,7 @@ steps:
|
|||||||
- cargo test
|
- cargo test
|
||||||
|
|
||||||
|
|
||||||
- name: backend_compile
|
- name: backend_build
|
||||||
image: rust
|
image: rust
|
||||||
volumes:
|
volumes:
|
||||||
- name: rust_registry
|
- name: rust_registry
|
||||||
@ -67,15 +68,16 @@ steps:
|
|||||||
- cd moneymgr_backend
|
- cd moneymgr_backend
|
||||||
- mv /tmp/web_build/dist static
|
- mv /tmp/web_build/dist static
|
||||||
- cargo build --release
|
- cargo build --release
|
||||||
- ls -lah target/release/moneymgr_backend
|
- cargo build --release --example api_curl
|
||||||
- cp target/release/moneymgr_backend /tmp/release
|
- ls -lah target/release/moneymgr_backend target/release/examples/api_curl
|
||||||
|
- cp target/release/moneymgr_backend target/release/examples/api_curl /tmp/release
|
||||||
|
|
||||||
|
|
||||||
# Release
|
# Release
|
||||||
- name: gitea_release
|
- name: gitea_release
|
||||||
image: plugins/gitea-release
|
image: plugins/gitea-release
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend_compile
|
- backend_build
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
- tag
|
- tag
|
||||||
|
38
README.md
38
README.md
@ -3,10 +3,46 @@
|
|||||||
|
|
||||||
Open Source web-based personal expenses tool.
|
Open Source web-based personal expenses tool.
|
||||||
|
|
||||||
|
**Note :** This project does not handle authentication itself. Instead, it relies on OpenID to achieve users authentication.
|
||||||
|
|
||||||
|
## Setup prod env
|
||||||
|
1. Install prerequisites:
|
||||||
|
1. docker
|
||||||
|
2. docker compose
|
||||||
|
3. git
|
||||||
|
|
||||||
|
2. Clone this git repository:
|
||||||
|
```bash
|
||||||
|
git clone https://gitea.communiquons.org/pierre/MoneyMgr
|
||||||
|
cd MoneyMgr/docker_prod
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Copy and adapt env values
|
||||||
|
```bash
|
||||||
|
cp .env.sample .env
|
||||||
|
nano .env
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Create required directories:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p storage/{db,redis-data,redis-conf,minio}
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Start containers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Checkout http://localhost:8000/
|
||||||
|
|
||||||
|
> The default credentials are `admin` / `admin`
|
||||||
|
|
||||||
## Setup dev env
|
## Setup dev env
|
||||||
1. Install prerequisites:
|
1. Install prerequisites:
|
||||||
1. docker
|
1. docker
|
||||||
2. docker-compose
|
2. docker compose
|
||||||
3. rust
|
3. rust
|
||||||
4. node
|
4. node
|
||||||
|
|
||||||
|
10
docker_prod/.env.sample
Normal file
10
docker_prod/.env.sample
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
MINIO_ROOT_USER=rootuser
|
||||||
|
MINIO_ROOT_PASSWORD=rootpassword
|
||||||
|
DB_USER=db_user
|
||||||
|
DB_PASSWORD=db_password
|
||||||
|
REDIS_PASS=redis_password
|
||||||
|
WEBSITE_ORIGIN=http://localhost:8000
|
||||||
|
APP_SECRET=secretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecret
|
||||||
|
AUTH_SECRET_KEY=secretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecret
|
||||||
|
OIDC_CLIENT_ID=bar
|
||||||
|
OIDC_CLIENT_SECRET=foo
|
3
docker_prod/.gitignore
vendored
Normal file
3
docker_prod/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.env
|
||||||
|
storage
|
||||||
|
auth/users.json
|
5
docker_prod/auth/clients.yaml
Normal file
5
docker_prod/auth/clients.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
- id: ${OIDC_CLIENT_ID}
|
||||||
|
name: MoneyMgr
|
||||||
|
description: Money management tool
|
||||||
|
secret: ${OIDC_CLIENT_SECRET}
|
||||||
|
redirect_uri: ${APP_ORIGIN}/oidc_cb
|
79
docker_prod/docker-compose.yml
Normal file
79
docker_prod/docker-compose.yml
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
services:
|
||||||
|
minio:
|
||||||
|
image: minio/minio
|
||||||
|
user: "1000"
|
||||||
|
environment:
|
||||||
|
- MINIO_ROOT_USER=$MINIO_ROOT_USER
|
||||||
|
- MINIO_ROOT_PASSWORD=$MINIO_ROOT_PASSWORD
|
||||||
|
volumes:
|
||||||
|
- ./storage/minio:/data
|
||||||
|
command: [ "minio", "server", "/data", "--console-address", ":9090" ]
|
||||||
|
ports:
|
||||||
|
- 9000:9000
|
||||||
|
- 9090:9090
|
||||||
|
expose:
|
||||||
|
- 9000
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres
|
||||||
|
user: "1000"
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
expose:
|
||||||
|
- 5432
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=$DB_USER
|
||||||
|
- POSTGRES_PASSWORD=$DB_PASSWORD
|
||||||
|
- POSTGRES_DB=moneymgr
|
||||||
|
volumes:
|
||||||
|
- ./storage/db:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
oidc:
|
||||||
|
image: pierre42100/basic_oidc
|
||||||
|
user: "1000"
|
||||||
|
environment:
|
||||||
|
- LISTEN_ADDRESS=0.0.0.0:9001
|
||||||
|
- STORAGE_PATH=/storage
|
||||||
|
- TOKEN_KEY=$AUTH_SECRET_KEY
|
||||||
|
- WEBSITE_ORIGIN=http://localhost:9001
|
||||||
|
- OIDC_CLIENT_ID=$OIDC_CLIENT_ID
|
||||||
|
- OIDC_CLIENT_SECRET=$OIDC_CLIENT_SECRET
|
||||||
|
- APP_ORIGIN=$WEBSITE_ORIGIN
|
||||||
|
expose:
|
||||||
|
- 9001
|
||||||
|
ports:
|
||||||
|
- 9001:9001
|
||||||
|
volumes:
|
||||||
|
- ./auth:/storage
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
user: "1000"
|
||||||
|
command: redis-server --requirepass ${REDIS_PASS:-secretredis}
|
||||||
|
expose:
|
||||||
|
- 6379
|
||||||
|
volumes:
|
||||||
|
- ./storage/redis-data:/data
|
||||||
|
- ./storage/redis-conf:/usr/local/etc/redis/redis.conf
|
||||||
|
|
||||||
|
moneymgr:
|
||||||
|
image: pierre42100/moneymgr_backend
|
||||||
|
user: "1000"
|
||||||
|
ports:
|
||||||
|
- 8000:8000
|
||||||
|
environment:
|
||||||
|
- WEBSITE_ORIGIN=${WEBSITE_ORIGIN}
|
||||||
|
- SECRET=${APP_SECRET}
|
||||||
|
- DB_HOST=db
|
||||||
|
- DB_USERNAME=$DB_USER
|
||||||
|
- DB_PASSWORD=$DB_PASSWORD
|
||||||
|
- DB_NAME=moneymgr
|
||||||
|
- OIDC_CONFIGURATION_URL=http://oidc:9001/.well-known/openid-configuration
|
||||||
|
- OIDC_PROVIDER_NAME=OIDC
|
||||||
|
- OIDC_CLIENT_ID=$OIDC_CLIENT_ID
|
||||||
|
- OIDC_CLIENT_SECRET=$OIDC_CLIENT_SECRET
|
||||||
|
- S3_ENDPOINT=http://minio:9000
|
||||||
|
- S3_ACCESS_KEY=$MINIO_ROOT_USER
|
||||||
|
- S3_SECRET_KEY=$MINIO_ROOT_PASSWORD
|
||||||
|
- REDIS_HOSTNAME=redis
|
||||||
|
- REDIS_PASSWORD=${REDIS_PASS:-secretredis}
|
4
moneymgr_backend/Cargo.lock
generated
4
moneymgr_backend/Cargo.lock
generated
@ -3571,9 +3571,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.45.0"
|
version = "1.45.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
|
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -7,27 +7,27 @@ edition = "2024"
|
|||||||
env_logger = "0.11.8"
|
env_logger = "0.11.8"
|
||||||
log = "0.4.27"
|
log = "0.4.27"
|
||||||
diesel = { version = "2.2.10", features = ["postgres", "r2d2"] }
|
diesel = { version = "2.2.10", features = ["postgres", "r2d2"] }
|
||||||
diesel_migrations = "2.1.0"
|
diesel_migrations = "2.2.0"
|
||||||
clap = { version = "4.5.38", features = ["env", "derive"] }
|
clap = { version = "4.5.38", features = ["env", "derive"] }
|
||||||
actix-web = "4.11.0"
|
actix-web = "4.11.0"
|
||||||
actix-cors = "0.7.1"
|
actix-cors = "0.7.1"
|
||||||
actix-multipart = "0.7.2"
|
actix-multipart = "0.7.2"
|
||||||
actix-remote-ip = "0.1.0"
|
actix-remote-ip = "0.1.0"
|
||||||
actix-session = { version = "0.10.0", features = ["redis-session"] }
|
actix-session = { version = "0.10.1", features = ["redis-session"] }
|
||||||
actix-files = "0.6.6"
|
actix-files = "0.6.6"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.98"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
rust-s3 = "0.36.0-beta.2"
|
rust-s3 = "0.36.0-beta.2"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
tokio = "1.45.0"
|
tokio = "1.45.1"
|
||||||
futures-util = "0.3.31"
|
futures-util = "0.3.31"
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
light-openid = "1.0.4"
|
light-openid = "1.0.4"
|
||||||
rand = "0.9.1"
|
rand = "0.9.1"
|
||||||
ipnet = { version = "2.11.0", features = ["serde"] }
|
ipnet = { version = "2.11.0", features = ["serde"] }
|
||||||
lazy-regex = "3.4.1"
|
lazy-regex = "3.4.1"
|
||||||
jwt-simple = { version = "0.12.11", default-features = false, features = ["pure-rust"] }
|
jwt-simple = { version = "0.12.12", default-features = false, features = ["pure-rust"] }
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
rust-embed = { version = "8.7.2" }
|
rust-embed = { version = "8.7.2" }
|
||||||
sha2 = "0.11.0-pre.5"
|
sha2 = "0.11.0-pre.5"
|
||||||
|
@ -14,7 +14,7 @@ use std::process::Command;
|
|||||||
struct Args {
|
struct Args {
|
||||||
/// URL to Money manager API
|
/// URL to Money manager API
|
||||||
#[arg(short('U'), long, env, default_value = "http://localhost:8000/api")]
|
#[arg(short('U'), long, env, default_value = "http://localhost:8000/api")]
|
||||||
matrix_gw_url: String,
|
moneymgr_url: String,
|
||||||
|
|
||||||
/// Token ID
|
/// Token ID
|
||||||
#[arg(short('i'), long, env)]
|
#[arg(short('i'), long, env)]
|
||||||
@ -39,7 +39,8 @@ struct Args {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let args: Args = Args::parse();
|
let args: Args = Args::parse();
|
||||||
|
|
||||||
let full_url = format!("{}{}", args.matrix_gw_url, args.uri);
|
let full_url = format!("{}{}", args.moneymgr_url, args.uri);
|
||||||
|
|
||||||
log::debug!("Full URL: {full_url}");
|
log::debug!("Full URL: {full_url}");
|
||||||
|
|
||||||
let key = HS256Key::from_bytes(args.token_secret.as_bytes());
|
let key = HS256Key::from_bytes(args.token_secret.as_bytes());
|
||||||
|
@ -78,12 +78,15 @@ pub async fn finances_manager_import(auth: AuthExtractor, file: FileExtractor) -
|
|||||||
|
|
||||||
/// Export data to a [FinancesManager](https://gitlab.com/pierre42100/cpp-financesmanager) file
|
/// Export data to a [FinancesManager](https://gitlab.com/pierre42100/cpp-financesmanager) file
|
||||||
pub async fn finances_manager_export(auth: AuthExtractor) -> HttpResult {
|
pub async fn finances_manager_export(auth: AuthExtractor) -> HttpResult {
|
||||||
let accounts = accounts_service::get_list_user(auth.user_id()).await?;
|
let mut accounts = accounts_service::get_list_user(auth.user_id()).await?;
|
||||||
|
accounts.sort_by_key(|a| a.id());
|
||||||
|
|
||||||
let mut out = FinancesManagerFile { accounts: vec![] };
|
let mut out = FinancesManagerFile { accounts: vec![] };
|
||||||
|
|
||||||
for account in accounts {
|
for account in accounts {
|
||||||
let movements = movements_service::get_list_account(account.id()).await?;
|
let mut movements = movements_service::get_list_account(account.id()).await?;
|
||||||
|
movements.sort_by(|a, b| b.time.cmp(&a.time));
|
||||||
|
|
||||||
let mut file_account = FinancesManagerAccount {
|
let mut file_account = FinancesManagerAccount {
|
||||||
name: account.name,
|
name: account.name,
|
||||||
movements: Vec::with_capacity(movements.len()),
|
movements: Vec::with_capacity(movements.len()),
|
||||||
|
146
moneymgr_web/package-lock.json
generated
146
moneymgr_web/package-lock.json
generated
@ -16,8 +16,8 @@
|
|||||||
"@mdi/react": "^1.6.1",
|
"@mdi/react": "^1.6.1",
|
||||||
"@mui/icons-material": "^7.1.0",
|
"@mui/icons-material": "^7.1.0",
|
||||||
"@mui/material": "^7.1.0",
|
"@mui/material": "^7.1.0",
|
||||||
"@mui/x-charts": "^8.3.1",
|
"@mui/x-charts": "^8.4.0",
|
||||||
"@mui/x-data-grid": "^8.3.1",
|
"@mui/x-data-grid": "^8.4.0",
|
||||||
"@mui/x-date-pickers": "^8.3.1",
|
"@mui/x-date-pickers": "^8.3.1",
|
||||||
"date-and-time": "^3.6.0",
|
"date-and-time": "^3.6.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
@ -25,18 +25,18 @@
|
|||||||
"qrcode.react": "^4.2.0",
|
"qrcode.react": "^4.2.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-router": "^7.6.0",
|
"react-router": "^7.6.1",
|
||||||
"react-router-dom": "^7.6.0",
|
"react-router-dom": "^7.6.0",
|
||||||
"ts-pattern": "^5.7.0"
|
"ts-pattern": "^5.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.26.0",
|
"@eslint/js": "^9.27.0",
|
||||||
"@types/react": "^19.1.4",
|
"@types/react": "^19.1.5",
|
||||||
"@types/react-dom": "^19.1.5",
|
"@types/react-dom": "^19.1.5",
|
||||||
"@vitejs/plugin-react": "^4.4.1",
|
"@vitejs/plugin-react": "^4.4.1",
|
||||||
"eslint": "^9.26.0",
|
"eslint": "^9.26.0",
|
||||||
"eslint-plugin-react-dom": "^1.49.0",
|
"eslint-plugin-react-dom": "^1.49.0",
|
||||||
"eslint-plugin-react-hooks": "^5.1.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react-refresh": "^00.4.20",
|
"eslint-plugin-react-refresh": "^00.4.20",
|
||||||
"eslint-plugin-react-x": "^1.49.0",
|
"eslint-plugin-react-x": "^1.49.0",
|
||||||
"globals": "^16.1.0",
|
"globals": "^16.1.0",
|
||||||
@ -1145,13 +1145,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.26.0",
|
"version": "9.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
|
||||||
"integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==",
|
"integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://eslint.org/donate"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/object-schema": {
|
"node_modules/@eslint/object-schema": {
|
||||||
@ -1588,15 +1591,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/x-charts": {
|
"node_modules/@mui/x-charts": {
|
||||||
"version": "8.3.1",
|
"version": "8.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-8.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-8.4.0.tgz",
|
||||||
"integrity": "sha512-jZClK40++ftcMwCeHKudGKmazd0MsgnrIP6RhYi2lH1kg0jK2upueokyxVIIxqquwWsQYE3WsflJBP61DvYXOQ==",
|
"integrity": "sha512-XXXt6cHgpTTkLWIImBy0OPD0FwuOdux4AprP/0Zvs0PXuM9D9eeN1piZvo5gjZbPHSsCzIyZzwx9PGncFScgEQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.27.1",
|
"@babel/runtime": "^7.27.1",
|
||||||
"@mui/utils": "^7.0.2",
|
"@mui/utils": "^7.0.2",
|
||||||
"@mui/x-charts-vendor": "8.3.1",
|
"@mui/x-charts-vendor": "8.4.0",
|
||||||
"@mui/x-internals": "8.3.1",
|
"@mui/x-internals": "8.4.0",
|
||||||
"bezier-easing": "^2.1.0",
|
"bezier-easing": "^2.1.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
@ -1624,9 +1627,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/x-charts-vendor": {
|
"node_modules/@mui/x-charts-vendor": {
|
||||||
"version": "8.3.1",
|
"version": "8.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/x-charts-vendor/-/x-charts-vendor-8.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/x-charts-vendor/-/x-charts-vendor-8.4.0.tgz",
|
||||||
"integrity": "sha512-UcUa7HDIpSfeVBYgeHewWoVALcB4Gg9we53l78j2cyadYBZOWdtLj8fezo9zAhxfZ5s9T+1yIyuD+CCnYJnUpQ==",
|
"integrity": "sha512-0VvwJSeFezJTnjoKg5YUbbI82a60+VZfH2RyqaosmKH516lKYKSCuAFmkj4vUBP6+ZJPZDW5mWI3VhwD4DN6hg==",
|
||||||
"license": "MIT AND ISC",
|
"license": "MIT AND ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.27.1",
|
"@babel/runtime": "^7.27.1",
|
||||||
@ -1648,15 +1651,35 @@
|
|||||||
"robust-predicates": "^3.0.2"
|
"robust-predicates": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mui/x-charts/node_modules/@mui/x-internals": {
|
||||||
|
"version": "8.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.4.0.tgz",
|
||||||
|
"integrity": "sha512-Z7FCahC4MLfTVzEwnKOB7P1fiR9DzFuMzHOPRNaMXc/rsS7unbtBKAG94yvsRzReCyjzZUVA7h37lnQ1DoPKJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.27.1",
|
||||||
|
"@mui/utils": "^7.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mui/x-data-grid": {
|
"node_modules/@mui/x-data-grid": {
|
||||||
"version": "8.3.1",
|
"version": "8.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-8.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-8.4.0.tgz",
|
||||||
"integrity": "sha512-mSo2g0ZZzasDQ4kKrFdJVk7dJgz77jF/e8udvGqnnTgnQXlqLMpKne/veL3gRdi3TJxxTv2vqXtX7IZfWGJecQ==",
|
"integrity": "sha512-c0fgMhvQTjCSo3LgRK1Mdk2msktCl9uwMYUYlP6bbqJ7I03IvS+1aZ+s3nSLmaq1aVh7sE2Bnuz63OnVerTLJA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.27.1",
|
"@babel/runtime": "^7.27.1",
|
||||||
"@mui/utils": "^7.0.2",
|
"@mui/utils": "^7.0.2",
|
||||||
"@mui/x-internals": "8.3.1",
|
"@mui/x-internals": "8.4.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"reselect": "^5.1.1",
|
"reselect": "^5.1.1",
|
||||||
@ -1686,6 +1709,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mui/x-data-grid/node_modules/@mui/x-internals": {
|
||||||
|
"version": "8.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.4.0.tgz",
|
||||||
|
"integrity": "sha512-Z7FCahC4MLfTVzEwnKOB7P1fiR9DzFuMzHOPRNaMXc/rsS7unbtBKAG94yvsRzReCyjzZUVA7h37lnQ1DoPKJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.27.1",
|
||||||
|
"@mui/utils": "^7.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mui/x-date-pickers": {
|
"node_modules/@mui/x-date-pickers": {
|
||||||
"version": "8.3.1",
|
"version": "8.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-8.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-8.3.1.tgz",
|
||||||
@ -2229,9 +2272,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "19.1.4",
|
"version": "19.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.5.tgz",
|
||||||
"integrity": "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==",
|
"integrity": "sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
@ -3494,6 +3537,16 @@
|
|||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint/node_modules/@eslint/js": {
|
||||||
|
"version": "9.26.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz",
|
||||||
|
"integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/espree": {
|
"node_modules/espree": {
|
||||||
"version": "10.3.0",
|
"version": "10.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
|
||||||
@ -4860,9 +4913,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "7.6.0",
|
"version": "7.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.1.tgz",
|
||||||
"integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==",
|
"integrity": "sha512-hPJXXxHJZEsPFNVbtATH7+MMX43UDeOauz+EAU4cgqTn7ojdI9qQORqS8Z0qmDlL1TclO/6jLRYUEtbWidtdHQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie": "^1.0.1",
|
"cookie": "^1.0.1",
|
||||||
@ -4897,6 +4950,37 @@
|
|||||||
"react-dom": ">=18"
|
"react-dom": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-router-dom/node_modules/cookie": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-router-dom/node_modules/react-router": {
|
||||||
|
"version": "7.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz",
|
||||||
|
"integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cookie": "^1.0.1",
|
||||||
|
"set-cookie-parser": "^2.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18",
|
||||||
|
"react-dom": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-router/node_modules/cookie": {
|
"node_modules/react-router/node_modules/cookie": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
|
||||||
@ -5448,9 +5532,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-pattern": {
|
"node_modules/ts-pattern": {
|
||||||
"version": "5.7.0",
|
"version": "5.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-5.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-5.7.1.tgz",
|
||||||
"integrity": "sha512-0/FvIG4g3kNkYgbNwBBW5pZBkfpeYQnH+2AA3xmjkCAit/DSDPKmgwC3fKof4oYUq6gupClVOJlFl+939VRBMg==",
|
"integrity": "sha512-EGs8PguQqAAUIcQfK4E9xdXxB6s2GK4sJfT/vcc9V1ELIvC4LH/zXu2t/5fajtv6oiRCxdv7BgtVK3vWgROxag==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
"@mdi/react": "^1.6.1",
|
"@mdi/react": "^1.6.1",
|
||||||
"@mui/icons-material": "^7.1.0",
|
"@mui/icons-material": "^7.1.0",
|
||||||
"@mui/material": "^7.1.0",
|
"@mui/material": "^7.1.0",
|
||||||
"@mui/x-charts": "^8.3.1",
|
"@mui/x-charts": "^8.4.0",
|
||||||
"@mui/x-data-grid": "^8.3.1",
|
"@mui/x-data-grid": "^8.4.0",
|
||||||
"@mui/x-date-pickers": "^8.3.1",
|
"@mui/x-date-pickers": "^8.3.1",
|
||||||
"date-and-time": "^3.6.0",
|
"date-and-time": "^3.6.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
@ -27,18 +27,18 @@
|
|||||||
"qrcode.react": "^4.2.0",
|
"qrcode.react": "^4.2.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-router": "^7.6.0",
|
"react-router": "^7.6.1",
|
||||||
"react-router-dom": "^7.6.0",
|
"react-router-dom": "^7.6.0",
|
||||||
"ts-pattern": "^5.7.0"
|
"ts-pattern": "^5.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.26.0",
|
"@eslint/js": "^9.27.0",
|
||||||
"@types/react": "^19.1.4",
|
"@types/react": "^19.1.5",
|
||||||
"@types/react-dom": "^19.1.5",
|
"@types/react-dom": "^19.1.5",
|
||||||
"@vitejs/plugin-react": "^4.4.1",
|
"@vitejs/plugin-react": "^4.4.1",
|
||||||
"eslint": "^9.26.0",
|
"eslint": "^9.26.0",
|
||||||
"eslint-plugin-react-dom": "^1.49.0",
|
"eslint-plugin-react-dom": "^1.49.0",
|
||||||
"eslint-plugin-react-hooks": "^5.1.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react-refresh": "^00.4.20",
|
"eslint-plugin-react-refresh": "^00.4.20",
|
||||||
"eslint-plugin-react-x": "^1.49.0",
|
"eslint-plugin-react-x": "^1.49.0",
|
||||||
"globals": "^16.1.0",
|
"globals": "^16.1.0",
|
||||||
|
@ -121,13 +121,15 @@ function MovementsTable(p: {
|
|||||||
|
|
||||||
const chooseAccount = useSelectAccount();
|
const chooseAccount = useSelectAccount();
|
||||||
|
|
||||||
const [labelFilter, setLabelFilter] = React.useState("");
|
const [filter, setFilter] = React.useState("");
|
||||||
|
|
||||||
const filteredList = React.useMemo(() => {
|
const filteredList = React.useMemo(() => {
|
||||||
return p.movements.filter((m) =>
|
return p.movements.filter(
|
||||||
m.label.toLowerCase().includes(labelFilter.toLowerCase())
|
(m) =>
|
||||||
|
m.label.toLowerCase().includes(filter.toLowerCase()) ||
|
||||||
|
m.amount.toString().includes(filter)
|
||||||
);
|
);
|
||||||
}, [p.movements, labelFilter]);
|
}, [p.movements, filter]);
|
||||||
|
|
||||||
const [rowSelectionModel, setRowSelectionModel] =
|
const [rowSelectionModel, setRowSelectionModel] =
|
||||||
React.useState<GridRowSelectionModel>({ type: "include", ids: new Set() });
|
React.useState<GridRowSelectionModel>({ type: "include", ids: new Set() });
|
||||||
@ -382,12 +384,12 @@ function MovementsTable(p: {
|
|||||||
<>
|
<>
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: "flex" }}>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Filter by label"
|
placeholder="Filter by label or amount"
|
||||||
variant="standard"
|
variant="standard"
|
||||||
size="small"
|
size="small"
|
||||||
value={labelFilter}
|
value={filter}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setLabelFilter(e.target.value);
|
setFilter(e.target.value);
|
||||||
}}
|
}}
|
||||||
style={{ padding: "0px", flex: 1 }}
|
style={{ padding: "0px", flex: 1 }}
|
||||||
/>
|
/>
|
||||||
|
@ -158,7 +158,7 @@ function ImportExportModal(p: {
|
|||||||
</CardContent>{" "}
|
</CardContent>{" "}
|
||||||
<CardActions>
|
<CardActions>
|
||||||
<span style={{ flex: 1 }}>
|
<span style={{ flex: 1 }}>
|
||||||
<RouterLink to={p.exportURL}>
|
<RouterLink to={p.exportURL} target="_blank">
|
||||||
<Button
|
<Button
|
||||||
startIcon={<DownloadIcon />}
|
startIcon={<DownloadIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { DatePicker } from "@mui/x-date-pickers";
|
import { DateField } from "@mui/x-date-pickers";
|
||||||
import { dateToTime, timeToDate } from "../../utils/DateUtils";
|
import { dateToTime, timeToDate } from "../../utils/DateUtils";
|
||||||
import { TextFieldVariants } from "@mui/material";
|
import { TextFieldVariants } from "@mui/material";
|
||||||
|
|
||||||
@ -13,13 +13,16 @@ export function DateInput(p: {
|
|||||||
variant?: TextFieldVariants;
|
variant?: TextFieldVariants;
|
||||||
}): React.ReactElement {
|
}): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<DatePicker
|
<DateField
|
||||||
autoFocus={p.autoFocus}
|
autoFocus={p.autoFocus}
|
||||||
readOnly={p.editable === false}
|
readOnly={p.editable === false}
|
||||||
label={p.label}
|
label={p.label}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
field: { ref: p.ref },
|
textField: {
|
||||||
textField: { variant: p.variant ?? "standard", style: p.style },
|
ref: p.ref,
|
||||||
|
variant: p.variant ?? "standard",
|
||||||
|
style: p.style,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
value={timeToDate(p.value)}
|
value={timeToDate(p.value)}
|
||||||
onChange={(v) => {
|
onChange={(v) => {
|
||||||
|
Reference in New Issue
Block a user