From 74e7ee886e99d57d8dcb8d80e381b1b046acccd9 Mon Sep 17 00:00:00 2001
From: Andrew4Coding <andrewdevitoaryo@gmail.com>
Date: Mon, 17 Mar 2025 15:02:53 +0700
Subject: [PATCH] fix: models

---
 education/admin.py                            |  4 -
 education/apps.py                             |  6 --
 education/forms.py                            | 24 -----
 education/migrations/0001_initial.py          | 25 ------
 .../migrations/0002_alter_edukasi_npm.py      | 18 ----
 education/models.py                           | 39 --------
 education/templates/education/create.html     | 29 ------
 education/templates/education/edit.html       | 32 -------
 education/templates/education/index.html      | 50 -----------
 education/urls.py                             |  9 --
 education/views.py                            | 40 ---------
 finance/__init__.py                           |  0
 finance/admin.py                              |  4 -
 finance/forms.py                              | 27 ------
 finance/migrations/0001_initial.py            | 25 ------
 finance/migrations/__init__.py                |  0
 finance/models.py                             | 38 --------
 finance/templates/finance/create.html         | 27 ------
 finance/templates/finance/edit.html           | 30 -------
 finance/templates/finance/index.html          | 50 -----------
 finance/tests.py                              |  3 -
 finance/urls.py                               |  9 --
 finance/views.py                              | 40 ---------
 finance/widgets.py                            | 27 ------
 main/settings.py                              |  8 +-
 main/urls.py                                  |  3 +-
 main/views.py                                 |  4 +-
 templates/base.html                           |  9 +-
 templates/components/button.html              |  4 +-
 templates/components/input.html               |  2 +-
 templates/components/navbar.html              | 38 ++------
 templates/index.html                          | 26 +++---
 {education => user}/__init__.py               |  0
 user/admin.py                                 | 30 +++++++
 {finance => user}/apps.py                     |  4 +-
 user/forms.py                                 | 49 +++++++++++
 user/migrations/0001_initial.py               | 51 +++++++++++
 {education => user}/migrations/__init__.py    |  0
 user/models.py                                | 88 +++++++++++++++++++
 user/templates/login.html                     | 22 +++++
 user/templates/register.html                  | 38 ++++++++
 {education => user}/tests.py                  |  0
 user/urls.py                                  |  8 ++
 user/views.py                                 | 51 +++++++++++
 {education => user}/widgets.py                |  0
 45 files changed, 373 insertions(+), 618 deletions(-)
 delete mode 100644 education/admin.py
 delete mode 100644 education/apps.py
 delete mode 100644 education/forms.py
 delete mode 100644 education/migrations/0001_initial.py
 delete mode 100644 education/migrations/0002_alter_edukasi_npm.py
 delete mode 100644 education/models.py
 delete mode 100644 education/templates/education/create.html
 delete mode 100644 education/templates/education/edit.html
 delete mode 100644 education/templates/education/index.html
 delete mode 100644 education/urls.py
 delete mode 100644 education/views.py
 delete mode 100644 finance/__init__.py
 delete mode 100644 finance/admin.py
 delete mode 100644 finance/forms.py
 delete mode 100644 finance/migrations/0001_initial.py
 delete mode 100644 finance/migrations/__init__.py
 delete mode 100644 finance/models.py
 delete mode 100644 finance/templates/finance/create.html
 delete mode 100644 finance/templates/finance/edit.html
 delete mode 100644 finance/templates/finance/index.html
 delete mode 100644 finance/tests.py
 delete mode 100644 finance/urls.py
 delete mode 100644 finance/views.py
 delete mode 100644 finance/widgets.py
 rename {education => user}/__init__.py (100%)
 create mode 100644 user/admin.py
 rename {finance => user}/apps.py (63%)
 create mode 100644 user/forms.py
 create mode 100644 user/migrations/0001_initial.py
 rename {education => user}/migrations/__init__.py (100%)
 create mode 100644 user/models.py
 create mode 100644 user/templates/login.html
 create mode 100644 user/templates/register.html
 rename {education => user}/tests.py (100%)
 create mode 100644 user/urls.py
 create mode 100644 user/views.py
 rename {education => user}/widgets.py (100%)

