From d57fdb5eb52222be4f6b6b2c5c96942a847b108c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Valenta?= <git@imaniti.org>
Date: Sat, 18 Feb 2023 19:49:56 +0900
Subject: [PATCH] wip - contract table

---
 contracts/admin.py                         |   5 +-
 contracts/forms.py                         |   2 +
 contracts/models.py                        |  38 ++++--
 contracts/templates/contracts/index.html   |  28 ++++-
 contracts/views.py                         |  13 +-
 media/.gitignore                           |   0
 oidc/templates/oidc/base.html              |  32 -----
 oidc/templates/oidc/login.html             |   5 -
 oidc/urls.py                               |   4 +-
 oidc/views.py                              |   8 --
 package.json                               |   2 +
 registry/settings/base.py                  |   6 +-
 requirements/base.txt                      |   1 +
 shared/static/shared/default_avatar.svg    |  59 +++++++++
 shared/templates/shared/includes/base.html | 136 +++++++++++----------
 static_src/base.js                         |  22 ++++
 16 files changed, 236 insertions(+), 125 deletions(-)
 create mode 100644 media/.gitignore
 delete mode 100644 oidc/templates/oidc/base.html
 delete mode 100644 oidc/templates/oidc/login.html
 create mode 100644 shared/static/shared/default_avatar.svg

diff --git a/contracts/admin.py b/contracts/admin.py
index fbac371..5c97886 100644
--- a/contracts/admin.py
+++ b/contracts/admin.py
@@ -2,7 +2,10 @@ from django.contrib import admin
 
 from shared.admin import MarkdownxGuardedModelAdmin
 
-from .forms import ContractAdminForm, SigneeAdminForm
+from .forms import (
+    ContractAdminForm,
+    SigneeAdminForm,
+)
 from .models import (
     Contract,
     Contractee,
diff --git a/contracts/forms.py b/contracts/forms.py
index f6c1dc0..1ee82f7 100644
--- a/contracts/forms.py
+++ b/contracts/forms.py
@@ -1,6 +1,8 @@
 from django import forms
 from webpack_loader.loader import WebpackLoader
 
+from .models import Contractee, Signee
+
 
 class ContractAdminForm(forms.ModelForm):
     class Media:
diff --git a/contracts/models.py b/contracts/models.py
index b25179a..375b08d 100644
--- a/contracts/models.py
+++ b/contracts/models.py
@@ -1,6 +1,7 @@
 from django.conf import settings
 from django.db import models
 from django_countries.fields import CountryField
+from colorfield.fields import ColorField
 from markdownx.models import MarkdownxField
 
 from shared.models import NameStrMixin
@@ -84,8 +85,7 @@ class Signee(models.Model):
         verbose_name="Organizační složka",
     )
 
