Add account type
This commit is contained in:
		
							
								
								
									
										1
									
								
								moneymgr_backend/assets/cash.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								moneymgr_backend/assets/cash.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3,6H21V18H3V6M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9M7,8A2,2 0 0,1 5,10V14A2,2 0 0,1 7,16H17A2,2 0 0,1 19,14V10A2,2 0 0,1 17,8H7Z" /></svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 237 B  | 
							
								
								
									
										1
									
								
								moneymgr_backend/assets/credit-card.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								moneymgr_backend/assets/credit-card.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20,8H4V6H20M20,18H4V12H20M20,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V6C22,4.89 21.1,4 20,4Z" /></svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 190 B  | 
							
								
								
									
										1
									
								
								moneymgr_backend/assets/saving.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								moneymgr_backend/assets/saving.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M16,9C20,11 21,18 21,18C21,18 22,22 16,22C10,22 8,22 8,22C2,22 3,18 3,18C3,18 4,11 8,9M14,4L12,2L10,4L6,2L8,7H16L18,2L14,4Z" /></svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 202 B  | 
@@ -43,6 +43,7 @@ CREATE TABLE accounts
 | 
				
			|||||||
    user_id         INTEGER     NOT NULL REFERENCES users ON DELETE CASCADE,
 | 
					    user_id         INTEGER     NOT NULL REFERENCES users ON DELETE CASCADE,
 | 
				
			||||||
    time_create     BIGINT      NOT NULL,
 | 
					    time_create     BIGINT      NOT NULL,
 | 
				
			||||||
    time_update     BIGINT      NOT NULL,
 | 
					    time_update     BIGINT      NOT NULL,
 | 
				
			||||||
 | 
					    type            VARCHAR(1)  NOT NULL DEFAULT 'C',
 | 
				
			||||||
    default_account BOOLEAN     NOT NULL DEFAULT false
 | 
					    default_account BOOLEAN     NOT NULL DEFAULT false
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
//! # Project constants
 | 
					//! # Project constants
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::models::accounts::AccountType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Length of generated tokens
 | 
					/// Length of generated tokens
 | 
				
			||||||
pub const TOKENS_LEN: usize = 50;
 | 
					pub const TOKENS_LEN: usize = 50;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -24,3 +26,30 @@ pub const MAX_UPLOAD_FILE_SIZE: usize = 15 * 1024 * 1024;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// Minimum elapsed time before a file can be deleted by garbage collector (2 hours)
 | 
					/// Minimum elapsed time before a file can be deleted by garbage collector (2 hours)
 | 
				
			||||||