diff --git a/education/admin.py b/education/admin.py
deleted file mode 100644
index 7dfd38e..0000000
--- a/education/admin.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from django.contrib import admin
-from .models import Edukasi
-
-admin.site.register(Edukasi)
\ No newline at end of file
diff --git a/education/apps.py b/education/apps.py
deleted file mode 100644
index 4a92e70..0000000
--- a/education/apps.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from django.apps import AppConfig
-
-
-class EducationConfig(AppConfig):
-    default_auto_field = 'django.db.models.BigAutoField'
-    name = 'education'
diff --git a/education/forms.py b/education/forms.py
deleted file mode 100644
index 077ecfd..0000000
--- a/education/forms.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from django import forms
-from .models import Edukasi
-from .widgets import TailwindInput
-
-class EdukasiForm(forms.ModelForm):
-    class Meta:
-        model = Edukasi
-        fields = '__all__'
-        widgets = {
-            'npm': TailwindInput(input_type="number", attrs={'placeholder': 'Enter NPM (10 digits)'}),
-            'ipk': TailwindInput(input_type="number", attrs={'step': '0.01', 'placeholder': 'Enter IPK (0.00 - 4.00)'}),
-            'tahun_masuk': TailwindInput(input_type="number", attrs={'min': 1900, 'max': 2024, 'placeholder': 'Enter Year of Admission'}),
-            'kode_prodi': TailwindInput(input_type="text", attrs={'placeholder': 'Enter Program Code (e.g., INF12345)'}),
-            'tingkat_pendidikan': forms.Select(
-                attrs={
-                    'class': 'mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm'
-                }
-            ),
-        }
-
-    labels = {
-        'tingkat_pendidikan': 'Tingkat Pendidikan',
-    }
-
diff --git a/education/migrations/0001_initial.py b/education/migrations/0001_initial.py
deleted file mode 100644
index 113222d..0000000
--- a/education/migrations/0001_initial.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 5.1.7 on 2025-03-13 13:41
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Edukasi',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('npm', models.BigIntegerField(unique=True)),
-                ('ipk', models.DecimalField(decimal_places=2, max_digits=4)),
-                ('tahun_masuk', models.PositiveIntegerField()),
-                ('kode_prodi', models.CharField(max_length=8)),
-                ('tingkat_pendidikan', models.CharField(choices=[('S1', 'S1'), ('S2', 'S2'), ('S3', 'S3')], max_length=2)),
-            ],
-        ),
-    ]
diff --git a/education/migrations/0002_alter_edukasi_npm.py b/education/migrations/0002_alter_edukasi_npm.py
deleted file mode 100644
index f4437b7..0000000
--- a/education/migrations/0002_alter_edukasi_npm.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.1.7 on 2025-03-14 12:55
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('education', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='edukasi',
-            name='npm',
-            field=models.CharField(max_length=10, unique=True),
-        ),
-    ]
diff --git a/education/models.py b/education/models.py
deleted file mode 100644
index 10306e5..0000000
--- a/education/models.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from django.db import models
-from django.core.exceptions import ValidationError
-import re
-
-class Edukasi(models.Model):
-    TINGKAT_PENDIDIKAN_CHOICES = [
-        ('S1', 'S1'),
-        ('S2', 'S2'),
-        ('S3', 'S3')
-    ]
-
-    npm = models.CharField(max_length=10, unique=True)  # Changed to CharField with max_length of 10
-    ipk = models.DecimalField(max_digits=4, decimal_places=2)  
-    tahun_masuk = models.PositiveIntegerField()  
-    kode_prodi = models.CharField(max_length=8)  
-    tingkat_pendidikan = models.CharField(
-        max_length=2,
-        choices=TINGKAT_PENDIDIKAN_CHOICES
-    )
-
-    def clean(self):
-        # Handle None values safely
-        if self.npm and not len(self.npm) == 10:
-            raise ValidationError({'npm': "NPM harus terdiri dari 10 digit angka."})
-        
-        if self.ipk is not None and not (0 <= self.ipk <= 4):
-            raise ValidationError({'ipk': "IPK harus dalam rentang 0.00 - 4.00."})
-
-        current_year = 2025
-        
-        
-        if self.tahun_masuk is not None and not (2020 <= self.tahun_masuk <= current_year):
-            raise ValidationError({'tahun_masuk': f"Tahun masuk harus antara 2020 - {current_year}."})
-
-        if self.kode_prodi and not re.match(r'^[A-Za-z]{3}\d{5}$', self.kode_prodi):
-            raise ValidationError({'kode_prodi': "Kode Prodi harus diawali 3 huruf diikuti 5 angka (contoh: INF12345)."})
-
-    def __str__(self):
-        return f"{self.npm} - {self.kode_prodi} ({self.tingkat_pendidikan})"
diff --git a/education/templates/education/create.html b/education/templates/education/create.html
deleted file mode 100644
index 5db5021..0000000
--- a/education/templates/education/create.html
+++ /dev/null
@@ -1,29 +0,0 @@
-{% extends 'base.html' %}
-
-{% block title %}
-  Create Education
-{% endblock %}
-
-{% block content %}
-  <section class="w-full px-5 md:px-10">
-    <div class="w-full flex items-center gap-10">
-      <a href="/education">
-        {% include 'components/button.html' with variant='btn-primary' text='< Back' %}
-      </a>
-      <h1 class="text-xl md:text-3xl font-bold">Create Education</h1>
-    </div>
-    <form method="post" class="space-y-4 md:w-96 ">
-      {% csrf_token %}
-      {% for field in form %}
-        {% if field.name == 'tingkat_pendidikan' %}
-          <label for="{{ field.id_for_label }}" class="block text-sm font-medium text-gray-700">TINGKAT PENDIDIKAN</label>
-        {% endif %}
-        {{ field }}
-        {% if field.errors %}
-          <p class="text-red-500 text-xs mt-1">{{ field.errors.0 }}</p>
-        {% endif %}
-      {% endfor %}
-      <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded">Submit</button>
-    </form>
-  </section>
-{% endblock %}
diff --git a/education/templates/education/edit.html b/education/templates/education/edit.html
deleted file mode 100644
index 294ef55..0000000
--- a/education/templates/education/edit.html
+++ /dev/null
@@ -1,32 +0,0 @@
-{% extends 'base.html' %}
-
-{% block title %}
-  Edit Education
-{% endblock %}
-
-{% block content %}
-  <section 
-    class="w-full px-5 md:px-10"
-  >
-      <div class="w-full flex items-center gap-10">
-        <a href="/education">
-          {% include 'components/button.html' with variant='btn-primary' text='< Back' %}
-        </a>
-        <h1 class="text-xl md:text-3xl font-bold">Edit Education</h1>
-      </div>
-    
-      <form method="post" class="space-y-4 w-96">
-        {% csrf_token %}
-        {% for field in form %}
-          {% if field.name == 'tingkat_pendidikan' %}
-            <label for="{{ field.id_for_label }}" class="block text-sm font-medium text-gray-700">TINGKAT PENDIDIKAN</label>
-          {% endif %}
-          {{ field }}
-          {% if field.errors %}
-            <p class="text-red-500 text-xs mt-1">{{ field.errors.0 }}</p>
-          {% endif %}
-        {% endfor %}
-        <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded">Update</button>
-      </form>
-  </section>
-{% endblock %}
diff --git a/education/templates/education/index.html b/education/templates/education/index.html
deleted file mode 100644
index a265c41..0000000
--- a/education/templates/education/index.html
+++ /dev/null
@@ -1,50 +0,0 @@
-{% extends 'base.html' %}
-
-{% block title %}
-  Daftar Education
-{% endblock %}
-
-{% block content %}
-  <section class="w-full px-5 md:px-10">
-    <div class="w-full flex flex-col md:flex-row items-center justify-between">
-      <h1 class="font-bold text-3xl mb-4 md:mb-0">Daftar Edukasi</h1>
-      <a href="/education/create">
-        {% include 'components/button.html' with variant='btn-primary' text='+ Create' %}
-      </a>
-    </div>
-    <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 mt-6">
-      {% for edukasi in edukasi_list %}
-        <div class="border p-6 rounded-lg shadow-lg bg-white">
-          <h2 class="font-bold text-2xl mb-2">{{ edukasi.npm }}</h2>
-          <table class="text-gray-700 w-full text-sm">
-            <tr>
-              <td class="font-normal">IPK:</td>
-              <td>{{ edukasi.ipk }}</td>
-            </tr>
-            <tr>
-              <td class="font-normal">Tahun Masuk:</td>
-              <td>{{ edukasi.tahun_masuk }}</td>
-            </tr>
-            <tr>
-              <td class="font-normal">Kode Prodi:</td>
-              <td>{{ edukasi.kode_prodi }}</td>
-            </tr>
-            <tr>
-              <td class="font-normal">Tingkat Pendidikan:</td>
-              <td>{{ edukasi.get_tingkat_pendidikan_display }}</td>
-            </tr>
-          </table>
-          <div class="flex justify-end mt-4 gap-2">
-            <a href="/education/{{ edukasi.id }}/edit" class="mb-2 md:mb-0 md:mr-2">
-              {% include 'components/button.html' with variant='btn-secondary' text='Edit' %}
-            </a>
-            <form action="/education/{{ edukasi.id }}/delete" method="post" onsubmit="return confirm('Are you sure you want to delete this item?');">
-              {% csrf_token %}
-              {% include 'components/button.html' with variant='btn-danger' text='Delete' %}
-            </form>
-          </div>
-        </div>
-      {% endfor %}
-    </div>
-  </section>
-{% endblock %}
diff --git a/education/urls.py b/education/urls.py
deleted file mode 100644
index a48fa5e..0000000
--- a/education/urls.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from django.urls import path
-from .views import show_all_education, show_create, show_edit, delete_education
-
-urlpatterns = [
-    path('', show_all_education, name="education-show-all"),
-    path('create', show_create, name="education-create"),
-    path('<int:pk>/edit', show_edit, name='education-edit'),
-    path('<int:pk>/delete', delete_education, name='education-delete')
-]
diff --git a/education/views.py b/education/views.py
deleted file mode 100644
index 0d9307a..0000000
--- a/education/views.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from django.shortcuts import render, get_object_or_404
-from django.http import HttpRequest
-from .forms import EdukasiForm
-from django.shortcuts import redirect
-from .models import Edukasi
-
-# Create your views here.
-def show_all_education(request: HttpRequest):
-    edukasi_list = Edukasi.objects.all()
-    return render(request, 'education/index.html', {'edukasi_list': edukasi_list})
-
-def show_create(request: HttpRequest):
-    if request.method == "POST":
-        form = EdukasiForm(request.POST)
-        if form.is_valid():
-            form.save()
-            return redirect('education-show-all')  # Redirect to a success page
-    else:
-        form = EdukasiForm()
-    
-    return render(request, 'education/create.html', {'form': form})
-
-def show_edit(request, pk):
-    edukasi = get_object_or_404(Edukasi, pk=pk)  # Fetch the specific record
-
-    if request.method == "POST":
-        form = EdukasiForm(request.POST, instance=edukasi)
-        if form.is_valid():
-            form.save()
-            return redirect('education-show-all')  # Redirect to list after update
-    else:
-        form = EdukasiForm(instance=edukasi)  # Populate form with existing data
-
-    return render(request, 'education/edit.html', {'form': form, 'edukasi': edukasi})
-
-
-def delete_education(request, pk):
-    edukasi = get_object_or_404(Edukasi, pk=pk)
-    edukasi.delete()
-    return redirect('education-show-all') 
\ No newline at end of file
diff --git a/finance/__init__.py b/finance/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/finance/admin.py b/finance/admin.py
deleted file mode 100644
index 50ec569..0000000
--- a/finance/admin.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from django.contrib import admin
-from .models import Keuangan
-
-admin.site.register(Keuangan)
\ No newline at end of file
diff --git a/finance/forms.py b/finance/forms.py
deleted file mode 100644
index c7651e8..0000000
--- a/finance/forms.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from django import forms
-from .models import Keuangan
-from .widgets import TailwindInput
-
-class KeuanganForm(forms.ModelForm):
-    class Meta:
-        model = Keuangan
-        fields = '__all__'
-        widgets = {
-            'nomor_rekening': TailwindInput(input_type="text", attrs={'placeholder': 'Enter Account Number (8-10 digits)'}),
-            'skor_kredit': TailwindInput(input_type="number", attrs={'min': 0, 'max': 800, 'placeholder': 'Enter Credit Score (0-800)'}),
-            'nomor_kartu_atm': TailwindInput(input_type="number", attrs={'placeholder': 'Enter ATM Card Number (15-16 digits)'}),
-            'status_pekerjaan': forms.Select(
-                attrs={
-                    'class': 'mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm'
-                }
-            ),
-            'kode_mata_uang': TailwindInput(input_type="text", attrs={'placeholder': 'Enter Currency Code (e.g., USD)'}),
-        }
-
-    labels = {
-        'nomor_rekening': 'Nomor Rekening',
-        'skor_kredit': 'Skor Kredit',
-        'nomor_kartu_atm': 'Nomor Kartu ATM',
-        'status_pekerjaan': 'Status Pekerjaan',
-        'kode_mata_uang': 'Kode Mata Uang',
-    }
diff --git a/finance/migrations/0001_initial.py b/finance/migrations/0001_initial.py
deleted file mode 100644
index fca1d1d..0000000
--- a/finance/migrations/0001_initial.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 5.1.7 on 2025-03-13 13:41
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Keuangan',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('nomor_rekening', models.CharField(max_length=10)),
-                ('skor_kredit', models.PositiveIntegerField()),
-                ('nomor_kartu_atm', models.BigIntegerField()),
-                ('status_pekerjaan', models.CharField(choices=[('Karyawan', 'Karyawan'), ('PNS', 'PNS'), ('Mahasiswa', 'Mahasiswa'), ('Dosen', 'Dosen')], max_length=10)),
-                ('kode_mata_uang', models.CharField(max_length=3)),
-            ],
-        ),
-    ]
diff --git a/finance/migrations/__init__.py b/finance/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/finance/models.py b/finance/models.py
deleted file mode 100644
index 986986e..0000000
--- a/finance/models.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from django.db import models
-from django.core.exceptions import ValidationError
-import re
-
-class Keuangan(models.Model):
-    nomor_rekening = models.CharField(max_length=10)  # 8-10 digit, bisa diawali 0
-    skor_kredit = models.PositiveIntegerField()  # Rentang 0-800
-    nomor_kartu_atm = models.BigIntegerField()  # 15-16 digit angka
-    status_pekerjaan = models.CharField(
-        max_length=10,
-        choices=[
-            ('Karyawan', 'Karyawan'),
-            ('PNS', 'PNS'),
-            ('Mahasiswa', 'Mahasiswa'),
-            ('Dosen', 'Dosen')
-        ]
-    )  # Predefined Value
-    kode_mata_uang = models.CharField(max_length=3)  # 3 huruf sesuai ISO 4217
-
-    def clean(self):
-        # Validasi Nomor Rekening (8-10 digit angka, bisa diawali 0)
-        if not re.fullmatch(r'^\d{8,10}$', self.nomor_rekening):
-            raise ValidationError({'nomor_rekening': "Nomor rekening harus 8-10 digit angka dan dapat diawali 0."})
-
-        # Validasi Skor Kredit (0-800)
-        if not (0 <= self.skor_kredit <= 800):
-            raise ValidationError({'skor_kredit': "Skor kredit harus dalam rentang 0-800."})
-
-        # Validasi Nomor Kartu ATM (15-16 digit angka)
-        if not (100000000000000 <= self.nomor_kartu_atm <= 9999999999999999):
-            raise ValidationError({'nomor_kartu_atm': "Nomor kartu ATM harus terdiri dari 15-16 digit angka."})
-
-        # Validasi Kode Mata Uang (harus 3 huruf kapital)
-        if not re.fullmatch(r'^[A-Z]{3}$', self.kode_mata_uang):
-            raise ValidationError({'kode_mata_uang': "Kode mata uang harus 3 huruf sesuai ISO 4217."})
-
-    def __str__(self):
-        return f"{self.nomor_rekening} - {self.kode_mata_uang} ({self.status_pekerjaan})"
diff --git a/finance/templates/finance/create.html b/finance/templates/finance/create.html
deleted file mode 100644
index 2bd4acf..0000000
--- a/finance/templates/finance/create.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{% extends 'base.html' %}
-
-{% block title %}
-  Create Financial
-{% endblock %}
-
-{% block content %}
-  <div class="w-full px-5 md:px-10">
-    <div class="w-full flex items-center gap-10">
-      <a href="/finance">{% include 'components/button.html' with variant='btn-primary' text='< Back' %}</a>
-      <h1 class="text-xl md:text-3xl font-bold">Create Financial</h1>
-    </div>
-    <form method="post" class="space-y-4 w-96">
-      {% csrf_token %}
-      {% for field in form %}
-        {% if field.name == 'status_pekerjaan' %}
-          <label for="{{ field.id_for_label }}" class="block text-sm font-medium text-gray-700">STATUS PEKERJAAN</label>
-        {% endif %}
-        {{ field }}
-        {% if field.errors %}
-          <p class="text-red-500 text-xs mt-1">{{ field.errors.0 }}</p>
-        {% endif %}
-      {% endfor %}
-      <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded">Submit</button>
-    </form>
-  </div>
-{% endblock %}
diff --git a/finance/templates/finance/edit.html b/finance/templates/finance/edit.html
deleted file mode 100644
index 34cebd1..0000000
--- a/finance/templates/finance/edit.html
+++ /dev/null
@@ -1,30 +0,0 @@
-{% extends 'base.html' %}
-
-{% block title %}
-  Edit Financial
-{% endblock %}
-
-{% block content %}
-<div class="w-full px-5 md:px-10">
-  <div class="w-full flex items-center gap-10">
-    <a href="/finance">
-      {% include 'components/button.html' with variant='btn-primary' text='< Back' %}
-    </a>
-    <h1 class="text-xl md:text-3xl font-bold">Edit Financial</h1>
-  </div>
-
-  <form method="post" class="space-y-4 w-96">
-    {% csrf_token %}
-    {% for field in form %}
-      <label for="{{ field.id_for_label }}" class="block text-sm font-medium text-gray-700">
-        {{ field.label }}
-      </label>
-      {{ field }}
-      {% if field.errors %}
-        <p class="text-red-500 text-xs mt-1">{{ field.errors.0 }}</p>
-      {% endif %}
-    {% endfor %}
-    <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded">Update</button>
-  </form>
-</div>
-{% endblock %}
diff --git a/finance/templates/finance/index.html b/finance/templates/finance/index.html
deleted file mode 100644
index 25e42d4..0000000
--- a/finance/templates/finance/index.html
+++ /dev/null
@@ -1,50 +0,0 @@
-{% extends 'base.html' %}
-
-{% block title %}
-  Daftar Financials
-{% endblock %}
-
-{% block content %}
-  <section class="w-full px-5 md:px-10">
-    <div class="w-full flex items-center justify-between">
-      <h1 class="font-bold text-3xl">Daftar Financial</h1>
-      <a href="/finance/create">
-        {% include 'components/button.html' with variant='btn-primary' text='+ Create' %}
-      </a>
-    </div>
-    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mt-6">
-      {% for finance in finance_list %}
-        <div class="border p-6 rounded-lg shadow-lg bg-white">
-          <h2 class="font-bold text-2xl mb-2">{{ finance.nomor_rekening }}</h2>
-          <table class="text-gray-700 w-full text-sm">
-            <tr>
-              <td class="font-normal">Skor Kredit:</td>
-              <td>{{ finance.skor_kredit }}</td>
-            </tr>
-            <tr>
-              <td class="font-normal">Nomor Kartu ATM:</td>
-              <td>{{ finance.nomor_kartu_atm }}</td>
-            </tr>
-            <tr>
-              <td class="font-normal">Status Pekerjaan:</td>
-              <td>{{ finance.status_pekerjaan }}</td>
-            </tr>
-            <tr>
-              <td class="font-normal">Kode Mata Uang:</td>
-              <td>{{ finance.kode_mata_uang }}</td>
-            </tr>
-          </table>
-          <div class="flex justify-end mt-4">
-            <a href="/finance/{{ finance.id }}/edit" class="mr-2">
-              {% include 'components/button.html' with variant='btn-secondary' text='Edit' %}
-            </a>
-            <form action="/finance/{{ finance.id }}/delete" method="post" onsubmit="return confirm('Are you sure you want to delete this item?');">
-              {% csrf_token %}
-              {% include 'components/button.html' with variant='btn-danger' text='Delete' %}
-            </form>
-          </div>
-        </div>
-      {% endfor %}
-    </div>
-  </section>
-{% endblock %}
diff --git a/finance/tests.py b/finance/tests.py
deleted file mode 100644
index 7ce503c..0000000
--- a/finance/tests.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from django.test import TestCase
-
-# Create your tests here.
diff --git a/finance/urls.py b/finance/urls.py
deleted file mode 100644
index 633d339..0000000
--- a/finance/urls.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from django.urls import path
-from .views import show_all_finance, show_create, show_edit, delete_finance
-
-urlpatterns = [
-    path('', show_all_finance, name="finance-show-all"),
-    path('create', show_create, name="finance-create"),
-    path('<int:pk>/edit', show_edit, name='finance-edit'),
-    path('<int:pk>/delete', delete_finance, name='finance-delete')
-]
diff --git a/finance/views.py b/finance/views.py
deleted file mode 100644
index 86348a4..0000000
--- a/finance/views.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from django.shortcuts import render, get_object_or_404
-from django.http import HttpRequest
-from .forms import KeuanganForm
-from django.shortcuts import redirect
-from .models import Keuangan
-
-# Create your views here.
-def show_all_finance(request: HttpRequest):
-    finance_list = Keuangan.objects.all()
-    return render(request, 'finance/index.html', {'finance_list': finance_list})
-
-def show_create(request: HttpRequest):
-    if request.method == "POST":
-        form = KeuanganForm(request.POST)
-        if form.is_valid():
-            form.save()
-            return redirect('finance-show-all')  # Redirect to a success page
-    else:
-        form = KeuanganForm()
-    
-    return render(request, 'finance/create.html', {'form': form})
-
-def show_edit(request, pk):
-    finance = get_object_or_404(Keuangan, pk=pk)  # Fetch the specific record
-
-    if request.method == "POST":
-        form = KeuanganForm(request.POST, instance=finance)
-        if form.is_valid():
-            form.save()
-            return redirect('finance-show-all')  # Redirect to list after update
-    else:
-        form = KeuanganForm(instance=finance)  # Populate form with existing data
-
-    return render(request, 'finance/edit.html', {'form': form, 'finance': finance})
-
-
-def delete_finance(request, pk):
-    finance = get_object_or_404(Keuangan, pk=pk)
-    finance.delete()
-    return redirect('finance-show-all') 
\ No newline at end of file
diff --git a/finance/widgets.py b/finance/widgets.py
deleted file mode 100644
index e460c0a..0000000
--- a/finance/widgets.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from django import forms
-from django.template.loader import render_to_string
-
-class TailwindInput(forms.Widget):
-    template_name = "components/input.html"
-
-    def __init__(self, input_type="text", attrs=None):
-        self.input_type = input_type
-        super().__init__(attrs)
-
-    def get_context(self, name, value, attrs):
-        attrs = attrs or {}
-        context = {
-            "class": attrs.get("class", "mb-4"),
-            "id": attrs.get("id", f"id_{name}"),
-            "label": attrs.get("label", name.replace("_", " ").upper()),
-            "type": self.input_type,
-            "name": name,
-            "value": value or "",
-            "placeholder": attrs.get("placeholder", ""),
-            "step": attrs.get("step", ""),
-        }
-        return context
-
-    def render(self, name, value, attrs=None, renderer=None):
-        context = self.get_context(name, value, attrs)
-        return render_to_string(self.template_name, context)
diff --git a/main/settings.py b/main/settings.py
index 69b2072..baace99 100644
--- a/main/settings.py
+++ b/main/settings.py
@@ -16,7 +16,6 @@ import os
 # Build paths inside the project like this: BASE_DIR / 'subdir'.
 BASE_DIR = Path(__file__).resolve().parent.parent
 
