Skip to content

Commit

Permalink
wip: basic html done in demo#
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobi-De committed Dec 21, 2023
1 parent a880814 commit d9a2669
Show file tree
Hide file tree
Showing 17 changed files with 306 additions and 73 deletions.
8 changes: 8 additions & 0 deletions demo/demo/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
"django.contrib.staticfiles",
"core",
"products",
"crispy_forms",
"crispy_tailwind",
"template_partials",
"django_htmx",
]

MIDDLEWARE = [
Expand All @@ -48,6 +52,7 @@
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"django_htmx.middleware.HtmxMiddleware",
]

ROOT_URLCONF = "demo.urls"
Expand Down Expand Up @@ -124,5 +129,8 @@
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"


CRISPY_ALLOWED_TEMPLATE_PACKS = "tailwind"
CRISPY_TEMPLATE_PACK = "tailwind"

SUPERUSER_EMAIL = "project@gmail.com"
SUPERUSER_PASSWORD = "password"
18 changes: 1 addition & 17 deletions demo/demo/urls.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
"""
URL configuration for demo project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include
from django.urls import path
Expand All @@ -23,5 +7,5 @@
urlpatterns = [
path("admin/", admin.site.urls),
path("", RedirectView.as_view(url=reverse_lazy("products:product_list"))),
path("products/", include("products.urls")),
path("products/", include("products.urls", namespace="products")),
]
2 changes: 1 addition & 1 deletion demo/products/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
class ProductForm(ModelForm):
class Meta:
model = Product
fields = ("id", "name", "description", "price", "slug", "sku")
fields = ("id", "name", "description", "price", "sku")
28 changes: 28 additions & 0 deletions demo/products/migrations/0002_category.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 5.0 on 2023-12-21 14:36
from django.db import migrations
from django.db import models


class Migration(migrations.Migration):
dependencies = [
("products", "0001_initial"),
]

operations = [
migrations.CreateModel(
name="Category",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=100)),
("slug", models.SlugField(max_length=100, unique=True)),
],
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 4.2.8 on 2023-12-21 19:46
from django.db import migrations
from django.db import models


class Migration(migrations.Migration):
dependencies = [
("products", "0002_category"),
]

operations = [
migrations.RemoveField(
model_name="product",
name="slug",
),
migrations.AlterField(
model_name="product",
name="name",
field=models.CharField(max_length=100, unique=True),
),
]
8 changes: 5 additions & 3 deletions demo/products/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@


class Product(models.Model):
name = models.CharField(max_length=100)
name = models.CharField(max_length=100, unique=True)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
# image = models.ImageField(upload_to="products/", null=True, blank=True)
slug = models.SlugField(max_length=100, unique=True)
sku = models.CharField(max_length=100, unique=True)

@property
def slug(self):
return self.name.lower().replace(" ", "-")


class Category(models.Model):
name = models.CharField(max_length=100)
Expand Down
1 change: 1 addition & 0 deletions demo/products/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from . import views


app_name = "products"

urlpatterns = [
Expand Down
5 changes: 3 additions & 2 deletions demo/products/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@

def product_list(request: HttpRequest):
products = Product.objects.all()
template_name = "products/product_list.html#table" if request.htmx else "products/product_list.html"
return TemplateResponse(
request,
"products/product_list.html",
context={"products": paginate_queryset(request, products)},
template_name,
context={"products": paginate_queryset(request, products, 2)},
)


Expand Down
5 changes: 5 additions & 0 deletions demo/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
django
django-crispy-forms
crispy-tailwind
django-template-partials
django-htmx
27 changes: 15 additions & 12 deletions demo/templates/base.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Website</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<main class="container mx-auto prose">
{% block content %}
{% endblock %}
</main>
</body>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Website</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/htmx.org@1.9.9"></script>
</head>
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
<main class="container mx-auto py-24">
<div class="align-center>">
{% block content %}
{% endblock content %}
</div>
</main>
</body>
</html>
15 changes: 15 additions & 0 deletions demo/templates/products/product_create.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% extends "base.html" %}
{% load crispy_forms_tags %}

{% block content %}
<h1 class="text-lg font-bold py-4">Create Product</h1>

<form method="post">
{% csrf_token %}
{{form|crispy}}

<button type="submit" class="cursor-pointer py-2 px-4 bg-blue-600 hover:bg-blue-700 focus:ring-blue-500 focus:ring-offset-blue-200 text-white text-center text-base font-semibold focus:outline-none focus:ring-2 focus:ring-offset-2 rounded-lg">
Save
</button>
</form>
{% endblock content %}
38 changes: 38 additions & 0 deletions demo/templates/products/product_detail.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{% extends "base.html" %}

{% block content %}

<section class="flex justify-between">
<h1 class="text-lg font-bold">Product - {{ product }}</h1>

<a href="{% url 'products:product_list' %}" class="text-blue-600 hover:underline hover:text-blue-500">
All Products
</a>
</section>


<ul class="py-5">
<li>
<span class="font-bold">Name:</span> {{ product.name }}
</li>
<li>
<span class="font-bold">Description:</span> {{ product.description }}
</li>
<li>
<span class="font-bold">Price:</span> {{ product.price }}
</li>
<li>
<span class="font-bold">Slug:</span> {{ product.slug }}
</li>
<li>
<span class="font-bold">Sku:</span> {{ product.sku }}
</li>
</ul>


<a class="py-2 px-4 bg-blue-600 hover:bg-blue-700 focus:ring-blue-500 focus:ring-offset-blue-200 text-white text-center text-base font-semibold focus:outline-none focus:ring-2 focus:ring-offset-2 rounded-lg"
href="{% url 'products:product_update' product.pk %}">Edit</a>



{% endblock content %}
142 changes: 117 additions & 25 deletions demo/templates/products/product_list.html
Original file line number Diff line number Diff line change
@@ -1,30 +1,122 @@
{% extends 'base.html' %}
{% load partials %}


{% block content %}
<table>
<thead>
<tr>
<th>Product Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{% for product in products %}
<tr>
<td>{{ product.name }}</td>
<td>{{ product.price }}</td>
</tr>
{% endfor %}
</tbody>
</table>

<div class="pagination">
{% if products.has_previous %}
<a href="?page={{ products.previous_page_number }}">Previous</a>
{% endif %}
<span class="current-page">{{ products.number }}</span>
{% if products.has_next %}
<a href="?page={{ products.next_page_number }}">Next</a>
{% endif %}

<section class="flex justify-between">
<h1 class="text-lg font-bold">Products</h1>

<a href="{% url 'products:product_create' %}"
class="cursor-pointer py-2 px-4 bg-blue-600 hover:bg-blue-700 focus:ring-blue-500 focus:ring-offset-blue-200 text-white text-center text-base font-semibold focus:outline-none focus:ring-2 focus:ring-offset-2 rounded-lg">
New Product
</a>
</section>

{% partialdef table inline=True %}

<div id="table">

<div class="flex flex-col">
<div class="overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="py-2 inline-block min-w-full sm:px-6 lg:px-8">
<div class="overflow-x-auto">
<table class="min-w-full">
<thead class="border">
{% with th_class="text-sm font-medium text-gray-900 border-r px-6 py-4 text-left" %}
<tr>
<th scope="col" class="{{ th_class }}">Name</th>
<th scope="col" class="{{ th_class }}">Description</th>
<th scope="col" class="{{ th_class }}">Price</th>
<th scope="col" class="{{ th_class }}">Slug</th>
<th scope="col" class="{{ th_class }}">Sku</th>
<th scope="col" class="{{ th_class }}">Actions</th>
</tr>
{% endwith %}
</thead>
<tbody>
{% with td_class="px-6 py-4 whitespace-nowrap text-sm font-medium border-r text-gray-900" %}
{% for product in products %}
<tr class="border">
<td class="{{ td_class }}">{{product.name}}</td>
<td class="{{ td_class }}">{{product.description}}</td>
<td class="{{ td_class }}">{{product.price}}</td>
<td class="{{ td_class }}">{{product.slug}}</td>
<td class="{{ td_class }}">{{product.sku}}</td>
<td class="{{ td_class }}">
{% with href_class="text-blue-600 hover:underline hover:text-blue-500" %}
<a class="{{ href_class }}}"
href="{% url 'products:product_detail' product.pk %}">View
</a>|
<a class="{{ href_class }}}"
href="{% url 'products:product_update' product.pk %}">Edit
</a>|
<a
class="cursor-pointer {{ href_class }}"
hx-target="closest tr"
hx-swap="outerHTML"
hx-confirm="Are you sure you want to delete {{product}}?"
hx-delete="{% url 'products:product_delete' product.pk %}">
Delete
</a>
</td>
</tr>
{% endwith %}
{% endfor %}
{% endwith %}
</tbody>
</table>
</div>
</div>
</div>
</div>

{% if products.paginator.num_pages > 1 %}
<div class="flex justify-end py-4">
<ul class="flex list-reset border border-grey-light rounded"
hx-target="#table" hx-swap="outerHTML"
>
{% with page_class="cursor-pointer block px-3 py-2 hover:text-white hover:bg-blue-500 border-r border-grey-light" %}

{% if products.has_previous %}
<li>
<a class="{{ page_class }}}" hx-get="?page=1">First</a>
</li>
<li>
<a class="{{ page_class }}}" hx-get="?page={{ products.previous_page_number }}">
Previous
</a>
</li>
{% endif %}

{% for num in products.paginator.page_range %}
{% if products.number == num %}
<li>
<a class="{{ page_class }}}" hx-get="?page={{ num }}">{{ num }}</a>
</li>
{% elif num > products.number|add:'-3' and num < products.number|add:'3' %}
<li>
<a class="{{ page_class }}}" hx-get="?page={{ num }}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}

{% if products.has_next %}
<li>
<a class="{{ page_class }}}" hx-get="?page={{ products.next_page_number }}">
Next
</a>
</li>
<li>
<a class="cursor-pointer block px-3 py-2 hover:text-white hover:bg-blue-500"
hx-get="?page={{ products.num_pages }}">Last
</a>
</li>
{% endif %}
{% endwith %}
</ul>
</div>
{% endif %}
</div>
{% endpartialdef %}
{% endblock %}
Loading

0 comments on commit d9a2669

Please sign in to comment.