This commit is contained in:
Flatlogic Bot 2025-12-17 19:53:37 +00:00
parent cd0902b381
commit 5b190bc3f3
25 changed files with 650 additions and 67 deletions

5
cookies.txt Normal file
View File

@ -0,0 +1,5 @@
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
127.0.0.1 FALSE / TRUE 1797450401 csrftoken gNby2S5EHlDB1Xsw0eGC7JVKq4F45wQW

View File

@ -1,3 +1,14 @@
from django.contrib import admin from django.contrib import admin
from .models import Project, Client
# Register your models here. @admin.register(Client)
class ClientAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'phone')
search_fields = ('name',)
@admin.register(Project)
class ProjectAdmin(admin.ModelAdmin):
list_display = ('name', 'client', 'start_date', 'end_date', 'status')
list_filter = ('status', 'client')
search_fields = ('name', 'client__name')
autocomplete_fields = ('client',)

View File

@ -1,5 +1,5 @@
from django import forms from django import forms
from .models import Project from .models import Project, Client
class ProjectForm(forms.ModelForm): class ProjectForm(forms.ModelForm):
class Meta: class Meta:
@ -7,8 +7,18 @@ class ProjectForm(forms.ModelForm):
fields = ['name', 'client', 'start_date', 'end_date', 'status'] fields = ['name', 'client', 'start_date', 'end_date', 'status']
widgets = { widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}), 'name': forms.TextInput(attrs={'class': 'form-control'}),
'client': forms.TextInput(attrs={'class': 'form-control'}), 'client': forms.Select(attrs={'class': 'form-select'}),
'start_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'start_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
'end_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'end_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
'status': forms.Select(attrs={'class': 'form-select'}), 'status': forms.Select(attrs={'class': 'form-select'}),
} }
class ClientForm(forms.ModelForm):
class Meta:
model = Client
fields = ['name', 'email', 'phone']
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'email': forms.EmailInput(attrs={'class': 'form-control'}),
'phone': forms.TextInput(attrs={'class': 'form-control'}),
}

View File

@ -0,0 +1,93 @@
# Generated by Django 5.2.7 on 2025-12-17 19:22
from django.db import migrations, models
import django.db.models.deletion
def migrate_clients_forward(apps, schema_editor):
Project = apps.get_model('core', 'Project')
Client = apps.get_model('core', 'Client')
for project in Project.objects.all():
client_name = getattr(project, 'client_temp', None)
if client_name:
client, created = Client.objects.get_or_create(name=client_name)
project.client = client
project.save()
def migrate_clients_backward(apps, schema_editor):
# This is a one-way migration, but we can try to restore the old values
Project = apps.get_model('core', 'Project')
for project in Project.objects.all():
if project.client:
project.client_temp = project.client.name
project.save()
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
operations = [
# 1. Create the new Client model
migrations.CreateModel(
name='Client',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Client Name')),
('email', models.EmailField(blank=True, max_length=200, null=True, verbose_name='Email')),
('phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='Phone')),
],
),
# 2. Rename the existing 'client' CharField to a temporary field
migrations.RenameField(
model_name='project',
old_name='client',
new_name='client_temp',
),
# 3. Add the new 'client' ForeignKey field, allowing it to be null for now
migrations.AddField(
model_name='project',
name='client',
field=models.ForeignKey(
to='core.Client',
on_delete=django.db.models.deletion.CASCADE,
related_name='projects',
verbose_name='Client',
null=True, # Allow null during migration
blank=True
),
),
# 4. Run the Python script to migrate the data
migrations.RunPython(migrate_clients_forward, migrate_clients_backward),
# 5. Remove the temporary CharField
migrations.RemoveField(
model_name='project',
name='client_temp',
),
# 6. Alter other fields (translations)
migrations.AlterField(
model_name='project',
name='end_date',
field=models.DateField(verbose_name='End Date'),
),
migrations.AlterField(
model_name='project',
name='name',
field=models.CharField(max_length=200, verbose_name='Project Name'),
),
migrations.AlterField(
model_name='project',
name='start_date',
field=models.DateField(verbose_name='Start Date'),
),
migrations.AlterField(
model_name='project',
name='status',
field=models.CharField(choices=[('planning', 'Planning'), ('in_progress', 'In Progress'), ('completed', 'Completed')], default='planning', max_length=20, verbose_name='Status'),
),
]

