From 7f96e859145fc07b86efcd459c135144cf35a158 Mon Sep 17 00:00:00 2001 From: pac7 <47831526-pac7@users.noreply.replit.com> Date: Sun, 21 Sep 2025 17:02:08 +0000 Subject: [PATCH] Add new component system for buttons, cards, and inputs Integrates Django Cotton to replace existing UI components with a new templating system. Adds a test page to compare the new components against the old ones. Replit-Commit-Author: Agent Replit-Commit-Session-Id: dcfff319-6e91-4220-98a9-8295b87755b7 Replit-Commit-Checkpoint-Type: intermediate_checkpoint --- backend/config/django/base.py | 1 + backend/pyproject.toml | 1 + backend/templates/cotton/button.html | 72 ++ backend/templates/cotton/card.html | 50 ++ backend/templates/cotton/input.html | 47 ++ backend/templates/test_button_comparison.html | 654 ++++++++++++++++++ backend/thrillwiki/urls.py | 2 + backend/thrillwiki/views.py | 8 + backend/uv.lock | 16 +- 9 files changed, 850 insertions(+), 1 deletion(-) create mode 100644 backend/templates/cotton/button.html create mode 100644 backend/templates/cotton/card.html create mode 100644 backend/templates/cotton/input.html create mode 100644 backend/templates/test_button_comparison.html diff --git a/backend/config/django/base.py b/backend/config/django/base.py index 957b46cb..f5020ffb 100644 --- a/backend/config/django/base.py +++ b/backend/config/django/base.py @@ -87,6 +87,7 @@ THIRD_PARTY_APPS = [ "whitenoise", "django_tailwind_cli", "autocomplete", # Django HTMX Autocomplete + "django_cotton", # Django Cotton for component templates "health_check", # Health checks "health_check.db", "health_check.cache", diff --git a/backend/pyproject.toml b/backend/pyproject.toml index d8977ac5..d103e556 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -62,6 +62,7 @@ dependencies = [ "djangorestframework-simplejwt>=5.5.1", "django-forwardemail>=1.0.0", "django-cloudflareimages-toolkit>=1.0.6", + "django-cotton>=2.1.3", ] [dependency-groups] diff --git a/backend/templates/cotton/button.html b/backend/templates/cotton/button.html new file mode 100644 index 00000000..05caa1b1 --- /dev/null +++ b/backend/templates/cotton/button.html @@ -0,0 +1,72 @@ +{% comment %} +Button Component - Django Cotton Version of shadcn/ui Button +Usage: Click me +Preserves EXACT same HTML output as the original include version +{% endcomment %} + + + + \ No newline at end of file diff --git a/backend/templates/cotton/card.html b/backend/templates/cotton/card.html new file mode 100644 index 00000000..b50e3ead --- /dev/null +++ b/backend/templates/cotton/card.html @@ -0,0 +1,50 @@ +{% comment %} +Card Component - Django Cotton Version of shadcn/ui Card +Usage: Card content +Preserves EXACT same HTML output as the original include version +{% endcomment %} + + + +
+ {% if title or header_content %} +
+ {% if title %} +

{{ title }}

+ {% endif %} + {% if description %} +

{{ description }}

+ {% endif %} + {% if header_content %} + {{ header_content|safe }} + {% endif %} +
+ {% endif %} + + {% if content or body_content or slot %} +
+ {% if content %} + {{ content|safe }} + {% endif %} + {% if body_content %} + {{ body_content|safe }} + {% endif %} + {% if slot %} + {{ slot }} + {% endif %} +
+ {% endif %} + + {% if footer_content %} +
+ {{ footer_content|safe }} +
+ {% endif %} +
\ No newline at end of file diff --git a/backend/templates/cotton/input.html b/backend/templates/cotton/input.html new file mode 100644 index 00000000..f9e74e57 --- /dev/null +++ b/backend/templates/cotton/input.html @@ -0,0 +1,47 @@ +{% comment %} +Input Component - Django Cotton Version of shadcn/ui Input +Usage: +Preserves EXACT same HTML output as the original include version +{% endcomment %} + + + + \ No newline at end of file diff --git a/backend/templates/test_button_comparison.html b/backend/templates/test_button_comparison.html new file mode 100644 index 00000000..fb686516 --- /dev/null +++ b/backend/templates/test_button_comparison.html @@ -0,0 +1,654 @@ +{% load static %} + + + + + + UI Component Comparison Test + + + + +
+

UI Component Comparison Test

+

Comparing old include method vs new cotton component method for Button, Input, and Card components

+ + +
+

Test 1: All Variants (Default Size)

+ +
Default Variant
+
+
+ {% include 'components/ui/button.html' with variant='default' text='Default Button' %} +
+
+ Default Button +
+
+ +
Destructive Variant
+
+
+ {% include 'components/ui/button.html' with variant='destructive' text='Delete Item' %} +
+
+ Delete Item +
+
+ +
Outline Variant
+
+
+ {% include 'components/ui/button.html' with variant='outline' text='Outline Button' %} +
+
+ Outline Button +
+
+ +
Secondary Variant
+
+
+ {% include 'components/ui/button.html' with variant='secondary' text='Secondary Button' %} +
+
+ Secondary Button +
+
+ +
Ghost Variant
+
+
+ {% include 'components/ui/button.html' with variant='ghost' text='Ghost Button' %} +
+
+ Ghost Button +
+
+ +
Link Variant
+
+
+ {% include 'components/ui/button.html' with variant='link' text='Link Button' %} +
+
+ Link Button +
+
+
+ + +
+

Test 2: All Sizes (Default Variant)

+ +
Default Size
+
+
+ {% include 'components/ui/button.html' with size='default' text='Default Size' %} +
+
+ Default Size +
+
+ +
Small Size
+
+
+ {% include 'components/ui/button.html' with size='sm' text='Small Size' %} +
+
+ Small Size +
+
+ +
Large Size
+
+
+ {% include 'components/ui/button.html' with size='lg' text='Large Size' %} +
+
+ Large Size +
+
+ +
Icon Size
+
+
+ {% include 'components/ui/button.html' with size='icon' text='🔥' %} +
+
+ 🔥 +
+
+
+ + +
+

Test 3: HTMX Attributes

+ +
hx-get Attribute
+
+
+ {% include 'components/ui/button.html' with text='Load Content' hx_get='/api/content/' hx_target='#content' %} +
+
+ Load Content +
+
+ +
hx-post Attribute
+
+
+ {% include 'components/ui/button.html' with text='Submit Form' hx_post='/api/submit/' hx_target='#result' hx_swap='innerHTML' %} +
+
+ Submit Form +
+
+
+ + +
+

Test 4: Icons

+ +
Left Icon
+
+
+ {% include 'components/ui/button.html' with text='Save' icon_left='fas fa-save' %} +
+
+ Save +
+
+ +
Right Icon
+
+
+ {% include 'components/ui/button.html' with text='Next' icon_right='fas fa-arrow-right' %} +
+
+ Next +
+
+
+ + +
+

Test 5: Additional Classes and Attributes

+ +
Custom Classes
+
+
+ {% include 'components/ui/button.html' with text='Custom Button' class='border-2 border-red-500 shadow-lg' %} +
+
+ Custom Button +
+
+ +
Type Attribute
+
+
+ {% include 'components/ui/button.html' with text='Submit' type='submit' %} +
+
+ Submit +
+
+ +
Disabled State
+
+
+ {% include 'components/ui/button.html' with text='Disabled' disabled=True %} +
+
+ Disabled +
+
+
+ + +
+

Test 6: Complex Combinations

+ +
Destructive + Large + Icon + HTMX
+
+
+ {% include 'components/ui/button.html' with variant='destructive' size='lg' text='Delete All' icon_left='fas fa-trash' hx_post='/api/delete-all/' hx_target='#main' class='font-bold' %} +
+
+ Delete All +
+
+ +
Outline + Small + Right Icon + Custom Attributes
+
+
+ {% include 'components/ui/button.html' with variant='outline' size='sm' text='Export' icon_right='fas fa-download' attrs='data-testid="export-btn" title="Export data"' %} +
+
+ Export +
+
+
+ + +
+

Test 7: Legacy Underscore Props vs Modern Hyphenated Attributes

+

This test verifies backward compatibility - cotton components should accept both legacy underscore props (hx_get, x_data) and modern hyphenated attributes (hx-get, x-data).

+ +
Legacy hx_get vs Modern hx-get
+
+
+ {% include 'components/ui/button.html' with text='Load Content' hx_get='/api/content/' hx_target='#content' %} +
+
+ Load Content +
+
+ Load Content +
+
+ +
Legacy hx_post vs Modern hx-post
+
+
+ {% include 'components/ui/button.html' with text='Submit Form' hx_post='/api/submit/' hx_target='#result' hx_swap='innerHTML' %} +
+
+ Submit Form +
+
+ Submit Form +
+
+ +
Legacy x_data vs Modern x-data
+
+
+ {% include 'components/ui/button.html' with text='Alpine Button' x_data='{ clicked: false }' %} +
+
+ Alpine Button +
+
+ Alpine Button +
+
+ +
Legacy onclick vs Modern onclick
+
+
+ {% include 'components/ui/button.html' with text='Click Handler' onclick="alert('Clicked!')" %} +
+
+ Click Handler +
+
+ +
Legacy type vs Modern type
+
+
+ {% include 'components/ui/button.html' with text='Submit Form' type='submit' %} +
+
+ Submit Form +
+
+ +
Legacy disabled vs Modern disabled
+
+
+ {% include 'components/ui/button.html' with text='Disabled Button' disabled=True %} +
+
+ Disabled Button +
+
+ +
Complex Legacy Props Combination
+
+
+ {% include 'components/ui/button.html' with variant='destructive' size='lg' text='Legacy Complex' hx_post='/api/complex/' hx_target='#result' x_data='{ processing: false }' type='submit' %} +
+
+ Legacy Complex +
+
+ Modern Complex +
+
+ +
Alpine.js x_on Special Case
+
+
+ {% include 'components/ui/button.html' with text='Alpine Click' x_on='@click="clicked = true"' %} +
+
+ Alpine Click +
+
+ Alpine Click +
+
+
+ + +
+

Test 8: Input Component Comparison

+ +
Basic Text Input
+
+
+ {% include 'components/ui/input.html' with type='text' placeholder='Enter your name' name='name' %} +
+
+ +
+
+ +
Email Input with Value
+
+
+ {% include 'components/ui/input.html' with type='email' placeholder='email@example.com' name='email' value='test@example.com' %} +
+
+ +
+
+ +
Password Input
+
+
+ {% include 'components/ui/input.html' with type='password' placeholder='Password' name='password' required=True %} +
+
+ +
+
+ +
Number Input with Min/Max
+
+
+ {% include 'components/ui/input.html' with type='number' placeholder='Age' name='age' attrs='min="18" max="100"' %} +
+
+ +
+
+ +
Disabled Input
+
+
+ {% include 'components/ui/input.html' with type='text' placeholder='Disabled input' name='disabled' disabled=True value='Cannot edit' %} +
+
+ +
+
+ +
Readonly Input
+
+
+ {% include 'components/ui/input.html' with type='text' name='readonly' readonly=True value='Read only value' %} +
+
+ +
+
+ +
Input with Custom Class
+
+
+ {% include 'components/ui/input.html' with type='text' placeholder='Custom styled' name='custom' class='border-blue-500 bg-blue-50' %} +
+
+ +
+
+ +
Input with HTMX Attributes
+
+
+ {% include 'components/ui/input.html' with type='text' placeholder='Search...' name='search' hx_get='/api/search/' hx_target='#results' hx_trigger='keyup changed delay:300ms' %} +
+
+ +
+
+ +
Input with Alpine.js
+
+
+ {% include 'components/ui/input.html' with type='text' placeholder='Alpine input' name='alpine' x_model='inputValue' %} +
+
+ +
+
+
+ + +
+

Test 8: Card Component Comparison

+ +
Basic Card with Title
+
+
+ {% include 'components/ui/card.html' with title='Basic Card' content='This is a simple card with just a title and content.' %} +
+
+ This is a simple card with just a title and content. +
+
+ +
Card with Title and Description
+
+
+ {% include 'components/ui/card.html' with title='Card with Description' description='This card has both a title and a description' content='Here is the main content of the card.' %} +
+
+ Here is the main content of the card. +
+
+ +
Card with Custom Header Content
+
+
+ {% include 'components/ui/card.html' with header_content='

Custom Header

New
' content='This card has custom header content instead of a simple title.' %} +
+
+ This card has custom header content instead of a simple title. +
+
+ +
Card with Body Content (instead of slot)
+
+
+ {% include 'components/ui/card.html' with title='Body Content Card' body_content='

This content is passed via body_content parameter.

' %} +
+
+ +
+
+ +
Card with Footer
+
+
+ {% include 'components/ui/card.html' with title='Card with Footer' content='This card has footer content at the bottom.' footer_content='' %} +
+
+ This card has footer content at the bottom. +
+
+ +
Complete Card (All Sections)
+
+
+ {% include 'components/ui/card.html' with title='Complete Card' description='This card has all possible sections' content='Main content goes here. This is the primary content area.' footer_content='Last updated: Today' %} +
+
+ Main content goes here. This is the primary content area. +
+
+ +
Card with Custom Classes
+
+
+ {% include 'components/ui/card.html' with title='Custom Styled Card' content='This card has custom styling applied.' class='border-2 border-green-200 bg-green-50 shadow-lg' %} +
+
+ This card has custom styling applied. +
+
+ +
Content-Only Card (No Header)
+
+
+ {% include 'components/ui/card.html' with content='This card has only content, no header or footer sections.' %} +
+
+ This card has only content, no header or footer sections. +
+
+
+ + +
+

HTML Output Inspection

+

Use browser dev tools to inspect the HTML output and ensure it's identical.

+ +
+
+

Include Version HTML:

+
+ +
+
+
+

Cotton Version HTML:

+
+ +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/backend/thrillwiki/urls.py b/backend/thrillwiki/urls.py index 2a2946e7..b41e6b4b 100644 --- a/backend/thrillwiki/urls.py +++ b/backend/thrillwiki/urls.py @@ -101,6 +101,8 @@ urlpatterns = [ views.environment_and_settings_view, name="environment_and_settings", ), + # Button component testing + path("test-button/", views.test_button_comparison, name="test_button_comparison"), ] # Add autocomplete URLs if available diff --git a/backend/thrillwiki/views.py b/backend/thrillwiki/views.py index 2ca6119e..c8752c13 100644 --- a/backend/thrillwiki/views.py +++ b/backend/thrillwiki/views.py @@ -156,3 +156,11 @@ def environment_and_settings_view(request): "environment_and_settings.html", {"env_vars": env_vars, "settings_vars": settings_vars}, ) + + +def test_button_comparison(request): + """ + Test view to compare cotton button component with original include version. + Renders a comprehensive test page with all button variants and combinations. + """ + return render(request, "test_button_comparison.html") diff --git a/backend/uv.lock b/backend/uv.lock index 60fa43b9..2c29131e 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.13" [[package]] @@ -642,6 +642,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/30/d8/19ed1e47badf477d17fb177c1c19b5a21da0fd2d9f093f23be3fb86c5fab/django_cors_headers-4.9.0-py3-none-any.whl", hash = "sha256:15c7f20727f90044dcee2216a9fd7303741a864865f0c3657e28b7056f61b449", size = 12809, upload-time = "2025-09-18T10:40:50.843Z" }, ] +[[package]] +name = "django-cotton" +version = "2.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c3/99/36e318ebd1ace3fc874541a02e7e4149def8e21aab85aceb7bb01e17607b/django_cotton-2.1.3.tar.gz", hash = "sha256:737f9c088549d7febbf78532856ddf1270799675a4bc9fa191a5db0e195a9c13", size = 23432, upload-time = "2025-06-30T17:31:29.29Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/ec/5e5318af0304962be43e3b912aef024d8ac08c0f9a9dfcc4f0cd55d0e74e/django_cotton-2.1.3-py3-none-any.whl", hash = "sha256:f33658d05a8f5ecf7448bdf1089e2ad27d2ce42e59c752216129701d7d153c89", size = 22214, upload-time = "2025-06-30T17:31:28.093Z" }, +] + [[package]] name = "django-debug-toolbar" version = "6.0.0" @@ -2238,6 +2250,7 @@ dependencies = [ { name = "django-cleanup" }, { name = "django-cloudflareimages-toolkit" }, { name = "django-cors-headers" }, + { name = "django-cotton" }, { name = "django-debug-toolbar" }, { name = "django-environ" }, { name = "django-extensions" }, @@ -2309,6 +2322,7 @@ requires-dist = [ { name = "django-cleanup", specifier = ">=8.0.0" }, { name = "django-cloudflareimages-toolkit", specifier = ">=1.0.6" }, { name = "django-cors-headers", specifier = ">=4.3.1" }, + { name = "django-cotton", specifier = ">=2.1.3" }, { name = "django-debug-toolbar", specifier = ">=4.0.0" }, { name = "django-environ", specifier = ">=0.12.0" }, { name = "django-extensions", specifier = ">=4.1" },