Compare commits

...

5 Commits

Author SHA1 Message Date
Flatlogic Bot
8bf7a2e341 Edit core/templates/core/sale_form.html via Editor 2025-12-24 01:50:42 +00:00
Flatlogic Bot
dd595804d4 frete inclusion 2025-12-24 01:46:00 +00:00
Flatlogic Bot
5d2e1de750 MOBILE VERSION 2025-12-23 18:06:56 +00:00
Flatlogic Bot
fc22ac9ea6 FINAL 2025-12-23 18:05:13 +00:00
Flatlogic Bot
d9564b7a3a Edit core/templates/base.html via Editor 2025-12-23 16:21:43 +00:00
50 changed files with 1551 additions and 169 deletions

Binary file not shown.

View File

@ -1,3 +1,24 @@
from django.contrib import admin from django.contrib import admin
from .models import Product, Customer, Seller, Sale, SaleItem
# Register your models here.
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ("name", "price", "stock_quantity")
@admin.register(Customer)
class CustomerAdmin(admin.ModelAdmin):
list_display = ("name", "email", "phone")
@admin.register(Seller)
class SellerAdmin(admin.ModelAdmin):
list_display = ("name", "email", "phone", "commission")
class SaleItemInline(admin.TabularInline):
model = SaleItem
extra = 1
@admin.register(Sale)
class SaleAdmin(admin.ModelAdmin):
list_display = ("id", "customer", "sale_date", "total_amount")
inlines = [SaleItemInline]

36
core/forms.py Normal file
View File

@ -0,0 +1,36 @@
from django import forms
from .models import Product, Customer, Seller, Sale, SaleItem
class SellerForm(forms.ModelForm):
class Meta:
model = Seller
fields = '__all__'
class SaleForm(forms.ModelForm):
class Meta:
model = Sale
fields = ['customer', 'seller']
labels = {
'customer': 'Cliente',
'seller': 'Vendedor',
}
class SaleItemForm(forms.ModelForm):
class Meta:
model = SaleItem
fields = ['product', 'quantity', 'lote']
labels = {
'product': 'Produto',
'quantity': 'Quantidade',
'lote': 'Lote',
}
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = '__all__'
class CustomerForm(forms.ModelForm):
class Meta:
model = Customer
fields = '__all__'

View File

@ -0,0 +1,25 @@
# Generated by Django 5.2.7 on 2025-12-23 14:42
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Product',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('description', models.TextField()),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('stock_quantity', models.PositiveIntegerField(default=0)),
('image_url', models.URLField(blank=True, max_length=1024, null=True)),
],
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 5.2.7 on 2025-12-23 14:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Customer',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('email', models.EmailField(max_length=254, unique=True)),
('phone', models.CharField(blank=True, max_length=20, null=True)),
('address', models.TextField(blank=True, null=True)),
],
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 5.2.7 on 2025-12-23 14:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0002_customer'),
]
operations = [
migrations.CreateModel(
name='Supplier',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('contact_person', models.CharField(blank=True, max_length=200, null=True)),
('email', models.EmailField(max_length=254, unique=True)),
('phone', models.CharField(blank=True, max_length=20, null=True)),
('address', models.TextField(blank=True, null=True)),
('website', models.URLField(blank=True, max_length=1024, null=True)),
],
),
]

View File

@ -0,0 +1,33 @@
# Generated by Django 5.2.7 on 2025-12-23 14:58
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0003_supplier'),
]
operations = [
migrations.CreateModel(
name='Sale',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sale_date', models.DateTimeField(auto_now_add=True)),
('total_amount', models.DecimalField(decimal_places=2, default=0.0, max_digits=10)),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.customer')),
],
),
migrations.CreateModel(
name='SaleItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(default=1)),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.product')),
('sale', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='core.sale')),
],
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2025-12-23 15:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_sale_saleitem'),
]
operations = [
migrations.AddField(
model_name='sale',
name='lote',
field=models.CharField(blank=True, max_length=200, null=True),
),
]

View File