pub const MIN_FILE_LIFETIME: u64 = 3600 * 2;
 | 
					pub const MIN_FILE_LIFETIME: u64 = 3600 * 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Description of an account type
 | 
				
			||||||
 | 
					#[derive(serde::Serialize, Debug)]
 | 
				
			||||||
 | 
					pub struct AccountTypeDesc {
 | 
				
			||||||
 | 
					    label: &'static str,
 | 
				
			||||||
 | 
					    code: AccountType,
 | 
				
			||||||
 | 
					    icon: &'static str,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Enumeration of accounts types
 | 
				
			||||||
 | 
					pub const ACCOUNT_TYPES: [AccountTypeDesc; 3] = [
 | 
				
			||||||
 | 
					    AccountTypeDesc {
 | 
				
			||||||
 | 
					        label: "Cash",
 | 
				
			||||||
 | 
					        code: AccountType::Cash,
 | 
				
			||||||
 | 
					        icon: include_str!("../assets/cash.svg"),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    AccountTypeDesc {
 | 
				
			||||||
 | 
					        label: "Bank",
 | 
				
			||||||
 | 
					        code: AccountType::Bank,
 | 
				
			||||||
 | 
					        icon: include_str!("../assets/credit-card.svg"),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    AccountTypeDesc {
 | 
				
			||||||
 | 
					        label: "Saving",
 | 
				
			||||||
 | 
					        code: AccountType::Saving,
 | 
				
			||||||
 | 
					        icon: include_str!("../assets/saving.svg"),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
use crate::app_config::AppConfig;
 | 
					use crate::app_config::AppConfig;
 | 
				
			||||||
 | 
					use crate::constants::{ACCOUNT_TYPES, AccountTypeDesc};
 | 
				
			||||||
use actix_web::HttpResponse;
 | 
					use actix_web::HttpResponse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Serve robots.txt (disallow ranking)
 | 
					/// Serve robots.txt (disallow ranking)
 | 
				
			||||||
@@ -57,6 +58,7 @@ impl Default for ServerConstraints {
 | 
				
			|||||||
struct ServerConfig {
 | 
					struct ServerConfig {
 | 
				
			||||||
    auth_disabled: bool,
 | 
					    auth_disabled: bool,
 | 
				
			||||||
    oidc_provider_name: &'static str,
 | 
					    oidc_provider_name: &'static str,
 | 
				
			||||||
 | 
					    accounts_types: &'static [AccountTypeDesc],
 | 
				
			||||||
    constraints: ServerConstraints,
 | 
					    constraints: ServerConstraints,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -66,6 +68,7 @@ impl Default for ServerConfig {
 | 
				
			|||||||
            auth_disabled: AppConfig::get().is_auth_disabled(),
 | 
					            auth_disabled: AppConfig::get().is_auth_disabled(),
 | 
				
			||||||
            oidc_provider_name: AppConfig::get().openid_provider().name,
 | 
					            oidc_provider_name: AppConfig::get().openid_provider().name,
 | 
				
			||||||
            constraints: Default::default(),
 | 
					            constraints: Default::default(),
 | 
				
			||||||
 | 
					            accounts_types: &ACCOUNT_TYPES,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,31 @@
 | 
				
			|||||||
use crate::models::users::UserID;
 | 
					use crate::models::users::UserID;
 | 
				
			||||||
use crate::schema::*;
 | 
					use crate::schema::*;
 | 
				
			||||||
use diesel::prelude::*;
 | 
					use diesel::prelude::*;
 | 
				
			||||||
 | 
					use std::fmt::{Display, Formatter};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
 | 
				
			||||||
pub struct AccountID(pub i32);
 | 
					pub struct AccountID(pub i32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
 | 
				
			||||||
 | 
					pub enum AccountType {
 | 
				
			||||||
 | 
					    #[serde(rename = "C")]
 | 
				
			||||||
 | 
					    Cash,
 | 
				
			||||||
 | 
					    #[serde(rename = "B")]
 | 
				
			||||||
 | 
					    Bank,
 | 
				
			||||||
 | 
					    #[serde(rename = "S")]
 | 
				
			||||||
 | 
					    Saving,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Display for AccountType {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
 | 
					        write!(
 | 
				
			||||||
 | 
					            f,
 | 
				
			||||||
 | 
					            "{}",
 | 
				
			||||||
 | 
					            serde_json::to_string(self).unwrap().replace('"', "")
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Queryable, Debug, Clone, serde::Serialize)]
 | 
					#[derive(Queryable, Debug, Clone, serde::Serialize)]
 | 
				
			||||||
pub struct Account {
 | 
					pub struct Account {
 | 
				
			||||||
    id: i32,
 | 
					    id: i32,
 | 
				
			||||||
@@ -12,6 +33,7 @@ pub struct Account {
 | 
				
			|||||||
    user_id: i32,
 | 
					    user_id: i32,
 | 
				
			||||||
    pub time_create: i64,
 | 
					    pub time_create: i64,
 | 
				
			||||||
    pub time_update: i64,
 | 
					    pub time_update: i64,
 | 
				
			||||||
 | 
					    r#type: String,
 | 
				
			||||||
    pub default_account: bool,
 | 
					    pub default_account: bool,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,13 +45,22 @@ impl Account {
 | 
				
			|||||||
    pub fn user_id(&self) -> UserID {
 | 
					    pub fn user_id(&self) -> UserID {
 | 
				
			||||||
        UserID(self.user_id)
 | 
					        UserID(self.user_id)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn account_type(&self) -> AccountType {
 | 
				
			||||||
 | 
					        serde_json::from_str(format!("\"{}\"", self.r#type).as_str()).unwrap()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_account_type(&mut self, t: AccountType) {
 | 
				
			||||||
 | 
					        self.r#type = t.to_string();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Insertable)]
 | 
					#[derive(Insertable, Debug)]
 | 
				
			||||||
#[diesel(table_name = accounts)]
 | 
					#[diesel(table_name = accounts)]
 | 
				
			||||||
pub struct NewAccount<'a> {
 | 
					pub struct NewAccount<'a> {
 | 
				
			||||||
    pub name: &'a str,
 | 
					    pub name: &'a str,
 | 
				
			||||||
    pub user_id: i32,
 | 
					    pub user_id: i32,
 | 
				
			||||||
    pub time_create: i64,
 | 
					    pub time_create: i64,
 | 
				
			||||||
    pub time_update: i64,
 | 
					    pub time_update: i64,
 | 
				
			||||||
 | 
					    pub type_: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,9 @@ diesel::table! {
 | 
				
			|||||||
        user_id -> Int4,
 | 
					        user_id -> Int4,
 | 
				
			||||||
        time_create -> Int8,
 | 
					        time_create -> Int8,
 | 
				
			||||||
        time_update -> Int8,
 | 
					        time_update -> Int8,
 | 
				
			||||||
 | 
					        #[sql_name = "type"]
 | 
				
			||||||
 | 
					        #[max_length = 1]
 | 
				
			||||||
 | 
					        type_ -> Varchar,
 | 
				
			||||||
        default_account -> Bool,
 | 
					        default_account -> Bool,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
use crate::connections::db_connection::db;
 | 
					use crate::connections::db_connection::db;
 | 
				
			||||||
use crate::controllers::server_controller::ServerConstraints;
 | 
					use crate::controllers::server_controller::ServerConstraints;
 | 
				
			||||||
use crate::models::accounts::{Account, AccountID, NewAccount};
 | 
					use crate::models::accounts::{Account, AccountID, AccountType, NewAccount};
 | 
				
			||||||
use crate::models::users::UserID;
 | 
					use crate::models::users::UserID;
 | 
				
			||||||
use crate::schema::accounts;
 | 
					use crate::schema::accounts;
 | 
				
			||||||
use crate::utils::time_utils::time;
 | 
					use crate::utils::time_utils::time;
 | 
				
			||||||
@@ -10,6 +10,7 @@ use diesel::prelude::*;
 | 
				
			|||||||
#[derive(serde::Deserialize)]
 | 
					#[derive(serde::Deserialize)]
 | 
				
			||||||
pub struct UpdateAccountQuery {
 | 
					pub struct UpdateAccountQuery {
 | 
				
			||||||
    pub name: String,
 | 
					    pub name: String,
 | 
				
			||||||
 | 
					    pub r#type: AccountType,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl UpdateAccountQuery {
 | 
					impl UpdateAccountQuery {
 | 
				
			||||||
@@ -31,6 +32,7 @@ pub async fn create(user_id: UserID, query: &UpdateAccountQuery) -> anyhow::Resu
 | 
				
			|||||||
        user_id: user_id.0,
 | 
					        user_id: user_id.0,
 | 
				
			||||||
        time_create: time() as i64,
 | 
					        time_create: time() as i64,
 | 
				
			||||||
        time_update: time() as i64,
 | 
					        time_update: time() as i64,
 | 
				
			||||||
 | 
					        type_: query.r#type.to_string(),
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let res: Account = diesel::insert_into(accounts::table)
 | 
					    let res: Account = diesel::insert_into(accounts::table)
 | 
				
			||||||
@@ -48,6 +50,7 @@ pub async fn update(id: AccountID, q: &UpdateAccountQuery) -> anyhow::Result<()>
 | 
				
			|||||||
        .set((
 | 
					        .set((
 | 
				
			||||||
            accounts::dsl::time_update.eq(time() as i64),
 | 
					            accounts::dsl::time_update.eq(time() as i64),
 | 
				
			||||||
            accounts::dsl::name.eq(&q.name),
 | 
					            accounts::dsl::name.eq(&q.name),
 | 
				
			||||||
 | 
					            accounts::dsl::type_.eq(&q.r#type.to_string()),
 | 
				
			||||||
        ))
 | 
					        ))
 | 
				
			||||||
        .execute(&mut db()?)?;
 | 
					        .execute(&mut db()?)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								moneymgr_web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										24
									
								
								moneymgr_web/package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -11,6 +11,7 @@
 | 
				
			|||||||
        "@emotion/react": "^11.14.0",
 | 
					        "@emotion/react": "^11.14.0",
 | 
				
			||||||
        "@emotion/styled": "^11.14.0",
 | 
					        "@emotion/styled": "^11.14.0",
 | 
				
			||||||
        "@fontsource/roboto": "^5.2.5",
 | 
					        "@fontsource/roboto": "^5.2.5",
 | 
				
			||||||
 | 
					        "@jsonjoy.com/base64": "^1.1.2",
 | 
				
			||||||
        "@mdi/js": "^7.4.47",
 | 
					        "@mdi/js": "^7.4.47",
 | 
				
			||||||
        "@mdi/react": "^1.6.1",
 | 
					        "@mdi/react": "^1.6.1",
 | 
				
			||||||
        "@mui/icons-material": "^7.0.1",
 | 
					        "@mui/icons-material": "^7.0.1",
 | 
				
			||||||
@@ -1321,6 +1322,22 @@
 | 
				
			|||||||
        "@jridgewell/sourcemap-codec": "^1.4.14"
 | 
					        "@jridgewell/sourcemap-codec": "^1.4.14"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@jsonjoy.com/base64": {
 | 
				
			||||||
 | 
					      "version": "1.1.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==",
 | 
				
			||||||
 | 
					      "license": "Apache-2.0",
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=10.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "type": "github",
 | 
				
			||||||
 | 
					        "url": "https://github.com/sponsors/streamich"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "tslib": "2"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/@mdi/js": {
 | 
					    "node_modules/@mdi/js": {
 | 
				
			||||||
      "version": "7.4.47",
 | 
					      "version": "7.4.47",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz",
 | 
				
			||||||
@@ -4238,6 +4255,13 @@
 | 
				
			|||||||
      "dev": true,
 | 
					      "dev": true,
 | 
				
			||||||
      "license": "MIT"
 | 
					      "license": "MIT"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/tslib": {
 | 
				
			||||||
 | 
					      "version": "2.8.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
 | 
				
			||||||
 | 
					      "license": "0BSD",
 | 
				
			||||||
 | 
					      "peer": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/turbo-stream": {
 | 
					    "node_modules/turbo-stream": {
 | 
				
			||||||
      "version": "2.4.0",
 | 
					      "version": "2.4.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@
 | 
				
			|||||||
    "@emotion/react": "^11.14.0",
 | 
					    "@emotion/react": "^11.14.0",
 | 
				
			||||||
    "@emotion/styled": "^11.14.0",
 | 
					    "@emotion/styled": "^11.14.0",
 | 
				
			||||||
    "@fontsource/roboto": "^5.2.5",
 | 
					    "@fontsource/roboto": "^5.2.5",
 | 
				
			||||||
 | 
					    "@jsonjoy.com/base64": "^1.1.2",
 | 
				
			||||||
    "@mdi/js": "^7.4.47",
 | 
					    "@mdi/js": "^7.4.47",
 | 
				
			||||||
    "@mdi/react": "^1.6.1",
 | 
					    "@mdi/react": "^1.6.1",
 | 
				
			||||||
    "@mui/icons-material": "^7.0.1",
 | 
					    "@mui/icons-material": "^7.0.1",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import { APIClient } from "./ApiClient";
 | 
					import { APIClient } from "./ApiClient";
 | 
				
			||||||
 | 
					import { ServerApi } from "./ServerApi";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Account {
 | 
					export interface Account {
 | 
				
			||||||
  id: number;
 | 
					  id: number;
 | 
				
			||||||
@@ -6,6 +7,7 @@ export interface Account {
 | 
				
			|||||||
  user_id: number;
 | 
					  user_id: number;
 | 
				
			||||||
  time_create: number;
 | 
					  time_create: number;
 | 
				
			||||||
  time_update: number;
 | 
					  time_update: number;
 | 
				
			||||||
 | 
					  type: string;
 | 
				
			||||||
  default_account: boolean;
 | 
					  default_account: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -66,6 +68,7 @@ export class AccountApi {
 | 
				
			|||||||
      method: "POST",
 | 
					      method: "POST",
 | 
				
			||||||
      jsonData: {
 | 
					      jsonData: {
 | 
				
			||||||
        name,
 | 
					        name,
 | 
				
			||||||
 | 
					        type: ServerApi.Config.accounts_types[0].code,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -79,6 +82,7 @@ export class AccountApi {
 | 
				
			|||||||
      method: "PUT",
 | 
					      method: "PUT",
 | 
				
			||||||
      jsonData: {
 | 
					      jsonData: {
 | 
				
			||||||
        name: account.name,
 | 
					        name: account.name,
 | 
				
			||||||
 | 
					        type: account.type,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,9 +3,16 @@ import { APIClient } from "./ApiClient";
 | 
				
			|||||||
export interface ServerConfig {
 | 
					export interface ServerConfig {
 | 
				
			||||||
  auth_disabled: boolean;
 | 
					  auth_disabled: boolean;
 | 
				
			||||||
  oidc_provider_name: string;
 | 
					  oidc_provider_name: string;
 | 
				
			||||||
 | 
					  accounts_types: AccountType[];
 | 
				
			||||||
  constraints: ServerConstraints;
 | 
					  constraints: ServerConstraints;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface AccountType {
 | 
				
			||||||
 | 
					  label: string;
 | 
				
			||||||
 | 
					  code: string;
 | 
				
			||||||
 | 
					  icon: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ServerConstraints {
 | 
					export interface ServerConstraints {
 | 
				
			||||||
  token_name: LenConstraint;
 | 
					  token_name: LenConstraint;
 | 
				
			||||||
  token_ip_net: LenConstraint;
 | 
					  token_ip_net: LenConstraint;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,8 @@ import { MoneyMgrWebRouteContainer } from "../widgets/MoneyMgrWebRouteContainer"
 | 
				
			|||||||
import { TimeWidget } from "../widgets/TimeWidget";
 | 
					import { TimeWidget } from "../widgets/TimeWidget";
 | 
				
			||||||
import DeleteIcon from "@mui/icons-material/DeleteOutlined";
 | 
					import DeleteIcon from "@mui/icons-material/DeleteOutlined";
 | 
				
			||||||
import { useConfirm } from "../hooks/context_providers/ConfirmDialogProvider";
 | 
					import { useConfirm } from "../hooks/context_providers/ConfirmDialogProvider";
 | 
				
			||||||
 | 
					import { AccountWidget } from "../widgets/AccountWidget";
 | 
				
			||||||
 | 
					import { ServerApi } from "../api/ServerApi";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function AccountsRoute(): React.ReactElement {
 | 
					export function AccountsRoute(): React.ReactElement {
 | 
				
			||||||
  const alert = useAlert();
 | 
					  const alert = useAlert();
 | 
				
			||||||
@@ -88,6 +90,35 @@ export function AccountsRoute(): React.ReactElement {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const columns: GridColDef<(typeof list)[number]>[] = [
 | 
					  const columns: GridColDef<(typeof list)[number]>[] = [
 | 
				
			||||||
    { field: "id", headerName: "ID", flex: 1 },
 | 
					    { field: "id", headerName: "ID", flex: 1 },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      field: "type",
 | 
				
			||||||
 | 
					      headerName: "Type",
 | 
				
			||||||
 | 
					      flex: 2,
 | 
				
			||||||
 | 
					      editable: true,
 | 
				
			||||||
 | 
					      type: "singleSelect",
 | 
				
			||||||
 | 
					      valueOptions: ServerApi.Config.accounts_types.map((v) => {
 | 
				
			||||||
 | 
					        return { label: v.label, value: v.code };
 | 
				
			||||||
 | 
					      }),
 | 
				
			||||||
 | 
					      renderCell(params) {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					          <span
 | 
				
			||||||
 | 
					            style={{
 | 
				
			||||||
 | 
					              display: "flex",
 | 
				
			||||||
 | 
					              flexDirection: "row",
 | 
				
			||||||
 | 
					              justifyContent: "start",
 | 
				
			||||||
 | 
					              alignItems: "center",
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <AccountWidget account={params.row} />
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              ServerApi.Config.accounts_types.find(
 | 
				
			||||||
 | 
					                (t) => t.code === params.row.type
 | 
				
			||||||
 | 
					              )!.label
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    { field: "name", headerName: "Name", flex: 6, editable: true },
 | 
					    { field: "name", headerName: "Name", flex: 6, editable: true },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      field: "time_create",
 | 
					      field: "time_create",
 | 
				
			||||||
@@ -159,7 +190,10 @@ export function AccountsRoute(): React.ReactElement {
 | 
				
			|||||||
            return setDefaultAccount(updated);
 | 
					            return setDefaultAccount(updated);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (updated.name !== original.name) {
 | 
					          if (
 | 
				
			||||||
 | 
					            updated.name !== original.name ||
 | 
				
			||||||
 | 
					            updated.type !== original.type
 | 
				
			||||||
 | 
					          ) {
 | 
				
			||||||
            return updateAccount(updated);
 | 
					            return updateAccount(updated);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										25
									
								
								moneymgr_web/src/widgets/AccountWidget.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								moneymgr_web/src/widgets/AccountWidget.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import { Account } from "../api/AccountApi";
 | 
				
			||||||
 | 
					import { ServerApi } from "../api/ServerApi";
 | 
				
			||||||
 | 
					import { useDarkTheme } from "../hooks/context_providers/DarkThemeProvider";
 | 
				
			||||||
 | 
					import { toBase64 } from "@jsonjoy.com/base64";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function AccountWidget(p: { account: Account }): React.ReactElement {
 | 
				
			||||||
 | 
					  const darkTheme = useDarkTheme();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <img
 | 
				
			||||||
 | 
					      style={{
 | 
				
			||||||
 | 
					        height: "1.5em",
 | 
				
			||||||
 | 
					        width: "1.5em",
 | 
				
			||||||
 | 
					        backgroundColor: darkTheme.enabled ? "white" : "black",
 | 
				
			||||||
 | 
					        mask: `url(\"data:image/svg+xml;base64,${toBase64(
 | 
				
			||||||
 | 
					          new TextEncoder().encode(
 | 
				
			||||||
 | 
					            ServerApi.Config.accounts_types.find(
 | 
				
			||||||
 | 
					              (t) => t.code === p.account.type
 | 
				
			||||||
 | 
					            )!.icon
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        )}\")`,
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import { mdiAccount, mdiApi, mdiCashMultiple, mdiHome } from "@mdi/js";
 | 
					import { mdiApi, mdiCashMultiple, mdiHome } from "@mdi/js";
 | 
				
			||||||
import Icon from "@mdi/react";
 | 
					import Icon from "@mdi/react";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Divider,
 | 
					  Divider,
 | 
				
			||||||
@@ -8,8 +8,10 @@ import {
 | 
				
			|||||||
  ListItemText,
 | 
					  ListItemText,
 | 
				
			||||||
  Typography,
 | 
					  Typography,
 | 
				
			||||||
} from "@mui/material";
 | 
					} from "@mui/material";
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
import { useLocation } from "react-router-dom";
 | 
					import { useLocation } from "react-router-dom";
 | 
				
			||||||
import { useAccounts } from "../hooks/AccountsListProvider";
 | 
					import { useAccounts } from "../hooks/AccountsListProvider";
 | 
				
			||||||
 | 
					import { AccountWidget } from "./AccountWidget";
 | 
				
			||||||
import { RouterLink } from "./RouterLink";
 | 
					import { RouterLink } from "./RouterLink";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function MoneyNavList(): React.ReactElement {
 | 
					export function MoneyNavList(): React.ReactElement {
 | 
				
			||||||
@@ -59,7 +61,7 @@ export function MoneyNavList(): React.ReactElement {
 | 
				
			|||||||
        <NavLink
 | 
					        <NavLink
 | 
				
			||||||
          label={a.name}
 | 
					          label={a.name}
 | 
				
			||||||
          uri={`/account/${a.id}`}
 | 
					          uri={`/account/${a.id}`}
 | 
				
			||||||
          icon={<Icon path={mdiAccount} size={1} />}
 | 
					          icon={<AccountWidget account={a} />}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
      ))}
 | 
					      ))}
 | 
				
			||||||
    </List>
 | 
					    </List>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user