diff --git a/package-lock.json b/package-lock.json index ddf3a87..aa8fdb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "@tauri-apps/api": "^2", + "@tauri-apps/plugin-dialog": "^2.7.1", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-sql": "^2.4.0" }, @@ -1219,6 +1220,15 @@ "node": ">= 10" } }, + "node_modules/@tauri-apps/plugin-dialog": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.7.1.tgz", + "integrity": "sha512-OK1UBXYt+ojcmxMktzzuyonYIFta8CmAASpX+CA+DTGK24KlHjhYI6x2iOJ/TjZF4N7/ACK1oFmEOjIY9IhzOQ==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.11.0" + } + }, "node_modules/@tauri-apps/plugin-opener": { "version": "2.5.4", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.4.tgz", diff --git a/package.json b/package.json index 642fb56..737d8eb 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "license": "MIT", "dependencies": { "@tauri-apps/api": "^2", + "@tauri-apps/plugin-dialog": "^2.7.1", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-sql": "^2.4.0" }, diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 97f8242..02a756c 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -562,8 +562,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-link 0.2.1", ] @@ -2519,6 +2521,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.13.0", "block2", + "libc", "objc2", "objc2-core-foundation", ] @@ -3154,6 +3157,30 @@ dependencies = [ "web-sys", ] +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "block2", + "dispatch2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + [[package]] name = "rkyv" version = "0.7.46" @@ -3607,10 +3634,12 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" name = "sloneczko-menager" version = "0.1.0" dependencies = [ + "chrono", "serde", "serde_json", "tauri", "tauri-build", + "tauri-plugin-dialog", "tauri-plugin-opener", "tauri-plugin-sql", ] @@ -4211,6 +4240,48 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tauri-plugin-dialog" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65981abb771e74e571a38196c3baa11c459379164791eba0e67abc1a5fac9884" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ecc274121aca0c036a2b42d1cbe83d368d348f54e0bb8a735c2b1548e8f371" +dependencies = [ + "anyhow", + "dunce", + "glob", + "log", + "objc2-foundation", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", + "url", +] + [[package]] name = "tauri-plugin-opener" version = "2.5.4" @@ -5352,6 +5423,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -5400,13 +5480,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + [[package]] name = "windows-threading" version = "0.1.0" @@ -5443,6 +5540,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -5461,6 +5564,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -5479,12 +5588,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -5503,6 +5624,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -5521,6 +5648,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -5539,6 +5672,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -5557,6 +5696,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "0.5.40" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index ad682d6..8c3c098 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -23,4 +23,6 @@ tauri-plugin-opener = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" tauri-plugin-sql = { version = "2.4.0", features = ["sqlite"] } +tauri-plugin-dialog = "2.7.1" +chrono = "0.4.45" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 37314fe..268eec3 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -7,6 +7,7 @@ "core:default", "opener:default", "sql:default", - "sql:allow-execute" + "sql:allow-execute", + "dialog:default" ] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 24b631d..1fc7309 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -92,17 +92,95 @@ fn migrations() -> Vec { CREATE INDEX idx_invoice_items_invoice ON invoice_items (invoice_id); ", kind: MigrationKind::Up, + }, + Migration { + version: 3, + description: "add_guests_settled_price_seasons", + sql: " + CREATE TABLE guests ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + address TEXT, + phone TEXT, + notes TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + ALTER TABLE reservations ADD COLUMN guest_id INTEGER REFERENCES guests(id) ON DELETE SET NULL; + + INSERT INTO guests (name, address, phone) + SELECT guest_name, MIN(address), phone + FROM reservations + GROUP BY guest_name, COALESCE(phone, ''); + UPDATE reservations SET guest_id = + (SELECT g.id FROM guests g + WHERE g.name = reservations.guest_name + AND COALESCE(g.phone, '') = COALESCE(reservations.phone, '')); + + ALTER TABLE reservations ADD COLUMN settled INTEGER NOT NULL DEFAULT 0; + + CREATE TABLE price_seasons ( + id INTEGER PRIMARY KEY, + cabin_id INTEGER REFERENCES cabins(id) ON DELETE CASCADE, + date_from TEXT NOT NULL, + date_to TEXT NOT NULL, + price REAL NOT NULL, + CHECK (date_to > date_from) + ); + ", + kind: MigrationKind::Up, }] } +/// Kopia bazy przy starcie (zanim plugin SQL otworzy połączenia): +/// backups/sloneczko-RRRR-MM-DD.db obok pliku bazy, retencja 14 ostatnich. +fn auto_backup(app: &tauri::AppHandle) -> std::io::Result<()> { + use tauri::Manager; + + let candidates = [app.path().app_config_dir(), app.path().app_data_dir()]; + for dir in candidates.into_iter().flatten() { + let db = dir.join("sloneczko.db"); + if !db.exists() { + continue; + } + let backups = dir.join("backups"); + std::fs::create_dir_all(&backups)?; + let name = format!("sloneczko-{}.db", chrono::Local::now().format("%Y-%m-%d")); + let dest = backups.join(name); + if !dest.exists() { + std::fs::copy(&db, &dest)?; + } + let mut files: Vec<_> = std::fs::read_dir(&backups)? + .flatten() + .map(|e| e.path()) + .filter(|p| { + p.file_name() + .is_some_and(|n| n.to_string_lossy().starts_with("sloneczko-")) + }) + .collect(); + files.sort(); + while files.len() > 14 { + let _ = std::fs::remove_file(files.remove(0)); + } + break; + } + Ok(()) +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() + .setup(|app| { + if let Err(e) = auto_backup(app.handle()) { + eprintln!("Nie udało się wykonać kopii zapasowej: {e}"); + } + Ok(()) + }) .plugin( tauri_plugin_sql::Builder::default() .add_migrations("sqlite:sloneczko.db", migrations()) .build(), ) + .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_opener::init()) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src/lib/components/GuestField.svelte b/src/lib/components/GuestField.svelte new file mode 100644 index 0000000..c8a7f3f --- /dev/null +++ b/src/lib/components/GuestField.svelte @@ -0,0 +1,157 @@ + + +
+ + {#if open} + + {/if} +
+ + diff --git a/src/lib/components/ReservationBar.svelte b/src/lib/components/ReservationBar.svelte index c356f00..d7ef5a4 100644 --- a/src/lib/components/ReservationBar.svelte +++ b/src/lib/components/ReservationBar.svelte @@ -1,14 +1,30 @@ + + diff --git a/src/routes/raporty/+page.svelte b/src/routes/raporty/+page.svelte new file mode 100644 index 0000000..f9fd871 --- /dev/null +++ b/src/routes/raporty/+page.svelte @@ -0,0 +1,329 @@ + + + + Słoneczko — raporty + + +
+
+

Raporty

+ + + {year} + +
+ + {#if error} +

{error}

+ {:else if yearStats} +
+
+ Doby (wszystkie) + {Math.round(yearStats.nights_total)} +
+
+ Obłożenie + {occupancy(yearStats.nights_total, cabins.length * daysInYear)} +
+
+ Przychód potencjalny + {formatMoney(yearStats.revenue_total)} zł +
+
+ Przychód potwierdzony + {formatMoney(yearStats.revenue_confirmed)} zł +
+
+ +
+

Przychód miesięczny

+
+ {#each monthRows as m, i (i)} +
+ {m.revenue_total > 0 ? formatMoney(m.revenue_total) : ""} +
+
+
+
+
+ {MONTH_NAMES[i].slice(0, 3)} +
+ {/each} +
+

Ciemniejsza część słupka to przychód z rezerwacji potwierdzonych.

+
+ +
+
+

Wg domków

+ + + + + + + + + + + {#each cabins as cabin (cabin.id)} + {@const row = cabinRows.find((r) => r.cabin_id === cabin.id)} + + + + + + + {/each} + +
DomekDobyObłożeniePrzychód
{cabin.name}{row ? Math.round(row.nights_total) : 0}{occupancy(row?.nights_total ?? 0, daysInYear)}{formatMoney(row?.revenue_total ?? 0)} zł
+
+ +
+

Wg miesięcy

+ + + + + + + + + + + {#each monthRows as m, i (i)} + + + + + + + {/each} + +
MiesiącDobyObłożeniePrzychód
{MONTH_NAMES[i]}{Math.round(m.nights_total)}{occupancy(m.nights_total, cabins.length * daysInMonth(year, i))}{formatMoney(m.revenue_total)} zł
+
+
+ {:else if loading} +

Wczytywanie…

+ {/if} +
+ + diff --git a/src/routes/ustawienia/+page.svelte b/src/routes/ustawienia/+page.svelte index fcdcdef..386329b 100644 --- a/src/routes/ustawienia/+page.svelte +++ b/src/routes/ustawienia/+page.svelte @@ -1,20 +1,110 @@