@ -0,0 +1,149 @@
# Generated by Django 5.2.7 on 2025-12-23 16:43
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0005_sale_lote'),
]
operations = [
migrations.AlterModelOptions(
name='customer',
options={'verbose_name': 'Cliente', 'verbose_name_plural': 'Clientes'},
),
migrations.AlterModelOptions(
name='product',
options={'verbose_name': 'Produto', 'verbose_name_plural': 'Produtos'},
),
migrations.AlterModelOptions(
name='sale',
options={'verbose_name': 'Venda', 'verbose_name_plural': 'Vendas'},
),
migrations.AlterModelOptions(
name='saleitem',
options={'verbose_name': 'Item da Venda', 'verbose_name_plural': 'Itens da Venda'},
),
migrations.AlterModelOptions(
name='supplier',
options={'verbose_name': 'Fornecedor', 'verbose_name_plural': 'Fornecedores'},
),
migrations.AlterField(
model_name='customer',
name='address',
field=models.TextField(blank=True, null=True, verbose_name='Endereço'),
),
migrations.AlterField(
model_name='customer',
name='email',
field=models.EmailField(max_length=254, unique=True, verbose_name='Email'),
),
migrations.AlterField(
model_name='customer',
name='name',
field=models.CharField(max_length=200, verbose_name='Nome'),
),
migrations.AlterField(
model_name='customer',
name='phone',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='Telefone'),
),
migrations.AlterField(
model_name='product',
name='description',
field=models.TextField(verbose_name='Descrição'),
),
migrations.AlterField(
model_name='product',
name='image_url',
field=models.URLField(blank=True, max_length=1024, null=True, verbose_name='URL da Imagem'),
),
migrations.AlterField(
model_name='product',
name='name',
field=models.CharField(max_length=200, verbose_name='Nome'),
),
migrations.AlterField(
model_name='product',
name='price',
field=models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Preço'),
),
migrations.AlterField(
model_name='product',
name='stock_quantity',
field=models.PositiveIntegerField(default=0, verbose_name='Quantidade em Estoque'),
),
migrations.AlterField(
model_name='sale',
name='customer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.customer', verbose_name='Cliente'),
),
migrations.AlterField(
model_name='sale',
name='lote',
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Lote'),
),
migrations.AlterField(
model_name='sale',
name='sale_date',
field=models.DateTimeField(auto_now_add=True, verbose_name='Data da Venda'),
),
migrations.AlterField(
model_name='sale',
name='total_amount',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='Valor Total'),
),
migrations.AlterField(
model_name='saleitem',
name='price',
field=models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Preço'),
),
migrations.AlterField(
model_name='saleitem',
name='product',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.product', verbose_name='Produto'),
),
migrations.AlterField(
model_name='saleitem',
name='quantity',
field=models.PositiveIntegerField(default=1, verbose_name='Quantidade'),
),
migrations.AlterField(
model_name='saleitem',
name='sale',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='core.sale', verbose_name='Venda'),
),
migrations.AlterField(
model_name='supplier',
name='address',
field=models.TextField(blank=True, null=True, verbose_name='Endereço'),
),
migrations.AlterField(
model_name='supplier',
name='contact_person',
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Pessoa de Contato'),
),
migrations.AlterField(
model_name='supplier',
name='email',
field=models.EmailField(max_length=254, unique=True, verbose_name='Email'),
),
migrations.AlterField(
model_name='supplier',
name='name',
field=models.CharField(max_length=200, verbose_name='Nome'),
),
migrations.AlterField(
model_name='supplier',
name='phone',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='Telefone'),
),
migrations.AlterField(
model_name='supplier',
name='website',
field=models.URLField(blank=True, max_length=1024, null=True, verbose_name='Website'),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 5.2.7 on 2025-12-23 17:08
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0006_alter_customer_options_alter_product_options_and_more'),
]
operations = [
migrations.RenameModel(
old_name='Supplier',
new_name='Seller',
),
]

View File

@ -0,0 +1,31 @@
# Generated by Django 5.2.7 on 2025-12-23 16:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0007_rename_supplier_to_seller'),
]
operations = [
migrations.AlterModelOptions(
name='seller',
options={'verbose_name': 'Vendedor', 'verbose_name_plural': 'Vendedores'},
),
migrations.RemoveField(
model_name='seller',
name='contact_person',
),
migrations.AddField(
model_name='seller',
name='commission',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=5, verbose_name='Comissão'),
),
migrations.AddField(
model_name='seller',
name='description',
field=models.TextField(blank=True, null=True, verbose_name='Descrição'),
),
]

View File

