Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion BookStation/BookStation/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.humanize',
'django.contrib.staticfiles',
'widget_tweaks',
'django.contrib.humanize',
'accounts',
'books',
'orders',
Expand Down
3 changes: 2 additions & 1 deletion BookStation/books/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ class Meta:
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
fields = ['name', 'description']
fields = ['name', 'description']

17 changes: 17 additions & 0 deletions BookStation/books/migrations/0002_alter_author_birth_date.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.2.1 on 2025-06-10 17:00

from django.db import migrations, models


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

operations = [
migrations.AlterField(
model_name="author",
name="birth_date",
field=models.DateField(blank=True, null=True),
),
]
2 changes: 1 addition & 1 deletion BookStation/books/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def __str__(self):

class Book(models.Model):
title = models.CharField(max_length=200)
price = models.DecimalField(max_digits=6, decimal_places=2)
price = models.DecimalField(max_digits=8, decimal_places=4)
description = models.TextField(blank=True)
stock = models.PositiveIntegerField(default=0) #tồn kho
cover_image = models.ImageField(upload_to='book_covers/', blank=True, null=True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
<li class="breadcrumb-item active">Xóa tác giả</li>
</ol>
</nav>

<div class="row justify-content-center">
<div class="col-md-6">
<div class="card shadow">
Expand Down
3 changes: 3 additions & 0 deletions BookStation/orders/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.db import models
from django.conf import settings
from books.models import Book
from decimal import Decimal

class Order(models.Model):
STATUS_CHOICES = [
Expand Down Expand Up @@ -41,4 +42,6 @@ def __str__(self):
return f"{self.quantity} x {self.book.title}"

def subtotal(self):
if self.price is None:
return Decimal('0.00') # Trả về 0 nếu price là None
return self.quantity * self.price
26 changes: 26 additions & 0 deletions BookStation/staff/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,31 @@
from accounts.models import Users
from django import forms
from books.models import Book
from django import forms
from orders.forms import OrderForm
from orders.models import Order
from orders.forms import OrderItemFormSet as BaseOrderItemFormSet


class BookImportForm(forms.Form):
json_file = forms.FileField(label="File JSON chứa thông tin sách", required=True)
image_zip = forms.FileField(label="File ZIP chứa ảnh bìa sách", required=True)


class StaffOrderForm(OrderForm):
class Meta(OrderForm.Meta):
fields = ['status', 'note'] # Thêm status
widgets = {
'status': forms.Select(attrs={'class': 'w-full p-2 border rounded'}),
'note': forms.Textarea(attrs={'rows': 3, 'class': 'w-full p-2 border rounded'}),
}

class StaffOrderItemFormSet(BaseOrderItemFormSet):
def clean(self):
super().clean()
for form in self.forms:
if not form.cleaned_data.get('DELETE'):
book = form.cleaned_data.get('book')
quantity = form.cleaned_data.get('quantity')
if book and quantity and quantity > book.stock:
raise forms.ValidationError(f"Sách {book.title} chỉ còn {book.stock} cuốn.")
178 changes: 65 additions & 113 deletions BookStation/staff/templates/staff/accounts/add_users.html
Original file line number Diff line number Diff line change
@@ -1,127 +1,79 @@
{% extends 'staff/dashboard_staff.html' %}
{% load static %}
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Thêm người dùng - BookStation</title>
<link rel="stylesheet" href="{% static 'css/register/style.css' %}">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
</head>
<body>
<div class="register-container">
<div class="background-animation"></div>
{% load widget_tweaks %} {# <-- Thêm dòng này #}

<div class="register-card">
<div class="register-header">
<a class="brand">
<i class="fas fa-user-cog"></i> Quản trị nhân viên
</a>
<h2>Thêm người dùng mới</h2>
<p>Tạo tài khoản mới cho nhân viên hoặc khách hàng</p>
</div>

{% if messages %}
<div class="messages">
{% for message in messages %}
<div class="message {% if message.tags == 'error' %}error-message{% else %}success-message{% endif %}">
<i class="{% if message.tags == 'error' %}fas fa-exclamation-circle{% else %}fas fa-check-circle{% endif %}"></i>
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}

<form method="post" class="register-form">
{% csrf_token %}

<div class="form-fields">
<div class="input-group">
<i class="fas fa-user"></i>
<input type="text" name="first_name" placeholder="Nhập tên " class="form-control">
{% if form.first_name.errors %}
<div class="field-error">
{{ form.first_name.errors|striptags }}
</div>
{% endif %}
</div>

<div class="input-group">
<i class="fas fa-user"></i>
<input type="text" name="last_name" placeholder="Nhập họ " class="form-control">
{% if form.last_name.errors %}
<div class="field-error">
{{ form.last_name.errors|striptags }}
</div>
{% endif %}
</div>

<div class="input-group">
<i class="fas fa-user"></i>
<input type="text" name="username" placeholder="Nhập tên đăng nhập" class="form-control">
{% if form.username.errors %}
<div class="field-error">
{{ form.username.errors|striptags }}
</div>
{% endif %}
</div>
{% block content %}
<div class="bg-white p-6 rounded-lg shadow-md max-w-lg mx-auto mt-8">
<h2 class="text-2xl font-bold text-gray-800 mb-6 text-center flex items-center justify-center">
<i class="fas fa-user-plus mr-3"></i> ➕ Thêm người dùng mới
</h2>

<div class="input-group">
<i class="fas fa-envelope"></i>
<input type="email" name="email" placeholder="Nhập email " class="form-control">
{% if form.email.errors %}
<div class="field-error">
{{ form.email.errors|striptags }}
</div>
{% endif %}
{# Hiển thị thông báo (từ Django messages framework) #}
{% if messages %}
<div class="mb-4">
{% for message in messages %}
<div class="p-3 mb-2 rounded-md
{% if message.tags == 'success' %}bg-green-100 border border-green-400 text-green-700
{% elif message.tags == 'error' %}bg-red-100 border border-red-400 text-red-700
{% elif message.tags == 'warning' %}bg-yellow-100 border border-yellow-400 text-yellow-700
{% else %}bg-blue-100 border border-blue-400 text-blue-700{% endif %}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}

<div class="input-group">
<i class="fas fa-phone"></i>
<input type="tel" name="phone" placeholder="Nhập số điện thoại" class="form-control">
<form method="post" class="space-y-4">
{% csrf_token %}

{% if form.phone.errors %}
<div class="field-error">
{{ form.phone.errors|striptags }}
</div>
{% endif %}
</div>
{# Hiển thị lỗi tổng quát của form (non-field errors) #}
{% if form.non_field_errors %}
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
<strong class="font-bold">Lỗi:</strong>
<ul class="list-disc list-inside mt-1">
{% for error in form.non_field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</div>
{% endif %}

<div class="input-group">
<i class="fas fa-lock"></i>
<input type="password" name="password1" placeholder="Nhập mật khẩu" class="form-control">
{% if form.password1.errors %}
<div class="field-error">
{{ form.password1.errors|striptags }}
</div>
{# Lặp qua từng trường trong form để hiển thị #}
{% for field in form %}
<div class="mb-4">
<label for="{{ field.id_for_label }}" class="block text-sm font-medium text-gray-700 mb-1">
{{ field.label }}
{% if field.field.required %}
<span class="text-red-500">*</span>
{% endif %}
</div>
</label>
{# Sử dụng |add_class để thêm Tailwind classes #}
{# Thêm placeholder và icon nếu bạn muốn, sử dụng |attr:"placeholder:Giá trị" #}
{% render_field field class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" %}

<div class="input-group">
<i class="fas fa-lock"></i>
<input type="password" name="password2" placeholder="Xác nhận mật khẩu" class="form-control">
{% if form.password2.errors %}
<div class="field-error">
{{ form.password2.errors|striptags }}
</div>
{% endif %}
</div>
{% if field.help_text %}
<p class="mt-1 text-sm text-gray-500">{{ field.help_text }}</p>
{% endif %}
{% if field.errors %}
<div class="text-red-600 text-sm mt-1">
{% for error in field.errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}

<button type="submit" class="register-button">
<span>Tạo người dùng mới</span>
<i class="fas fa-arrow-right"></i>
<div class="flex justify-end space-x-3 pt-4">
<a href="{% url 'list_user' %}"
class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-semibold py-2 px-4 rounded-lg shadow-sm transition duration-200 flex items-center">
<i class="fas fa-times-circle mr-2"></i> Hủy
</a>
<button type="submit"
class="bg-green-600 hover:bg-green-700 text-white font-semibold py-2 px-4 rounded-lg shadow-md transition duration-200 flex items-center">
<i class="fas fa-save mr-2"></i> Thêm người dùng
</button>
</form>

<div class="or-divider">
</div>

<div class="login-link">
Quay lại <a href="{% url 'staff_dashboard' %}">Trang nhân viên</a>
</div>
</div>
</form>
</div>
</body>
</html>
{% endblock content %}
Loading
Loading