View File

@ -1,17 +1,25 @@
from django.db import models from django.db import models
class Client(models.Model):
name = models.CharField(max_length=200, verbose_name="Client Name")
email = models.EmailField(max_length=200, verbose_name="Email", blank=True, null=True)
phone = models.CharField(max_length=20, verbose_name="Phone", blank=True, null=True)
def __str__(self):
return self.name
class Project(models.Model): class Project(models.Model):
STATUS_CHOICES = [ STATUS_CHOICES = [
('planowany', 'Planowany'), ('planning', 'Planning'),
('realizowany', 'Realizowany'), ('in_progress', 'In Progress'),
('zakonczony', 'Zakończony'), ('completed', 'Completed'),
] ]
name = models.CharField(max_length=200, verbose_name="Nazwa projektu") name = models.CharField(max_length=200, verbose_name="Project Name")
client = models.CharField(max_length=200, verbose_name="Kontrahent") client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='projects', verbose_name="Client", null=True, blank=True)
start_date = models.DateField(verbose_name="Data rozpoczęcia") start_date = models.DateField(verbose_name="Start Date")
end_date = models.DateField(verbose_name="Data zakończenia") end_date = models.DateField(verbose_name="End Date")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='planowany', verbose_name="Status") status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='planning', verbose_name="Status")
def __str__(self): def __str__(self):
return self.name return self.name

View File

@ -33,7 +33,7 @@
<li class="{% if request.path == '/' %}active{% endif %}"><a href="{% url 'index' %}"><i data-feather="home"></i> Dashboard</a></li> <li class="{% if request.path == '/' %}active{% endif %}"><a href="{% url 'index' %}"><i data-feather="home"></i> Dashboard</a></li>
<li class="{% if '/projects' in request.path %}active{% endif %}"><a href="{% url 'projects' %}"><i data-feather="briefcase"></i> Projects</a></li> <li class="{% if '/projects' in request.path %}active{% endif %}"><a href="{% url 'projects' %}"><i data-feather="briefcase"></i> Projects</a></li>
<li><a href="#"><i data-feather="file-text"></i> Invoices</a></li> <li><a href="#"><i data-feather="file-text"></i> Invoices</a></li>
<li><a href="#"><i data-feather="users"></i> Clients</a></li> <li><a href="{% url 'clients' %}"><i data-feather="users"></i> Clients</a></li>
<li><a href="#"><i data-feather="settings"></i> Settings</a></li> <li><a href="#"><i data-feather="settings"></i> Settings</a></li>
</ul> </ul>
<ul class="auth-nav"> <ul class="auth-nav">

View File