@ -0,0 +1,29 @@
# Generated by Django 5.2.7 on 2025-12-23 16:55
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0008_alter_seller_options_remove_seller_contact_person_and_more'),
]
operations = [
migrations.AddField(
model_name='product',
name='commission_rate',
field=models.DecimalField(decimal_places=2, default=5.0, max_digits=5, verbose_name='Taxa de Comissão'),
),
migrations.AddField(
model_name='sale',
name='commission_amount',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='Valor da Comissão'),
),
migrations.AddField(
model_name='sale',
name='seller',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.seller', verbose_name='Vendedor'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2025-12-23 17:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0009_product_commission_rate_sale_commission_amount_and_more'),
]
operations = [
migrations.AddField(
model_name='saleitem',
name='commission_rate',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=5, verbose_name='Taxa de Comissão'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2025-12-23 17:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0010_saleitem_commission_rate'),
]
operations = [
migrations.AddField(
model_name='saleitem',
name='lote',
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Lote'),
),
]

View File

@ -1,3 +1,77 @@
from django.db import models from django.db import models
# Create your models here. class Product(models.Model):
name = models.CharField("Nome", max_length=200)
description = models.TextField("Descrição")
price = models.DecimalField("Preço", max_digits=10, decimal_places=2)
stock_quantity = models.PositiveIntegerField("Quantidade em Estoque", default=0)
image_url = models.URLField("URL da Imagem", max_length=1024, blank=True, null=True)
commission_rate = models.DecimalField("Taxa de Comissão", max_digits=5, decimal_places=2, default=5.00)
class Meta:
verbose_name = "Produto"
verbose_name_plural = "Produtos"
def __str__(self):
return self.name
class Customer(models.Model):
name = models.CharField("Nome", max_length=200)
email = models.EmailField("Email", max_length=254, unique=True)
phone = models.CharField("Telefone", max_length=20, blank=True, null=True)
address = models.TextField("Endereço", blank=True, null=True)
class Meta:
verbose_name = "Cliente"
verbose_name_plural = "Clientes"
def __str__(self):
return self.name
class Seller(models.Model):
name = models.CharField("Nome", max_length=200)
description = models.TextField("Descrição", blank=True, null=True)
email = models.EmailField("Email", max_length=254, unique=True)
phone = models.CharField("Telefone", max_length=20, blank=True, null=True)
address = models.TextField("Endereço", blank=True, null=True)
website = models.URLField("Website", max_length=1024, blank=True, null=True)
commission = models.DecimalField("Comissão", max_digits=5, decimal_places=2, default=0.00)
class Meta:
verbose_name = "Vendedor"
verbose_name_plural = "Vendedores"
def __str__(self):
return self.name
class Sale(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, verbose_name="Cliente")
seller = models.ForeignKey(Seller, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Vendedor")
sale_date = models.DateTimeField("Data da Venda", auto_now_add=True)
total_amount = models.DecimalField("Valor Total", max_digits=10, decimal_places=2, default=0.00)
commission_amount = models.DecimalField("Valor da Comissão", max_digits=10, decimal_places=2, default=0.00)
lote = models.CharField("Lote", max_length=200, blank=True, null=True)
class Meta:
verbose_name = "Venda"
verbose_name_plural = "Vendas"
def __str__(self):
return f"Venda #{self.pk} - {self.customer.name}"
class SaleItem(models.Model):
sale = models.ForeignKey(Sale, related_name='items', on_delete=models.CASCADE, verbose_name="Venda")
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name="Produto")
lote = models.CharField("Lote", max_length=200, blank=True, null=True)
quantity = models.PositiveIntegerField("Quantidade", default=1)
price = models.DecimalField("Preço", max_digits=10, decimal_places=2)
commission_rate = models.DecimalField("Taxa de Comissão", max_digits=5, decimal_places=2, default=0.00)
class Meta:
verbose_name = "Item da Venda"
verbose_name_plural = "Itens da Venda"
def __str__(self):
return f"{self.quantity} de {self.product.name} na Venda #{self.sale.pk}"

View File

@ -1,9 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="pt-BR">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{% block title %}Knowledge Base{% endblock %}</title> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Meta description for the page">
<title>EletroNorteAr</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&family=Roboto:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
{% if project_description %} {% if project_description %}
<meta name="description" content="{{ project_description }}"> <meta name="description" content="{{ project_description }}">
<meta property="og:description" content="{{ project_description }}"> <meta property="og:description" content="{{ project_description }}">
@ -19,7 +26,39 @@
</head> </head>
<body> <body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="{% url 'index' %}">EletroNorteAr</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Alternar navegação">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="{% url 'product_list' %}">Produtos</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'customer_list' %}">Clientes</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'seller-list' %}">Vendedores</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'sale_list' %}">Vendas</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin">Administração</a>
</li>
</ul>
</div>
</div>
</nav>
{% block content %}{% endblock %} {% block content %}{% endblock %}
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body> </body>
</html> </html>

View File

@ -5,7 +5,7 @@
{% block content %} {% block content %}
<div class="container mt-5"> <div class="container mt-5">
<h1>{{ article.title }}</h1> <h1>{{ article.title }}</h1>
<p class="text-muted">Published on {{ article.created_at|date:"F d, Y" }}</p> <p class="text-muted">Publicado em {{ article.created_at|date:"d \\de F \\de Y" }}</p>
<hr> <hr>
<div> <div>
{{ article.content|safe }} {{ article.content|safe }}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% block content %}
<h1>Excluir Cliente</h1>
<p>Tem certeza que deseja excluir "{{ object.name }}"?</p>
<form method="post">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Excluir</button>
<a href="{% url 'customer_list' %}" class="btn btn-secondary">Cancelar</a>
</form>
{% endblock %}

View File

@ -0,0 +1,10 @@
{% extends 'base.html' %}
{% block content %}
<h1>{% if object %}Atualizar Cliente{% else %}Novo Cliente{% endif %}</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Salvar</button>
</form>
{% endblock %}

View File

