Metadata-Version: 2.4
Name: django-cotton-status
Version: 0.2.0
Summary: A django-tables2 Column for status/tag fields with a Bootstrap popover header and colored badge cells. Cotton-friendly.
Author-email: Franco Duran <francoduran232@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/FrancoDuran23/django-cotton-status
Project-URL: Repository, https://github.com/FrancoDuran23/django-cotton-status
Project-URL: Issues, https://github.com/FrancoDuran23/django-cotton-status/issues
Project-URL: Changelog, https://github.com/FrancoDuran23/django-cotton-status/blob/main/CHANGELOG.md
Keywords: django,cotton,tables2,popover,status,badge,tags
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Framework :: Django :: 5.2
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Django>=4.2
Requires-Dist: django-cotton>=0.9
Requires-Dist: django-tables2>=2.4
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-django>=4.5; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# django-cotton-status

A [django-tables2](https://django-tables2.readthedocs.io/) `Column` that renders a **status / tag field** as a Bootstrap popover header (documenting each possible value) plus a colored badge in each cell — all from a single declaration.

Designed for projects using [Django Cotton](https://django-cotton.com/) and Bootstrap 5.

## What it gives you

```python
from django_cotton_status import StatusColumn

class OrderTable(tables.Table):
    status = StatusColumn(
        label='Status',
        items=[
            {'value': 'PENDING',  'label': 'Pending',  'badge': 'warning', 'description': 'Awaiting confirmation.'},
            {'value': 'SHIPPED',  'label': 'Shipped',  'badge': 'info',    'description': 'En route to customer.'},
            {'value': 'DELIVERED','label': 'Delivered','badge': 'success', 'description': 'Customer received.'},
            {'value': 'CANCELED', 'label': 'Canceled', 'badge': 'danger',  'description': 'Order canceled.'},
        ],
    )
```

That single line gives you:

- A column **header** with an info icon. Hovering or focusing the icon shows a popover listing every possible status with its colored badge and description.
- **Cells** rendered as `<span class="badge rounded-pill bg-{color}">{label}</span>` based on the row's value.

No more duplicated `ESTADO_BADGE = {...}` dicts and `render_status` methods scattered across your codebase. **One list of items defines everything**.

## Requirements

- Python 3.10+
- Django 4.2+
- [django-cotton](https://django-cotton.com/) 0.9+
- [django-tables2](https://django-tables2.readthedocs.io/) 2.4+
- Bootstrap 5 on the client side (loaded by your project)

## Installation

```bash
pip install django-cotton-status
```

Add to `INSTALLED_APPS`:

```python
INSTALLED_APPS = [
    # ...
    'django_cotton',
    'django_tables2',
    'django_cotton_status',
]
```

## Bootstrap popover initialization

Bootstrap 5 does **not** auto-initialize popovers — you need to call `new bootstrap.Popover(el)` per trigger. If your project uses HTMX, you also need to clean up and re-init on swap, otherwise orphaned popovers stay floating and freshly-swapped triggers stay dead.

Drop this once in your base template:

```html
<script>
    function cleanupPopovers() {
        document.querySelectorAll('.popover').forEach(el => el.remove());
    }

    function initPopovers() {
        cleanupPopovers();
        document.querySelectorAll('[data-bs-toggle="popover"]').forEach(el => {
            if (el._popover) {
                el._popover.dispose();
                el._popover = null;
            }
            el._popover = new bootstrap.Popover(el);
        });
    }

    document.addEventListener('DOMContentLoaded', initPopovers);
    document.body.addEventListener('htmx:beforeSwap', cleanupPopovers);
    document.body.addEventListener('htmx:afterSettle', initPopovers);
    window.addEventListener('beforeunload', cleanupPopovers);
</script>
```

`htmx:afterSettle` fires after HTMX swap animations finish, so new triggers are stable when initialized.

## Usage

### Main use case — `StatusColumn`

```python
import django_tables2 as tables
from django_cotton_status import StatusColumn

ORDER_STATUSES = [
    {'value': 'PENDING',  'label': 'Pending',  'badge': 'warning', 'description': 'Awaiting confirmation.'},
    {'value': 'SHIPPED',  'label': 'Shipped',  'badge': 'info',    'description': 'En route to customer.'},
    {'value': 'DELIVERED','label': 'Delivered','badge': 'success', 'description': 'Customer received.'},
    {'value': 'CANCELED', 'label': 'Canceled', 'badge': 'danger',  'description': 'Order canceled.'},
]

class OrderTable(tables.Table):
    status = StatusColumn(label='Status', items=ORDER_STATUSES)

    class Meta:
        model = Order
        fields = ('status',)
```

If a row's value isn't in `items`, the cell falls back to the raw value (no badge).

### Cotton component (outside tables)

For places that aren't a `Column` — detail page headers, cards, anywhere — use the cotton component:

```html
<c-status-popover label="Status" :items="order_statuses" />
```

Same look, same data shape (without `value`, since there's nothing to match against).

### Python helper (advanced)

If you build your own `verbose_name` and need just the popover string:

```python
from django_cotton_status import info_popover

label_html = info_popover(
    'Status',
    items=[
        {'badge': 'success', 'label': 'Approved',  'description': '...'},
        {'badge': 'danger',  'label': 'Rejected',  'description': '...'},
    ],
)
# label_html is a Django SafeString — safe to inject into HTML attributes.
```

## Item shape

```python
{
    'value': 'APPROVED',                # required for StatusColumn cell rendering
    'label': 'Approved',                # visible chip text
    'badge': 'success',                 # Bootstrap color name
    'description': 'Approved order.',   # popover body text
}
```

For the cotton component and `info_popover()`, the `value` field is optional (only used by `StatusColumn` to match the cell value).

Bootstrap colors supported: `primary`, `secondary`, `success`, `danger`, `warning`, `info`, `light`, `dark`.

## Customization

### Placement

```python
status = StatusColumn(label='Status', items=..., placement='right')
```

Accepts any value Bootstrap supports: `top`, `bottom`, `left`, `right`, `auto`.

### Icon

By default the trigger uses `fas fa-circle-info` (Font Awesome). Override:

```python
# Bootstrap Icons
status = StatusColumn(label='Status', items=..., icon_class='bi bi-info-circle')
```

## Status

Alpha. API may change before 1.0.

## License

MIT — see [LICENSE](LICENSE).
