diff --git a/framadate/__init__.py b/framadate/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/framadate/admin.py b/framadate/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/framadate/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/framadate/apps.py b/framadate/apps.py new file mode 100644 index 00000000..027fcb08 --- /dev/null +++ b/framadate/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class FramadateConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'framadate' diff --git a/framadate/migrations/0001_initial.py b/framadate/migrations/0001_initial.py new file mode 100644 index 00000000..ce443dc2 --- /dev/null +++ b/framadate/migrations/0001_initial.py @@ -0,0 +1,54 @@ +# Generated by Django 4.2.16 on 2025-01-26 17:14 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Framadate', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField()), + ], + ), + migrations.CreateModel( + name='FramadateDate', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('day', models.DateField()), + ('time', models.TimeField(blank=True, null=True)), + ('framadate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='days_set', to='framadate.framadate')), + ], + options={ + 'ordering': ['day', 'time'], + }, + ), + migrations.CreateModel( + name='NameInFramadate', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('framadate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='names_set', to='framadate.framadate')), + ], + ), + migrations.CreateModel( + name='Record', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('choice', models.IntegerField(choices=[(0, 'Unknown'), (3, 'Yes'), (2, 'No'), (1, 'Maybe')], default=0)), + ('framadateDate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='records_set', to='framadate.framadatedate')), + ('nameInFramadate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='records_set', to='framadate.nameinframadate')), + ], + options={ + 'ordering': ['framadateDate__day', 'framadateDate__time'], + }, + ), + ] diff --git a/framadate/migrations/__init__.py b/framadate/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/framadate/models.py b/framadate/models.py new file mode 100644 index 00000000..81ea15e5 --- /dev/null +++ b/framadate/models.py @@ -0,0 +1,59 @@ +from django.db import models +from datetime import datetime + +# Create your models here +class Framadate(models.Model): + name = models.TextField() + + "isRelevant if all dates are in the future" + @property + def isRelevant(self): + for day in self.days_set.all(): + if day.day < datetime.now().date(): + return False + return True + + def __str__(self): + return self.name + +class NameInFramadate(models.Model): + framadate = models.ForeignKey(Framadate, on_delete=models.CASCADE, related_name="names_set") + name = models.CharField(max_length=200) + + def __str__(self): + return self.name + " - in - " + self.framadate.name + +class FramadateDate(models.Model): + framadate = models.ForeignKey(Framadate, on_delete=models.CASCADE, related_name="days_set") + day = models.DateField() + time = models.TimeField(null=True, blank=True) + + def __str__(self): + return self.day.strftime("%Y-%m-%d") + " - in- " + self.framadate.name + + class Meta: + ordering = ['day', 'time'] + +class Choice(models.IntegerChoices): + UNKNOWN = 0 + YES = 3 + NO = 2 + MAYBE = 1 + +# Quirk -> name and framadateDate needs to share same Framadate... +class Record(models.Model): + nameInFramadate = models.ForeignKey(NameInFramadate, on_delete=models.CASCADE, related_name="records_set") + framadateDate = models.ForeignKey(FramadateDate, on_delete=models.CASCADE, related_name="records_set") + choice = models.IntegerField(choices=Choice.choices, default=Choice.UNKNOWN) + + @property + def choiceName(self): + return Choice(self.choice).name + + def __str__(self): + if self.framadateDate.time: + return self.nameInFramadate.name + " - in - " + self.framadateDate.framadate.name + " - " + self.framadateDate.day.strftime("%Y-%m-%d") + " - " + self.framadateDate.time.strftime("%H") + ":00 - " + Choice(self.choice).name + return self.nameInFramadate.name + " - in - " + self.framadateDate.framadate.name + " - " + self.framadateDate.day.strftime("%Y-%m-%d") + " - " + Choice(self.choice).name + + class Meta: + ordering = ['framadateDate__day', 'framadateDate__time'] diff --git a/framadate/static/framadate/framadate_detail.css b/framadate/static/framadate/framadate_detail.css new file mode 100644 index 00000000..6afe7b30 --- /dev/null +++ b/framadate/static/framadate/framadate_detail.css @@ -0,0 +1,132 @@ + +textarea { + font-family: "Tomorrow", serif; + font-weight: 400; + font-style: normal; + font-size: 1.5em; + padding: 0.3em 0.5em; +} + +.framadate-detail { + display: flex; + flex-direction: column; + gap: 0.8em; + overflow-x: hidden; +} + +.table { + overflow-x: auto; + padding-bottom: 2em; + display: flex; + flex-direction: column; + gap: 0.3em; + + .row { + display: flex; + gap: 0.3em; + width: max-content; + + .item { + /* background-color: red; */ + width: 140px; + text-align: center; + padding: 0.8em 0; + } + } + + .YES { + background-color: green; + } + + .NO { + background-color: red; + } + + .MAYBE { + background-color: goldenrod; + } + + .UNKNOWN { + background-color: gray; + } + + .radio-column { + display: flex; + flex-direction: column; + align-items: start; + + label { + width: 100%; + padding: 0.5em 0; + } + + input { + display: none; + } + + label:has(input:checked) { + background-color: #2973B2; + color: white; + border-radius: 10px; + } + } + + #name { + font-family: "Tomorrow", serif; + font-size: 1em; + } + + .submit { + transition: box-shadow 0.2s ease; + background-color: #2973B2; + color: white; + text-decoration: none; + padding: 0.5em 1em; + margin: 1em; + width: min-content; + white-space: nowrap; + border-radius: 20px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + border: none; + font-size: 1.4em; + font-family: "Tomorrow", serif; + width: 150px; + } + + .delete { + background-color: red; + color: black; + + } + + .submit:hover { + box-shadow: none; + } +} + +.center { + display: flex; + align-items: center; + justify-content: center; +} + +.mini-row .item { + font-size: 0.8em; +} + +.item-with-edit { + display: flex; + align-items: center; + justify-content: center; + gap: 1em; + + a { + color: #2973B2; + } +} + +.delete-framadate { + font-size: 0.5em; + margin-left: 2em; + color: red; +} \ No newline at end of file diff --git a/framadate/static/framadate/new_framadate.css b/framadate/static/framadate/new_framadate.css new file mode 100644 index 00000000..6b43a30e --- /dev/null +++ b/framadate/static/framadate/new_framadate.css @@ -0,0 +1,82 @@ +.label-name { + display: flex; + flex-direction: row; + gap: 2em; + align-items: center; + font-family: "Tomorrow", serif; + + input { + font-family: "Tomorrow", serif; + font-size: 1em; + padding: 0.5em; + border-radius: 10px; + text-align: center; + border: 1px solid #606060; + } +} + +.dates { + border: 1px solid #ccc; + padding: 1em; + margin: 1em 0; + border-radius: 10px; + display: flex; + flex-direction: column; + gap: 0.5em; + + .date { + display: flex; + flex-direction: row; + gap: 2em; + align-items: center; + } +} + +input[type="date"], input[type="time"] { + font-family: "Tomorrow", serif; + font-size: 1em; + padding: 0.3em 0.7em; + border: 1px solid #606060; + border-radius: 10px; + margin-left: 0.5em; +} + +.controls { + margin-bottom: 2em; + display: flex; + flex-direction: column; + gap: 0.5em; + align-items: start; +} + +.add-button { + border: none; + background-color: #2973B2; + color: white; + padding: 0.4em 1em; + font-size: 1em; + font-family: "Tomorrow", serif; + border-radius: 10px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + transition: box-shadow 0.2s ease; + margin: 0.5em; +} + +.add-button:hover { + box-shadow: none; +} + +.span-controls { + display: flex; + gap: 2em; + + label { + display: flex; + align-items: center; + gap: 1em; + } +} + +.create { + background-color: green; +} \ No newline at end of file diff --git a/framadate/static/framadate/style.css b/framadate/static/framadate/style.css new file mode 100644 index 00000000..fe89301b --- /dev/null +++ b/framadate/static/framadate/style.css @@ -0,0 +1,47 @@ +body { + font-family: "Tomorrow", serif; + font-weight: 400; + font-style: normal; + margin: 1em; + padding: 1em; + border: 1px solid black; + border-radius: 20px; + font-size: 1.2em; + + .framadates { + display: flex; + flex-direction: column; + gap: 0.8em; + + a { + color: black; + } + } + + h1 { + margin: 0.3em 0.5em; + } + + .column { + display: flex; + flex-direction: column; + justify-content: start; + } + + .new { + transition: box-shadow 0.2s ease; + background-color: #2973B2; + color: white; + text-decoration: none; + padding: 0.5em 1em; + margin: 1em 0; + width: min-content; + white-space: nowrap; + border-radius: 10px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + } + + .new:hover { + box-shadow: none; + } +} diff --git a/framadate/templates/framadate/framadate_detail.html b/framadate/templates/framadate/framadate_detail.html new file mode 100644 index 00000000..8fde59bf --- /dev/null +++ b/framadate/templates/framadate/framadate_detail.html @@ -0,0 +1,98 @@ + + +{% load static %} + + + + + + + + + +
+ + diff --git a/framadate/templates/framadate/framadate_list.html b/framadate/templates/framadate/framadate_list.html new file mode 100644 index 00000000..2d01aab5 --- /dev/null +++ b/framadate/templates/framadate/framadate_list.html @@ -0,0 +1,31 @@ + + +{% load static %} + + + + + + +