@ -14,6 +14,7 @@
<meta property="twitter:image" content="{{ project_image_url }}"> <meta property="twitter:image" content="{{ project_image_url }}">
{% endif %} {% endif %}
{% load static %} {% load static %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
@ -37,6 +38,7 @@
{% block content %}{% endblock %} {% block content %}{% endblock %}
</main> </main>
</div> </div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script> <script>
feather.replace() feather.replace()
</script> </script>

View File

@ -0,0 +1,48 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}Add Client - webFirma{% endblock %}
{% block content %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card shadow-sm">
<div class="card-header bg-primary text-white">
<h4 class="mb-0">Create a New Client</h4>
</div>
<div class="card-body">
<form method="post">
{% csrf_token %}
<div class="form-floating mb-3">
{{ form.name }}
<label for="{{ form.name.id_for_label }}">Name</label>
</div>
<div class="form-floating mb-3">
{{ form.email }}
<label for="{{ form.email.id_for_label }}">Email</label>
</div>
<div class="form-floating mb-3">
{{ form.phone }}
<label for="{{ form.phone.id_for_label }}">Phone</label>
</div>
<div class="d-flex justify-content-end">
<a href="{% url 'clients' %}" class="btn btn-secondary me-2">Cancel</a>
<button type="submit" class="btn btn-primary">Save Client</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<style>
.form-floating .form-control {
height: calc(3.5rem + 2px);
padding: 1rem;
}
.form-floating > label {
padding: 1rem;
}
</style>
{% endblock %}

View File

@ -1,22 +1,68 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static %} {% load static %}
{% block title %}Dodaj Projekt - webFirma{% endblock %} {% block title %}Add Project - webFirma{% endblock %}
{% block content %} {% block content %}
<div class="main-header"> <div class="container mt-5">
<h1>Dodaj Nowy Projekt</h1> <div class="row justify-content-center">
</div> <div class="col-md-8">
<div class="card shadow-sm">
<div class="content-grid"> <div class="card-header bg-primary text-white">
<div class="project-card"> <h4 class="mb-0">Create a New Project</h4>
<div class="card-body"> </div>
<form method="post"> <div class="card-body">
{% csrf_token %} <form method="post">
{{ form.as_p }} {% csrf_token %}
<button type="submit" class="btn btn-primary">Zapisz projekt</button> <div class="row">
</form> <div class="col-md-6 mb-3">
<div class="form-floating">
{{ form.name }}
<label for="{{ form.name.id_for_label }}">Project Name</label>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="form-floating">
{{ form.client }}
<label for="{{ form.client.id_for_label }}">Client</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<div class="form-floating">
{{ form.start_date }}
<label for="{{ form.start_date.id_for_label }}">Start Date</label>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="form-floating">
{{ form.end_date }}
<label for="{{ form.end_date.id_for_label }}">End Date</label>
</div>
</div>
</div>
<div class="form-floating mb-3">
{{ form.status }}
<label for="{{ form.status.id_for_label }}">Status</label>
</div>
<div class="d-flex justify-content-end">
<a href="{% url 'projects' %}" class="btn btn-secondary me-2">Cancel</a>
<button type="submit" class="btn btn-primary">Save Project</button>
</div>
</form>
</div>
</div>
</div>
</div> </div>
</div>
</div> </div>
<style>
.form-floating .form-control, .form-floating .form-select {
height: calc(3.5rem + 2px);
padding: 1rem;
}
.form-floating > label {
padding: 1rem;
}
</style>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,45 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}Clients - webFirma{% endblock %}
{% block content %}
<div class="main-header">
<div class="main-header-left">
<h1>Clients</h1>
<p>You have {{ total_clients }} clients</p>
</div>
<div class="header-actions">
<div class="search-bar">
<i data-feather="search"></i>
<input type="text" placeholder="Search clients...">
</div>
<a href="{% url 'add_client' %}" class="btn btn-primary">Add client</a>
</div>
</div>
<div class="content-grid">
{% if clients %}
{% for client in clients %}
<div class="project-card">
<div class="card-header">
<h3>{{ client.name }}</h3>
</div>
<div class="card-body">
<p><strong>Email:</strong> {{ client.email }}</p>
<p><strong>Phone:</strong> {{ client.phone_number }}</p>
</div>
</div>
{% endfor %}
{% else %}
<div class="empty-state">
<div class="empty-state-icon">
<i data-feather="users"></i>
</div>
<h2>No clients yet</h2>
<p>You don't have any clients yet. Click the button to add your first client.</p>
<a href="{% url 'add_client' %}" class="btn btn-primary">Add client</a>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -1,11 +1,11 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static %} {% load static %}
{% block title %}Edytuj Projekt - webFirma{% endblock %} {% block title %}Edit Project - webFirma{% endblock %}
{% block content %} {% block content %}
<div class="main-header"> <div class="main-header">
<h1>Edytuj Projekt</h1> <h1>Edit Project</h1>
</div> </div>
<div class="content-grid"> <div class="content-grid">
@ -14,7 +14,7 @@
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<button type="submit" class="btn btn-primary">Zapisz zmiany</button> <button type="submit" class="btn btn-primary">Save changes</button>
</form> </form>
</div> </div>
</div> </div>