@ -0,0 +1,31 @@
{% extends 'base.html' %}
{% block content %}
<h1>Clientes</h1>
<a href="{% url 'customer_create' %}" class="btn btn-primary mb-3">Novo Cliente</a>
<table class="table">
<thead>
<tr>
<th>Nome</th>
<th>Email</th>
<th>Telefone</th>
<th>Endereço</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
{% for customer in customers %}
<tr>
<td>{{ customer.name }}</td>
<td>{{ customer.email }}</td>
<td>{{ customer.phone }}</td>
<td>{{ customer.address }}</td>
<td>
<a href="{% url 'customer_update' customer.pk %}" class="btn btn-sm btn-info">Editar</a>
<a href="{% url 'customer_delete' customer.pk %}" class="btn btn-sm btn-danger">Excluir</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -1,145 +1,42 @@
{% extends "base.html" %} {% extends 'base.html' %}
{% load static %}
{% block title %}{{ project_name }}{% endblock %}
{% block head %}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M-10 10L110 10M10 -10L10 110' stroke-width='1' stroke='rgba(255,255,255,0.05)'/></svg>");
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% {
background-position: 0% 0%;
}
100% {
background-position: 100% 100%;
}
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2.5rem 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25);
}
h1 {
font-size: clamp(2.2rem, 3vw + 1.2rem, 3.2rem);
font-weight: 700;
margin: 0 0 1.2rem;
letter-spacing: -0.02em;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
opacity: 0.92;
}
.loader {
margin: 1.5rem auto;
width: 56px;
height: 56px;
border: 4px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.runtime code {
background: rgba(0, 0, 0, 0.25);
padding: 0.15rem 0.45rem;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
footer {
position: absolute;
bottom: 1rem;
width: 100%;
text-align: center;
font-size: 0.85rem;
opacity: 0.75;
}
</style>
{% endblock %}
{% block content %} {% block content %}
<main> <!-- Hero Section -->
<div class="card"> <section class="hero-section text-center">
<h1>Analyzing your requirements and generating your app…</h1> <div class="container">
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes"> <h1 class="display-4">Gestão Inteligente de Climatização</h1>
<span class="sr-only">Loading…</span> <p class="lead">Controle seu estoque e vendas de forma eficiente e moderna.</p>
</div> </div>
<p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p> </section>
<p class="hint">This page will refresh automatically as the plan is implemented.</p>
<p class="runtime"> <!-- Product Section -->
Runtime: Django <code>{{ django_version }}</code> · Python <code>{{ python_version }}</code> <section class="product-section">
— UTC <code>{{ current_time|date:"Y-m-d H:i:s" }}</code> <div class="container">
</p> <h2 class="text-center mb-5">Nossos Produtos</h2>
<div class="row">
{% if products %}
{% for product in products %}
<div class="col-md-4 mb-4">
<div class="card product-card h-100">
<img src="{{ product.image_url|default:'https://via.placeholder.com/300x200.png?text=Sem+Imagem' }}" class="card-img-top product-card-img" alt="{{ product.name }}">
<div class="card-body d-flex flex-column">
<h5 class="card-title">{{ product.name }}</h5>
<p class="card-text text-muted flex-grow-1">{{ product.description|truncatewords:20 }}</p>
<div class="d-flex justify-content-between align-items-center mt-3">
<p class="mb-0 product-price">R$ {{ product.price }}</p>
<p class="mb-0 text-muted stock-level">Estoque: {{ product.stock_quantity }}</p>
</div> </div>
</main> </div>
<footer> </div>
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC) </div>
</footer> {% endfor %}
{% else %}
<div class="col-12">
<p class="text-center">Nenhum produto cadastrado ainda. Adicione produtos no <a href="/admin">painel de administração</a>.</p>
</div>
{% endif %}
</div>
</div>
</section>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% block content %}
<h1>Excluir Produto</h1>
<p>Tem certeza que deseja excluir "{{ object.name }}"?</p>
<form method="post">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Excluir</button>
<a href="{% url 'product_list' %}" class="btn btn-secondary">Cancelar</a>
</form>
{% endblock %}

View File

@ -0,0 +1,10 @@
{% extends 'base.html' %}
{% block content %}
<h1>{% if object %}Atualizar Produto{% else %}Novo Produto{% endif %}</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Salvar</button>
</form>
{% endblock %}

View File

@ -0,0 +1,29 @@
{% extends 'base.html' %}
{% block content %}
<h1>Produtos</h1>
<a href="{% url 'product_create' %}" class="btn btn-primary mb-3">Novo Produto</a>
<table class="table">
<thead>
<tr>
<th>Nome</th>
<th>Preço</th>
<th>Estoque</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
{% for product in products %}
<tr>
<td>{{ product.name }}</td>
<td>{{ product.price }}</td>
<td>{{ product.stock_quantity }}</td>
<td>
<a href="{% url 'product_update' product.pk %}" class="btn btn-sm btn-info">Editar</a>
<a href="{% url 'product_delete' product.pk %}" class="btn btn-sm btn-danger">Excluir</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% block content %}
<h1>Excluir Venda</h1>
<p>Tem certeza que deseja excluir a venda #{{ object.id }}?</p>
<form method="post">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Excluir</button>
<a href="{% url 'sale_list' %}" class="btn btn-secondary">Cancelar</a>
</form>
{% endblock %}

View File