-
 # Quick-start development settings - unsuitable for production
 # See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
 
@@ -43,8 +42,7 @@ INSTALLED_APPS = [
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
-    'finance', 
-    'education',
+    'user'
 ]
 
 MIDDLEWARE = [
@@ -142,4 +140,6 @@ STATICFILES_DIRS = [
 ]
 
 
-STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
\ No newline at end of file
+STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
+
+AUTH_USER_MODEL = 'user.ExtendedUser'
\ No newline at end of file
diff --git a/main/urls.py b/main/urls.py
index c3e770c..8894dec 100644
--- a/main/urls.py
+++ b/main/urls.py
@@ -21,6 +21,5 @@ from .views import show_home
 urlpatterns = [
     path('admin/', admin.site.urls),
     path("", show_home, name="home"),
-    path('education/', include('education.urls')),
-    path('finance/', include('finance.urls')),
+    path('user/', include('user.urls')),
 ]
diff --git a/main/views.py b/main/views.py
index 61ecf03..a0b4e30 100644
--- a/main/views.py
+++ b/main/views.py
@@ -2,4 +2,6 @@ from django.http import HttpResponse, HttpRequest
 from django.shortcuts import render
 
 def show_home(request: HttpRequest) -> HttpResponse:
-    return render(request, 'index.html')
\ No newline at end of file
+    return render(request, 'index.html', context={
+        "isLoggedIn": request.user.is_authenticated,
+    })
\ No newline at end of file
diff --git a/templates/base.html b/templates/base.html
index de17676..05a0c58 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -22,14 +22,13 @@
   <body>
     {% include 'components/navbar.html' %}
 
-    <div class="container mx-auto pt-32 min-h-screen">
+    <div class="container mx-auto pt-32 min-h-screen flex flex-col justify-between">
       {% block content %}
-        <!-- Child templates will insert content here -->
       {% endblock %}
+      <footer class="bg-gray-100 text-center py-3 mt-4">
+        <p>&copy; 2025 Andrew Devito Aryo - 2306152494</p>
+      </footer>
     </div>
 
-    <footer class="bg-gray-100 text-center py-3 mt-4">
-      <p>&copy; 2025 Andrew Devito Aryo - 2306152494</p>
-    </footer>
   </body>
 </html>
diff --git a/templates/components/button.html b/templates/components/button.html
index c21a0dc..b6b53d6 100644
--- a/templates/components/button.html
+++ b/templates/components/button.html
@@ -39,7 +39,9 @@
     }
 </style>
 
