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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion src/labzero/fe/js/main.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
import Swal from 'sweetalert2';

const toast = Swal.mixin({
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 3500,
timerProgressBar: true,
customClass: {
popup: 'rounded-xl shadow-xl',
},
});

function showToast(detail = {}) {
if (!detail.message) {
return;
}

toast.fire({
icon: detail.type || 'success',
title: detail.message,
});
}

document.addEventListener('DOMContentLoaded', () => {
const showSweetAlertButton = document.getElementById('showSweetAlertButton');

Expand Down Expand Up @@ -31,4 +53,8 @@ document.addEventListener('DOMContentLoaded', () => {
});
});
}
});
});

document.body.addEventListener('labzero:notify', (event) => {
showToast(event.detail);
});
80 changes: 80 additions & 0 deletions src/labzero/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth import password_validation


class ProfileForm(forms.ModelForm):
current_password = forms.CharField(
label="Current password",
required=False,
strip=False,
widget=forms.PasswordInput(render_value=False),
help_text="Required only when setting a new password.",
)
new_password1 = forms.CharField(
label="New password",
required=False,
strip=False,
widget=forms.PasswordInput(render_value=False),
help_text=password_validation.password_validators_help_text_html(),
)
new_password2 = forms.CharField(
label="Confirm new password",
required=False,
strip=False,
widget=forms.PasswordInput(render_value=False),
)

class Meta:
model = get_user_model()
fields = ["name", "email"]

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.password_changed = False

def clean(self):
cleaned_data = super().clean()
current_password = cleaned_data.get("current_password")
new_password1 = cleaned_data.get("new_password1")
new_password2 = cleaned_data.get("new_password2")
password_requested = any([current_password, new_password1, new_password2])

if not password_requested:
return cleaned_data

if not current_password:
self.add_error(
"current_password",
"Enter your current password to set a new password.",
)
elif not self.instance.check_password(current_password):
self.add_error("current_password", "Your current password is incorrect.")

if not new_password1:
self.add_error("new_password1", "Enter a new password.")

if new_password1 and new_password2 and new_password1 != new_password2:
self.add_error("new_password2", "The new passwords do not match.")

if (
new_password1
and not self.errors.get("current_password")
and not self.errors.get("new_password2")
):
password_validation.validate_password(new_password1, self.instance)

return cleaned_data

def save(self, commit=True):
user = super().save(commit=False)
new_password = self.cleaned_data.get("new_password1")

if new_password:
user.set_password(new_password)
self.password_changed = True

if commit:
user.save()

return user
2 changes: 2 additions & 0 deletions src/labzero/templates/labzero/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% vite_asset "css/app.css" "labzero" %}
{% block 'extra_head' %}{% endblock %}
</head>
<body class="font-sans">
{% block 'content' %}{% endblock %}
{% block 'extra_body' %}{% endblock %}
{% vite_asset "js/main.js" "labzero" %}
</body>
</html>
162 changes: 50 additions & 112 deletions src/labzero/templates/labzero/dashboard.html
Original file line number Diff line number Diff line change
@@ -1,118 +1,56 @@
{% extends "base.html" %}
{% extends "labzero_base.html" %}

{% block 'content' %}
<div class="flex h-screen bg-gray-200 font-sans">
<!-- Sidebar -->
<div class="w-64 bg-gray-800 text-white flex flex-col">
<div class="px-8 py-4 border-b border-gray-700">
<h1 class="text-2xl font-bold">Admin Panel</h1>
</div>
<nav class="flex-1 px-4 py-4">
<a href="#" class="flex items-center px-4 py-2 text-gray-300 hover:bg-gray-700 hover:text-white rounded-md">
<svg class="h-6 w-6 mr-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
<span>Home</span>
</a>
<a href="#" class="mt-2 flex items-center px-4 py-2 text-gray-300 hover:bg-gray-700 hover:text-white rounded-md">
<svg class="h-6 w-6 mr-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
<span>Analytics</span>
</a>
<a href="#" class="mt-2 flex items-center px-4 py-2 text-gray-300 hover:bg-gray-700 hover:text-white rounded-md">
<svg class="h-6 w-6 mr-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
<span>Reports</span>
</a>
</nav>
<div class="px-4 py-4 border-t border-gray-700">
<a href="#" class="flex items-center px-4 py-2 text-gray-300 hover:bg-gray-700 hover:text-white rounded-md">
<svg class="h-6 w-6 mr-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span>Profile</span>
</a>
<form action="{% url 'labzero:logout' %}" method="post" class="mt-2">
{% csrf_token %}
<button type="submit" class="flex items-center px-4 py-2 text-gray-300 hover:bg-gray-700 hover:text-white rounded-md w-full text-left">
<svg class="h-6 w-6 mr-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
</svg>
<span>Logout</span>
</button>
</form>
</div>
</div>