View File

@ -12,7 +12,7 @@
<input type="text" placeholder="Search projects..."> <input type="text" placeholder="Search projects...">
</div> </div>
{% if user.is_authenticated %} {% if user.is_authenticated %}
<a href="{% url 'add_project' %}" class="btn btn-primary"><i data-feather="plus"></i> Dodaj projekt</a> <a href="{% url 'add_project' %}" class="btn btn-primary"><i data-feather="plus"></i> Add project</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -59,7 +59,7 @@
<div class="content-grid"> <div class="content-grid">
{% if request.path == '/projects/' %} {% if request.path == '/projects/' %}
<div class="project-list-header"> <div class="project-list-header">
<h2>Wszystkie projekty</h2> <h2>All Projects</h2>
</div> </div>
{% endif %} {% endif %}
{% if projects %} {% if projects %}
@ -70,8 +70,8 @@
<span class="badge status-{{ project.status|lower }}">{{ project.get_status_display }}</span> <span class="badge status-{{ project.status|lower }}">{{ project.get_status_display }}</span>
</div> </div>
<div class="card-body"> <div class="card-body">
<p><strong>Klient:</strong> {{ project.client }}</p> <p><strong>Client:</strong> {{ project.client.name }}</p>
<p><strong>Okres:</strong> {{ project.start_date|date:"d M Y" }} - {{ project.end_date|date:"d M Y" }}</p> <p><strong>Period:</strong> {{ project.start_date|date:"d M Y" }} - {{ project.end_date|date:"d M Y" }}</p>
</div> </div>
<div class="card-footer"> <div class="card-footer">
@ -87,9 +87,9 @@
<div class="empty-state-icon"> <div class="empty-state-icon">
<i data-feather="briefcase"></i> <i data-feather="briefcase"></i>
</div> </div>
<h2>Brak projektów</h2> <h2>No projects yet</h2>
<p>Nie masz jeszcze żadnych projektów. Dodaj swój pierwszy projekt, aby rozpocząć.</p> <p>You don't have any projects yet. Add your first project to get started.</p>
<a href="{% url 'add_project' %}" class="btn btn-primary">Dodaj projekt</a> <a href="{% url 'add_project' %}" class="btn btn-primary">Add project</a>
</div> </div>
{% endif %} {% endif %}
</div> </div>

View File

@ -1,14 +1,51 @@
{% extends 'base_public.html' %} {% extends 'base_public.html' %}
{% load static %}
{% block title %}Login - webFirma{% endblock %} {% block title %}Login - webFirma{% endblock %}
{% block content %} {% block content %}
<div class="container"> <div class="container-fluid">
<h2>Login</h2> <div class="row min-vh-100">
<form method="post"> <div class="col-md-6 d-flex flex-column justify-content-center align-items-center text-white" style="background: linear-gradient(45deg, #007bff, #0056b3);">
{% csrf_token %} <div class="text-center p-5">
{{ form.as_p }} <h1 class="display-4 font-weight-bold">webFirma</h1>
<button type="submit" class="btn btn-primary">Login</button> <p class="lead mt-3">Streamline your client and project management.</p>
</form> </div>
</div>
<div class="col-md-6 d-flex justify-content-center align-items-center">
<div class="card shadow-lg p-5" style="width: 100%; max-width: 450px;">
<div class="card-body">
<h2 class="card-title text-center mb-4">Login</h2>
<form method="post">
{% csrf_token %}
<div class="form-floating mb-3">
{{ form.username }}
<label for="{{ form.username.id_for_label }}">Username</label>
</div>
<div class="form-floating mb-3">
{{ form.password }}
<label for="{{ form.password.id_for_label }}">Password</label>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg">Login</button>
</div>
</form>
<div class="text-center mt-3">
<p>Don't have an account? <a href="{% url 'register' %}">Register here</a></p>
</div>
</div>
</div>
</div>
</div>
</div> </div>
<style>
.form-floating .form-control {
height: calc(3.5rem + 2px);
padding: 1rem;
}
.form-floating > label {
padding: 1rem;
}
</style>
{% endblock %} {% endblock %}

