From e123a904fc00a9162d1f906151a5318a1c9b5f8b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Valenta?= <tomas@imaniti.org>
Date: Wed, 29 May 2024 11:32:38 +0200
Subject: [PATCH] add district program type

---
 .../molecules/popouts/popout_point.html       | 32 +++++-
 .../molecules/popouts/popout_point.yaml       |  3 +
 .../header/district/center_header.html        |  2 +-
 .../header/simple_header_with_ui_switch.html  |  2 +
 .../templates/main/program/program.html       | 95 ++++++++++++------
 .../templates/main/program/program.yaml       |  7 +-
 src/css/molecules/popout.pcss                 |  2 +-
 src/css/molecules/switch.pcss                 | 16 ++-
 src/css/organisms/newsletter.pcss             |  4 -
 src/js/components/SecondaryViewProvider.vue   | 98 +++++++++++++++++++
 src/js/components/countdown/Countdown.vue     | 38 +++++--
 src/js/main.js                                |  2 +
 12 files changed, 252 insertions(+), 49 deletions(-)
 create mode 100644 src/js/components/SecondaryViewProvider.vue

diff --git a/majak_uistyleguide/templates/patterns/molecules/popouts/popout_point.html b/majak_uistyleguide/templates/patterns/molecules/popouts/popout_point.html
index 8aeaa9a..287c6f2 100644
--- a/majak_uistyleguide/templates/patterns/molecules/popouts/popout_point.html
+++ b/majak_uistyleguide/templates/patterns/molecules/popouts/popout_point.html
@@ -4,8 +4,36 @@
       {{ name }}
     </template>
     <ui-popout-content>
-      <div class="prose max-w-screen-lg">
-        {% include "patterns/atoms/text/paragraph.html" with text=text %}
+      <div>
+        {% if guarantor_name %}
+          <div
+            class="
+              !text-grey-250 [&_*]:!text-grey-250
+
+              mb-3 flex gap-2 items-center justify-end
+            "
+          >
+            <div class="mr-2">
+              Garant programového bodu:
+            </div>
+
+            <img
+              alt="Obrázek osoby {{ guarantor_name }}"
+              class="w-10 h-10 opacity-75 m-0 p-0 rounded-full"
+              src="{{ guarantor_image_url }}"
+            >
+
+            <strong>
+              <a href="{{ guarantor_url }}">
+                {{ guarantor_name }}
+              </a>
+            </strong>
+          </div>
+        {% endif %}
+
+        <div class="prose max-w-screen-lg">
+          {% include "patterns/atoms/text/paragraph.html" with text=text %}
+        </div>
       </div>
     </ui-popout-content>
   </ui-popout>
diff --git a/majak_uistyleguide/templates/patterns/molecules/popouts/popout_point.yaml b/majak_uistyleguide/templates/patterns/molecules/popouts/popout_point.yaml
index cc4d9dd..de8fde5 100644
--- a/majak_uistyleguide/templates/patterns/molecules/popouts/popout_point.yaml
+++ b/majak_uistyleguide/templates/patterns/molecules/popouts/popout_point.yaml
@@ -5,3 +5,6 @@ context:
     asperiores voluptatibus enim qui velit quaerat. Perspiciatis 
     eum autem quidem et beatae quia dolore. Esse fuga architecto 
     delectus tenetur nesciunt aut aut dolore.'
+  guarantor_name: 'Petr Vepřový'
+  guarantor_url: 'https://example.com'
+  guarantor_image_url: '../../../../../static/images/person-table.png'
diff --git a/majak_uistyleguide/templates/patterns/organisms/header/district/center_header.html b/majak_uistyleguide/templates/patterns/organisms/header/district/center_header.html
index fcec266..36c17d4 100644
--- a/majak_uistyleguide/templates/patterns/organisms/header/district/center_header.html
+++ b/majak_uistyleguide/templates/patterns/organisms/header/district/center_header.html
@@ -42,7 +42,7 @@
             </strong>
           </div>
 
-          <div class="whitespace-pre">Mozilla Foundation
+          <div class="whitespace-pre-line">Mozilla Foundation
 331 E Evelyn Ave
 Mountain View, CA 94041
 USA</div>
diff --git a/majak_uistyleguide/templates/patterns/organisms/header/simple_header_with_ui_switch.html b/majak_uistyleguide/templates/patterns/organisms/header/simple_header_with_ui_switch.html
index 3ba50b3..c2b1151 100644
--- a/majak_uistyleguide/templates/patterns/organisms/header/simple_header_with_ui_switch.html
+++ b/majak_uistyleguide/templates/patterns/organisms/header/simple_header_with_ui_switch.html
@@ -1,5 +1,7 @@
 {% extends 'patterns/organisms/header/simple_header.html' %}
 
