first commit
This commit is contained in:
0
accounts/__init__.py
Normal file
0
accounts/__init__.py
Normal file
3
accounts/admin.py
Normal file
3
accounts/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
9
accounts/apps.py
Normal file
9
accounts/apps.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class AccountsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'accounts'
|
||||
|
||||
def ready(self):
|
||||
# Mendaftarkan file signals saat aplikasi Django siap dijalankan
|
||||
import accounts.signals
|
||||
0
accounts/migrations/__init__.py
Normal file
0
accounts/migrations/__init__.py
Normal file
3
accounts/models.py
Normal file
3
accounts/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
13
accounts/signals.py
Normal file
13
accounts/signals.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def deactivate_new_ldap_user(sender, instance, created, **kwargs):
|
||||
# Jika user baru saja dibuat di database lokal dan dia adalah akun LDAP
|
||||
# (ditandai dengan tidak adanya password lokal/usable password)
|
||||
if created and not instance.has_usable_password():
|
||||
# Otomatis matikan keaktifannya agar statusnya menjadi "Menunggu Persetujuan"
|
||||
instance.is_active = False
|
||||
instance.save()
|
||||
|
||||
251
accounts/templates/accounts/login.html
Normal file
251
accounts/templates/accounts/login.html
Normal file
@@ -0,0 +1,251 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - MONITORING ENERGY PT PRIMA LESTARI NUSANTARA</title>
|
||||
<!-- Import FontAwesome untuk ikon petir jika diperlukan -->
|
||||
<link rel="stylesheet" href="https://cloudflare.com">
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
/* Latar belakang gelap lembut netral */
|
||||
background-color: #f1f5f9;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 1fr;
|
||||
/* Menggunakan warna biru tua sidebar #1e2640 / #1a2238 */
|
||||
background: #1a2238;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.15), 0 10px 10px -5px rgba(0, 0, 0, 0.1);
|
||||
width: 100%;
|
||||
max-width: 850px;
|
||||
min-height: 500px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Sisi Kiri: Branding & Identity (Selaras dengan warna brand sidebar) */
|
||||
.brand-section {
|
||||
background: #222c4c; /* Sedikit lebih terang dari sisi kanan untuk dimensi visual */
|
||||
padding: 45px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
/* Badge putih minimalis pembungkus logo FLN */
|
||||
.brand-logo {
|
||||
background: #ffffff;
|
||||
padding: 10px 16px;
|
||||
border-radius: 6px;
|
||||
display: inline-block;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.15);
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.brand-logo img {
|
||||
height: 32px;
|
||||
width: auto;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.brand-info {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
/* Judul aplikasi menggunakan aksen ikon petir orange */
|
||||
.brand-info h1 {
|
||||
color: #ffffff;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
margin-bottom: 12px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.brand-info h1 span {
|
||||
color: #f97316; /* Aksen Orange seperti tombol aktif dashboard */
|
||||
}
|
||||
|
||||
.brand-info .company-name {
|
||||
color: #94a3b8;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 15px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.brand-info p {
|
||||
color: #94a3b8;
|
||||
font-size: 13.5px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Sisi Kanan: Form Otentikasi */
|
||||
.form-section {
|
||||
padding: 45px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
background: #1a2238; /* Warna murni sidebar utama */
|
||||
}
|
||||
|
||||
.form-section h2 {
|
||||
color: #ffffff;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
color: #94a3b8;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 11px 14px;
|
||||
background-color: #111827; /* Input field gelap kontras */
|
||||
border: 1px solid #374151;
|
||||
border-radius: 6px;
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.form-group input:focus {
|
||||
outline: none;
|
||||
border-color: #f97316; /* Fokus input sewarna dengan orange brand */
|
||||
box-shadow: 0 0 0 2px rgba(249, 115, 22, 0.2);
|
||||
}
|
||||
|
||||
.form-group input::placeholder {
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
/* TOMBOL UTAMA: Menggunakan warna Orange Khas Dashboard Anda (#f97316 / #ea580c) */
|
||||
.btn-submit {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: #f97316;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
transition: background 0.2s ease;
|
||||
margin-top: 10px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.btn-submit:hover {
|
||||
background: #ea580c; /* Warna orange sedikit lebih tua saat di-hover */
|
||||
}
|
||||
|
||||
.error-container {
|
||||
background-color: rgba(239, 68, 68, 0.15);
|
||||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
color: #fca5a5;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Responsif untuk Layar Handphone / Tablet */
|
||||
@media (max-width: 768px) {
|
||||
.login-container {
|
||||
grid-template-columns: 1fr;
|
||||
max-width: 420px;
|
||||
}
|
||||
.brand-section {
|
||||
padding: 35px;
|
||||
min-height: 200px;
|
||||
justify-content: flex-start;
|
||||
gap: 25px;
|
||||
}
|
||||
.brand-info {
|
||||
margin-top: 0;
|
||||
}
|
||||
.form-section {
|
||||
padding: 35px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="login-container">
|
||||
|
||||
<!-- Sisi Kiri: Informasi Brand Internal -->
|
||||
<div class="brand-section">
|
||||
<div class="brand-logo">
|
||||
<img src="{% static 'images/FLN.png' %}" alt="Logo Perusahaan">
|
||||
</div>
|
||||
<div class="brand-info">
|
||||
<span class="company-name">PT. FRINA LESTARI NUSANTARA</span>
|
||||
<h1><i class="fa-solid fa-bolt" style="color: #f97316; margin-right: 4px;"></i> MONITORING <span>ENERGY</span></h1>
|
||||
<p>Sistem Informasi Pengawasan Distribusi Daya & Monitoring Real-time parameter listrik berbasis Web.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sisi Kanan: Form Input Akun -->
|
||||
<div class="form-section">
|
||||
<h2>Sign In</h2>
|
||||
|
||||
<!-- Validasi Pesan Error Django -->
|
||||
{% if messages %}
|
||||
<div class="error-container">
|
||||
{% for message in messages %}
|
||||
<div class="error-text">• {{ message }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label for="id_username">Username</label>
|
||||
<input type="text" name="username" placeholder="Masukkan nama pengguna" required id="id_username" autocomplete="username">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="id_password">Password</label>
|
||||
<input type="password" name="password" placeholder="••••••••" required id="id_password" autocomplete="current-password">
|
||||
</div>
|
||||
<button type="submit" class="btn-submit">Masuk ke Sistem</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
3
accounts/tests.py
Normal file
3
accounts/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
13
accounts/urls.py
Normal file
13
accounts/urls.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from django.urls import path
|
||||
from .views import login_view, logout_view, user_management_view, toggle_user_role
|
||||
|
||||
urlpatterns = [
|
||||
# UBAH BARIS INI (Hapus 'login/' menjadi '')
|
||||
path('', login_view, name='login'),
|
||||
|
||||
path('logout/', logout_view, name='logout'),
|
||||
|
||||
# Route baru untuk User Accounts
|
||||
path('users/', user_management_view, name='user_management'),
|
||||
path('users/toggle-role/<int:user_id>/', toggle_user_role, name='toggle_user_role'),
|
||||
]
|
||||
87
accounts/views.py
Normal file
87
accounts/views.py
Normal file
@@ -0,0 +1,87 @@
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.contrib import messages
|
||||
|
||||
def login_view(request):
|
||||
# Jika user sudah login, langsung lempar ke dashboard
|
||||
if request.user.is_authenticated:
|
||||
return redirect('dashboard')
|
||||
|
||||
if request.method == 'POST':
|
||||
username_input = request.POST.get('username')
|
||||
password_input = request.POST.get('password')
|
||||
|
||||
# authenticate() akan mencoba LDAP Backend dulu, jika gagal/error akan dilanjutkan ke ModelBackend Lokal
|
||||
user = authenticate(request, username=username_input, password=password_input)
|
||||
|
||||
if user is not None:
|
||||
# 📌 VALIDASI BARU: Cek apakah user berstatus aktif (Sudah di-accept oleh Admin)
|
||||
if not user.is_active:
|
||||
messages.error(request, "Akun Anda berhasil terdaftar. Harap tunggu persetujuan dari Departmen IT sebelum dapat masuk.")
|
||||
return render(request, 'accounts/login.html')
|
||||
|
||||
login(request, user)
|
||||
return redirect('dashboard')
|
||||
else:
|
||||
messages.error(request, "Username atau Password salah, atau akun LDAP Anda kedaluwarsa.")
|
||||
|
||||
return render(request, 'accounts/login.html')
|
||||
|
||||
@csrf_exempt
|
||||
def logout_view(request):
|
||||
logout(request)
|
||||
return redirect('login')
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# KODE UNTUK HALAMAN USER ACCOUNTS
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
def is_admin(user):
|
||||
return user.is_superuser or user.is_staff
|
||||
|
||||
@login_required
|
||||
@user_passes_test(is_admin)
|
||||
def user_management_view(request):
|
||||
users = User.objects.all().order_by('-date_joined')
|
||||
|
||||
for user in users:
|
||||
# Cek tipe akun: User LDAP tidak memiliki password terenkripsi usable di DB Django
|
||||
if not user.has_usable_password():
|
||||
user.account_type = "LDAP"
|
||||
else:
|
||||
user.account_type = "Lokal"
|
||||
|
||||
return render(request, 'dashboard/user_accounts.html', {'users_list': users})
|
||||
|
||||
@login_required
|
||||
@user_passes_test(is_admin)
|
||||
def toggle_user_role(request, user_id):
|
||||
if request.method == "POST":
|
||||
target_user = get_object_or_404(User, id=user_id)
|
||||
|
||||
# Mencegah pengguna mengubah role dirinya sendiri
|
||||
if target_user == request.user:
|
||||
messages.error(request, "Anda tidak bisa mengubah role akun Anda sendiri!")
|
||||
return redirect('user_management')
|
||||
|
||||
role = request.POST.get('role')
|
||||
|
||||
# 📌 AKSI BARU: Menerima/Mengaktifkan User LDAP Baru
|
||||
if role == 'accept':
|
||||
target_user.is_active = True
|
||||
messages.success(request, f"Akun {target_user.username} telah disetujui dan berhasil diaktifkan.")
|
||||
elif role == 'admin':
|
||||
target_user.is_staff = True
|
||||
target_user.is_superuser = True
|
||||
messages.success(request, f"Role {target_user.username} berhasil diubah menjadi Administrator.")
|
||||
else:
|
||||
target_user.is_staff = False
|
||||
target_user.is_superuser = False
|
||||
messages.success(request, f"Role {target_user.username} berhasil diubah menjadi User Biasa.")
|
||||
|
||||
target_user.save()
|
||||
|
||||
return redirect('user_management')
|
||||
Reference in New Issue
Block a user