first commit

This commit is contained in:
2026-10-01 15:50:43 +07:00
commit debeb8773c
48 changed files with 7547 additions and 0 deletions

0
accounts/__init__.py Normal file
View File

3
accounts/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

9
accounts/apps.py Normal file
View 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

View File

3
accounts/models.py Normal file
View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

13
accounts/signals.py Normal file
View 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()

View 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
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

13
accounts/urls.py Normal file
View 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
View 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')