<!-- Main content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Header -->
<header class="flex justify-between items-center p-6 bg-green-500 border-b-2 border-gray-200">
<div class="flex items-center">
<h2 class="text-3xl font-bold text-gray-800">Dashboard</h2>
</div>
<div class="flex items-center">
<div class="relative">
<input type="text" placeholder="Search..." class="bg-gray-200 text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white rounded-full py-2 px-4 w-64">
</div>
</div>
</header>
{% block 'page_title' %}Dashboard{% endblock %}

<!-- Content -->
<main class="flex-1 overflow-x-hidden overflow-y-auto bg-gray-200 p-6">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="bg-white rounded-lg shadow-lg p-6">
<h3 class="text-xl font-bold text-gray-800">New Users</h3>
<p class="text-gray-600 mt-2">1,234</p>
</div>
<div class="bg-white rounded-lg shadow-lg p-6">
<h3 class="text-xl font-bold text-gray-800">Sales</h3>
<p class="text-gray-600 mt-2">$56,789</p>
</div>
<div class="bg-white rounded-lg shadow-lg p-6">
<h3 class="text-xl font-bold text-gray-800">Subscriptions</h3>
<p class="text-gray-600 mt-2">5,432</p>
</div>
</div>
{% block 'page_content' %}
<div class="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
<div class="rounded-lg bg-white p-6 shadow-lg">
<h3 class="text-xl font-bold text-gray-800">New Users</h3>
<p class="mt-2 text-gray-600">1,234</p>
</div>
<div class="rounded-lg bg-white p-6 shadow-lg">
<h3 class="text-xl font-bold text-gray-800">Sales</h3>
<p class="mt-2 text-gray-600">$56,789</p>
</div>
<div class="rounded-lg bg-white p-6 shadow-lg">
<h3 class="text-xl font-bold text-gray-800">Subscriptions</h3>
<p class="mt-2 text-gray-600">5,432</p>
</div>
</div>

<div class="mt-6 flex justify-center">
<button id="showSweetAlertButton" class="px-6 py-3 bg-green-500 text-white rounded-md hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-opacity-50">
Show SweetAlert Modal
</button>
</div>
<div class="mt-6 flex justify-center">
<button id="showSweetAlertButton" class="rounded-md bg-green-500 px-6 py-3 text-white hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-opacity-50">
Show SweetAlert Modal
</button>
</div>

<div class="mt-6 bg-white rounded-lg shadow-lg p-6">
<h3 class="text-xl font-bold text-gray-800 mb-4">Recent Activity</h3>
<table class="w-full text-left">
<thead>
<tr>
<th class="py-2">User</th>
<th class="py-2">Activity</th>
<th class="py-2">Time</th>
</tr>
</thead>
<tbody class="text-gray-600">
<tr>
<td class="py-2">John Doe</td>
<td class="py-2">Upgraded to Pro</td>
<td class="py-2">2 hours ago</td>
</tr>
<tr>
<td class="py-2">Jane Smith</td>
<td class="py-2">Joined</td>
<td class="py-2">5 hours ago</td>
</tr>
<tr>
<td class="py-2">Sam Wilson</td>
<td class="py-2">Updated profile</td>
<td class="py-2">1 day ago</td>
</tr>
</tbody>
</table>
</div>
</main>
</div>
<div class="mt-6 rounded-lg bg-white p-6 shadow-lg">
<h3 class="mb-4 text-xl font-bold text-gray-800">Recent Activity</h3>
<table class="w-full text-left">
<thead>
<tr>
<th class="py-2">User</th>
<th class="py-2">Activity</th>
<th class="py-2">Time</th>
</tr>
</thead>
<tbody class="text-gray-600">
<tr>
<td class="py-2">John Doe</td>
<td class="py-2">Upgraded to Pro</td>
<td class="py-2">2 hours ago</td>
</tr>
<tr>
<td class="py-2">Jane Smith</td>
<td class="py-2">Joined</td>
<td class="py-2">5 hours ago</td>
</tr>
<tr>
<td class="py-2">Sam Wilson</td>
<td class="py-2">Updated profile</td>
<td class="py-2">1 day ago</td>
</tr>
</tbody>
</table>
</div>
{% endblock %}
21 changes: 21 additions & 0 deletions src/labzero/templates/labzero/profile_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% extends "labzero_base.html" %}
{% load django_umin_vite %}

