Metadata-Version: 2.4
Name: django-kpforms
Version: 0.1.0
Summary: Reusable Django app for dynamic form templates and submissions
Project-URL: Homepage, https://github.com/kodexprojects/django-kpforms
Project-URL: Bug Tracker, https://github.com/kodexprojects/django-kpforms/issues
License-Expression: MIT
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.14
Requires-Dist: django>=5.0
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: pytest-django>=4.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# django-kpforms

A reusable Django application for **dynamic form templates and submissions**, powered by Lit-based web components.

- Define form templates with sections, groups, and typed fields (numeric, string, text, radio, checkbox, datetime, calculated, ranged-radio)
- Built-in tolerance checking for numeric and string fields
- Interactive web component (`<dynamic-form>`) for end-user submission
- Visual form-builder web component (`<dynamic-form-builder>`) for creating and editing templates
- Printable-form component for generating PDFs via `html2pdf.js`
- Full version history for form templates

---

## Table of Contents

1. [Requirements](#requirements)
2. [Installation](#installation)
3. [Quick Start](#quick-start)
4. [Django Models & Helpers](#django-models--helpers)
5. [Web Components](#web-components)
   - [`<dynamic-form>`](#dynamic-form)
   - [`<dynamic-form-builder>`](#dynamic-form-builder)
   - [`<printable-form>`](#printable-form)
6. [CSS Customisation Variables](#css-customisation-variables)
   - [Typography](#typography)
   - [Colours](#colours)
   - [Accent Colour](#accent-colour)
   - [Status / Tolerance Colours](#status--tolerance-colours)
   - [Layout](#layout)
7. [Contributing / Building from Source](#contributing--building-from-source)

---

## Requirements

- Python ≥ 3.14
- Django ≥ 5.0
- Pydantic ≥ 2.0

---

## Installation

```bash
pip install django-kpforms
```

Or with [uv](https://docs.astral.sh/uv/):

```bash
uv add django-kpforms
```

---

## Quick Start

### 1. Add to `INSTALLED_APPS`

```python
# settings.py
INSTALLED_APPS = [
    ...
    "kpforms",
]
```

### 2. Run migrations

```bash
python manage.py migrate
```

### 3. Serve static files

`django-kpforms` ships with a compiled JavaScript bundle (`kpforms.js`) and
KaTeX font files under `kpforms/static/`.

Make sure Django's static-file serving is configured and `collectstatic` has
been run in production:

```bash
python manage.py collectstatic
```

### 4. Load the bundle in your template

```html
{% load static %}

<!-- Load the kpforms web-component bundle -->
<script type="module" src="{% static 'kpforms.js' %}"></script>

<!-- Optional: load html2pdf for the printable-form component -->
<script src="{% static 'html2pdf.bundle.min.js' %}"></script>
```

---

## Django Models & Helpers

### Models

| Model | Description |
|---|---|
| `FormTemplate` | A versioned form definition. The `schema` field holds a `FormTemplateSchema` JSON object. |
| `FormTemplateVersionHistory` | Immutable snapshot of a `FormTemplate` at each version. |
| `FormSubmission` | A single submission against a `FormTemplate`. Validates and stores tolerance results. |

### Helper functions (`kpforms.helpers`)

```python
from kpforms.helpers import (
    create_form_template,
    update_form_template,
    submit_form_data,
    get_form_by_template_id_and_version,
    get_form_template_list,
    get_form_template_list_paginated,
    get_all_results_for_form_template,
)
```

| Function | Description |
|---|---|
| `create_form_template(schema, user)` | Creates a new `FormTemplate` from a `CreateFormTemplateSchema`. |
| `update_form_template(schema, user)` | Saves a new version of an existing template. |
| `submit_form_data(template_id, form_data, user)` | Validates and saves a `FormSubmission`. |
| `get_form_by_template_id_and_version(id, version=None)` | Returns the `FormTemplateSchema` for a given ID and optional version. |
| `get_form_template_list(only_active=True)` | Returns a list of `{id, name, description}` dicts. |
| `get_form_template_list_paginated(page_size, only_active)` | Returns a Django `Paginator`. |
| `get_all_results_for_form_template(template_id)` | Returns all submissions with labelled field data. |

### Schema classes (`kpforms.form_schema`)

The core schema models are Pydantic models. The most important ones:

| Class | Description |
|---|---|
| `FormTemplateSchema` | Full form definition: `title`, `instruction`, `sections`. |
| `CreateFormTemplateSchema` | Input payload for creating a template. |
| `UpdateFormTemplateSchema` | Input payload for updating a template (adds `guid`). |
| `FormSubmissionSchema` | Submission payload: `data` (list of `{fieldId, value}`), `fieldsOutOfTolerance`. |

---

## Web Components

Include `kpforms.js` once on the page (see Quick Start above) — all components
are self-registering and require no additional setup.

### `<dynamic-form>`

Renders a form for end-user completion and submission.

```html
<dynamic-form id="my-form"></dynamic-form>

<script type="module">
  const formEl = document.getElementById("my-form");

  // Pass the form schema (FormSchema JSON) as a property
  formEl.schema = {
    guid: "...",
    title: "My Form",
    sections: [...],
    generatePdfOption: false,
    generatePdfString: "Generate PDF",
    submitFormString: "Submit",
    submittingString: "Submitting…",
    apiConfig: {
      url: "/api/forms/submit/",
      method: "POST",
      successMessage: "Form submitted!",
      errorMessage: "Submission failed."
    }
  };
</script>
```

**Properties / attributes:**

| Property | Type | Description |
|---|---|---|
| `schema` | `FormSchema` (object) | The full form schema to render. |
| `lang` | `string` | BCP-47 language tag for UI strings (e.g. `"en"`, `"sv"`). Defaults to `"en"`. |

### `<dynamic-form-builder>`

An interactive editor that lets users create and edit form templates.

```html
<dynamic-form-builder id="builder"></dynamic-form-builder>

<script type="module">
  const builder = document.getElementById("builder");

  // Optional: load an existing template for editing
  builder.schema = { guid: "...", name: "...", formSchema: { ... } };

  // Listen for save events
  builder.addEventListener("form-save", (e) => {
    const payload = e.detail; // CreateFormTemplateSchema | UpdateFormTemplateSchema
    fetch("/api/forms/templates/", {
      method: payload.guid ? "PUT" : "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    });
  });
</script>
```

**Properties / attributes:**

| Property | Type | Description |
|---|---|---|
| `schema` | `UpdateFormTemplateSchema` (object) | Optional existing template to edit. |
| `lang` | `string` | BCP-47 language tag. |

**Events emitted:**

| Event | `detail` | Description |
|---|---|---|
| `form-save` | `CreateFormTemplateSchema \| UpdateFormTemplateSchema` | Fired when the user clicks Save. |

### `<printable-form>`

Renders a read-only printable view of a submitted form that can be exported as a PDF using `html2pdf.js`.

```html
<printable-form id="print-form"></printable-form>

<script type="module">
  const el = document.getElementById("print-form");
  el.schema   = { /* FormTemplateSchema */ };
  el.submission = { /* FormSubmission data */ };
</script>
```

**Properties:**

| Property | Type | Description |
|---|---|---|
| `schema` | `FormTemplateSchema` (object) | The form template. |
| `submission` | `FormSubmissionSchema` (object) | The submission data to display. |

---

## CSS Customisation Variables

All web components use CSS custom properties (variables) to control their appearance.
Set these on the element itself or any ancestor to theme the components.

The **public API** variables are prefixed `--df-*`. Internal variables map them
to `--form-*` and to [Shoelace](https://shoelace.style/) design tokens.

### Typography

| Variable | Default | Description |
|---|---|---|
| `--df-font-size-base` | `1rem` | Base font size. |
| `--df-font-size-h1` | `1.8rem` | Form title size. |
| `--df-font-size-h2` | `1.4rem` | Section heading size. |
| `--df-font-size-label` | `0.9rem` | Field label size. |
| `--df-font-size-description` | `0.85rem` | Field description / helper text size. |

### Colours

| Variable | Default | Description |
|---|---|---|
| `--df-color-text` | `#333` | Primary text colour. |
| `--df-color-input-text` | `#111` | Text colour inside input fields. |
| `--df-color-text-secondary` | `#555` | Secondary / description text colour. |
| `--df-color-text-label` | `#111` | Field label text colour. |
| `--df-color-background` | `#ffffff` | Component background. |
| `--df-color-background-input-base` | `#ffffff` | Input field background (resting). |
| `--df-color-background-input-focus` | `#ffffff` | Input field background on focus. |
| `--df-color-background-section` | `#f9f9f9` | Form section background. |
| `--df-color-border` | `#ccc` | Default border colour. |

### Accent Colour

Used for buttons, active tabs, focused borders, and Shoelace's primary palette.

| Variable | Default | Description |
|---|---|---|
| `--df-color-accent` | `#007bff` | Primary accent / brand colour. |
| `--df-color-accent-lighter` | `#66b3ff` | Lighter variant (e.g. hover states). |
| `--df-color-accent-darker` | `#0056b3` | Darker variant (e.g. active states). |
| `--df-color-accent-text` | `#ffffff` | Text on accent-coloured elements. |
| `--df-color-success` | `#28a745` | Success colour (Shoelace success palette). |
| `--df-color-success-darker` | `#1e7e34` | Darker success variant. |
| `--df-color-success-text` | `#ffffff` | Text on success-coloured elements. |
| `--df-color-warning` | `#fbb34e` | Warning colour. |
| `--df-color-warning-darker` | `#fea429` | Darker warning variant. |
| `--df-color-warning-text` | `#ffffff` | Text on warning-coloured elements. |
| `--df-color-neutral` | `#6c757d` | Neutral colour. |
| `--df-color-neutral-darker` | `#5a6268` | Darker neutral variant. |
| `--df-color-neutral-text` | `#ffffff` | Text on neutral-coloured elements. |
| `--df-color-error` | `#dc3545` | Error / danger colour. |
| `--df-color-error-lighter` | `#fa5969` | Lighter error variant. |
| `--df-color-error-text` | `#721c24` | Text on error-coloured elements. |

### Status / Tolerance Colours

These control how fields are highlighted when their values are within or outside
the defined tolerance range.

| Variable | Default | Description |
|---|---|---|
| `--df-color-required-star` | `#f98989` | Colour of the asterisk on required fields. |
| `--df-color-within-tolerance-border` | `#8fff8f` | Border colour when field value is within tolerance. |
| `--df-color-within-tolerance-background` | `#b9fb9f` | Background when field value is within tolerance. |
| `--df-color-within-tolerance-input-text` | `#214521` | Text colour in input when within tolerance. |
| `--df-color-within-tolerance-text` | `#3c763d` | Label/message text when within tolerance. |
| `--df-color-out-of-tolerance-border` | `#fdf8b2` | Border colour when field value is out of tolerance. |
| `--df-color-out-of-tolerance-text` | `#d9534f` | Text/label colour when out of tolerance. |
| `--df-color-has-error-border` | *(inherits error colour)* | Border colour when field has a validation error. |
| `--df-color-has-error-background` | `#d58d8b` | Background when field has a validation error. |

### Layout

| Variable | Default | Description |
|---|---|---|
| `--df-border-radius` | `4px` | Corner radius for inputs, sections, and the component itself. |
| `--df-spacing-medium` | `1rem` | Medium spacing unit used for padding/gaps. |
| `--df-spacing-large` | `1.5rem` | Large spacing unit used for section padding. |
| `--df-button-padding` | `0.3rem 1rem` | Padding applied to Shoelace `<sl-button>` elements. |

### Example: Dark Theme

```css
dynamic-form {
  --df-color-background:            #1e1e2e;
  --df-color-background-section:    #2a2a3e;
  --df-color-background-input-base: #2d2d42;
  --df-color-text:                  #cdd6f4;
  --df-color-text-label:            #cdd6f4;
  --df-color-input-text:            #cdd6f4;
  --df-color-border:                #45475a;
  --df-color-accent:                #89b4fa;
  --df-color-accent-darker:         #74a0e8;
  --df-color-accent-text:           #1e1e2e;
}
```

---

## Contributing / Building from Source

### Prerequisites

- Python ≥ 3.14 (with [uv](https://docs.astral.sh/uv/))
- Node.js ≥ 20 with npm

### Build

Use the included `build.sh` to produce a distributable wheel and sdist:

```bash
# Build both sdist and wheel (default)
./build.sh

# Build wheel only
./build.sh --wheel

# Build sdist only
./build.sh --sdist
```

The script will:

1. Run `npm run build` inside `kpforms/kpforms/` to compile the TypeScript
   web components and copy the output into `kpforms/static/`.
2. Run `python manage.py makemigrations kpforms` to ensure migrations are
   up-to-date.
3. Run `uv build` to produce the final packages in `dist/`.

### Running Tests

```bash
uv run pytest
```

---

## Licence

MIT — see [LICENSE](../LICENSE) for details.