View File

@ -1,18 +1,18 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static %} {% load static %}
{% block title %}Projekty - webFirma{% endblock %} {% block title %}Projects - webFirma{% endblock %}
{% block content %} {% block content %}
<div class="main-header"> <div class="main-header">
<h1>Projekty</h1> <h1>Projects</h1>
<div class="header-actions"> <div class="header-actions">
<div class="search-bar"> <div class="search-bar">
<i data-feather="search"></i> <i data-feather="search"></i>
<input type="text" placeholder="Search projects..."> <input type="text" placeholder="Search projects...">
</div> </div>
{% if user.is_authenticated %} {% if user.is_authenticated %}
<a href="{% url 'add_project' %}" class="btn btn-primary"><i data-feather="plus"></i> Dodaj projekt</a> <a href="{% url 'add_project' %}" class="btn btn-primary"><i data-feather="plus"></i> Add project</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -26,8 +26,8 @@
<span class="badge status-{{ project.status|lower }}">{{ project.get_status_display }}</span> <span class="badge status-{{ project.status|lower }}">{{ project.get_status_display }}</span>
</div> </div>
<div class="card-body"> <div class="card-body">
<p><strong>Klient:</strong> {{ project.client }}</p> <p><strong>Client:</strong> {{ project.client.name }}</p>
<p><strong>Okres:</strong> {{ project.start_date|date:"d M Y" }} - {{ project.end_date|date:"d M Y" }}</p> <p><strong>Period:</strong> {{ project.start_date|date:"d M Y" }} - {{ project.end_date|date:"d M Y" }}</p>
</div> </div>
<div class="card-footer"> <div class="card-footer">
@ -43,9 +43,9 @@
<div class="empty-state-icon"> <div class="empty-state-icon">
<i data-feather="briefcase"></i> <i data-feather="briefcase"></i>
</div> </div>
<h2>Brak projektów</h2> <h2>No projects yet</h2>
<p>Nie masz jeszcze żadnych projektów. Dodaj swój pierwszy projekt, aby rozpocząć.</p> <p>You don't have any projects yet. Add your first project to get started.</p>
<a href="{% url 'add_project' %}" class="btn btn-primary">Dodaj projekt</a> <a href="{% url 'add_project' %}" class="btn btn-primary">Add project</a>
</div> </div>
{% endif %} {% endif %}
</div> </div>

View File

@ -1,14 +1,59 @@
{% extends 'base_public.html' %} {% extends 'base_public.html' %}
{% load static %}
{% block title %}Register - webFirma{% endblock %} {% block title %}Register - webFirma{% endblock %}
{% block content %} {% block content %}
<div class="container"> <div class="container-fluid">
<h2>Register</h2> <div class="row min-vh-100">
<form method="post"> <div class="col-md-6 d-flex flex-column justify-content-center align-items-center text-white" style="background: linear-gradient(45deg, #007bff, #0056b3);">
{% csrf_token %} <div class="text-center p-5">
{{ form.as_p }} <h1 class="display-4 font-weight-bold">webFirma</h1>
<button type="submit" class="btn btn-primary">Register</button> <p class="lead mt-3">Join us and streamline your workflow.</p>
</form> </div>
</div>
<div class="col-md-6 d-flex justify-content-center align-items-center">
<div class="card shadow-lg p-5" style="width: 100%; max-width: 450px;">
<div class="card-body">
<h2 class="card-title text-center mb-4">Create Account</h2>
<form method="post">
{% csrf_token %}
<div class="form-floating mb-3">
{{ form.username }}
<label for="{{ form.username.id_for_label }}">Username</label>
</div>
<div class="form-floating mb-3">
{{ form.email }}
<label for="{{ form.email.id_for_label }}">Email</label>
</div>
<div class="form-floating mb-3">
{{ form.password }}
<label for="{{ form.password.id_for_label }}">Password</label>
</div>
<div class="form-floating mb-3">
{{ form.password2 }}
<label for="{{ form.password2.id_for_label }}">Confirm Password</label>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg">Register</button>
</div>
</form>
<div class="text-center mt-3">
<p>Already have an account? <a href="{% url 'login' %}">Login here</a></p>
</div>
</div>
</div>
</div>
</div>
</div> </div>
<style>
.form-floating .form-control {
height: calc(3.5rem + 2px);
padding: 1rem;
}
.form-floating > label {
padding: 1rem;
}
</style>
{% endblock %} {% endblock %}