{% block 'extra_head' %}
{{ block.super }}
{% vite_asset "css/app.css" "django_umin" %}
{% endblock %}

{% block 'page_title' %}Profile Settings{% endblock %}

{% block 'page_content' %}
<div class="mx-auto max-w-4xl space-y-6">
{% include "django_umin/includes/messages.html" %}
{% include "django_umin/includes/form_page.html" %}
</div>
{% endblock %}

{% block 'extra_body' %}
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
{% endblock %}
1 change: 1 addition & 0 deletions src/labzero/templates/labzero/profile_form_htmx.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% include "django_umin/includes/form_card.html" %}
12 changes: 6 additions & 6 deletions src/labzero/templates/labzero_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
<div class="w-64 bg-gray-800 text-white flex flex-col">
<div class="px-8 py-4 border-b border-gray-700">
<h1 class="text-2xl font-bold">
<a href="{% url 'dashboard' %}" class="text-white hover:text-gray-300">
<a href="{% url 'labzero:dashboard' %}" class="text-white hover:text-gray-300">
LabZero
</a>
</h1>
</div>
<nav class="flex-1 px-4 py-4">
<a href="{% url 'dashboard' %}" class="flex items-center px-4 py-2 text-gray-300 hover:bg-gray-700 hover:text-white rounded-md">
<a href="{% url 'labzero:dashboard' %}" class="flex items-center px-4 py-2 text-gray-300 hover:bg-gray-700 hover:text-white rounded-md {% if request.resolver_match.url_name == 'dashboard' %}bg-gray-700 text-white{% endif %}">
<svg class="h-6 w-6 mr-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
Expand All @@ -32,14 +32,14 @@ <h1 class="text-2xl font-bold">
</a>
</nav>
<div class="px-4 py-4 border-t border-gray-700">
<a href="#" class="flex items-center px-4 py-2 text-gray-300 hover:bg-gray-700 hover:text-white rounded-md">
<a href="{% url 'labzero:profile' %}" class="flex items-center px-4 py-2 text-gray-300 hover:bg-gray-700 hover:text-white rounded-md {% if request.resolver_match.url_name == 'profile' %}bg-gray-700 text-white{% endif %}">
<svg class="h-6 w-6 mr-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span>Profile</span>
</a>
{% if user.is_authenticated %}
<form action="{% url 'logout' %}" method="post" class="mt-2">
<form action="{% url 'labzero:logout' %}" method="post" class="mt-2">
{% csrf_token %}
<button type="submit" class="flex items-center px-4 py-2 text-gray-300 hover:bg-gray-700 hover:text-white rounded-md w-full text-left">
<svg class="h-6 w-6 mr-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
Expand All @@ -62,7 +62,7 @@ <h1 class="text-2xl font-bold">
<!-- Main content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Header -->
<header class="flex justify-between items-center p-6 bg-white border-b-2 border-gray-200">
<header class="flex justify-between items-center border-b-2 border-gray-200 bg-green-500 p-6">
<div class="flex items-center">
<h2 class="text-3xl font-bold text-gray-800">
{% block 'page_title' %}LabZero Application{% endblock %}
Expand All @@ -84,4 +84,4 @@ <h2 class="text-3xl font-bold text-gray-800">
</main>
</div>
</div>
{% endblock %}
{% endblock %}
5 changes: 5 additions & 0 deletions src/labzero/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ def get_urlpatterns():
"""
urlpatterns = [
path("", labzero_views.dashboard, name="dashboard"),
path(
"profile/",
labzero_views.ProfileUpdateView.as_view(crud_view=labzero_views.ProfileCRUD()),
name="profile",
),
path(
"login/",
labzero_views.LoginView.as_view(),
Expand Down
Loading