Minimal Leptos setup
4
.gitignore
vendored
|
@ -9,3 +9,7 @@ gen/
|
||||||
# Generated by `cargo tauri icon`
|
# Generated by `cargo tauri icon`
|
||||||
# will have various versions of the app icon
|
# will have various versions of the app icon
|
||||||
icons/
|
icons/
|
||||||
|
|
||||||
|
# Generated by Trunk
|
||||||
|
# will have the files to be served over http
|
||||||
|
www/
|
||||||
|
|
1282
Cargo.lock
generated
39
Cargo.toml
|
@ -5,6 +5,22 @@ description = "Buchhaltung für „Darmstadt sagt Nein zur Bezahlkarte!“"
|
||||||
authors = ["Bianca Fürstenau"]
|
authors = ["Bianca Fürstenau"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
tauri = ["server"]
|
||||||
|
leptos = []
|
||||||
|
server = [
|
||||||
|
"dep:tauri",
|
||||||
|
"dep:tauri-plugin-opener",
|
||||||
|
"dep:tauri-plugin-fs",
|
||||||
|
"dep:rusqlite",
|
||||||
|
"dep:rand",
|
||||||
|
"dep:chrono",
|
||||||
|
"dep:tokio",
|
||||||
|
"dep:curl",
|
||||||
|
"dep:openssl",
|
||||||
|
"dep:ring-compat",
|
||||||
|
]
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
@ -18,15 +34,18 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
tauri-build = { version = "2", features = [] }
|
tauri-build = { version = "2", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "2", features = [] }
|
leptos = { version = "^0.7", features = ["ssr"] }
|
||||||
tauri-plugin-opener = "2"
|
tauri-sys = { git = "https://github.com/JonasKruckenberg/tauri-sys", features = ["tauri"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
rusqlite = {version = "^0.33", features = ["backup", "bundled"] }
|
|
||||||
rand = {version = "^0.8"}
|
tauri = { version = "2", features = [], optional = true }
|
||||||
chrono = {version = "^0.4"}
|
tauri-plugin-opener = { version = "2", optional = true }
|
||||||
tokio = {version = "^1.43"}
|
tauri-plugin-fs = {version = "2", optional = true}
|
||||||
tauri-plugin-fs = {version = "2"}
|
rusqlite = {version = "^0.33", features = ["backup", "bundled"], optional = true }
|
||||||
curl = {version = "^0.4"}
|
rand = {version = "^0.8", optional = true}
|
||||||
openssl = {version = "^0.10", features = ["vendored"] }
|
chrono = {version = "^0.4", optional = true}
|
||||||
ring-compat = {version = "^0.8", features = ["signature", "rand_core"] }
|
tokio = {version = "^1.43", optional = true}
|
||||||
|
curl = {version = "^0.4", optional = true}
|
||||||
|
openssl = {version = "^0.10", features = ["vendored"], optional = true}
|
||||||
|
ring-compat = {version = "^0.8", features = ["signature", "rand_core"], optional = true}
|
||||||
|
|
9
Trunk.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[build]
|
||||||
|
target = "trunk.html"
|
||||||
|
html_output = "index.html"
|
||||||
|
dist = "www"
|
||||||
|
|
||||||
|
[serve]
|
||||||
|
port = 1420
|
||||||
|
open = false
|
||||||
|
ws_protocol = "ws"
|
6
build.rs
|
@ -1,3 +1,7 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
tauri_build::build()
|
if cfg!(feature = "tauri") {
|
||||||
|
tauri_build::build();
|
||||||
|
} else {
|
||||||
|
println!("cargo::rustc-check-cfg=cfg(mobile)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 7 KiB After Width: | Height: | Size: 7 KiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
5
src/bin/leptos.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
use leptos::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
leptos::mount::mount_to_body(|| view! { <p>"Hello, world!"</p> })
|
||||||
|
}
|
|
@ -1,11 +1,12 @@
|
||||||
use tauri_sys::Error;
|
use tauri_sys::Error;
|
||||||
use tauri_sys::tauri::invoke;
|
use tauri_sys::tauri::invoke;
|
||||||
|
|
||||||
pub struct Swap {
|
#[derive(serde::Serialize)]
|
||||||
store: &str,
|
pub struct Swap<'a> {
|
||||||
|
store: &'a str,
|
||||||
acc: i64,
|
acc: i64,
|
||||||
}
|
}
|
||||||
pub async fn swap(store: &str, acc: i64) -> Result<(), Error> {
|
pub async fn swap(store: &str, acc: i64) -> Result<(), Error> {
|
||||||
let args = Swap { store, acc };
|
let args = Swap { store, acc };
|
||||||
invoke("swap", &args)
|
invoke("swap", &args).await
|
||||||
}
|
}
|
||||||
|
|
202
src/lib.rs
|
@ -1,201 +1,9 @@
|
||||||
use chrono::offset::Utc;
|
mod commands;
|
||||||
use rusqlite::{types::ToSqlOutput, ToSql};
|
#[cfg(feature = "server")]
|
||||||
use tauri::{Manager, State};
|
mod server;
|
||||||
use tauri_plugin_fs::FsExt;
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
mod app_state;
|
|
||||||
mod data_door;
|
|
||||||
|
|
||||||
use app_state::AppState;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
enum Store {
|
|
||||||
Aldi,
|
|
||||||
Edeka,
|
|
||||||
Dm,
|
|
||||||
Lidl,
|
|
||||||
Rewe,
|
|
||||||
Tegut,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
enum Account {
|
|
||||||
Sumpf,
|
|
||||||
Heinersyndikat,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Inventory {
|
|
||||||
acc: Account,
|
|
||||||
cash: i64,
|
|
||||||
vouchers: Vec<VoucherInventory>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct VoucherInventory {
|
|
||||||
store: Store,
|
|
||||||
count: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&str> for Store {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
|
||||||
match s {
|
|
||||||
"aldi" => Ok(Store::Aldi),
|
|
||||||
"edeka" => Ok(Store::Edeka),
|
|
||||||
"dm" => Ok(Store::Dm),
|
|
||||||
"lidl" => Ok(Store::Lidl),
|
|
||||||
"rewe" => Ok(Store::Rewe),
|
|
||||||
"tegut" => Ok(Store::Tegut),
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&str> for Account {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
|
||||||
match s {
|
|
||||||
"sumpf" => Ok(Account::Sumpf),
|
|
||||||
"hs" => Ok(Account::Heinersyndikat),
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToSql for Store {
|
|
||||||
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
|
|
||||||
match self {
|
|
||||||
Store::Aldi => 0.to_sql(),
|
|
||||||
Store::Edeka => 1.to_sql(),
|
|
||||||
Store::Dm => 2.to_sql(),
|
|
||||||
Store::Lidl => 3.to_sql(),
|
|
||||||
Store::Rewe => 4.to_sql(),
|
|
||||||
Store::Tegut => 5.to_sql(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToSql for Account {
|
|
||||||
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
|
|
||||||
match self {
|
|
||||||
Account::Sumpf => 0.to_sql(),
|
|
||||||
Account::Heinersyndikat => 1.to_sql(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_inventory(data: HashMap<String, String>) -> Result<Inventory, ()> {
|
|
||||||
let a = data.get("cafe-inventory-acc").ok_or(())?;
|
|
||||||
let acc: Account = Account::try_from(a.as_ref())?;
|
|
||||||
let mut vouchers = Vec::new();
|
|
||||||
for s in ["aldi", "dm", "lidl", "rewe", "tegut"] {
|
|
||||||
let Ok(store) = s.try_into() else {
|
|
||||||
println!("Did not find '{}' in inventory data.", s);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
match data.get(&format!("cafe-inventory-{}", s)) {
|
|
||||||
None => (),
|
|
||||||
Some(c) => {
|
|
||||||
let c = if c == "" {"0"} else {c};
|
|
||||||
let Ok(count) = c.parse() else {
|
|
||||||
println!("Invalid count '{}' for '{}' in inventory data.", c, s);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let v = VoucherInventory { store, count };
|
|
||||||
vouchers.push(v);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn inventory(
|
|
||||||
data: HashMap<String, String>,
|
|
||||||
state: State<'_, Mutex<AppState>>,
|
|
||||||
) -> Result<(), ()> {
|
|
||||||
let now = Utc::now().timestamp();
|
|
||||||
let state = state.lock().await;
|
|
||||||
let inv = parse_inventory(data)?;
|
|
||||||
for v in inv.vouchers {
|
|
||||||
state.db.execute(
|
|
||||||
"INSERT INTO voucher_inventory VALUES ()",
|
|
||||||
(
|
|
||||||
inv.acc,
|
|
||||||
v.store,
|
|
||||||
v.count,
|
|
||||||
now,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.map_err(|e| println!("{:?}", e))?;
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn swap(
|
|
||||||
store: &str,
|
|
||||||
acc: i64,
|
|
||||||
state: State<'_, Mutex<AppState>>,
|
|
||||||
) -> Result<(), ()> {
|
|
||||||
let state = state.lock().await;
|
|
||||||
let store: Store = store.try_into()?;
|
|
||||||
state.db.execute(
|
|
||||||
"INSERT INTO swap VALUES (?1, ?2, ?3, ?4, ?5)",
|
|
||||||
(
|
|
||||||
store,
|
|
||||||
acc,
|
|
||||||
i64::from_ne_bytes(state.id.to_ne_bytes()),
|
|
||||||
Utc::now().timestamp(),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.map_err(|e| println!("{:?}", e))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn count(state: State<'_, Mutex<AppState>>) -> Result<String, ()> {
|
|
||||||
let state = state.lock().await;
|
|
||||||
let mut stmt =
|
|
||||||
state.db.prepare("SELECT COUNT(*) FROM swap")
|
|
||||||
.map_err(|e| println!("{:?}", e))?;
|
|
||||||
let mut rows = stmt.query([]).map_err(|e| println!("{:?}", e))?;
|
|
||||||
let row = rows.next().map_err(|e| println!("{:?}", e))?;
|
|
||||||
let row = match row {
|
|
||||||
Some(r) => Ok(r),
|
|
||||||
None => {
|
|
||||||
println!("No rows");
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}?;
|
|
||||||
let cnt: u64 = row.get(0).map_err(|e| println!("{:?}", e))?;
|
|
||||||
Ok(cnt.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#[cfg(feature = "server")]
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
let state = AppState::new();
|
server::run();
|
||||||
tauri::Builder::default()
|
|
||||||
.plugin(tauri_plugin_fs::init())
|
|
||||||
.setup(|app| {
|
|
||||||
app.manage(Mutex::new(state));
|
|
||||||
let scope = app.fs_scope();
|
|
||||||
let path = app.path();
|
|
||||||
scope.allow_directory(path.temp_dir()?, false)?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.invoke_handler(tauri::generate_handler![
|
|
||||||
swap,
|
|
||||||
count,
|
|
||||||
inventory,
|
|
||||||
data_door::pull_data,
|
|
||||||
data_door::push_data,
|
|
||||||
])
|
|
||||||
.run(tauri::generate_context!())
|
|
||||||
.expect("error while running tauri application");
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use ring_compat::signature::ed25519::SigningKey;
|
||||||
use tauri::{Manager, State};
|
use tauri::{Manager, State};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::app_state::AppState;
|
use crate::server::app_state::AppState;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Collector(Vec<u8>, Vec<u8>, usize);
|
struct Collector(Vec<u8>, Vec<u8>, usize);
|
200
src/server/mod.rs
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
use chrono::offset::Utc;
|
||||||
|
use rusqlite::{types::ToSqlOutput, ToSql};
|
||||||
|
use tauri::{Manager, State};
|
||||||
|
use tauri_plugin_fs::FsExt;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
mod app_state;
|
||||||
|
mod data_door;
|
||||||
|
|
||||||
|
use app_state::AppState;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum Store {
|
||||||
|
Aldi,
|
||||||
|
Edeka,
|
||||||
|
Dm,
|
||||||
|
Lidl,
|
||||||
|
Rewe,
|
||||||
|
Tegut,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum Account {
|
||||||
|
Sumpf,
|
||||||
|
Heinersyndikat,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Inventory {
|
||||||
|
acc: Account,
|
||||||
|
cash: i64,
|
||||||
|
vouchers: Vec<VoucherInventory>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct VoucherInventory {
|
||||||
|
store: Store,
|
||||||
|
count: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Store {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
|
match s {
|
||||||
|
"aldi" => Ok(Store::Aldi),
|
||||||
|
"edeka" => Ok(Store::Edeka),
|
||||||
|
"dm" => Ok(Store::Dm),
|
||||||
|
"lidl" => Ok(Store::Lidl),
|
||||||
|
"rewe" => Ok(Store::Rewe),
|
||||||
|
"tegut" => Ok(Store::Tegut),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Account {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
|
match s {
|
||||||
|
"sumpf" => Ok(Account::Sumpf),
|
||||||
|
"hs" => Ok(Account::Heinersyndikat),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToSql for Store {
|
||||||
|
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
|
||||||
|
match self {
|
||||||
|
Store::Aldi => 0.to_sql(),
|
||||||
|
Store::Edeka => 1.to_sql(),
|
||||||
|
Store::Dm => 2.to_sql(),
|
||||||
|
Store::Lidl => 3.to_sql(),
|
||||||
|
Store::Rewe => 4.to_sql(),
|
||||||
|
Store::Tegut => 5.to_sql(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToSql for Account {
|
||||||
|
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
|
||||||
|
match self {
|
||||||
|
Account::Sumpf => 0.to_sql(),
|
||||||
|
Account::Heinersyndikat => 1.to_sql(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_inventory(data: HashMap<String, String>) -> Result<Inventory, ()> {
|
||||||
|
let a = data.get("cafe-inventory-acc").ok_or(())?;
|
||||||
|
let acc: Account = Account::try_from(a.as_ref())?;
|
||||||
|
let mut vouchers = Vec::new();
|
||||||
|
for s in ["aldi", "dm", "lidl", "rewe", "tegut"] {
|
||||||
|
let Ok(store) = s.try_into() else {
|
||||||
|
println!("Did not find '{}' in inventory data.", s);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
match data.get(&format!("cafe-inventory-{}", s)) {
|
||||||
|
None => (),
|
||||||
|
Some(c) => {
|
||||||
|
let c = if c == "" {"0"} else {c};
|
||||||
|
let Ok(count) = c.parse() else {
|
||||||
|
println!("Invalid count '{}' for '{}' in inventory data.", c, s);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let v = VoucherInventory { store, count };
|
||||||
|
vouchers.push(v);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn inventory(
|
||||||
|
data: HashMap<String, String>,
|
||||||
|
state: State<'_, Mutex<AppState>>,
|
||||||
|
) -> Result<(), ()> {
|
||||||
|
let now = Utc::now().timestamp();
|
||||||
|
let state = state.lock().await;
|
||||||
|
let inv = parse_inventory(data)?;
|
||||||
|
for v in inv.vouchers {
|
||||||
|
state.db.execute(
|
||||||
|
"INSERT INTO voucher_inventory VALUES ()",
|
||||||
|
(
|
||||||
|
inv.acc,
|
||||||
|
v.store,
|
||||||
|
v.count,
|
||||||
|
now,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.map_err(|e| println!("{:?}", e))?;
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn swap(
|
||||||
|
store: &str,
|
||||||
|
acc: i64,
|
||||||
|
state: State<'_, Mutex<AppState>>,
|
||||||
|
) -> Result<(), ()> {
|
||||||
|
let state = state.lock().await;
|
||||||
|
let store: Store = store.try_into()?;
|
||||||
|
state.db.execute(
|
||||||
|
"INSERT INTO swap VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||||
|
(
|
||||||
|
store,
|
||||||
|
acc,
|
||||||
|
i64::from_ne_bytes(state.id.to_ne_bytes()),
|
||||||
|
Utc::now().timestamp(),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.map_err(|e| println!("{:?}", e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn count(state: State<'_, Mutex<AppState>>) -> Result<String, ()> {
|
||||||
|
let state = state.lock().await;
|
||||||
|
let mut stmt =
|
||||||
|
state.db.prepare("SELECT COUNT(*) FROM swap")
|
||||||
|
.map_err(|e| println!("{:?}", e))?;
|
||||||
|
let mut rows = stmt.query([]).map_err(|e| println!("{:?}", e))?;
|
||||||
|
let row = rows.next().map_err(|e| println!("{:?}", e))?;
|
||||||
|
let row = match row {
|
||||||
|
Some(r) => Ok(r),
|
||||||
|
None => {
|
||||||
|
println!("No rows");
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
let cnt: u64 = row.get(0).map_err(|e| println!("{:?}", e))?;
|
||||||
|
Ok(cnt.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run() {
|
||||||
|
let state = AppState::new();
|
||||||
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_fs::init())
|
||||||
|
.setup(|app| {
|
||||||
|
app.manage(Mutex::new(state));
|
||||||
|
let scope = app.fs_scope();
|
||||||
|
let path = app.path();
|
||||||
|
scope.allow_directory(path.temp_dir()?, false)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
swap,
|
||||||
|
count,
|
||||||
|
inventory,
|
||||||
|
data_door::pull_data,
|
||||||
|
data_door::push_data,
|
||||||
|
])
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("error while running tauri application");
|
||||||
|
}
|
|
@ -4,7 +4,11 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"identifier": "de.mathebau.bkbh",
|
"identifier": "de.mathebau.bkbh",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "www"
|
"beforeDevCommand": "cd bkbh && trunk serve",
|
||||||
|
"devUrl": "http://localhost:1420",
|
||||||
|
"beforeBuildCommand": "cd bkbh && trunk build",
|
||||||
|
"frontendDist": "www",
|
||||||
|
"features": ["tauri"]
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"withGlobalTauri": true,
|
"withGlobalTauri": true,
|
||||||
|
|
12
trunk.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link data-trunk rel="scss" href="styles.scss" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Darmstadt sagt Nein zur Bezahlkartei!</title>
|
||||||
|
<link data-trunk rel="rust" href="." data-bin="leptos" data-wasm-opt="4" data-weak-refs />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
188
www/index.html
|
@ -1,188 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="stylesheet" href="styles.css" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Darmstadt sagt Nein zur Bezahlkartei!</title>
|
|
||||||
<script type="module" src="main.js" defer></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="v-container" id="cafe" style="display: none">
|
|
||||||
<div id="cafe-voucher" style="display: none">
|
|
||||||
<h1 class="shout">
|
|
||||||
Sumpf
|
|
||||||
</h1>
|
|
||||||
<div class="h-container">
|
|
||||||
<form class="column" id="cafe-voucher-aldi">
|
|
||||||
<button type="submit">
|
|
||||||
<img
|
|
||||||
src="assets/aldi.svg"
|
|
||||||
class="logo"
|
|
||||||
alt="ALDI-Süd-Logo"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
<form class="column" id="cafe-voucher-dm">
|
|
||||||
<button type="submit">
|
|
||||||
<img
|
|
||||||
src="assets/dm.svg"
|
|
||||||
class="logo"
|
|
||||||
alt="dm-Logo"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
<form class="column" id="cafe-voucher-lidl">
|
|
||||||
<button type="submit">
|
|
||||||
<img
|
|
||||||
src="assets/lidl.svg"
|
|
||||||
class="logo"
|
|
||||||
alt="Lidl-Logo"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
<form class="column" id="cafe-voucher-rewe">
|
|
||||||
<button type="submit">
|
|
||||||
<img
|
|
||||||
src="assets/rewe.svg"
|
|
||||||
class="logo"
|
|
||||||
alt="Rewe-Logo"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
<form class="column" id="cafe-voucher-tegut">
|
|
||||||
<button type="submit">
|
|
||||||
<img
|
|
||||||
src="assets/tegut.svg"
|
|
||||||
class="logo"
|
|
||||||
alt="Tegut-Logo"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="cafe-inventory" style="display: none">
|
|
||||||
<form>
|
|
||||||
<div style="display: contents">
|
|
||||||
<div class="labelled-input">
|
|
||||||
<input type="radio" id="cafe-inventory-acc-sumpf" name="cafe-inventory-acc" value="sumpf" required>
|
|
||||||
<label for="cafe-inventory-acc-sumpf">Sumpf</label>
|
|
||||||
</div>
|
|
||||||
<div class="labelled-input">
|
|
||||||
<input type="radio" id="cafe-inventory-acc-hs" name="cafe-inventory-acc" value="hs" required>
|
|
||||||
<label for="cafe-inventory-acc-hs">Heinersyndikat</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="display: contents">
|
|
||||||
<div class="labelled-input" id="cafe-inventory-aldi">
|
|
||||||
<label for="cafe-inventory-aldi">
|
|
||||||
<img
|
|
||||||
src="assets/aldi.svg"
|
|
||||||
class="logo"
|
|
||||||
alt="ALDI-Süd-Logo"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<input type="number" name="cafe-inventory-aldi" min="0">
|
|
||||||
</div>
|
|
||||||
<div class="labelled-input" id="cafe-inventory-dm">
|
|
||||||
<label for="cafe-inventory-dm">
|
|
||||||
<img
|
|
||||||
src="assets/dm.svg"
|
|
||||||
class="logo"
|
|
||||||
alt="dm-Logo"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<input type="number" name="cafe-inventory-dm" min="0">
|
|
||||||
</div>
|
|
||||||
<div class="labelled-input" id="cafe-inventory-lidl">
|
|
||||||
<label for="cafe-inventory-lidl">
|
|
||||||
<img
|
|
||||||
src="assets/lidl.svg"
|
|
||||||
class="logo"
|
|
||||||
alt="Lidl-Logo"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<input type="number" name="cafe-inventory-lidl" min="0">
|
|
||||||
</div>
|
|
||||||
<div class="labelled-input" id="cafe-inventory-rewe">
|
|
||||||
<label for="cafe-inventory-rewe">
|
|
||||||
<img
|
|
||||||
src="assets/rewe.svg"
|
|
||||||
class="logo"
|
|
||||||
alt="Rewe-Logo"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<input type="number" name="cafe-inventory-rewe" min="0">
|
|
||||||
</div>
|
|
||||||
<div class="labelled-input" id="cafe-inventory-tegut">
|
|
||||||
<label for="cafe-inventory-tegut">
|
|
||||||
<img
|
|
||||||
src="assets/tegut.svg"
|
|
||||||
class="logo"
|
|
||||||
alt="Tegut-Logo"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<input type="number" name="cafe-inventory-tegut" min="0">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="labelled-input" id="cafe-inventory-cash">
|
|
||||||
<label for="cafe-inventory-cash">
|
|
||||||
<img
|
|
||||||
src="assets/cash.svg"
|
|
||||||
class="logo"
|
|
||||||
alt="Bargeld"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<input type="number" name="cafe-inventory-cash" min="0" step=".01">
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="shout">
|
|
||||||
Senden
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="v-container nav" id="cafe-nav">
|
|
||||||
<form id="cafe-nav-inventory">
|
|
||||||
<button type="submit">
|
|
||||||
<span class="shout">
|
|
||||||
Bestand
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
<form id="cafe-nav-voucher">
|
|
||||||
<button type="submit">
|
|
||||||
<span class="shout">
|
|
||||||
Annahme
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="angel">
|
|
||||||
<div>
|
|
||||||
<form class="h-container">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="v-container nav" id="nav">
|
|
||||||
<form class="column" id="nav-cafe">
|
|
||||||
<button type="submit">
|
|
||||||
<img
|
|
||||||
src="assets/cafe.svg"
|
|
||||||
class="logo"
|
|
||||||
alt="Tauschcafé"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
<form class="column" id="nav-angel">
|
|
||||||
<button type="submit">
|
|
||||||
<img
|
|
||||||
src="assets/angel.svg"
|
|
||||||
class="logo"
|
|
||||||
alt="Botengang"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
67
www/main.js
|
@ -1,67 +0,0 @@
|
||||||
const { invoke } = window.__TAURI__.core;
|
|
||||||
|
|
||||||
let callbacks = {
|
|
||||||
'#nav-cafe': () => activate("", "cafe", ["angel"]),
|
|
||||||
'#nav-angel': () => activate("", "angel", ["cafe"]),
|
|
||||||
'#cafe-nav-inventory': () => activate("cafe-", "inventory", ["voucher"]),
|
|
||||||
'#cafe-nav-voucher': () => activate("cafe-", "voucher", ["inventory"]),
|
|
||||||
'#cafe-inventory form': () => inventory(),
|
|
||||||
'#cafe-voucher-aldi': () => swap("aldi"),
|
|
||||||
'#cafe-voucher-dm': () => swap("dm"),
|
|
||||||
'#cafe-voucher-lidl': () => swap("lidl"),
|
|
||||||
'#cafe-voucher-rewe': () => swap("rewe"),
|
|
||||||
'#cafe-voucher-tegut': () => swap("tegut"),
|
|
||||||
}
|
|
||||||
let stores = [
|
|
||||||
"aldi",
|
|
||||||
"dm",
|
|
||||||
"lidl",
|
|
||||||
"rewe",
|
|
||||||
"tegut",
|
|
||||||
]
|
|
||||||
|
|
||||||
async function increment(el) {
|
|
||||||
var el = document.querySelector(el+" input");
|
|
||||||
let v = parseInt(el.value, 10);
|
|
||||||
v = isNaN(v) ? 0 : v;
|
|
||||||
v++;
|
|
||||||
el.value = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function inventory() {
|
|
||||||
const form = document.querySelector("#cafe-inventory form");
|
|
||||||
const fd = new FormData(form);
|
|
||||||
const obj = Object.fromEntries(fd);
|
|
||||||
await invoke("inventory", { data: obj });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function swap(s) {
|
|
||||||
await invoke("swap", { store: s, acc: 1 });
|
|
||||||
document.querySelector("h1").textContent = await invoke("count", {});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function activate(ctx, el, nels) {
|
|
||||||
document.querySelector("#"+ctx+el).style.display = "";
|
|
||||||
for (const nel of nels) {
|
|
||||||
document.querySelector("#"+ctx+nel).style.display = "none";
|
|
||||||
}
|
|
||||||
document.querySelector("#"+ctx+"nav").classList.remove("v-container");
|
|
||||||
document.querySelector("#"+ctx+"nav").classList.add("h-container");
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
|
||||||
for (let key in callbacks) {
|
|
||||||
if (callbacks.hasOwnProperty(key)) {
|
|
||||||
document.querySelector(key).addEventListener("submit", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
callbacks[key]();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const store of stores) {
|
|
||||||
document.querySelector("#cafe-inventory-"+store+" label").addEventListener("click", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
increment("#cafe-inventory-"+store);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|