diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 00000000..7ccbb78d
Binary files /dev/null and b/.DS_Store differ
diff --git a/.gitignore b/.gitignore
index 02605806..fc2708c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -135,4 +135,6 @@ dmypy.json
.idea/workspace.xml
# Exception for Tailwind CSS
-!theme/static/css/dist/
\ No newline at end of file
+!theme/static/css/dist/
+
+**/.DS_Store
diff --git a/jobs/__init__.py b/jobs/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/jobs/admin.py b/jobs/admin.py
new file mode 100644
index 00000000..8c38f3f3
--- /dev/null
+++ b/jobs/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/jobs/apps.py b/jobs/apps.py
new file mode 100644
index 00000000..241a7f1b
--- /dev/null
+++ b/jobs/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class JobsConfig(AppConfig):
+ default_auto_field = "django.db.models.BigAutoField"
+ name = "jobs"
diff --git a/jobs/forms.py b/jobs/forms.py
new file mode 100644
index 00000000..0dff4ebe
--- /dev/null
+++ b/jobs/forms.py
@@ -0,0 +1,33 @@
+from django import forms
+from django.core.exceptions import ValidationError
+
+from .models import Job
+
+
+class JobForm(forms.ModelForm):
+ class Meta:
+ model = Job
+ fields = [
+ "title",
+ "description",
+ "sector",
+ "contract_type",
+ "pay",
+ "employer_name",
+ "incorporation_number",
+ "website",
+ "expiry_date",
+ "application_url",
+ ]
+
+ def clean_expiry_date(self):
+ expiry_date = self.cleaned_data.get("expiry_date")
+
+ # Check if the expiry_date is in a valid format
+ if not expiry_date:
+ raise ValidationError("Please enter a valid date.")
+
+ # Custom validation logic for the expiry_date field
+ # ...
+
+ return expiry_date
diff --git a/jobs/management/__init__.py b/jobs/management/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/jobs/management/commands/__init__.py b/jobs/management/commands/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/jobs/management/commands/unpublish_listings.py b/jobs/management/commands/unpublish_listings.py
new file mode 100644
index 00000000..277f551b
--- /dev/null
+++ b/jobs/management/commands/unpublish_listings.py
@@ -0,0 +1,12 @@
+import datetime
+from django.core.management.base import BaseCommand
+from jobs.models import Job
+
+
+class Command(BaseCommand):
+ help = "Unpublish expired job listings"
+
+ def handle(self, *args, **options):
+ expired_date = datetime.date.today() - datetime.timedelta(days=30)
+ expired_listings = Job.objects.filter(created__lt=expired_date)
+ expired_listings.update(is_published=False)
diff --git a/jobs/migrations/0001_initial.py b/jobs/migrations/0001_initial.py
new file mode 100644
index 00000000..c002e748
--- /dev/null
+++ b/jobs/migrations/0001_initial.py
@@ -0,0 +1,49 @@
+# Generated by Django 3.2.19 on 2023-05-20 13:10
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ initial = True
+
+ dependencies = []
+
+ operations = [
+ migrations.CreateModel(
+ name="Job",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("title", models.CharField(max_length=100)),
+ ("description", models.TextField()),
+ (
+ "sector",
+ models.CharField(
+ choices=[("commercial", "Commercial"), ("charity", "Charity")],
+ max_length=20,
+ ),
+ ),
+ (
+ "pay",
+ models.DecimalField(
+ blank=True, decimal_places=2, max_digits=10, null=True
+ ),
+ ),
+ ("employer_name", models.CharField(max_length=100)),
+ (
+ "incorporation_number",
+ models.CharField(blank=True, max_length=20, null=True),
+ ),
+ ("website", models.URLField()),
+ ("expiry_date", models.DateField()),
+ ("application_url", models.URLField()),
+ ],
+ ),
+ ]
diff --git a/jobs/migrations/0002_job_contract_type.py b/jobs/migrations/0002_job_contract_type.py
new file mode 100644
index 00000000..f3ba4916
--- /dev/null
+++ b/jobs/migrations/0002_job_contract_type.py
@@ -0,0 +1,28 @@
+# Generated by Django 3.2.19 on 2023-05-20 13:55
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("jobs", "0001_initial"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="job",
+ name="contract_type",
+ field=models.CharField(
+ choices=[
+ ("voluntary", "Voluntary (Unpaid)"),
+ ("temporary", "Temporary"),
+ ("fixed_term_contract", "Fixed Term Contract"),
+ ("part_time_permanent", "Part-time Permanent Employed"),
+ ("full_time_permanent", "Full-time Permanent Employed"),
+ ],
+ default="",
+ max_length=20,
+ ),
+ preserve_default=False,
+ ),
+ ]
diff --git a/jobs/migrations/0003_auto_20230610_0843.py b/jobs/migrations/0003_auto_20230610_0843.py
new file mode 100644
index 00000000..81f592df
--- /dev/null
+++ b/jobs/migrations/0003_auto_20230610_0843.py
@@ -0,0 +1,34 @@
+# Generated by Django 3.2.19 on 2023-06-10 08:43
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("jobs", "0002_job_contract_type"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="job",
+ name="contract_type",
+ field=models.CharField(
+ choices=[
+ ("Voluntary", "Voluntary (Unpaid)"),
+ ("Temporary", "Temporary"),
+ ("Fixed Term Contract", "Fixed Term Contract"),
+ ("Part-time Permanent", "Part Time Permanent Employed"),
+ ("Full-time Permanent Employed", "Full-time Permanent Employed"),
+ ],
+ max_length=40,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="job",
+ name="sector",
+ field=models.CharField(
+ choices=[("Commercial", "Commercial"), ("Charity", "Charity")],
+ max_length=20,
+ ),
+ ),
+ ]
diff --git a/jobs/migrations/0004_auto_20230610_0852.py b/jobs/migrations/0004_auto_20230610_0852.py
new file mode 100644
index 00000000..a022bc94
--- /dev/null
+++ b/jobs/migrations/0004_auto_20230610_0852.py
@@ -0,0 +1,34 @@
+# Generated by Django 3.2.19 on 2023-06-10 08:52
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("jobs", "0003_auto_20230610_0843"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="job",
+ name="contract_type",
+ field=models.CharField(
+ choices=[
+ ("voluntary", "Voluntary"),
+ ("temporary", "Temporary"),
+ ("fixed_term_contract", "Fixed Term Contract"),
+ ("part_time_permanent", "Part-time Permanent"),
+ ("full_time_permanent", "Full-time Permanent Employed"),
+ ],
+ max_length=40,
+ ),
+ ),
+ migrations.AlterField(
+ model_name="job",
+ name="sector",
+ field=models.CharField(
+ choices=[("commercial", "Commercial"), ("charity", "Charity")],
+ max_length=20,
+ ),
+ ),
+ ]
diff --git a/jobs/migrations/0005_auto_20230610_0901.py b/jobs/migrations/0005_auto_20230610_0901.py
new file mode 100644
index 00000000..05d639ab
--- /dev/null
+++ b/jobs/migrations/0005_auto_20230610_0901.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.2.19 on 2023-06-10 09:01
+
+from django.db import migrations, models
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("jobs", "0004_auto_20230610_0852"),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name="job",
+ options={"ordering": ["-created"]},
+ ),
+ migrations.AddField(
+ model_name="job",
+ name="created",
+ field=models.DateTimeField(
+ auto_now_add=True, default=django.utils.timezone.now
+ ),
+ preserve_default=False,
+ ),
+ ]
diff --git a/jobs/migrations/0006_job_is_published.py b/jobs/migrations/0006_job_is_published.py
new file mode 100644
index 00000000..4a997025
--- /dev/null
+++ b/jobs/migrations/0006_job_is_published.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.2.19 on 2023-06-26 18:47
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("jobs", "0005_auto_20230610_0901"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="job",
+ name="is_published",
+ field=models.BooleanField(default=True),
+ ),
+ ]
diff --git a/jobs/migrations/0007_job_published_date.py b/jobs/migrations/0007_job_published_date.py
new file mode 100644
index 00000000..a69af9f6
--- /dev/null
+++ b/jobs/migrations/0007_job_published_date.py
@@ -0,0 +1,21 @@
+# Generated by Django 3.2.19 on 2023-06-26 19:05
+
+from django.db import migrations, models
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("jobs", "0006_job_is_published"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="job",
+ name="published_date",
+ field=models.DateField(
+ auto_now_add=True, default=django.utils.timezone.now
+ ),
+ preserve_default=False,
+ ),
+ ]
diff --git a/jobs/migrations/0008_auto_20230626_1917.py b/jobs/migrations/0008_auto_20230626_1917.py
new file mode 100644
index 00000000..9ad92d49
--- /dev/null
+++ b/jobs/migrations/0008_auto_20230626_1917.py
@@ -0,0 +1,21 @@
+# Generated by Django 3.2.19 on 2023-06-26 19:17
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("jobs", "0007_job_published_date"),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name="job",
+ name="published_date",
+ ),
+ migrations.AddField(
+ model_name="job",
+ name="updated",
+ field=models.DateField(auto_now=True),
+ ),
+ ]
diff --git a/jobs/migrations/0009_auto_20230626_1922.py b/jobs/migrations/0009_auto_20230626_1922.py
new file mode 100644
index 00000000..0b433763
--- /dev/null
+++ b/jobs/migrations/0009_auto_20230626_1922.py
@@ -0,0 +1,21 @@
+# Generated by Django 3.2.19 on 2023-06-26 19:22
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("jobs", "0008_auto_20230626_1917"),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name="job",
+ name="updated",
+ ),
+ migrations.AddField(
+ model_name="job",
+ name="updated_on",
+ field=models.DateTimeField(auto_now=True),
+ ),
+ ]
diff --git a/jobs/migrations/0010_auto_20230626_1924.py b/jobs/migrations/0010_auto_20230626_1924.py
new file mode 100644
index 00000000..72b5825e
--- /dev/null
+++ b/jobs/migrations/0010_auto_20230626_1924.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.2.19 on 2023-06-26 19:24
+
+from django.db import migrations, models
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("jobs", "0009_auto_20230626_1922"),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name="job",
+ name="updated_on",
+ ),
+ migrations.AddField(
+ model_name="job",
+ name="published_date",
+ field=models.DateField(
+ auto_now_add=True, default=django.utils.timezone.now
+ ),
+ preserve_default=False,
+ ),
+ ]
diff --git a/jobs/migrations/0011_alter_job_incorporation_number.py b/jobs/migrations/0011_alter_job_incorporation_number.py
new file mode 100644
index 00000000..e3542898
--- /dev/null
+++ b/jobs/migrations/0011_alter_job_incorporation_number.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.2.22 on 2023-10-08 13:27
+
+from django.db import migrations, models
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("jobs", "0010_auto_20230626_1924"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="job",
+ name="incorporation_number",
+ field=models.CharField(blank=True, default="", max_length=20),
+ preserve_default=False,
+ ),
+ ]
diff --git a/jobs/migrations/__init__.py b/jobs/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/jobs/models.py b/jobs/models.py
new file mode 100644
index 00000000..7246b0ab
--- /dev/null
+++ b/jobs/models.py
@@ -0,0 +1,42 @@
+from django.db import models
+from django.urls import reverse
+
+
+# Create your models here.
+
+
+class Job(models.Model):
+ SECTOR_CHOICES = [("commercial", "Commercial"), ("charity", "Charity")]
+
+ CONTRACT_TYPE_CHOICES = [
+ ("voluntary", "Voluntary"),
+ ("temporary", "Temporary"),
+ ("fixed_term_contract", "Fixed Term Contract"),
+ ("part_time_permanent", "Part-time Permanent"),
+ ("full_time_permanent", "Full-time Permanent Employed"),
+ ]
+
+ title = models.CharField(max_length=100)
+ description = models.TextField()
+ sector = models.CharField(max_length=20, choices=SECTOR_CHOICES)
+ contract_type = models.CharField(max_length=40, choices=CONTRACT_TYPE_CHOICES)
+ pay = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
+ employer_name = models.CharField(max_length=100)
+ incorporation_number = models.CharField(max_length=20, blank=True)
+ website = models.URLField(max_length=200)
+ expiry_date = models.DateField()
+ application_url = models.URLField(max_length=200)
+ is_published = models.BooleanField(default=True)
+ created = models.DateTimeField(auto_now_add=True)
+ published_date = models.DateField(auto_now_add=True)
+
+ class Meta:
+ ordering = ["-created"]
+
+ def is_expired(self):
+ from datetime import date
+
+ return date.today() > self.expiry_date
+
+ def get_absolute_url(self):
+ return reverse("jobs:job_detail", kwargs={"pk": self.pk})
diff --git a/jobs/templates/jobs/create_job.html b/jobs/templates/jobs/create_job.html
new file mode 100644
index 00000000..632d82eb
--- /dev/null
+++ b/jobs/templates/jobs/create_job.html
@@ -0,0 +1,191 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+ {% if form.errors %}
+ {{ form.errors }}
+ {% endif %}
+
+ Post Job
+
+
+
{{ job.employer_name }}
+No job listings available.
+ {% endfor %} + +