-    color = models.CharField(
-        max_length=6,  # e.g. "ffffff"
+    color = ColorField(
         blank=True,
         null=True,
         verbose_name="Barva",
@@ -107,6 +107,14 @@ class Signee(models.Model):
         if self.department is not None:
             result += f", {self.department}"
 
+        representative_names = []
+
+        for representative in self.representatives.all():
+            representative_names.append(representative.name)
+
+        if len(representative_names) != 0:
+            result += " - zástupci " + ", ".join(representative_names)
+
         return result
 
 
@@ -187,9 +195,7 @@ class Contractee(models.Model):
         verbose_name="Organizační složka",
     )
 
-    # TODO: Input validation
-    color = models.CharField(
-        max_length=6,  # e.g. "ffffff"
+    color = ColorField(
         blank=True,
         null=True,
         verbose_name="Barva",
@@ -205,6 +211,14 @@ class Contractee(models.Model):
         if self.department is not None:
             result += f", {self.department}"
 
+        representative_names = []
+
+        for representative in self.representatives.all():
+            representative_names.append(representative.name)
+
+        if len(representative_names) != 0:
+            result += " - zástupci " + ", ".join(representative_names)
+
         return result
 
 
@@ -496,7 +510,12 @@ class ContracteeSignature(models.Model):
         verbose_name_plural = "Podpisy našich smluvních stran"
 
     def __str__(self) -> str:
-        representatives = ", ".join(self.contractee.representatives)
+        representative_names = []
+        
+        for representative in self.contractee.representatives.all():
+            representative_names.append(representative.name)
+        
+        representatives = ", ".join(representative_names)
         result = self.contractee.name
 
         if len(representatives) != 0:
@@ -531,7 +550,12 @@ class SigneeSignature(models.Model):
         verbose_name_plural = "Podpisy ostatních smluvních stran"
 
     def __str__(self) -> str:
-        representatives = ", ".join(self.signee.representatives)
+        representative_names = []
+        
+        for representative in self.signee.representatives.all():
+            representative_names.append(representative.name)
+        
+        representatives = ", ".join(representative_names)
         result = self.signee.name
 
         if len(representatives) != 0:
diff --git a/contracts/templates/contracts/index.html b/contracts/templates/contracts/index.html
index 79cce94..982d4e4 100644
--- a/contracts/templates/contracts/index.html
+++ b/contracts/templates/contracts/index.html
@@ -1,5 +1,31 @@
 {% extends "shared/includes/base.html" %}
 
 {% block content %}
-
+    <h1 class="head-alt-lg mb-10">{{ title }}</h1>
+    <table class="table table--striped table--bordered">
+        <thead>
+            <tr>
+                <td>Identifikátor</td>
+                <td>Typ</td>
+                <td>Právní stav</td>
+                <td>Účinná od</td>
+                <td>Platná do</td>
+                <td>Naše smluvní strana</td>
+                <td>Ostatní smluvní strany</td>
+            </tr>
+        </thead>
+        <tbody>
+            {% for contract in page %}
+                <tr>
+                    <td>{{ contract.identifier }}</td>
+                    <td>{{ contract.get_type_display }}</td>
+                    <td>{{ contract.get_legal_state_display }}</td>
+                    <td>{{ contract.valid_start_date }}</td>
+                    <td>{{ contract.valid_end_date }}</td>
+                    <td></td>
+                    <td></td>
+                </tr>
+            {% endfor %}
+        </tbody>
+    </table>
 {% endblock %}
diff --git a/contracts/views.py b/contracts/views.py
index 36fa1cc..369c042 100644
--- a/contracts/views.py
+++ b/contracts/views.py
@@ -1,17 +1,28 @@
 from django.conf import settings
 from django.shortcuts import render
+from django.core.paginator import Paginator
+
+from .models import Contract
 
 # Create your views here.
 
 
 def index(request):
+    contracts = Contract.objects.all()
+    paginator = Paginator(contracts, 25)
+    
+    page = paginator.get_page(
+        request.GET.get('page')
+    )
+    
     return render(
         request,
         "contracts/index.html",
         {
             "site_url": settings.SITE_URL,
             "user": request.user,
-            "title": "Title",
+            "title": "Seznam smluv",
             "description": "Description",
+            "page": page,
         }
     )
diff --git a/media/.gitignore b/media/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/oidc/templates/oidc/base.html b/oidc/templates/oidc/base.html
deleted file mode 100644
index 610ef36..0000000
--- a/oidc/templates/oidc/base.html
+++ /dev/null
@@ -1,32 +0,0 @@
-{% load static %}
-{% load render_bundle from webpack_loader %}
-
-<!DOCTYPE html>
-<html lang="cs" class="h-full">
-    <head>
-        <meta encoding="utf-8">
-        <link
-            rel="stylesheet"
-            type="text/css"
-            href="{% static "shared/style.css" %}"
-        >
-
-        <link
-            rel="stylesheet"
-            href="https://gfonts.pirati.cz/css2?family=Bebas+Neue&family=Roboto+Condensed:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap"
-        >
-
-        <link
-            rel="stylesheet"
-            type="text/css"
-            href="{% static "shared/fonts/pirati-ui/style.css" %}"
-        >
-
-        <title>{% block current_page %}{% endblock %}</title>
-
-        {% render_bundle "base" %}
-    </head>
-    <body>
-        {% block content %}{% endblock %}
-    </body>
-</html>
diff --git a/oidc/templates/oidc/login.html b/oidc/templates/oidc/login.html
deleted file mode 100644
index f31980a..0000000
--- a/oidc/templates/oidc/login.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% extends "oidc/base.html" %}
-
-{% block content %}
-    <a href="{% url "oidc_authentication_init" %}">Přihlásit se</a>
-{% endblock %}
diff --git a/oidc/urls.py b/oidc/urls.py
index a15ad64..139738a 100644
--- a/oidc/urls.py
+++ b/oidc/urls.py
@@ -3,6 +3,4 @@ from django.urls import path
 from . import views
 
 app_name = "oidc"
-urlpatterns = [
-    path("login", views.login, name="login"),
-]
+urlpatterns = []
diff --git a/oidc/views.py b/oidc/views.py
index 23dbba5..e69de29 100644
--- a/oidc/views.py
+++ b/oidc/views.py
@@ -1,8 +0,0 @@
-from django.shortcuts import render
-
-
-def login(request):
-    return render(
-        request,
-        "oidc/login.html",
-    )
diff --git a/package.json b/package.json
index b6d5d92..83f28d5 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,9 @@
   "dependencies": {
     "css-loader": "^6.7.3",
     "jquery": "^3.6.3",
+    "style-loader": "^3.3.1",
     "tailwindcss": "^3.2.4",
+    "tippy.js": "^6.3.7",
     "vue": "v2-latest",
     "webpack": "^5.75.0",
     "webpack-bundle-tracker": "^1.8.0",
diff --git a/registry/settings/base.py b/registry/settings/base.py
index 0495288..3d354b1 100644
--- a/registry/settings/base.py
+++ b/registry/settings/base.py
@@ -35,6 +35,7 @@ SECRET_KEY = env.str("SECRET_KEY")
 ALLOWED_HOSTS = []
 
 STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
+MEDIA_ROOT = os.path.join(BASE_DIR, "media")
 
 SITE_URL = env.str("SITE_URL")
 
@@ -48,6 +49,7 @@ INSTALLED_APPS = [
     "django.contrib.sessions",
     "django.contrib.messages",
     "django.contrib.staticfiles",
+    "colorfield",
     "guardian",
     "markdownx",
     "pirates",
@@ -129,9 +131,9 @@ AUTHENTICATION_BACKENDS = (
     "guardian.backends.ObjectPermissionBackend",
 )
 
-LOGIN_URL = "/oidc/login"
+LOGIN_URL = "/"
 LOGIN_REDIRECT_URL = "/"
-LOGOUT_REDIRECT_URL = "/oidc/login"
+LOGOUT_REDIRECT_URL = "/"
 
 OIDC_RP_CLIENT_ID = env.str("OIDC_RP_CLIENT_ID")
 OIDC_RP_CLIENT_SECRET = env.str("OIDC_RP_CLIENT_SECRET")
diff --git a/requirements/base.txt b/requirements/base.txt
index 9ebad7d..62a0fab 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -4,6 +4,7 @@ psycopg2-binary==2.9.5
 django-webpack-loader==1.8.0
 nodeenv==1.7.0
 pirates==0.6.0
+django-colorfield==0.8.0
 django-markdownx==4.0.0b1
 django-environ==0.9.0
 django-http-exceptions==1.4.0
diff --git a/shared/static/shared/default_avatar.svg b/shared/static/shared/default_avatar.svg
new file mode 100644
index 0000000..ca87d5c
--- /dev/null
+++ b/shared/static/shared/default_avatar.svg
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   version="1.1"
+   width="560"
+   height="560"
+   fill="#ffffff"
+   id="svg15">
+  <metadata
+     id="metadata19">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Abstract user icon</dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <title
+     id="title2">Abstract user icon</title>
+  <defs
+     id="defs7">
+    <clipPath
+       id="circular-border">
+      <circle
+         cx="300"
+         cy="300"
+         r="250"
+         id="circle4" />
+    </clipPath>
+  </defs>
+  <circle
+     cx="280"
+     cy="280"
+     fill="#000000"
+     id="circle9"
+     r="280"
+     style="fill:#f5f5f5;fill-opacity:1" />
+  <circle
+     cx="280"
+     cy="210"
+     r="100"
+     id="circle11"
+     style="fill:#262626;fill-opacity:1" />
+  <circle
+     cx="300"
+     cy="550"
+     r="190"
+     clip-path="url(#circular-border)"
+     id="circle13"
+     transform="translate(-20,-20)"
+     style="fill:#262626;fill-opacity:1" />
+</svg>
diff --git a/shared/templates/shared/includes/base.html b/shared/templates/shared/includes/base.html
index a10c433..93d15aa 100644
--- a/shared/templates/shared/includes/base.html
+++ b/shared/templates/shared/includes/base.html
@@ -6,20 +6,20 @@
     <head>
         <meta charset="utf-8">
         
-        <meta name="title" content="{{ title }}">
+        <meta name="title" content="{{ title }} | Registr smluv Pirátské strany">
         <meta name="description" content="{{ description }}">
 
         {% comment %}Open Graph / Facebook{% endcomment %}
         <meta property="og:type" content="website">
         <meta property="og:url" content="{{ site_url }}">
-        <meta property="og:title" content="{{ title }}">
+        <meta property="og:title" content="{{ title }} | Registr smluv Pirátské strany">
         <meta property="og:description" content="{{ description }}">
         {% comment %}<meta property="og:image" content="">{% endcomment %}
 
         {% comment %}Twitter{% endcomment %}
         <meta property="twitter:card" content="app">
         <meta property="twitter:url" content="{{ site_url }}">
-        <meta property="twitter:title" content="{{ title }}">
+        <meta property="twitter:title" content="{{ title }} | Registr smluv Pirátské strany">
         <meta property="twitter:description" content="{{ description }}">
         {% comment %}<meta property="twitter:image" content="">{% endcomment %}
 
@@ -43,76 +43,82 @@
         {% render_bundle "shared" %}
         {% render_bundle "base" %}
         
-        <title>{{ title }}</title>
+        <title>{{ title }} | Registr smluv Pirátské strany</title>
     </head>
     <body>
-        <header>
-            <nav class="navbar navbar--simple __js-root">
-                <ui-app inline-template>
-                    <ui-navbar inline-template>
-                        <div>
-                            <div class="container container--default navbar__content" :class="{'navbar__content--initialized': true}">
-                                <div class="navbar__brand my-4 flex items-center lg:pr-8 lg:my-0">
-                                    <a href="/">
-                                        <img src="https://styleguide.pirati.cz/2.11.x/images/logo-round-white.svg" class="w-8" />
-                                    </a>
-                                    <a href="/" class="pl-4 font-bold text-xl lg:border-r lg:border-grey-300 lg:pr-8">Registr smluv</a>
-                                </div>
-                                
-                                <div class="navbar__menutoggle my-4 flex justify-end lg:hidden">
-                                    <a href="#" @click="show = !show" class="no-underline hover:no-underline">
-                                        <i class="ico--menu text-3xl"></i>
-                                    </a>
-                                </div>
-                                
-                                <div v-if="show || isLgScreenSize" class="navbar__main navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto">
-                                    <ul class="navbar-menu text-white">
-                                        <li class="navbar-menu__item">
-                                            <a href="#TODO" data-href="#TODO" class="navbar-menu__link">Všechny smlouvy</a>
-                                        </li>
+        <nav class="navbar navbar--simple __js-root">
+            <ui-app inline-template>
+                <ui-navbar inline-template>
+                    <div>
+                        <div class="container container--default navbar__content" :class="{'navbar__content--initialized': true}">
+                            <div class="navbar__brand my-4 flex items-center lg:pr-8 lg:my-0">
+                                <a href="/">
+                                    <img src="https://styleguide.pirati.cz/2.11.x/images/logo-round-white.svg" class="w-8" />
+                                </a>
+                                <a href="/" class="pl-4 font-bold text-xl hover:no-underline lg:border-r lg:border-grey-300 lg:pr-8">Registr smluv</a>
+                            </div>
+                            
+                            <div class="navbar__menutoggle my-4 flex justify-end lg:hidden">
+                                <a href="#" @click="show = !show" class="no-underline hover:no-underline">
+                                    <i class="ico--menu text-3xl"></i>
+                                </a>
+                            </div>
+                            
+                            <div v-if="show || isLgScreenSize" class="navbar__main navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto">
+                                <ul class="navbar-menu text-white">
+                                    <li class="navbar-menu__item">
+                                        <a href="{% url "contracts:index" %}" data-href="{% url "contracts:index" %}" class="navbar-menu__link">Všechny smlouvy</a>
+                                    </li>
+                                    <li class="navbar-menu__item">
+                                        <a href="#TODO" data-href="#TODO" class="navbar-menu__link">Hledání</a>
+                                    </li>
+                                    {% if user.is_staff %}
                                         <li class="navbar-menu__item">
-                                            <a href="#TODO" data-href="#TODO" class="navbar-menu__link">Hledání</a>
+                                            <a href="{% url "admin:index" %}" data-href="{% url "admin:index" %}" class="navbar-menu__link">Administrace</a>
                                         </li>
-                                    </ul>
-                                </div>
-                                
-                                <div v-if="show || isLgScreenSize" class="navbar__actions navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto self-start flex flex-col sm:flex-row lg:flex-col sm:space-x-4 space-y-2 sm:space-y-0 lg:space-y-2 xl:flex-row xl:space-x-2 xl:space-y-0">
-                                    {% if user %}
-                                        <div class="flex items-center space-x-4">
-                                            <span class="head-heavy-2xs">{{ user.get_username }}</span>
-                                            <div class="avatar avatar--2xs">
-                                                <img
-                                                    src="https://randomuser.me/api/portraits/women/83.jpg"
-                                                    alt="Tvůj profilový obrázek"
-                                                >
-                                            </div>
-                                            <form action="{% url "oidc_logout" %}" method="POST">
-                                                {% csrf_token %}
-                                                <button
-                                                    class="text-grey-200 hover:text-white"
-                                                    type="submit"
-                                                ><i class="ico--log-out"></i></button>
-                                            </form>
-                                        </div>
-                                    {% else %}
-                                        <a
-                                            class="btn btn--white btn--hoveractive cursor-pointer"
-                                            href="{% url "oidc:login" %}"
-                                        >
-                                            <div class="btn__body">Přihlásit se</div>
-                                        </a>
                                     {% endif %}
-                                </div>
+                                </ul>
+                            </div>
+                            
+                            <div v-if="show || isLgScreenSize" class="navbar__actions navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto self-start flex flex-col sm:flex-row lg:flex-col sm:space-x-4 space-y-2 sm:space-y-0 lg:space-y-2 xl:flex-row xl:space-x-2 xl:space-y-0">
+                                {% if user %}
+                                    <div class="flex items-center space-x-4">
+                                        <span class="head-heavy-2xs">{{ user.get_username }}</span>
+                                        <div class="avatar avatar--2xs">
+                                            <img
+                                                src="{% static "shared/default_avatar.svg" %}"
+                                                alt="Tvůj profilový obrázek"
+                                            >
+                                        </div>
+                                        <form action="{% url "oidc_logout" %}" method="POST">
+                                            {% csrf_token %}
+                                            <button
+                                                class="text-grey-200 hover:text-white __tooltipped"
+                                                type="submit"
+                                                aria-label="Odhlásit se"
+                                            ><i class="ico--log-out"></i></button>
+                                        </form>
+                                    </div>
+                                {% else %}
+                                    <a
+                                        class="btn btn--white btn--hoveractive cursor-pointer"
+                                        href="{% url "oidc_authentication_init" %}"
+                                    >
+                                        <div class="btn__body">Přihlásit se</div>
+                                    </a>
+                                {% endif %}
                             </div>
                         </div>
-                    </ui-navbar>
-                </ui-app>
-            </nav>
-        </header>
+                    </div>
+                </ui-navbar>
+            </ui-app>
+        </nav>
         
-        <main>
-            {% block content %}{% endblock %}
-        </main>
+        <div class="container container--default py-8 lg:py-24">
+            <main>
+                {% block content %}{% endblock %}
+            </main>
+        </div>
         
         <script
             src="https://styleguide.pirati.cz/2.11.x/js/main.bundle.js"
diff --git a/static_src/base.js b/static_src/base.js
index 156d4b7..524be4c 100644
--- a/static_src/base.js
+++ b/static_src/base.js
@@ -1,3 +1,25 @@
+import $ from "jquery";
+
 import Vue from "vue/dist/vue.esm.browser.min";
 
+import tippy from "tippy.js";
+import "tippy.js/dist/tippy.css";
+import "tippy.js/themes/light-border.css";
+import "tippy.js/animations/scale-subtle.css";
+
 window["Vue"] = Vue;
+
+$(window).ready(
+    () => {
+        // Add tooltips
+        for (const tooltipElement of $('.__tooltipped')) {
+            tippy(
+                tooltipElement,
+                {
+                    content: tooltipElement.getAttribute("aria-label"),
+                    animation: "scale-subtle"
+                }
+            )
+        }
+    }
+);
-- 
GitLab