-<button class="btn {% if variant %}{{ variant }}{% else %}btn-primary{% endif %}">
+<button 
+    type="{{type}}" 
+    class="btn {% if variant %}{{ variant }}{% else %}btn-primary{% endif %} w-full">
     {% if text %}
         {{ text }}
     {% else %}
diff --git a/templates/components/input.html b/templates/components/input.html
index 8ad7dd0..82e77eb 100644
--- a/templates/components/input.html
+++ b/templates/components/input.html
@@ -1,4 +1,4 @@
 <div class="{{ class }}">
     <label for="{{ id }}" class="block text-sm font-medium text-gray-700">{{ label }}</label>
-    <input type="{{ type }}" id="{{ id }}" name="{{ name }}" value="{{ value }}" placeholder="{{ placeholder }}" step="0.01" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm">
+    <input type="{{ type }}" id="{{ id }}" name="{{ name }}" value="{{ value }}" placeholder="{{ placeholder }}" step="0.01" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" {% if required %}required{% endif %}>
 </div>
diff --git a/templates/components/navbar.html b/templates/components/navbar.html
index 1df92ec..759f19c 100644
--- a/templates/components/navbar.html
+++ b/templates/components/navbar.html
@@ -1,33 +1,5 @@
-<nav class="bg-gray-100 px-5 md:px-10 fixed w-full top-0 py-5   ">
-    <div class="container flex items-center justify-between">
-        <a class="text-xl font-bold" href="#">El Pekape</a>
-         <div class="lg:flex lg:items-center lg:w-auto hidden transition-all duration-300 ease-in-out" id="navbarNav">
-            <ul class="lg:flex lg:justify-between text-sm">
-                <li class="nav-item">
-                    <a class="block mt-4 lg:inline-block lg:mt-0 text-gray-700 hover:text-gray-900 mr-4" href="/">Home</a>
-                </li>
-                <li class="nav-item">
-                    <a class="block mt-4 lg:inline-block lg:mt-0 text-gray-700 hover:text-gray-900 mr-4" href="/education">Education</a>
-                </li>
-                <li class="nav-item">
-                    <a class="block mt-4 lg:inline-block lg:mt-0 text-gray-700 hover:text-gray-900 mr-4" href="/finance">Finance</a>
-                </li>
-            </ul>
-        </div>
-        <button class="block lg:hidden px-3 py-2 border rounded text-gray-700 border-gray-700" id="navbar-toggler">
-            <svg class="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
-                <title>Menu</title>
-                <path d="M0 3h20v2H0zM0 9h20v2H0zM0 15h20v2H0z"/>
-            </svg>
-        </button>
-       
-    </div>
-</nav>
-
-<script>
-    document.getElementById('navbar-toggler').addEventListener('click', function() {
-        var nav = document.getElementById('navbarNav');
-        nav.classList.toggle('hidden');
-        nav.classList.toggle('block');
-    });
-</script>
\ No newline at end of file
+<nav class="bg-gray-100 px-5 md:px-10 fixed w-full top-0 py-5">
+  <div class="container flex items-center justify-between">
+    <a class="text-xl font-bold" href="#">El Pekape</a>
+  </div>
+</nav>
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
index 131febb..150f3ba 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -2,21 +2,21 @@
 {% load static %}
 
 {% block title %}
-    Home
+  Home
 {% endblock %}
 
 {% block content %}
-    <div class="h-screen flex items-center justify-center flex-col gap-4">
-        <img src="{% static 'images/logo.png' %}" alt="Logo" />
-        <h1 class="font-bold text-2xl">Welcome to the Home Page</h1>
-        <p>This is a simple home page for PKPL HW 06.</p>
-        <div class="">
-            <a href="/education">
-                {% include 'components/button.html' with variant='btn-primary' text='Education' %}
-            </a>
-            <a href="/finance">
-                {% include 'components/button.html' with variant='btn-primary' text='Financial' %}
-            </a>
-        </div>
+  <div class="h-full flex items-center justify-center flex-col gap-4">
+    <img src="{% static 'images/logo.png' %}" alt="Logo" />
+    <h1 class="font-bold text-2xl">Welcome to the Home Page</h1>
+    <p>This is a simple home page for PKPL Week 06.</p>
+    <div class="flex gap-2 items-center">
+      {% if isLoggedIn %}
+        <a href="{% url 'logout' %}">{% include 'components/button.html' with variant='btn-primary' text='Logout' %}</a>
+      {% else %}
+        <a href="/user/login">{% include 'components/button.html' with variant='btn-primary' text='Login' %}</a>
+        <a href="/user/register">{% include 'components/button.html' with variant='btn-primary' text='Register' %}</a>
+      {% endif %}
     </div>
+  </div>
 {% endblock %}
diff --git a/education/__init__.py b/user/__init__.py
similarity index 100%
rename from education/__init__.py
rename to user/__init__.py
diff --git a/user/admin.py b/user/admin.py
new file mode 100644
index 0000000..c0dfc75
--- /dev/null
+++ b/user/admin.py
@@ -0,0 +1,30 @@
+from django.contrib import admin
+from django.contrib.auth.admin import UserAdmin
+from .models import ExtendedUser
+
+class ExtendedUserAdmin(UserAdmin):
+    # Define the fields to display in the admin list view
+    list_display = ("username", "email", "fullname", "kategori_produk", "is_staff", "is_active")
+    search_fields = ("username", "email", "fullname", "nomor_hp")
+    list_filter = ("is_staff", "is_active", "kategori_produk")
+    
+    # Define the fieldsets for updating users in the admin panel
+    fieldsets = (
+        (None, {"fields": ("username", "email", "password")}),
+        ("Personal Info", {"fields": ("fullname", "tanggal_lahir", "nomor_hp", "url_blog", "deskripsi")}),
+        ("Permissions", {"fields": ("is_active", "is_staff", "is_superuser", "groups", "user_permissions")}),
+        ("Additional Info", {"fields": ("id_penjual", "kategori_produk")}),
+    )
+    
+    # Define the fieldsets for creating a new user
+    add_fieldsets = (
+        (None, {
+            "classes": ("wide",),
+            "fields": ("username", "email", "password1", "password2", "fullname", "kategori_produk"),
+        }),
+    )
+    
+    ordering = ("username",)
+
+# Register the custom user model
+admin.site.register(ExtendedUser, ExtendedUserAdmin)
diff --git a/finance/apps.py b/user/apps.py
similarity index 63%
rename from finance/apps.py
rename to user/apps.py
index 53023d8..36cce4c 100644
--- a/finance/apps.py
+++ b/user/apps.py
@@ -1,6 +1,6 @@
 from django.apps import AppConfig
 
 
