4.7 KiB
4.7 KiB
paths
| paths | |||||||
|---|---|---|---|---|---|---|---|
|
Frontend rules
Components
- Leptos 0.8 API only (
use leptos::prelude::*). Components are#[component]functions returningimpl IntoView, one page or dialog per file insrc/pages/; reusable building blocks go tosrc/components/. Register new files in the respectivemod.rs(pub modonly if used fromapp.rs). - This code runs on the server (SSR) and in the browser (hydration). Browser-only APIs (
window(),web_sys) must not run during SSR — keep them in event handlers/effects or behind#[cfg(not(feature = "ssr"))]. Markup must render the same on both sides or hydration breaks. - A new admin page needs a
<Route path="admin/..."/>insrc/app.rswrapped in<AdminPortal>, and a menu entry insrc/components/admin_portal.rs. Paths starting with/adminare login-protected by the middleware; data access is still guarded byperm_check!in the server fn.
Leptos 0.8 pitfalls
- Views are statically typed:
if/elseandmatcharms that return different markup must each end with.into_any(). - Context is not available in event handlers (
on:clicketc.). Calluse_contextin the component body and keep the result —DialogOpenerandMenuOpenerdo this innew(), so create them in a component body, not inside a handler. - Markup must be valid HTML, or hydration panics in the browser and the whole page stops reacting: no block elements (
<div>,<table>, another<p>) inside<p>, and<tr>only inside<tbody>/<thead>. The compiler doesn't catch this — load the page with a full reload and check the browser console. - HTML attributes are typed. Deprecated ones (
align,widthon<table>) don't compile; usestyle. - A value used twice in one
view!(e.g. in an attribute and as a child) must be cloned for the first use. - Client-side form validation hooks into
<ActionForm>withon:submit:captureandev.prevent_default(); a plainon:submitruns too late.DataFormalready does this. action.value()is not anRwSignal; pass it on asSignal::derive(move || action.value().get()).
Data loading and forms
- Load data with
Resource::new_blocking/Resource::newcalling the server fn, rendered inside<Transition fallback=...>with{trl("Loading...")}. Read the resource in a closure —{move || res.get().map(...)}— a plain{res.get()...}block is evaluated once and never updates. Use a dialog'sopener.visible()as the resource source so lists reload after the dialog closes (seesrc/pages/users.rs). - Handle all three outcomes of a server call:
Err(e),Ok(ApiResponse::Error(msg)),Ok(ApiResponse::Data(d)). - Edit/create dialogs:
DialogOpener::new()in the parent,ServerAction::<ServerFnStruct>::new()in the dialog, wrapped in<DataForm opener=.. action=.. title="..">. It supplies the modal, Save/Close buttons, client validation and server error display. Confirmations use<QuestionDialog>. - Form field names follow the server fn argument:
name="company[name]". Pass the id as a hidden input. Fill inputs withprop:value={move || signal.get().field}. DataForm/ModalDialogtaketitleas an untranslated English key (&'static str); they translate it themselves.
Translations
- Every user-visible string goes through
trl("English text"); never hard-code Czech (or any text) in a view. The English text is the key and the fallback. - When you add or change a key, add the Czech translation to the
csmap insrc/locales/catalogues.rs. This includes validationmessages andApiResponse::Errorstrings from the backend. trl()returns a closure — use it directly as{trl("...")}or attribute value; call it (trl("...")()) when aStringis needed.- Dates shown to users go through
loc_date(), weekday names throughshow_day().
Styling
- UI is the Bootstrap-based admin template in
assets/vendor/plus Boxicons (<i class="bx bx-...">). Reuse its classes (card,table,btn btn-primary,form-control,row/col) before writing CSS. - Custom styles go to a CSS file in
assets/css/and must be linked with<Link rel="stylesheet">insrc/components/header.rs(order matters: after the vendor files to override them).style/main.scssis not linked anywhere and has no effect. Never editassets/vendor/(the one app-owned file there isassets/vendor/css/control.css). - The public page banner style is
assets/data/banner.css, rewritten at runtime by the appearance upload (/*bg-img*/marker) — keep that marker / thebackground-image: url('...')form intact. - Stylesheets and meta tags are declared in
src/components/header.rs.