Skip to content
Snippets Groups Projects
Commit 4bb42030 authored by Tomáš Valenta's avatar Tomáš Valenta
Browse files

RV voting calc - Octopus GraphQL syncing, select2-ified UI

parent 0ba34c09
No related branches found
No related tags found
1 merge request!1Test release
Pipeline #11227 failed
......@@ -26,7 +26,7 @@ venv: .venv/bin/python
${PYTHON} -m venv ${VENV}
install: venv
${VENV}/bin/pip install -r requirements/base.txt -r requirements/prod.txt
${VENV}/bin/pip install -r requirements/base.txt -r requirements/production.txt
${VENV}/bin/nodeenv --python-virtualenv --node=19.3.0
${VENV}/bin/npm install
......
......@@ -2,5 +2,8 @@ DATABASE_URL="postgresql://rybicka:rybicka@localhost:5432/rybicka"
SECRET_KEY="%@=^sip3=tqn6d_-xvvidc1@-t0t3&*kab@vr4c4"
CHOBOTNICE_API_URL="https://chobotnice.pirati.cz/graphql/"
CHOBOTNICE_RV_GID="R3JvdXBUeXBlOjYyNQ=="
# Production settings
ALLOWED_HOSTS="tools.pirati.cz"
......@@ -7,6 +7,11 @@
{% block description %}Výpočet velikosti skupiny členů podle jednoacího řádu.{% endblock %}
{% block head %}
<link
rel="stylesheet"
href="https://styleguide.pirati.cz/2.11.x/css/styles.css"
>
{% render_bundle "member_group_size_calc" %}
{% endblock %}
......@@ -15,7 +20,7 @@
<h1 class="text-6xl font-bebas mb-5">Kalkulačka velikosti skupiny členů</h1>
<div class="bg-amber-100 p-4 flex flex-row items-center gap-4 mb-4 lg:w-[768px] md:w-full">
<i class="fa-solid fa-lightbulb text-3xl text-amber-800"></i>
<i class="ico--book text-3xl text-amber-800"></i>
<div class="text-amber-800">
Tato kalkulačka slouží pro výpočet skupiny členů podle <a
class="underline text-amber-900"
......@@ -25,7 +30,7 @@
</div>
<div class="bg-gray-100 p-4 flex flex-row items-start gap-4 mb-4 lg:w-[768px] md:w-full">
<i class="fa-solid fa-circle-info text-3xl text-gray-800"></i>
<i class="ico--info text-3xl text-gray-800"></i>
<div class="text-gray-800">
<h2 class="text-lg font-bold mb-3">Jednací řád celostátního fóra</h2>
......
......@@ -4,9 +4,9 @@
"description": "",
"private": true,
"dependencies": {
"@fortawesome/fontawesome-free": "^6.2.1",
"css-loader": "^6.7.3",
"jquery": "^3.6.3",
"select2": "^4.1.0-rc.0",
"style-loader": "^3.3.1",
"tailwindcss": "^3.2.4",
"webpack": "^5.75.0",
......
......@@ -4,3 +4,5 @@ django-environ==0.9.0
psycopg2-binary==2.9.5
django-webpack-loader==1.8.0
nodeenv==1.7.0
gql[requests]==3.4.0
requests==2.28.2
File moved
......@@ -12,6 +12,31 @@
{% block content %}
<main>
<h1 class="text-6xl font-bebas mb-5">Kalkulačka hlasování RV</h1>
<div class="grid grid-cols-2 gap-4">
<div>
<h2 class="text-2xl font-bebas mb-5">Hlasy členů</h2>
<ul class="flex flex-col gap-2">
{% for member in rv_members %}
<li class="flex gap-4 items-center">
<div class="basis-56 flex items-center">
<i class="ico--user text-xl mr-2"></i>
{{ member.displayName }}
</div>
<select class="grow __vote-selection" multiple="multiple"></select>
</li>
{% endfor %}
</ul>
</div>
<div>
<h2 class="text-2xl font-bebas mb-5">Výsledky</h2>
</div>
</div>
</main>
<script>
const RV_MEMBERS = {{ rv_members|safe }};
</script>
{% endblock %}
import json
import gql
import requests
from gql.transport.requests import RequestsHTTPTransport
from django.conf import settings
from django.shortcuts import render
# Create your views here.
def index(request):
transport = RequestsHTTPTransport(url=settings.CHOBOTNICE_API_URL)
client = gql.Client(
transport=transport,
fetch_schema_from_transport=True,
)
# Get members from query
query = gql.gql(
f"""
{{
group(id: "{settings.CHOBOTNICE_RV_GID}") {{
memberships {{
person {{
username
displayName
officialLastName
}}
}}
}}
}}
"""
)
result = client.execute(query)
rv_members = []
# Convert to a nicer format
for member in result["group"]["memberships"]:
rv_members.append(member["person"])
# Sort alphabetically
rv_members.sort(key=lambda value: value["displayName"])
return render(
request,
"rv_voting_calc/index.html"
"rv_voting_calc/index.html",
{
"rv_members": rv_members,
# JS-Readable format
"json_rv_members": json.dumps(rv_members),
}
)
......@@ -113,3 +113,11 @@ WEBPACK_LOADER = {
"IGNORE": [r".+\.hot-update.js", r".+\.map"],
}
}
# Chobotnice
CHOBOTNICE_API_URL=env.str(
"CHOBOTNICE_API_URL",
"https://chobotnice.pirati.cz/graphql/"
)
CHOBOTNICE_RV_GID=env.str("CHOBOTNICE_RV_GID")
File moved
import $ from "jquery";
import "@fortawesome/fontawesome-free/js/fontawesome";
import "@fortawesome/fontawesome-free/js/solid";
import "@fortawesome/fontawesome-free/js/regular";
$(window).ready(
() => {
$("#member-count").on(
......
import jQuery from "jquery";
Object.assign(window, { $: jQuery, jQuery });
require("select2/dist/js/i18n/cs");
import "select2/dist/js/select2.full";
import "select2/dist/css/select2.min.css";
$(window).ready(
() => {
$(".__vote-selection").select2({
tags: true,
tokenSeparators: [",", " "],
// https://stackoverflow.com/a/28657702 - Thanks to Artur Filipiak!
createTag: tag => {
return {
id: tag.term,
text: tag.term,
isNew : true
};
}
});
$(".__vote-selection").on(
"select2:select",
event => {
//// Sync the tag option with other selectors.
// If the tag isn't new for this selection do nothing.
if (!event.params.data.isNew) {
return;
}
const tagName = event.params.data.text;
const addedTag = new Option(
tagName,
tagName,
false,
false
);
// Get all other selections.
const unfilteredSelections = $(".__vote-selection").not(event.target);
let filteredSelections = [];
// Check if they contain the tag. If they do, ignore them.
for (const selection of unfilteredSelections) {
if ($(selection).children(`option[value=${tagName}]`).length === 0) {
filteredSelections.push(selection);
}
}
// Add the new tag to all selections that don't have it yet.
$(filteredSelections).append(addedTag).trigger("change");
}
);
$(".__vote-selection").on(
"select2:unselect",
event => {
//// Remove the tag option if it's not selected anywhere.
const tagName = event.params.data.text;
// Get all other selections.
const selections = $(".__vote-selection");
// Check if any of them have the tag selected. If they do, end the function.
for (const selection of selections) {
for (const data of $(selection).select2("data")) {
if (data.selected && data.id == tagName) {
// Workaround - add the option back to this select (TODO - improve)
$(event.target).append(
new Option(
tagName,
tagName,
false,
false
)
).trigger("change");
return;
}
}
}
// If the function has not ended by now, we can remove the tag.
for (const selection of selections) {
$(selection).children(`option[value=${tagName}]`).remove();
$(selection).trigger("change");
}
}
)
$(".__vote-selection").on(
"change.select2",
event => {
console.log("change");
}
);
}
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment