Developer Guidelines

    This document summarises the current project layout, application structure, and styling conventions used in KelpieBooks. It is intended to help contributors make changes that fit naturally with the existing codebase.

    Project Overview

    KelpieBooks is organised as a Rust workspace with separate backend, frontend, shared domain code, documentation, planning documents, and deployment/configuration assets.

    Top-Level Directories

    backend/

    The backend contains the Rocket-based server application.

    • backend/src/util/: Shared backend utilities, such as logging and common types.
    • backend/migrations/: SQL database migrations.
    • backend/static/: Static files served by the backend if required.
    • backend/src/: Rust code.

    The Rust code is divided into key functional and utility areas

    • core: Components common across the application, such as users and security
    • ledger: General Ledger
    • partners: Customers and vendor
    • payables: Vendor invoice
    • sales: Sales and Sales invoicing
    • util : Utility functions

    Each functional area is divided into

    • db/ : Database access layer. Files are grouped by domain concept, such as accounts, users, organisations, transactions, journal entries, and security.
    • routes/: HTTP route handlers grouped by feature area.
    • services/: Business logic that sits between routes and database access.
    • reports/: Reports

    frontend/

    The frontend is a Yew application compiled to WebAssembly and built with Trunk.

    Key areas:

    • frontend/src/main.rs: Frontend application entry point.
    • frontend/assets/: Static frontend assets, including images and CSS assets.
    • frontend/index.html: HTML entry point used by Trunk.
    • frontend/Trunk.toml: Trunk build configuration.
    • frontend/src: Rust code

    The src is divided into functional area in the same manner as the backend

    Each functional area is divided into

    • pages: UI pages
    • components: UI Components

    shared_core/

    Shared Rust code used across the backend and frontend.

    Typical responsibilities include:

    • Shared models
    • DTOs
    • Request types
    • Utility functions
    • Common business/domain types

    It is organised like the other top level modules into functional areas

    .docs/

    The home of all our documentation source

    This is divided into

    • general: Overall documentation about all aspects of the project
    • user: The end user documentation a.k.a. the user manual

    .planning/

    Project planning and design documentation.

    Current documents include onboarding and ledger planning notes. New design, architecture, feature, and implementation planning documents should be added here.

    templates/

    Configuration or deployment templates.

    logs/

    Runtime logs. These should generally be treated as generated output, not source material.

    Architectural Conventions

    Keep Domain Types Shared

    If a type is exchanged between frontend and backend, place it in shared_core rather than duplicating it.

    Good candidates for shared_core include:

    • Request payloads
    • Response DTOs
    • Domain models
    • Enum values used by both frontend and backend
    • Shared formatting or validation helpers

    Handling Currency values

    • All monetary values must be represented as rust_decimal::Decimal and stored in the database as NUMERIC(15, 4).
    • Never use f32 or f64 for tracking financial values.
    • All mathematical modifications must happen via safe integer calculations to completely eliminate rounding errors.

    Security Model

    Our security model is based on a combination of users, roles, and privileges.

    • Users: Individual accounts that can log in to the system.
    • Roles: A collection of privileges. Each user is assigned a role, which determines what they are allowed to do.
    • Privileges: Specific permissions that grant access to certain actions or data. These are defined as an enum in shared_core/src/models/auth.rs.

    Backend

    In the backend, we use Rocket's request guards to enforce security. These guards are defined in backend/src/security.rs. Each route that requires authentication or specific privileges should use the appropriate guard.

    For example, to protect a route that requires the manage_accounts privilege, you would use the RequirePrivilege<ManageAccounts> guard:

    #[get("/api/accounts")]
    async fn get_accounts(
        mut pool: Connection<DbKelpie>,
        guard: RequirePrivilege<ManageAccounts>,
    ) -> Result<Json<Vec<Account>>, ApiError> {
        // ...
    }

    Frontend

    In the frontend, the UserContext provides a has_privilege method to check if the current user has a specific privilege. This should be used to conditionally render UI elements that correspond to protected actions.

    For example, to only show a "Delete" button to users with the manage_accounts privilege:

    { if user_ctx.has_privilege(&SystemPrivilege::manage_accounts) {
        html! {
            <button class="icon-button btn-action" onclick={on_delete_click}>
                <img src="/images/delete.svg" alt={i18n.t("common-delete")} />
            </button>
        }
    } else {
        html! {}
    }}

    This ensures that the UI accurately reflects the user's permissions and prevents them from attempting actions they are not authorized to perform.

    i18n - Internationalization

    The back end and front end use similar techniques for internationalization.

    Translations and language files are all in the shared_core.

    Frontend

    • Use the LocalContext in each component that renders content
    • At the start of the component get the locale context
    let i18n = use_locale();
    • In the html retrieve the text by key
    i18n.t("common-expand")

    or if it has arguments

    i18n.t_args("vendor-invoice-drawer-inv-number", &fluent_args!["number" => props.invoice.invoice_number.clone()])
    • Format dates and currencies
    i18n.format_date(primary_entry.date)
    i18n.format_currency(total_amount)

    Backend

    • Use the LocalContext where appropriate, e.g. pdf exports
    • At the start of the route create a Context using the data from the AuthenticatedUser
    let i18n = LocaleContext::new(&user.locale);
    • Use the same techniques as above for translation and formatting.

    Keep Backend Layers Separate

    Backend changes should generally follow this flow:

    [HTTP Request] -> [Routes] -> [Services] -> [DB Modules] -> [Database]

    Use each layer for its intended purpose:

    • Routes should handle HTTP concerns:

      • Request parsing
      • Authentication/authorisation checks
      • Response construction
      • Status codes
    • Services should handle business logic:

      • Validation
      • Multi-step operations
      • Coordination between database functions
      • Domain rules
    • DB modules should handle database access:

      • SQL queries
      • Mapping database rows to Rust types
      • Insert/update/delete/select operations

    Avoid placing complex business logic directly inside route handlers or database functions.

    Use transactions where appropriate. Any route that makes multiple transaction entries, that must remain in balance in the accounts, must be placed in a transaction. Most of the database updates are write only so there is generally little need to consider deadlocks in this situation.

    Do not use sqlx macros suxh as sqlx::query_as!. They break CI.

    Keep Frontend Components Focused

    Frontend code is split into:

    • Pages for route-level screens
    • Components for reusable UI pieces
    • Auth logic for authentication helpers
    • Shared styles in kelpie.css

    A page should compose components rather than becoming one large component itself.

    Reusable UI patterns, such as modals, rows, layout wrappers, and navigation elements, should live in frontend/src/components/.

    Frontend Layout Guidelines

    The application uses a dashboard-style layout with:

    • A sidebar for primary navigation
    • A header for top-level page/application controls
    • A main content area for page content

    The general structure is:

    +--------------------------------------------------+
    | Header                                           |
    +----------+---------------------------------------+
    |          |                                       |
    | Sidebar  | Main Content Area                     |
    |          |                                       |
    |          |                                       |
    +----------+---------------------------------------+

    When adding authenticated/dashboard pages, they should fit inside the existing layout rather than creating independent page chrome.

    Authentication pages may use centred form layouts instead of the dashboard layout where appropriate.

    Styling Guidelines

    The frontend uses a central stylesheet. Key colour variables are defined in frontend/assets/scss/base/_variables.scss and should be used to maintain consistency.

    // Example from _variables.scss
    :root {
      --brand-color: #8B0000; /* Dark Red */
      --text-color: #333;
      --background-color: #f4f4f4;
      --border-color: #ddd;
      --success-color: #28a745;
      --error-color: #dc3545;
    }

    Use these variables for consistent colour usage.

    Typography

    Headings use the custom dark red brand colour and modest sizing.

    General conventions:

    • h1 is used for primary page titles.
    • h2 is used for section headings and includes a subtle bottom border.
    • h3 is used for smaller section or panel headings.

    Layout Containers

    Common layout classes include:

    • .page-container: Used for full-height centred page layouts, especially auth-style screens.
    • .content: Flexible vertical content container with scroll support.
    • .scrollable-table: Scrollable area for table-heavy views.
    • .scrollable-list: Fixed-height scrollable list region.

    When building new screens, prefer these existing layout classes before introducing new layout rules.

    Buttons

    Base button styles are defined globally.

    Default buttons use:

    • Light grey background
    • Dark red text
    • Rounded corners
    • Subtle border
    • Hover state
    • Disabled state

    Additional button patterns:

    • .button-secondary for secondary actions
    • .button-group for radio-style grouped choices
    • .icon-button for compact icon actions
    • .key-button for minimal key-style buttons
    • .button-row for horizontally spaced button groups

    Guidelines:

    • Use normal buttons for primary page actions.
    • Use .button-secondary for cancel/back/less important actions.
    • Use .icon-button for row-level table actions.
    • Keep destructive actions behind confirmation modals where appropriate.

    Forms

    Inputs, selects, and textareas share consistent styling:

    • Rounded border
    • Compact padding
    • Focus ring
    • Max width control
    • Validation classes

    Validation classes:

    • .input-success
    • .input-error
    • .error
    • .success-message

    Form layout patterns:

    • .auth-form uses a two-column grid with labels on the left and inputs on the right.
    • .modal-form uses a similar two-column grid inside modals.
    • .transaction-form is used for larger accounting/transaction entry screens.

    Guidelines:

    • Align labels consistently.
    • Use existing error and success message styles.
    • Avoid inline styles for validation states.
    • Prefer semantic form elements.

    Tables

    Tables use the shared .table class.

    Common behaviour:

    • Full-width layout
    • Collapsed borders
    • White background
    • Subtle shadow
    • Compact cell padding
    • Header background
    • Alternating row colour

    Special table conventions:

    • .coa-table is used for chart of accounts tables.
    • .actions-cell is used for action columns.
    • .amount is used for right-aligned numeric values.
    • .transaction-detail-row and .transaction-detail-content are used for expandable transaction details.

    Guidelines:

    • Use .table for tabular data.
    • Right-align monetary and amount fields.
    • Use action columns for edit/delete/view controls.
    • Keep row-level action buttons compact.

    Modals

    Modal styling uses:

    • .modal-overlay
    • .modal-content
    • .modal-form

    Modal conventions:

    • Overlay covers the full viewport.
    • Content is centred.
    • Forms use grid alignment.
    • Actions are placed at the bottom right.

    Use modals for focused interactions such as:

    • Adding records
    • Editing records
    • Confirming destructive actions
    • Confirming journal entry reversals

    Cards and Alerts

    Use .card for boxed content panels.

    Use .alert for prominent warning/error notices.

    Use .success-message for successful operation feedback.

    Use .error for form or request errors.

    Links use the shared link colour and underline on hover.

    Avoid styling links as buttons unless they perform navigation. Use buttons for actions.

    Accounting UI Conventions

    KelpieBooks contains accounting-focused screens, so consistency is especially important for financial data.

    Amounts

    Use .amount for numeric money values.

    Amounts should be:

    • Right-aligned
    • Consistently formatted
    • Clearly separated into debit/credit/balance columns where relevant

    Journal Entries

    Journal entry forms use grid-based rows with consistent columns.

    Existing classes include:

    • .journal-entry-header
    • .journal-entry-row
    • .journal-entry-line
    • .totals
    • .balanced
    • .unbalanced

    Guidelines:

    • Keep debit and credit columns aligned.
    • Clearly indicate whether an entry is balanced.
    • Use success/error colour states for balanced/unbalanced indicators.
    • Avoid hiding imbalance states from the user.

    Chart of Accounts

    Chart of accounts tables use .coa-table with specific column classes:

    • .code-col
    • .name-col
    • .category-col
    • .balance-col
    • .actions-col

    Parent accounts are visually emphasised with .parent-account.

    Use .collapse-toggle for expanding/collapsing account groups.

    Naming Guidelines

    Rust Modules

    Use snake_case for Rust files and modules.

    Examples:

    • backend/src/db/chart_of_accounts.rs
    • shared_core/src/models/transaction_dto.rs

    Frontend Components

    Frontend component files should be named after the component or UI element they contain, using PascalCase. The files themselves should be snake_case.

    Examples:

    • Component: AccountRow -> File: frontend/src/components/account_row.rs
    • Page: DashboardPage -> File: frontend/src/pages/dashboard_page.rs

    CSS Classes

    Prefer descriptive class names over abbreviated names unless the abbreviation is already established, such as coa-table. Use BEM (Block, Element, Modifier) naming conventions for clarity where appropriate.

    Examples:

    • .table (Block)
    • .table__row--highlighted (Block with Modifier)
    • .modal-form__input (Element within a Block)

    Adding New Features

    When adding a new feature:

    1. Define shared request/response/domain types in shared_core if needed.
    2. Add database logic under backend/src/db/.
    3. Add business rules under backend/src/services/.
    4. Add HTTP routes under backend/src/routes/.
    5. Add frontend API calls/auth handling where needed.
    6. Add reusable frontend components under frontend/src/components/.
    7. Add route-level screens under frontend/src/pages/.
    8. Reuse styles from frontend/styles/kelpie.css.
    9. Add or update planning documentation in planning/ for significant features.

    Style Maintenance Guidelines

    When modifying styles:

    • Use SCSS variables for colours, fonts, and spacing from frontend/assets/scss/base/_variables.scss.
    • Reuse existing utility and component classes before creating new ones.
    • Avoid creating new classes for one-off situations. Consider utility classes or inline styles if the style is not reusable.
    • Use the BEM (Block, Element, Modifier) naming convention for new CSS classes to keep styles scoped and understandable. For example: .card__header--large.

    Consistency Checklist

    Before submitting frontend changes, check:

    • Does the page fit into the established layout?
    • Are buttons using existing button styles?
    • Are forms using existing form patterns?
    • Are errors and success messages styled consistently?
    • Are tables using .table?
    • Are amounts right-aligned?
    • Are modals using the standard modal classes?
    • Are colours coming from CSS variables where appropriate?

    Before submitting backend changes, check:

    • Are route handlers thin?
    • Is business logic in services?
    • Is SQL/database logic isolated in db modules?
    • Are shared types placed in shared_core?
    • Are errors handled clearly?
    • Are new routes grouped in the correct route module?

    General Development Principles

    • Keep code organised by responsibility.
    • Prefer small, focused components and functions.
    • Avoid duplicating shared types between frontend and backend.
    • Keep user-facing interactions predictable and consistent.
    • Preserve the accounting-specific clarity of the interface.
    • Update this document when layout or styling conventions change.