@ -0,0 +1,118 @@
{% extends 'base.html' %}
{% load static %}
{% block content %}
<section class="hero-section text-center">
<div class="container">
<h1 class="display-4">Criar Venda</h1>
<p class="lead">Preencha os detalhes para criar uma nova venda.</p>
</div>
</section>
<section class="product-section">
<div class="container">
<form method="post">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
{% for field in form %}
<div class="form-group mb-3">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
{% for error in field.errors %}
<div class="alert alert-danger mt-1">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
<hr>
<h2>Itens da Venda</h2>
{{ formset.management_form }}
{% if formset.non_form_errors %}
<div class="alert alert-danger">
{{ formset.non_form_errors }}
</div>
{% endif %}
<table class="table">
<thead>
<tr>
<th>Produto</th>
<th>Quantidade</th>
<th>Lote</th>
<th>Excluir</th>
</tr>
</thead>
<tbody id="item-forms">
{% for form in formset.forms %}
<tr class="item-form">
{% for field in form.hidden_fields %}{{ field }}{% endfor %}
<td>
{{ form.product }}
{% for error in form.product.errors %}<div class="alert alert-danger mt-1">{{ error }}</div>{% endfor %}
</td>
<td>
{{ form.quantity }}
{% for error in form.quantity.errors %}<div class="alert alert-danger mt-1">{{ error }}</div>{% endfor %}
</td>
<td>
{{ form.lote }}
{% for error in form.lote.errors %}<div class="alert alert-danger mt-1">{{ error }}</div>{% endfor %}
</td>
<td>
{% if form.instance.pk %}{{ form.DELETE }}{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="button" id="add-item" class="btn btn-info">Adicionar Produto</button>
<br><br>
<button type="submit" class="btn btn-primary">Salvar Venda</button>
<a href="{% url 'sale_list' %}" class="btn btn-secondary">Cancelar</a>
</form>
</div>
</section>
<script type="text/template" id="item-form-template">
<tr class="item-form" id="{{ formset.prefix }}-__prefix__">
{% for field in formset.empty_form.hidden_fields %}{{ field }}{% endfor %}
<td>
{{ formset.empty_form.product }}
</td>
<td>
{{ formset.empty_form.quantity }}
</td>
<td>
{{ formset.empty_form.lote }}
</td>
<td>
{{ formset.empty_form.DELETE }}
</td>
</tr>
</script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const addItemButton = document.getElementById('add-item');
const formsetContainer = document.getElementById('item-forms');
const formTemplate = document.getElementById('item-form-template').innerHTML;
const totalFormsInput = document.querySelector('#id_items-TOTAL_FORMS');
addItemButton.addEventListener('click', function() {
let formNum = parseInt(totalFormsInput.value);
let newForm = formTemplate.replace(/__prefix__/g, formNum);
formsetContainer.insertAdjacentHTML('beforeend', newForm);
totalFormsInput.value = formNum + 1;
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,119 @@
{% extends 'base.html' %}
{% load static %}
{% block content %}
<section class="hero-section text-center">
<div class="container">
<h1 class="display-4">{% if sale %}Editar Venda{% else %}Criar Venda{% endif %}</h1>
<p class="lead">{% if sale %}Atualize os detalhes da venda.{% else %}Preencha os detalhes para criar uma nova venda.{% endif %}</p>
</div>
</section>
<section class="product-section">
<div class="container">
<form method="post">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
{% for field in form %}
<div class="form-group mb-3">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
{% for error in field.errors %}
<div class="alert alert-danger mt-1">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
<hr>
<h2>Itens da Venda</h2>
{{ formset.management_form }}
{% if formset.non_form_errors %}
<div class="alert alert-danger">
{{ formset.non_form_errors }}
</div>
{% endif %}
<table class="table">
<thead>
<tr>
<th>Produto</th>
<th>Quantidade</th>
<th>Lote</th>
<th>Excluir</th>
</tr>
</thead>
<tbody id="item-forms">
{% for form in formset.forms %}
<tr class="item-form">
{% for field in form.hidden_fields %}{{ field }}{% endfor %}
<td>
{{ form.product }}
{% for error in form.product.errors %}<div class="alert alert-danger mt-1">{{ error }}</div>{% endfor %}
</td>
<td>
{{ form.quantity }}
{% for error in form.quantity.errors %}<div class="alert alert-danger mt-1">{{ error }}</div>{% endfor %}
</td>
<td>
{{ form.lote }}
{% for error in form.lote.errors %}<div class="alert alert-danger mt-1">{{ error }}</div>{% endfor %}
</td>
<td>
{% if form.instance.pk %}{{ form.DELETE }}{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="button" id="add-item" class="btn btn-info">Adicionar Produto</button>
<br><br>
<button type="submit" class="btn btn-primary">Salvar Alterações</button>
<a href="{% url 'sale_list' %}" class="btn btn-secondary">Cancelar</a>
</form>
</div>
</section>
<script type="text/template" id="item-form-template">
<tr class="item-form" id="{{ formset.prefix }}-__prefix__">
{% for field in formset.empty_form.hidden_fields %}{{ field }}{% endfor %}
<td>
{{ formset.empty_form.product }}
</td>
<td>
{{ formset.empty_form.quantity }}
</td>
<td>
{{ formset.empty_form.lote }}
</td>
<td>
{{ formset.empty_form.DELETE }}
</td>
</tr>
</script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const addItemButton = document.getElementById('add-item');
const formsetContainer = document.getElementById('item-forms');
const formTemplate = document.getElementById('item-form-template').innerHTML;
const totalFormsInput = document.querySelector('#id_items-TOTAL_FORMS');
const formsetPrefix = '{{ formset.prefix }}';
addItemButton.addEventListener('click', function() {
let formNum = parseInt(totalFormsInput.value);
let newForm = formTemplate.replace(/__prefix__/g, formNum);
formsetContainer.insertAdjacentHTML('beforeend', newForm);
totalFormsInput.value = formNum + 1;
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,49 @@
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="display-4">Vendas</h1>
<a href="{% url 'sale_create' %}" class="btn btn-primary">Nova Venda</a>
</div>
<p class="lead">Gerencie suas vendas.</p>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Cliente</th>
<th>Vendedor</th>
<th>Valor Total</th>
<th>Comissão</th>
<th>Data</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
{% for sale in sales %}
<tr>
<td>#{{ sale.id }}</td>
<td>{{ sale.customer.name }}</td>
<td>{{ sale.seller.name|default:"N/A" }}</td>
<td>R$ {{ sale.total_amount|floatformat:2 }}</td>
<td>R$ {{ sale.commission_amount|floatformat:2 }}</td>
<td>{{ sale.sale_date|date:"d/m/Y" }}</td>
<td>
<a href="{% url 'sale_update' sale.id %}" class="btn btn-sm btn-warning">Editar</a>
<a href="{% url 'sale_delete' sale.id %}" class="btn btn-sm btn-danger">Excluir</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="7" class="text-center">Nenhuma venda cadastrada.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% block content %}
<h2>Excluir Vendedor</h2>
<p>Tem certeza que deseja excluir o vendedor "{{ seller.name }}"?</p>
<form method="post">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Excluir</button>
<a href="{% url 'seller-list' %}" class="btn btn-secondary">Cancelar</a>
</form>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% block content %}
<h2>{% if form.instance.pk %}Editar Vendedor{% else %}Adicionar Vendedor{% endif %}</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Salvar</button>
<a href="{% url 'seller-list' %}" class="btn btn-secondary">Cancelar</a>
</form>
{% endblock %}

View File

@ -0,0 +1,43 @@
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="display-4">Vendedores</h1>
<a href="{% url 'seller_create' %}" class="btn btn-primary">Adicionar Vendedor</a>
</div>
<p class="lead">Gerencie seus vendedores.</p>
<table class="table table-striped">
<thead>
<tr>
<th>Nome</th>
<th>Email</th>
<th>Telefone</th>
<th>Comissão (%)</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
{% for seller in sellers %}
<tr>
<td>{{ seller.name }}</td>
<td>{{ seller.email }}</td>
<td>{{ seller.phone }}</td>
<td>{{ seller.commission }}</td>
<td>
<a href="{% url 'seller_update' seller.pk %}" class="btn btn-sm btn-info">Editar</a>
<a href="{% url 'seller_delete' seller.pk %}" class="btn btn-sm btn-danger">Excluir</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="5" class="text-center">Nenhum vendedor cadastrado.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@ -1,7 +1,41 @@
from django.urls import path from django.urls import path
from .views import home from .views import (
index,
customer_list,
seller_list,
sale_list,
sale_create,
sale_update,
SaleDeleteView,
product_list,
ProductCreateView,
ProductUpdateView,
ProductDeleteView,
CustomerCreateView,
CustomerUpdateView,
CustomerDeleteView,
SellerCreateView,
SellerUpdateView,
SellerDeleteView,
)
urlpatterns = [ urlpatterns = [
path("", home, name="home"), path("", index, name="index"),
path("customers/", customer_list, name="customer_list"),
path("customers/create/", CustomerCreateView.as_view(), name="customer_create"),
path("customers/<int:pk>/update/", CustomerUpdateView.as_view(), name="customer_update"),
path("customers/<int:pk>/delete/", CustomerDeleteView.as_view(), name="customer_delete"),
path("sellers/", seller_list, name="seller-list"),
path("sellers/create/", SellerCreateView.as_view(), name="seller_create"),
path("sellers/<int:pk>/update/", SellerUpdateView.as_view(), name="seller_update"),
path("sellers/<int:pk>/delete/", SellerDeleteView.as_view(), name="seller_delete"),
path("sales/", sale_list, name="sale_list"),
path("sales/create/", sale_create, name="sale_create"),
path("sales/<int:pk>/update/", sale_update, name="sale_update"),
path("sales/<int:pk>/delete/", SaleDeleteView.as_view(), name="sale_delete"),
path("products/", product_list, name="product_list"),
path("products/create/", ProductCreateView.as_view(), name="product_create"),
path("products/<int:pk>/update/", ProductUpdateView.as_view(), name="product_update"),
path("products/<int:pk>/delete/", ProductDeleteView.as_view(), name="product_delete"),
] ]

View File

@ -2,17 +2,28 @@ import os
import platform import platform
from django import get_version as django_version from django import get_version as django_version
from django.shortcuts import render from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.views.generic import CreateView, UpdateView, DeleteView
from .forms import SaleForm, ProductForm, CustomerForm, SellerForm
from .models import Product, Customer, Seller, Sale, SaleItem
def home(request): def index(request):
"""Render the landing screen with loader and environment details.""" """Render the landing screen with loader and environment details."""
host_name = request.get_host().lower() host_name = request.get_host().lower()
agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
now = timezone.now() now = timezone.now()
products = Product.objects.all()
context = { context = {
"products": products,
"project_name": "New Style", "project_name": "New Style",
"agent_brand": agent_brand, "agent_brand": agent_brand,
"django_version": django_version(), "django_version": django_version(),
@ -22,4 +33,217 @@ def home(request):
"project_description": os.getenv("PROJECT_DESCRIPTION", ""), "project_description": os.getenv("PROJECT_DESCRIPTION", ""),
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
} }
return render(request, "core/index.html", context) return render(request, "core/index.html", context)
def customer_list(request):
customers = Customer.objects.all()
return render(request, 'core/customer_list.html', {'customers': customers})
class CustomerCreateView(CreateView):
model = Customer
form_class = CustomerForm
template_name = 'core/customer_form.html'
success_url = reverse_lazy('customer_list')
class CustomerUpdateView(UpdateView):
model = Customer
form_class = CustomerForm
template_name = 'core/customer_form.html'
success_url = reverse_lazy('customer_list')
class CustomerDeleteView(DeleteView):
model = Customer
template_name = 'core/customer_confirm_delete.html'
success_url = reverse_lazy('customer_list')
class SellerCreateView(CreateView):
model = Seller
form_class = SellerForm
template_name = 'core/seller_form.html'
success_url = reverse_lazy('seller-list')
class SellerUpdateView(UpdateView):
model = Seller
form_class = SellerForm
template_name = 'core/seller_form.html'
success_url = reverse_lazy('seller-list')
class SellerDeleteView(DeleteView):
model = Seller
template_name = 'core/seller_confirm_delete.html'
success_url = reverse_lazy('seller-list')
def seller_list(request):
"""Render the seller list page."""
sellers = Seller.objects.all()
context = {"sellers": sellers}
return render(request, "core/seller_list.html", context)
import logging
def sale_list(request):
"""Render the sale list page."""
logging.info("Sale list view called")
sales = Sale.objects.select_related('customer', 'seller').all()
logging.info(f"Sales: {sales}")
context = {"sales": sales}
return render(request, "core/sale_list.html", context)
from django.forms import inlineformset_factory
from django.db import transaction
from .forms import SaleForm, ProductForm, CustomerForm, SellerForm, SaleItemForm
def sale_create(request):
SaleItemFormSet = inlineformset_factory(
Sale, SaleItem, form=SaleItemForm, extra=1, can_delete=True
)
if request.method == 'POST':
form = SaleForm(request.POST)
formset = SaleItemFormSet(request.POST)
if form.is_valid() and formset.is_valid():
with transaction.atomic():
sale = form.save(commit=False)
# Initialize calculated fields
sale.total_amount = 0
sale.commission_amount = 0
sale.save() # Save sale to get an ID for the formset
formset.instance = sale
sale_items = formset.save(commit=False)
total_amount = 0
commission_amount = 0
for item in sale_items:
# Freeze price at the time of sale
item.price = item.product.price
# Determine commission rate: Use seller's rate if > 0, otherwise fallback to product's rate
if sale.seller and sale.seller.commission > 0:
item.commission_rate = sale.seller.commission
else:
item.commission_rate = item.product.commission_rate
item.save()
# Calculate totals
item_total = item.price * item.quantity
total_amount += item_total
commission_amount += item_total * (item.commission_rate / 100)
# Update the sale instance with calculated totals
sale.total_amount = total_amount
sale.commission_amount = commission_amount
sale.save()
return redirect('sale_list')
else:
form = SaleForm()
formset = SaleItemFormSet()
return render(request, 'core/sale_create.html', {'form': form, 'formset': formset})
def sale_update(request, pk):
sale = get_object_or_404(Sale, pk=pk)
SaleItemFormSet = inlineformset_factory(
Sale, SaleItem, form=SaleItemForm, extra=1, can_delete=True
)
if request.method == 'POST':
form = SaleForm(request.POST, instance=sale)
formset = SaleItemFormSet(request.POST, instance=sale)
if form.is_valid() and formset.is_valid():
with transaction.atomic():
sale = form.save()
# Handle deleted items first
for form in formset.deleted_forms:
if form.instance.pk:
form.instance.delete()
# Handle new and changed items
sale_items = formset.save(commit=False)
for item in sale_items:
if not item.pk: # If it's a new item
item.price = item.product.price
# Commission rate will be set in the recalculation step
item.save()
formset.save_m2m()
# Recalculate total amount and commission from all valid items
total_amount = 0
commission_amount = 0
for item in sale.items.all():
# Determine and save the correct commission rate for every item
if sale.seller and sale.seller.commission > 0:
item.commission_rate = sale.seller.commission
else:
item.commission_rate = item.product.commission_rate
item.save()
item_total = item.price * item.quantity
total_amount += item_total
commission_amount += item_total * (item.commission_rate / 100)
sale.total_amount = total_amount
sale.commission_amount = commission_amount
sale.save()
return redirect('sale_list')
else:
form = SaleForm(instance=sale)
formset = SaleItemFormSet(instance=sale)
return render(request, 'core/sale_form.html', {'form': form, 'formset': formset, 'sale': sale})
def product_list(request):
products = Product.objects.all()
return render(request, 'core/product_list.html', {'products': products})
class ProductCreateView(CreateView):
model = Product
form_class = ProductForm
template_name = 'core/product_form.html'
success_url = reverse_lazy('product_list')
class ProductUpdateView(UpdateView):
model = Product
form_class = ProductForm
template_name = 'core/product_form.html'
success_url = reverse_lazy('product_list')
class ProductDeleteView(DeleteView):
model = Product
template_name = 'core/product_confirm_delete.html'
success_url = reverse_lazy('product_list')
class SaleDeleteView(DeleteView):
model = Sale
template_name = 'core/sale_confirm_delete.html'
success_url = reverse_lazy('sale_list')

View File

@ -1,4 +1,116 @@
/* Custom styles for the application */ /*
body { Custom Styles for ACME Climsoft
font-family: system-ui, -apple-system, sans-serif; */
:root {
--primary-color: #007BFF;
--secondary-color: #343A40;
--accent-color: #17A2B8;
--background-color: #F8F9FA;
--text-color: #212529;
--heading-font: 'Poppins', sans-serif;
--body-font: 'Roboto', sans-serif;
}
body {
font-family: var(--body-font);
background-color: var(--background-color);
color: var(--text-color);
}
h1, h2, h3, h4, h5, h6 {
font-family: var(--heading-font);
font-weight: 600;
color: var(--secondary-color);
}
.btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
}
.btn-primary:hover {
background-color: #0056b3;
border-color: #0056b3;
}
.btn-accent {
background-color: var(--accent-color);
border-color: var(--accent-color);
color: #fff;
}
.btn-accent:hover {
background-color: #138496;
border-color: #138496;
}
/* Hero Section */
.hero-section {
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
padding: 6rem 0;
color: #fff;
}
.hero-section h1 {
font-size: 3.5rem;
font-weight: 700;
color: #fff;
}
.hero-section .lead {
font-size: 1.25rem;
}
/* Product Section */
.product-section {
padding: 4rem 0;
}
.product-card {
border: none;
border-radius: 10px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 12px rgba(0,0,0,0.15);
}
.product-card-img {
height: 200px;
object-fit: cover;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.product-card .card-body {
padding: 1.5rem;
}
.product-card .card-title {
font-size: 1.25rem;
font-weight: 600;
}
.product-card .card-text {
font-size: 0.9rem;
color: #6c757d;
}
.product-price {
font-size: 1.2rem;
font-weight: 700;
color: var(--primary-color);
}
.stock-level {
font-size: 0.9rem;
}
/* Navbar */
.navbar {
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
} }

View File

@ -1,21 +1,116 @@
/*
Custom Styles for ACME Climsoft
*/
:root { :root {
--bg-color-start: #6a11cb; --primary-color: #007BFF;
--bg-color-end: #2575fc; --secondary-color: #343A40;
--text-color: #ffffff; --accent-color: #17A2B8;
--card-bg-color: rgba(255, 255, 255, 0.01); --background-color: #F8F9FA;
--card-border-color: rgba(255, 255, 255, 0.1); --text-color: #212529;
--heading-font: 'Poppins', sans-serif;
--body-font: 'Roboto', sans-serif;
} }
body { body {
margin: 0; font-family: var(--body-font);
font-family: 'Inter', sans-serif; background-color: var(--background-color);
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color); color: var(--text-color);
display: flex; }
justify-content: center;
align-items: center; h1, h2, h3, h4, h5, h6 {
min-height: 100vh; font-family: var(--heading-font);
text-align: center; font-weight: 600;
overflow: hidden; color: var(--secondary-color);
position: relative; }
.btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
}
.btn-primary:hover {
background-color: #0056b3;
border-color: #0056b3;
}
.btn-accent {
background-color: var(--accent-color);
border-color: var(--accent-color);
color: #fff;
}
.btn-accent:hover {
background-color: #138496;
border-color: #138496;
}
/* Hero Section */
.hero-section {
background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
padding: 6rem 0;
color: #fff;
}
.hero-section h1 {
font-size: 3.5rem;
font-weight: 700;
color: #fff;
}
.hero-section .lead {
font-size: 1.25rem;
}
/* Product Section */
.product-section {
padding: 4rem 0;
}
.product-card {
border: none;
border-radius: 10px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 12px rgba(0,0,0,0.15);
}
.product-card-img {
height: 200px;
object-fit: cover;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.product-card .card-body {
padding: 1.5rem;
}
.product-card .card-title {
font-size: 1.25rem;
font-weight: 600;
}
.product-card .card-text {
font-size: 0.9rem;
color: #6c757d;
}
.product-price {
font-size: 1.2rem;
font-weight: 700;
color: var(--primary-color);
}
.stock-level {
font-size: 0.9rem;
}
/* Navbar */
.navbar {
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
} }