clients
This commit is contained in:
parent
cd0902b381
commit
5b190bc3f3
5
cookies.txt
Normal file
5
cookies.txt
Normal 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
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +1,14 @@
|
||||
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',)
|
||||
@ -1,5 +1,5 @@
|
||||
from django import forms
|
||||
from .models import Project
|
||||
from .models import Project, Client
|
||||
|
||||
class ProjectForm(forms.ModelForm):
|
||||
class Meta:
|
||||
@ -7,8 +7,18 @@ class ProjectForm(forms.ModelForm):
|
||||
fields = ['name', 'client', 'start_date', 'end_date', 'status']
|
||||
widgets = {
|
||||
'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'}),
|
||||
'end_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
||||
'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'}),
|
||||
}
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
Binary file not shown.
@ -1,17 +1,25 @@
|
||||
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):
|
||||
STATUS_CHOICES = [
|
||||
('planowany', 'Planowany'),
|
||||
('realizowany', 'Realizowany'),
|
||||
('zakonczony', 'Zakończony'),
|
||||
('planning', 'Planning'),
|
||||
('in_progress', 'In Progress'),
|
||||
('completed', 'Completed'),
|
||||
]
|
||||
|
||||
name = models.CharField(max_length=200, verbose_name="Nazwa projektu")
|
||||
client = models.CharField(max_length=200, verbose_name="Kontrahent")
|
||||
start_date = models.DateField(verbose_name="Data rozpoczęcia")
|
||||
end_date = models.DateField(verbose_name="Data zakończenia")
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='planowany', verbose_name="Status")
|
||||
name = models.CharField(max_length=200, verbose_name="Project Name")
|
||||
client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='projects', verbose_name="Client", null=True, blank=True)
|
||||
start_date = models.DateField(verbose_name="Start Date")
|
||||
end_date = models.DateField(verbose_name="End Date")
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='planning', verbose_name="Status")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@ -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 '/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="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>
|
||||
</ul>
|
||||
<ul class="auth-nav">
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
<meta property="twitter:image" content="{{ project_image_url }}">
|
||||
{% endif %}
|
||||
{% 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.gstatic.com" crossorigin>
|
||||
<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 %}
|
||||
</main>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
feather.replace()
|
||||
</script>
|
||||
|
||||
48
core/templates/core/add_client.html
Normal file
48
core/templates/core/add_client.html
Normal 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 %}
|
||||
@ -1,22 +1,68 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Dodaj Projekt - webFirma{% endblock %}
|
||||
{% block title %}Add Project - webFirma{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="main-header">
|
||||
<h1>Dodaj Nowy Projekt</h1>
|
||||
</div>
|
||||
|
||||
<div class="content-grid">
|
||||
<div class="project-card">
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="btn btn-primary">Zapisz projekt</button>
|
||||
</form>
|
||||
<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 Project</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
<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>
|
||||
<style>
|
||||
.form-floating .form-control, .form-floating .form-select {
|
||||
height: calc(3.5rem + 2px);
|
||||
padding: 1rem;
|
||||
}
|
||||
.form-floating > label {
|
||||
padding: 1rem;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
45
core/templates/core/clients.html
Normal file
45
core/templates/core/clients.html
Normal 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 %}
|
||||
@ -1,11 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Edytuj Projekt - webFirma{% endblock %}
|
||||
{% block title %}Edit Project - webFirma{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="main-header">
|
||||
<h1>Edytuj Projekt</h1>
|
||||
<h1>Edit Project</h1>
|
||||
</div>
|
||||
|
||||
<div class="content-grid">
|
||||
@ -14,7 +14,7 @@
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="btn btn-primary">Zapisz zmiany</button>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
<input type="text" placeholder="Search projects...">
|
||||
</div>
|
||||
{% 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 %}
|
||||
</div>
|
||||
</div>
|
||||
@ -59,7 +59,7 @@
|
||||
<div class="content-grid">
|
||||
{% if request.path == '/projects/' %}
|
||||
<div class="project-list-header">
|
||||
<h2>Wszystkie projekty</h2>
|
||||
<h2>All Projects</h2>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if projects %}
|
||||
@ -70,8 +70,8 @@
|
||||
<span class="badge status-{{ project.status|lower }}">{{ project.get_status_display }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p><strong>Klient:</strong> {{ project.client }}</p>
|
||||
<p><strong>Okres:</strong> {{ project.start_date|date:"d M Y" }} - {{ project.end_date|date:"d M Y" }}</p>
|
||||
<p><strong>Client:</strong> {{ project.client.name }}</p>
|
||||
<p><strong>Period:</strong> {{ project.start_date|date:"d M Y" }} - {{ project.end_date|date:"d M Y" }}</p>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
|
||||
@ -87,9 +87,9 @@
|
||||
<div class="empty-state-icon">
|
||||
<i data-feather="briefcase"></i>
|
||||
</div>
|
||||
<h2>Brak projektów</h2>
|
||||
<p>Nie masz jeszcze żadnych projektów. Dodaj swój pierwszy projekt, aby rozpocząć.</p>
|
||||
<a href="{% url 'add_project' %}" class="btn btn-primary">Dodaj projekt</a>
|
||||
<h2>No projects yet</h2>
|
||||
<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">Add project</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@ -1,14 +1,51 @@
|
||||
{% extends 'base_public.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Login - webFirma{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h2>Login</h2>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="btn btn-primary">Login</button>
|
||||
</form>
|
||||
<div class="container-fluid">
|
||||
<div class="row min-vh-100">
|
||||
<div class="col-md-6 d-flex flex-column justify-content-center align-items-center text-white" style="background: linear-gradient(45deg, #007bff, #0056b3);">
|
||||
<div class="text-center p-5">
|
||||
<h1 class="display-4 font-weight-bold">webFirma</h1>
|
||||
<p class="lead mt-3">Streamline your client and project management.</p>
|
||||
</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>
|
||||
|
||||
<style>
|
||||
.form-floating .form-control {
|
||||
height: calc(3.5rem + 2px);
|
||||
padding: 1rem;
|
||||
}
|
||||
.form-floating > label {
|
||||
padding: 1rem;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Projekty - webFirma{% endblock %}
|
||||
{% block title %}Projects - webFirma{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="main-header">
|
||||
<h1>Projekty</h1>
|
||||
<h1>Projects</h1>
|
||||
<div class="header-actions">
|
||||
<div class="search-bar">
|
||||
<i data-feather="search"></i>
|
||||
<input type="text" placeholder="Search projects...">
|
||||
</div>
|
||||
{% 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 %}
|
||||
</div>
|
||||
</div>
|
||||
@ -26,8 +26,8 @@
|
||||
<span class="badge status-{{ project.status|lower }}">{{ project.get_status_display }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p><strong>Klient:</strong> {{ project.client }}</p>
|
||||
<p><strong>Okres:</strong> {{ project.start_date|date:"d M Y" }} - {{ project.end_date|date:"d M Y" }}</p>
|
||||
<p><strong>Client:</strong> {{ project.client.name }}</p>
|
||||
<p><strong>Period:</strong> {{ project.start_date|date:"d M Y" }} - {{ project.end_date|date:"d M Y" }}</p>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
|
||||
@ -43,9 +43,9 @@
|
||||
<div class="empty-state-icon">
|
||||
<i data-feather="briefcase"></i>
|
||||
</div>
|
||||
<h2>Brak projektów</h2>
|
||||
<p>Nie masz jeszcze żadnych projektów. Dodaj swój pierwszy projekt, aby rozpocząć.</p>
|
||||
<a href="{% url 'add_project' %}" class="btn btn-primary">Dodaj projekt</a>
|
||||
<h2>No projects yet</h2>
|
||||
<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">Add project</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@ -1,14 +1,59 @@
|
||||
{% extends 'base_public.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Register - webFirma{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h2>Register</h2>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="btn btn-primary">Register</button>
|
||||
</form>
|
||||
<div class="container-fluid">
|
||||
<div class="row min-vh-100">
|
||||
<div class="col-md-6 d-flex flex-column justify-content-center align-items-center text-white" style="background: linear-gradient(45deg, #007bff, #0056b3);">
|
||||
<div class="text-center p-5">
|
||||
<h1 class="display-4 font-weight-bold">webFirma</h1>
|
||||
<p class="lead mt-3">Join us and streamline your workflow.</p>
|
||||
</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>
|
||||
|
||||
<style>
|
||||
.form-floating .form-control {
|
||||
height: calc(3.5rem + 2px);
|
||||
padding: 1rem;
|
||||
}
|
||||
.form-floating > label {
|
||||
padding: 1rem;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
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
|
||||
|
||||
urlpatterns = [
|
||||
path('', index, name='index'),
|
||||
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('register/', register, name='register'),
|
||||
path('login/', LoginView.as_view(template_name='core/login.html'), name='login'),
|
||||
|
||||
@ -3,17 +3,17 @@ from django.contrib.auth import login
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth.views import LoginView, LogoutView
|
||||
from .models import Project
|
||||
from .forms import ProjectForm
|
||||
from .models import Project, Client
|
||||
from .forms import ProjectForm, ClientForm
|
||||
|
||||
def index(request):
|
||||
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()
|
||||
planned_projects = projects.filter(status='planowany').count()
|
||||
in_progress_projects = projects.filter(status='realizowany').count()
|
||||
completed_projects = projects.filter(status='zakonczony').count()
|
||||
planned_projects = projects.filter(status='planning').count()
|
||||
in_progress_projects = projects.filter(status='in_progress').count()
|
||||
completed_projects = projects.filter(status='completed').count()
|
||||
|
||||
context = {
|
||||
'projects': projects,
|
||||
@ -68,7 +68,26 @@ def register(request):
|
||||
|
||||
def projects(request):
|
||||
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})
|
||||
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})
|
||||
|
||||
@ -145,6 +145,21 @@ body {
|
||||
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 */
|
||||
.content-grid {
|
||||
display: grid;
|
||||
@ -379,3 +394,94 @@ body {
|
||||
.stat-card.completed {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -145,6 +145,21 @@ body {
|
||||
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 */
|
||||
.content-grid {
|
||||
display: grid;
|
||||
@ -379,3 +394,94 @@ body {
|
||||
.stat-card.completed {
|
||||
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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user