+{% block header_margin_class %}{% endblock %}
+
 {% block after_description %}
   <div
     class="
diff --git a/majak_uistyleguide/templates/patterns/templates/main/program/program.html b/majak_uistyleguide/templates/patterns/templates/main/program/program.html
index 96da87a..2929f0f 100644
--- a/majak_uistyleguide/templates/patterns/templates/main/program/program.html
+++ b/majak_uistyleguide/templates/patterns/templates/main/program/program.html
@@ -8,39 +8,74 @@
   >
     {% include 'patterns/organisms/header/simple_header_with_ui_switch.html' with title='Program' ui_switch_iterable=programs %}
 
-    <main role="main" class="mb-4 xl:mb-20">
-      <div class="container--wide flex flex-col">
-        {% for program in programs %}
-          <template v-if="isCurrentView('view{{ forloop.counter }}')">
-            {% if program.type == "concise" %}
-              <div>
-                {% if program.perex %}
-                  <div class="mb-12">
-                    <div class="prose">
-                      {# BEGIN Cast generovana Majakem #}<div class="content-block">
-                        <p>
-                            {{ program.perex }}
-                        </p>
-                      </div>{# END Cast generovana Majakem #}
-                    </div>
+    <main role="main">
+      {% for program in programs %}
+        <template v-if="isCurrentView('view{{ forloop.counter }}')">
+          {% if program.type == "concise" %}
+            <div class="container--wide flex flex-col mt-20 mb-4 xl:mb-20">
+              {% if program.perex %}
+                <div class="mb-12">
+                  <div class="prose">
+                    {# BEGIN Cast generovana Majakem #}<div class="content-block">
+                      <p>
+                          {{ program.perex }}
+                      </p>
+                    </div>{# END Cast generovana Majakem #}
                   </div>
-                {% endif %}
+                </div>
+              {% endif %}
 
-                {% include 'patterns/molecules/blocks/icon_title_text_block.html' with icon='ico--book' %}
-                {% include 'patterns/molecules/blocks/icon_title_text_block.html' with icon=None %}
-              </div>
-            {% elif program.type == 'popout' %}
-              <div class="mb-12">
-                {% include 'patterns/organisms/popouts/popout_list.html' %}
-              </div>
-            {% elif program.type == 'crossroad' %}
-              <div class="mb-12">
-                {% include 'patterns/organisms/cards/card_list.html' with description_classes="!bg-grey-180" classes='drop-shadow' %}
+              {% include 'patterns/molecules/blocks/icon_title_text_block.html' with icon='ico--book' %}
+              {% include 'patterns/molecules/blocks/icon_title_text_block.html' with icon=None %}
+            </div>
+          {% elif program.type == 'popout' %}
+            <div class="container--wide flex flex-col mt-20 mb-12 xl:mb-20">
+              {% include 'patterns/organisms/popouts/popout_list.html' %}
+            </div>
+          {% elif program.type == 'crossroad' %}
+            <div class="container--wide flex flex-col mt-20 mb-12 xl:mb-20">
+              {% include 'patterns/organisms/cards/card_list.html' with description_classes="!bg-grey-180" classes='drop-shadow' %}
+            </div>
+          {% elif program.type == 'with_candidates' %}
+            <ui-secondary-view-provider
+              :initial="{candidates: true, program: false}"
+              :sync-location="true"
+              v-slot="{ isCurrentSecondaryView, toggleSecondaryView }"
+            >
+              <div class="bg-white py-12 container--wide text-center">
+                <a
+                  @click="toggleSecondaryView('candidates')"
+                  class="switch__item--program"
+                  :class="{'switch__item--active': isCurrentSecondaryView('candidates')}"
+                >Kandidáti</a>
+
+                <a
+                  @click="toggleSecondaryView('program')"
+                  class="switch__item--program"
+                  :class="{'switch__item--active': isCurrentSecondaryView('program')}"
+                >Program</a>
               </div>
-            {% endif %}
-          </template>
-        {% endfor %}
-      </div>
+
+              <template v-if="isCurrentSecondaryView('candidates')">
+                <div>
+                  {% include "patterns/organisms/candidates/elections/candidate_primary_list.html" %}
+                  {% include "patterns/organisms/candidates/district/candidate_secondary_list.html" %}
+                </div>
+              </template>
+
+              <template v-if="isCurrentSecondaryView('program')">
+                <div class="bg-grey-50 pb-4 xl:pb-20">
+                  <div class="container--wide flex flex-col mb-12 gap-8">
+                    {% include 'patterns/atoms/text/richtext.html' %}
+
+                    {% include 'patterns/organisms/popouts/popout_list.html' %}
+                  </div>
+                </div>
+              </template>
+            </ui-secondary-view-provider>
+          {% endif %}
+        </template>
+      {% endfor %}
     </main>
   </ui-view-provider>
 </div>
diff --git a/majak_uistyleguide/templates/patterns/templates/main/program/program.yaml b/majak_uistyleguide/templates/patterns/templates/main/program/program.yaml
index 6dd6995..4d7b0cd 100644
--- a/majak_uistyleguide/templates/patterns/templates/main/program/program.yaml
+++ b/majak_uistyleguide/templates/patterns/templates/main/program/program.yaml
@@ -20,4 +20,9 @@ context:
       name: 'dlouhodobý'
       type: 'popout'
       perex: 'Lorem ipsum dolor sit amet 5, který vysvětluje důležitost dlouhodobého
-        programu.'
+      programu.'
+    -
+      name: 'krajský'
+      type: 'with_candidates'
+      perex: 'Lorem ipsum dolor sit amet 6, který vysvětluje důležitost dlouhodobého
+      programu.'
diff --git a/src/css/molecules/popout.pcss b/src/css/molecules/popout.pcss
index 06b87e5..8639c4a 100644
--- a/src/css/molecules/popout.pcss
+++ b/src/css/molecules/popout.pcss
@@ -11,7 +11,7 @@
 }
 
 .popout__content-wrapper {
-  @apply px-5 py-4 flex flex-col gap-3;
+  @apply px-5 py-4 flex flex-col gap-3 bg-grey-150;
 }
 
 .popout__toggle-arrow {
diff --git a/src/css/molecules/switch.pcss b/src/css/molecules/switch.pcss
index 989d301..667b574 100644
--- a/src/css/molecules/switch.pcss
+++ b/src/css/molecules/switch.pcss
@@ -2,7 +2,7 @@
   @apply py-5 mx-auto;
 }
 
-.switch__item, .switch__item--elections {
+.switch__item, .switch__item--elections, .switch__item--program {
   @apply whitespace-nowrap cursor-pointer px-5 py-2 mb-2 font-normal text-center duration-200;
 
   &:not(:last-child) {
@@ -24,6 +24,20 @@
 }
 
 
+.switch__item--program {
+  @apply bg-grey-150 text-black;
+
+  &:hover {
+    @apply no-underline bg-grey-200;
+  }
+
+  &.switch__item--active,
+  &.switch__item--active:hover {
+    @apply bg-pirati-yellow;
+  }
+}
+
+
 .switch__item--elections {
   @apply bg-black text-white;
 
diff --git a/src/css/organisms/newsletter.pcss b/src/css/organisms/newsletter.pcss
index f25366f..3a55396 100644
--- a/src/css/organisms/newsletter.pcss
+++ b/src/css/organisms/newsletter.pcss
@@ -1,10 +1,6 @@
 .newsletter-section {
   @apply bg-cover bg-no-repeat;
 
-  &:not(.newsletter-section--small) {
-    background-position: -400px;
-  }
-
   @screen md {
     background-position: left top;
   }
diff --git a/src/js/components/SecondaryViewProvider.vue b/src/js/components/SecondaryViewProvider.vue
new file mode 100644
index 0000000..67d45ca
--- /dev/null
+++ b/src/js/components/SecondaryViewProvider.vue
@@ -0,0 +1,98 @@
+<template>
+  <div>
+    <slot
+      v-bind:secondaryViews="secondaryViews"
+      v-bind:isCurrentSecondaryView="isCurrentSecondaryView"
+      v-bind:toggleSecondaryView="toggleSecondaryView"
+      v-bind:showSecondaryView="showSecondaryView"
+      v-bind:setSecondaryView="setSecondaryView"
+    ></slot>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    initial: {
+      default: () => {}
+    },
+    syncLocation: {
+      type: Boolean,
+      default: false,
+    },
+    locationParam: {
+      type: String,
+      default: "secondary-view",
+    }
+  },
+  data() {
+    return {
+      secondaryViews: this.$props.initial,
+      queryParams: null,
+      keyListener: e => {
+        // Esc
+        if (e.keyCode === 27) {
+          this.hideAllViews();
+        }
+      }
+    };
+  },
+  watch: {
+    routeView() {
+      const queryParams = new URLSearchParams(window.location.search);
+    }
+  },
+  methods: {
+    setSecondaryView(viewId, show, hideOthers = false) {
+      if (hideOthers) {
+        Object.keys(this.$data.secondaryViews).forEach(key => {
+          if (key !== viewId) {
+            this.setSecondaryView(key, false);
+          }
+        });
+      }
+
+      this.$data.secondaryViews[viewId] = show;
+
+      if (show && this.$props.syncLocation) {
+        const queryParams = new URLSearchParams(window.location.search);
+
+        queryParams.set(this.$props.locationParam, viewId);
+        history.pushState(null, null, "?" + queryParams.toString());
+      }
+    },
+    setSecondaryViews(updates) {
+      this.$data.secondaryViews = Object.assign({}, this.data.secondaryViews, updates);
+    },
+    toggleSecondaryView(viewId) {
+      !this.isCurrentSecondaryView(viewId) && this.setSecondaryView(viewId, !this.isCurrentSecondaryView(viewId), true);
+    },
+    showSecondaryView(viewId) {
+      this.setSecondaryView(viewId, true, true);
+    },
+    isCurrentSecondaryView(viewId) {
+      return this.$data.secondaryViews[viewId];
+    },
+    hideAllViews() {
+      Object.keys(this.$data.secondaryViews).forEach(key => {
+        this.setSecondaryView(key, false);
+      });
+    }
+  },
+  mounted() {
+    window.addEventListener('keydown', this.$data.keyListener);
+
+    if (this.$props.syncLocation) {
+      const queryParams = new URLSearchParams(window.location.search);
+      const locationView = queryParams.get(this.$props.locationParam);
+
+      if (locationView && Object.keys(this.$data.secondaryViews).indexOf(locationView) !== -1) {
+        this.showSecondaryView(locationView);
+      }
+    }
+  },
+  destroyed() {
+    window.removeEventListener('keydown', this.$data.keyListener);
+  }
+}
+</script>
diff --git a/src/js/components/countdown/Countdown.vue b/src/js/components/countdown/Countdown.vue
index ca15798..afd9714 100644
--- a/src/js/components/countdown/Countdown.vue
+++ b/src/js/components/countdown/Countdown.vue
@@ -29,6 +29,34 @@ export default {
   },
   methods: {
     updateCountdown() {
+      const formatCountdown = (countdown) => {
+        let parts = [];
+
+        if (countdown.days > 0) {
+          parts.push(`${countdown.days} dní`);
+        }
+        if (countdown.hours > 0) {
+          parts.push(`${countdown.hours} hodin`);
+        }
+        if (countdown.minutes > 0) {
+          parts.push(`${countdown.minutes} minut`);
+        }
+
+        // Always include seconds
+        parts.push(`${countdown.seconds} sekund`);
+
+        // Determine pluralization for "sekund"
+        let secondsSuffix = '';
+
+        if (countdown.seconds === 1) {
+          secondsSuffix = 'a';
+        } else if (countdown.seconds > 1 && countdown.seconds < 5) {
+          secondsSuffix = 'y';
+        }
+
+        return parts.join(', ') + (parts.length > 0 ? secondsSuffix : '');
+      }
+
       const targetDate = new Date(this.to);
       const currentDate = new Date();
 
@@ -51,15 +79,7 @@ export default {
 
       this.countdown.seconds = Math.floor(timeDifference / 1000);
 
-      // Determine pluralization for "sekund"
-      let secondsSuffix = '';
-      if (this.countdown.seconds === 1) {
-          secondsSuffix = 'a';
-      } else if (this.countdown.seconds > 1 && this.countdown.seconds < 5) {
-          secondsSuffix = 'y';
-      }
-
-      this.countdownText = `${this.countdown.days} dní, ${this.countdown.hours} hodin, ${this.countdown.minutes} minut a ${this.countdown.seconds} sekund${secondsSuffix}`;
+      this.countdownText = formatCountdown(this.countdown);
     }
   }
 };
diff --git a/src/js/main.js b/src/js/main.js
index 7bd0fd4..25404be 100644
--- a/src/js/main.js
+++ b/src/js/main.js
@@ -14,6 +14,7 @@ import GoogleProvider from "./components/calendar/GoogleProvider";
 import FullCalendar from "./components/full_calendar/Calendar";
 import RegionMap from "./components/RegionMap";
 import ViewProvider from "./components/ViewProvider";
+import SecondaryViewProvider from "./components/SecondaryViewProvider";
 import Popout from "./components/popout/Popout";
 import PopoutContent from "./components/popout/PopoutContent";
 import PopoutItem from "./components/popout/PopoutItem";
@@ -35,6 +36,7 @@ Vue.component("ui-calendar-google-provider", GoogleProvider);
 Vue.component("ui-full-calendar", FullCalendar);
 Vue.component("ui-region-map", RegionMap);
 Vue.component("ui-view-provider", ViewProvider);
+Vue.component("ui-secondary-view-provider", SecondaryViewProvider);
 Vue.component("ui-popout", Popout);
 Vue.component("ui-popout-content", PopoutContent);
 Vue.component("ui-popout-item", PopoutItem);
-- 
GitLab