-class FinanceConfig(AppConfig):
+class UserConfig(AppConfig):
     default_auto_field = 'django.db.models.BigAutoField'
-    name = 'finance'
+    name = 'user'
diff --git a/user/forms.py b/user/forms.py
new file mode 100644
index 0000000..f4471b2
--- /dev/null
+++ b/user/forms.py
@@ -0,0 +1,49 @@
+from django import forms
+from django.contrib.auth.forms import UserCreationForm
+from .models import ExtendedUser
+from django.contrib.auth.forms import AuthenticationForm
+from django.forms.widgets import PasswordInput, TextInput
+
+
+class ExtendedUserForm(UserCreationForm):
+    class Meta:
+        model = ExtendedUser
+        fields = [
+            "username",
+            "email",
+            "password1",
+            "password2",
+            "fullname",
+            "tanggal_lahir",
+            "nomor_hp",
+            "url_blog",
+            "deskripsi",
+            "id_penjual",
+            "kategori_produk",
+        ]
+        
+        labels = {
+            "username": "Username",
+            "email": "Email",
+            "password1": "Password",
+            "password2": "Confirm Password",
+            "fullname": "Full Name",
+            "tanggal_lahir": "Date of Birth",
+            "nomor_hp": "Phone Number",
+            "url_blog": "Blog URL",
+            "deskripsi": "Description",
+            "id_penjual": "Seller ID",
+            "kategori_produk": "Product Category",
+        }
+        
+    tanggal_lahir = forms.DateField(widget=forms.DateInput(attrs={'type': 'date'}))
+    
+class ExtendedUserLoginForm(AuthenticationForm):
+    username = forms.CharField(
+        widget=TextInput(attrs={"class": "w-full px-3 py-2 border rounded-md", "placeholder": "Username"}),
+        label="Username"
+    )
+    password = forms.CharField(
+        widget=PasswordInput(attrs={"class": "w-full px-3 py-2 border rounded-md", "placeholder": "Password"}),
+        label="Password"
+    )
diff --git a/user/migrations/0001_initial.py b/user/migrations/0001_initial.py
new file mode 100644
index 0000000..9eed135
--- /dev/null
+++ b/user/migrations/0001_initial.py
@@ -0,0 +1,51 @@
+# Generated by Django 5.1.7 on 2025-03-17 06:16
+
+import django.contrib.auth.models
+import django.contrib.auth.validators
+import django.utils.timezone
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('auth', '0012_alter_user_first_name_max_length'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ExtendedUser',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('password', models.CharField(max_length=128, verbose_name='password')),
+                ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
+                ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
+                ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
+                ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
+                ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
+                ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
+                ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
+                ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
+                ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
+                ('fullname', models.CharField(max_length=100)),
+                ('tanggal_lahir', models.DateField()),
+                ('nomor_hp', models.CharField(max_length=15)),
+                ('url_blog', models.CharField(max_length=255)),
+                ('deskripsi', models.TextField()),
+                ('id_penjual', models.IntegerField()),
+                ('kategori_produk', models.CharField(choices=[('Elektronik', 'Elektronik'), ('Fashion', 'Fashion'), ('Furnitur', 'Furnitur'), ('Obat', 'Obat'), ('Kecantikan', 'Kecantikan')], max_length=50)),
+                ('groups', models.ManyToManyField(blank=True, related_name='extendeduser_groups', to='auth.group')),
+                ('user_permissions', models.ManyToManyField(blank=True, related_name='extendeduser_permissions', to='auth.permission')),
+            ],
+            options={
+                'verbose_name': 'user',
+                'verbose_name_plural': 'users',
+                'abstract': False,
+            },
+            managers=[
+                ('objects', django.contrib.auth.models.UserManager()),
+            ],
+        ),
+    ]
diff --git a/education/migrations/__init__.py b/user/migrations/__init__.py
similarity index 100%
rename from education/migrations/__init__.py
rename to user/migrations/__init__.py
diff --git a/user/models.py b/user/models.py
new file mode 100644
index 0000000..345c1b1
--- /dev/null
+++ b/user/models.py
@@ -0,0 +1,88 @@
+from django.db import models
+from django.contrib.auth.models import AbstractUser
+from django.core.validators import RegexValidator, MinLengthValidator, MaxLengthValidator, EmailValidator, URLValidator
+from django.utils import timezone
+from django.core.exceptions import ValidationError
+
+class ExtendedUser(AbstractUser):
+    enum_kategori = {
+        'Elektronik': 'Elektronik',
+        'Fashion': 'Fashion',
+        'Furnitur': 'Furnitur',
+        'Obat': 'Obat',
+        'Kecantikan': 'Kecantikan'
+    }
+    
+    fullname = models.CharField(
+        max_length=255,
+        validators=[
+            RegexValidator(
+                regex=r'^[a-zA-Z0-9._-]+$',
+                message='Nama hanya boleh berisi huruf, angka, dan karakter (., _, -).'
+            )
+        ]
+    )
+    password = models.CharField(
+        max_length=255,
+        validators=[
+            MinLengthValidator(8),
+            RegexValidator(
+                regex=r'^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$',
+                message='Password harus berisi huruf, angka, dan karakter spesial dengan panjang minimal 8.'
+            )
+        ]
+    )
+    tanggal_lahir = models.DateField(
+        validators=[
+            RegexValidator(
+                regex=r'^\d{4}-\d{2}-\d{2}$',
+                message='Tanggal lahir harus dalam format YYYY-MM-DD.'
+            )
+        ]
+    )
+    nomor_hp = models.CharField(
+        max_length=15,
+        validators=[
+            RegexValidator(
+                regex=r'^62\d{6,13}$',
+                message='Nomor HP harus dalam format (kode negara - nomor telepon) dengan panjang minimal 8 dan maksimal 15. Contoh: 62123456, bukan +62123456 dan 62-12345.'
+            )
+        ]
+    )
+    email = models.CharField(
+        max_length=255,
+        validators=[EmailValidator(message='Email harus sesuai dengan format email.')]
+    )
+    url_blog = models.CharField(
+        max_length=255,
+        validators=[URLValidator(message='URL Blog harus sesuai dengan format URL.')]
+    )
+    deskripsi = models.TextField(
+        validators=[
+            MinLengthValidator(5),
+            MaxLengthValidator(1000)
+        ]
+    )
+    id_penjual = models.IntegerField()
+    kategori_produk = models.CharField(
+        max_length=50,
+        choices=[(tag, tag) for tag in enum_kategori.keys()]
+    )
+    
+    groups = models.ManyToManyField(
+        "auth.Group",
+        related_name="extendeduser_groups",  # Custom related_name
+        blank=True
+    )
+    user_permissions = models.ManyToManyField(
+        "auth.Permission",
+        related_name="extendeduser_permissions",  # Custom related_name
+        blank=True
+    )
+    
+    def clean(self):
+        super().clean()
+        if self.tanggal_lahir:
+            age = (timezone.now().date() - self.tanggal_lahir).days / 365.25
+            if age < 12:
+                raise ValidationError('Usia pengguna minimal 12 tahun.')
\ No newline at end of file
diff --git a/user/templates/login.html b/user/templates/login.html
new file mode 100644
index 0000000..81fa9c3
--- /dev/null
+++ b/user/templates/login.html
@@ -0,0 +1,22 @@
+{% extends 'base.html' %}
+{% block title %}
+  Login Page
+{% endblock %}
+
+{% block content %}
+  <div class="p-10">
+    <form method="post" class="space-y-4">
+      {% csrf_token %}
+      {% include 'components/input.html' with id='username' name='username' label='Username' required='true' %}
+      {% include 'components/input.html' with id='password' name='password' label='Password' required='true' type='password' %}
+
+      {% if messages %}
+        {% for message in messages %}
+          <p class="text-xs text-red-700">* {{ message }}</p>
+        {% endfor %}
+      {% endif %}
+
+      {% include 'components/button.html' with text='Login' type='submit' %}
+    </form>
+  </div>
+{% endblock %}
diff --git a/user/templates/register.html b/user/templates/register.html
new file mode 100644
index 0000000..275f2de
--- /dev/null
+++ b/user/templates/register.html
@@ -0,0 +1,38 @@
+{% extends 'base.html' %}
+
+{% block title %}
+  Register Page
+{% endblock %}
+
+{% block content %}
+  <style>
+    input,
+    textarea,
+    select {
+      padding: 10px;
+      border: 1px solid #ddd;
+      border-radius: 5px;
+      width: 100%;
+      margin-top: 5px;
+    }
+  </style>
+  <div class="max-w-md mx-auto mt-10 bg-white p-8 rounded-lg shadow-md">
+    <h2 class="text-2xl font-bold mb-6 text-center text-gray-800">Register</h2>
+    <form method="post" class="space-y-4">
+      {% csrf_token %}
+
+      {% for field in form %}
+        <div>
+          <label for="{{ field.id_for_label }}" class="block text-sm font-medium text-gray-700">{{ field.label }}</label>
+          {{ field }}
+
+          {% if field.errors %}
+            <p class="text-sm text-red-500 mt-1">{{ field.errors.0 }}</p>
+          {% endif %}
+        </div>
+      {% endfor %}
+
+      {% include 'components/button.html' with type='submit' text='Register' %}
+    </form>
+  </div>
+{% endblock %}
diff --git a/education/tests.py b/user/tests.py
similarity index 100%
rename from education/tests.py
rename to user/tests.py
diff --git a/user/urls.py b/user/urls.py
new file mode 100644
index 0000000..5264dc3
--- /dev/null
+++ b/user/urls.py
@@ -0,0 +1,8 @@
+from django.urls import path
+from .views import login_view, register_view, logout_view
+
+urlpatterns = [
+    path("login/", login_view, name="login"),
+    path("register/", register_view, name="register"),
+    path("logout/", logout_view, name="logout"),
+]
diff --git a/user/views.py b/user/views.py
new file mode 100644
index 0000000..fc9118d
--- /dev/null
+++ b/user/views.py
@@ -0,0 +1,51 @@
+from django.shortcuts import render, redirect
+from django.contrib.auth import authenticate, login, logout
+from django.contrib import messages
+from .forms import ExtendedUserLoginForm
+
+from .forms import ExtendedUserForm
+from django.contrib import messages
+
+def login_view(request):
+    if request.user.is_authenticated:
+        return redirect("home")  # Change "home" to your desired redirect URL
+
+    if request.method == "POST":
+        form = ExtendedUserLoginForm(request, data=request.POST)
+        
+        print(form.errors)
+        
+        if form.is_valid():
+            username = form.cleaned_data["username"]
+            password = form.cleaned_data["password"]
+            user = authenticate(request, username=username, password=password)
+            if user is not None:
+                login(request, user)
+                return redirect("home")  # Redirect to home/dashboard
+            else:
+                messages.error(request, "Invalid username or password")
+        else:
+            messages.error(request, "Invalid form submission")
+
+    else:
+        form = ExtendedUserLoginForm()
+
+    return render(request, "login.html", {"form": form})
+
+def register_view(request):
+    if request.method == "POST":
+        form = ExtendedUserForm(request.POST)
+        if form.is_valid()  :
+            form.save()
+            messages.success(request, "Account created successfully. Please log in.")
+            return redirect("login")  # Redirect to login page
+        else:
+            messages.error(request, "Please correct the errors below.")
+    else:
+        form = ExtendedUserForm()
+
+    return render(request, "register.html", {"form": form})
+
+def logout_view(request):
+    logout(request)
+    return redirect("home")
\ No newline at end of file
diff --git a/education/widgets.py b/user/widgets.py
similarity index 100%
rename from education/widgets.py
rename to user/widgets.py
-- 
GitLab