View File

@ -1,10 +1,12 @@
from django.urls import path from django.urls import path
from .views import index, add_project, register, edit_project, delete_project, projects from .views import index, add_project, register, edit_project, delete_project, projects, clients, add_client
from django.contrib.auth.views import LoginView, LogoutView from django.contrib.auth.views import LoginView, LogoutView
urlpatterns = [ urlpatterns = [
path('', index, name='index'), path('', index, name='index'),
path('projects/', projects, name='projects'), path('projects/', projects, name='projects'),
path('clients/', clients, name='clients'),
path('clients/add/', add_client, name='add_client'),
path('add-project/', add_project, name='add_project'), path('add-project/', add_project, name='add_project'),
path('register/', register, name='register'), path('register/', register, name='register'),
path('login/', LoginView.as_view(template_name='core/login.html'), name='login'), path('login/', LoginView.as_view(template_name='core/login.html'), name='login'),

View File

@ -3,17 +3,17 @@ from django.contrib.auth import login
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.views import LoginView, LogoutView from django.contrib.auth.views import LoginView, LogoutView
from .models import Project from .models import Project, Client
from .forms import ProjectForm from .forms import ProjectForm, ClientForm
def index(request): def index(request):
if request.user.is_authenticated: if request.user.is_authenticated:
projects = Project.objects.all().order_by('-start_date') projects = Project.objects.select_related("client").all().order_by('-start_date')
total_projects = projects.count() total_projects = projects.count()
planned_projects = projects.filter(status='planowany').count() planned_projects = projects.filter(status='planning').count()
in_progress_projects = projects.filter(status='realizowany').count() in_progress_projects = projects.filter(status='in_progress').count()
completed_projects = projects.filter(status='zakonczony').count() completed_projects = projects.filter(status='completed').count()
context = { context = {
'projects': projects, 'projects': projects,
@ -68,7 +68,26 @@ def register(request):
def projects(request): def projects(request):
if request.user.is_authenticated: if request.user.is_authenticated:
projects = Project.objects.all().order_by('-start_date') projects = Project.objects.select_related("client").all().order_by('-start_date')
return render(request, 'core/projects.html', {'projects': projects}) return render(request, 'core/projects.html', {'projects': projects})
else: else:
return render(request, 'core/landing.html') return render(request, 'core/landing.html')
def clients(request):
if request.user.is_authenticated:
clients = Client.objects.all()
total_clients = clients.count()
return render(request, "core/clients.html", {"clients": clients, "total_clients": total_clients})
else:
return render(request, "core/landing.html")
@login_required
def add_client(request):
if request.method == 'POST':
form = ClientForm(request.POST)
if form.is_valid():
form.save()
return redirect('clients')
else:
form = ClientForm()
return render(request, 'core/add_client.html', {'form': form})

View File

@ -145,6 +145,21 @@ body {
background-color: var(--primary-dark); background-color: var(--primary-dark);
} }
.btn-secondary {
background-color: var(--white-color);
color: var(--text-color);
padding: 10px 16px;
border-radius: var(--border-radius);
text-decoration: none;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 8px;
border: 1px solid var(--medium-grey);
cursor: pointer;
transition: background-color 0.2s ease;
}
/* Project Cards */ /* Project Cards */
.content-grid { .content-grid {
display: grid; display: grid;
@ -379,3 +394,94 @@ body {
.stat-card.completed { .stat-card.completed {
border-left-color: #2E7D32; border-left-color: #2E7D32;
} }
/* Auth Card */
.auth-card {
background-color: var(--white-color);
border-radius: var(--border-radius);
box-shadow: var(--card-shadow);
padding: 48px;
width: 100%;
max-width: 450px;
}
.auth-card-header h2 {
font-size: 24px;
font-weight: 700;
color: var(--primary-dark);
margin-bottom: 8px;
}
.auth-card-header p {
color: var(--dark-grey);
margin-bottom: 32px;
}
.auth-card form p {
margin-bottom: 16px;
}
.auth-card form label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
.auth-card form input {
width: 100%;
padding: 12px;
border-radius: var(--border-radius);
border: 1px solid var(--medium-grey);
font-size: 14px;
}
.auth-card form .btn-primary {
width: 100%;
justify-content: center;
padding: 14px;
font-size: 16px;
}
/* Form Card */
.form-card {
background-color: var(--white-color);
border-radius: var(--border-radius);
box-shadow: var(--card-shadow);
padding: 32px;
}
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
.form-group {
margin-bottom: 16px;
}
.form-group.full-width {
grid-column: 1 / -1;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
.form-group input,
.form-group select {
width: 100%;
padding: 12px;
border-radius: var(--border-radius);
border: 1px solid var(--medium-grey);
font-size: 14px;
box-sizing: border-box;
}
.form-actions {
margin-top: 32px;
display: flex;
gap: 16px;
}

View File

@ -145,6 +145,21 @@ body {
background-color: var(--primary-dark); background-color: var(--primary-dark);
} }
.btn-secondary {
background-color: var(--white-color);
color: var(--text-color);
padding: 10px 16px;
border-radius: var(--border-radius);
text-decoration: none;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 8px;
border: 1px solid var(--medium-grey);
cursor: pointer;
transition: background-color 0.2s ease;
}
/* Project Cards */ /* Project Cards */
.content-grid { .content-grid {
display: grid; display: grid;
@ -379,3 +394,94 @@ body {
.stat-card.completed { .stat-card.completed {
border-left-color: #2E7D32; border-left-color: #2E7D32;
} }
/* Auth Card */
.auth-card {
background-color: var(--white-color);
border-radius: var(--border-radius);
box-shadow: var(--card-shadow);
padding: 48px;
width: 100%;
max-width: 450px;
}
.auth-card-header h2 {
font-size: 24px;
font-weight: 700;
color: var(--primary-dark);
margin-bottom: 8px;
}
.auth-card-header p {
color: var(--dark-grey);
margin-bottom: 32px;
}
.auth-card form p {
margin-bottom: 16px;
}
.auth-card form label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
.auth-card form input {
width: 100%;
padding: 12px;
border-radius: var(--border-radius);
border: 1px solid var(--medium-grey);
font-size: 14px;
}
.auth-card form .btn-primary {
width: 100%;
justify-content: center;
padding: 14px;
font-size: 16px;
}
/* Form Card */
.form-card {
background-color: var(--white-color);
border-radius: var(--border-radius);
box-shadow: var(--card-shadow);
padding: 32px;
}
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
.form-group {
margin-bottom: 16px;
}
.form-group.full-width {
grid-column: 1 / -1;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
.form-group input,
.form-group select {
width: 100%;
padding: 12px;
border-radius: var(--border-radius);
border: 1px solid var(--medium-grey);
font-size: 14px;
box-sizing: border-box;
}
.form-actions {
margin-top: 32px;
display: flex;
gap: 16px;
}