Booking system for sport/service facilities. Full-stack Rust: Leptos 0.8 (SSR + hydration) on Actix Web, PostgreSQL via sqlx. Crate name is `rezervator`, product name is "Rezervovator".
Run both `cargo check` commands and the tests after any change. They don't catch hydration errors, so after changing a view also load the page in a browser with a full reload (the browser caches the old WASM otherwise) and check the console; a plain `cargo check` / `cargo test` (no features) checks neither side properly.
Unit tests are `#[cfg(test)] mod tests` at the bottom of the file they test (`#[cfg(all(test, feature = "ssr"))]` for ssr-only code). They cover pure logic only — parsing, validation, paging, slot merging, mail templates, config. Nothing touches the database, so server functions, permissions and SQL are untested. Add tests for new pure logic; keep them free of DB and network.
`end2end/` is still the untouched Leptos template (it asserts "Welcome to Leptos") — it does not test this app.
-`config.toml` (gitignored) is read from the working directory, or from `-c <path>`. Copy `config_sample.toml`. The bind address comes from `[network]` there, not from `LEPTOS_SITE_ADDR`.
- Migrations in `migrations/` are embedded by `sqlx::migrate!()` and run on startup.
- First start seeds an `admin` / `admin` user, a placeholder company, empty mail templates and an appearance row (`create_admin`, `check_company`, `check_messages`, `check_appearance` in `src/main.rs`).
- With `transport = "File"` e-mails are written as `.eml` into `Maildir/` instead of being sent.
-`assets/` is copied to `target/site` on build. `assets/data/` (banner.css, uploaded banner) is untracked but required: the page links `/data/<css_name>` and uploads are written to `target/site/data/` at runtime, relative to the working directory. A rebuild wipes `target/site`, so runtime uploads there are lost in dev.
## Layout
-`src/main.rs` — Actix setup: config, pool, migrations, seeding, session + auth middleware, `/api/*` server fns, `/upload`.
-`src/backend/` — server functions per domain, plus `data.rs` (all DTOs/entities shared by server and client) and `auth_middleware.rs` (redirects unauthenticated `/admin*` page requests to `/login`).
`property` is a bookable thing with a price and slot length. A booking is one `reservation_sum` (date, customer, total price, state `New` → `Approved` / `Canceled`, public `uuid`) with one or more `reservation` rows (property + from/to time). `opening_hour` rows are per weekday, `closing_time` rows are date ranges when booking is closed. Customers are matched by e-mail. The public form is protected by a proof-of-work captcha (`leptos-captcha`).
## Conventions
- Code, comments, identifiers, log messages and commit messages are in English. UI strings are English keys translated at runtime (see `.claude/rules/frontend.md`).
- Commit messages are always written in English, even when the conversation is in Czech: short past-tense sentences ending with a period, e.g. "Added setting for closing days."
- The project has no rustfmt config and the code is not rustfmt-formatted. Don't run `cargo fmt` over existing files; match the surrounding style and keep diffs limited to what you changed.
- Leptos is 0.8 — use the 0.8 API (`use leptos::prelude::*`, `signal()`, `RwSignal::new`, `Resource::new_blocking`, `ServerAction::<T>::new()`), not the 0.6 `create_*` functions.
-`wasm-bindgen` is pinned to an exact version in `Cargo.toml`. The `wasm-bindgen` CLI that cargo-leptos runs must be exactly the same version (`cargo install -f wasm-bindgen-cli --version <same>`), otherwise the front-end build fails with a schema version mismatch. Leptos 0.8 needs at least 0.2.108. Change the pin and the CLI together, never one of them.
-`#![recursion_limit = "512"]` at the top of `src/lib.rs` and `src/main.rs` is required: without it the release build fails with "queries overflow the depth limit".
-`.cargo/config.toml` selects the `getrandom` backend for the WASM target (needed by the captcha's proof of work). `getrandom` and `uuid` have their `js` features enabled in `Cargo.toml` for the same reason; don't remove them.