Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • to/cf-online-ui
  • vpfafrin/cf2021
2 results
Show changes
Showing
with 113 additions and 111 deletions
......@@ -4,7 +4,7 @@ import pick from "lodash/pick";
import property from "lodash/property";
import { createAsyncAction, errorResult, successResult } from "pullstate";
import { fetch } from "api";
import { fetchApi } from "api";
import { markdownConverter } from "markdown";
import { ProgramStore } from "stores";
......@@ -13,7 +13,7 @@ import { loadPosts } from "./posts";
export const loadProgram = createAsyncAction(
async () => {
try {
const resp = await fetch("/program");
const resp = await fetchApi("/program");
const mappings = await resp.json();
return successResult(mappings);
} catch (err) {
......@@ -49,17 +49,17 @@ export const loadProgram = createAsyncAction(
expectedStartAt: parse(
entry.expected_start_at,
"yyyy-MM-dd HH:mm:ss",
new Date()
new Date(),
),
expectedFinishAt: entry.expected_finish_at
? parse(
entry.expected_finish_at,
"yyyy-MM-dd HH:mm:ss",
new Date()
new Date(),
)
: undefined,
};
}
},
)
.sort((a, b) => a.expectedStartAt - b.expectedStartAt);
......@@ -75,7 +75,7 @@ export const loadProgram = createAsyncAction(
});
}
},
}
},
);
/**
......@@ -87,7 +87,7 @@ export const renameProgramPoint = createAsyncAction(
const body = JSON.stringify({
title: newTitle,
});
await fetch(`/program/${programEntry.id}`, {
await fetchApi(`/program/${programEntry.id}`, {
method: "PUT",
body,
expectedStatus: 204,
......@@ -108,7 +108,7 @@ export const renameProgramPoint = createAsyncAction(
});
}
},
}
},
);
/**
......@@ -124,7 +124,7 @@ export const endProgramPoint = createAsyncAction(
const body = JSON.stringify({
is_live: false,
});
await fetch(`/program/${programEntry.id}`, {
await fetchApi(`/program/${programEntry.id}`, {
method: "PUT",
body,
expectedStatus: 204,
......@@ -142,7 +142,7 @@ export const endProgramPoint = createAsyncAction(
});
}
},
}
},
);
/**
......@@ -158,7 +158,7 @@ export const activateProgramPoint = createAsyncAction(
const body = JSON.stringify({
is_live: true,
});
await fetch(`/program/${programEntry.id}`, {
await fetchApi(`/program/${programEntry.id}`, {
method: "PUT",
body,
expectedStatus: 204,
......@@ -179,7 +179,7 @@ export const activateProgramPoint = createAsyncAction(
loadPosts.run({}, { respectCache: false });
}
},
}
},
);
/**
......@@ -195,7 +195,7 @@ export const openDiscussion = createAsyncAction(
const body = JSON.stringify({
discussion_opened: true,
});
await fetch(`/program/${programEntry.id}`, {
await fetchApi(`/program/${programEntry.id}`, {
method: "PUT",
body,
expectedStatus: 204,
......@@ -215,7 +215,7 @@ export const openDiscussion = createAsyncAction(
});
}
},
}
},
);
/**
......@@ -227,7 +227,7 @@ export const closeDiscussion = createAsyncAction(
const body = JSON.stringify({
discussion_opened: false,
});
await fetch(`/program/${programEntry.id}`, {
await fetchApi(`/program/${programEntry.id}`, {
method: "PUT",
body,
expectedStatus: 204,
......@@ -247,5 +247,5 @@ export const closeDiscussion = createAsyncAction(
});
}
},
}
},
);
import * as Sentry from "@sentry/react";
import { createAsyncAction, errorResult, successResult } from "pullstate";
import { fetch } from "api";
import { fetchApi } from "api";
import keycloak from "keycloak";
import { AuthStore, PostStore } from "stores";
import { updateWindowPosts } from "utils";
......@@ -12,7 +12,7 @@ export const loadMe = createAsyncAction(
*/
async () => {
try {
const response = await fetch(`/users/me`, {
const response = await fetchApi(`/users/me`, {
method: "GET",
expectedStatus: 200,
});
......@@ -35,7 +35,7 @@ export const loadMe = createAsyncAction(
});
}
},
}
},
);
export const ban = createAsyncAction(
......@@ -44,7 +44,7 @@ export const ban = createAsyncAction(
*/
async (user) => {
try {
await fetch(`/users/${user.id}/ban`, {
await fetchApi(`/users/${user.id}/ban`, {
method: "PATCH",
expectedStatus: 204,
});
......@@ -52,7 +52,7 @@ export const ban = createAsyncAction(
} catch (err) {
return errorResult([], err.toString());
}
}
},
);
export const unban = createAsyncAction(
......@@ -61,7 +61,7 @@ export const unban = createAsyncAction(
*/
async (user) => {
try {
await fetch(`/users/${user.id}/unban`, {
await fetchApi(`/users/${user.id}/unban`, {
method: "PATCH",
expectedStatus: 204,
});
......@@ -69,7 +69,7 @@ export const unban = createAsyncAction(
} catch (err) {
return errorResult([], err.toString());
}
}
},
);
export const inviteToJitsi = createAsyncAction(
......@@ -81,7 +81,7 @@ export const inviteToJitsi = createAsyncAction(
const body = JSON.stringify({
allowed: true,
});
await fetch(`/users/${user.id}/jitsi`, {
await fetchApi(`/users/${user.id}/jitsi`, {
method: "PATCH",
body,
expectedStatus: 204,
......@@ -90,7 +90,7 @@ export const inviteToJitsi = createAsyncAction(
} catch (err) {
return errorResult([], err.toString());
}
}
},
);
export const refreshAccessToken = async () => {
......@@ -105,7 +105,7 @@ export const refreshAccessToken = async () => {
console.info("[auth] access token refreshed");
} catch (exc) {
console.warn(
"[auth] could not refresh the access token, refresh token possibly expired, logging out"
"[auth] could not refresh the access token, refresh token possibly expired, logging out",
);
Sentry.setUser(null);
......
import baseFetch from "unfetch";
import { AuthStore } from "./stores";
export const fetch = async (
export const fetchApi = async (
url,
{ headers = {}, expectedStatus = 200, method = "GET", body = null } = {}
{ headers = {}, expectedStatus = 200, method = "GET", body = null } = {},
) => {
const { isAuthenticated, user } = AuthStore.getRawState();
......@@ -16,10 +14,11 @@ export const fetch = async (
headers["Content-Type"] = "application/json";
}
const response = await baseFetch(process.env.REACT_APP_API_BASE_URL + url, {
const response = await fetch(process.env.REACT_APP_API_BASE_URL + url, {
body,
method,
headers,
redirect: "follow",
});
if (!!expectedStatus && response.status !== expectedStatus) {
......
......@@ -19,7 +19,7 @@ const Footer = () => {
className="w-32 md:w-40 pb-6"
/>
<p className="para hidden md:block md:mb-4 lg:mb-0 text-grey-200">
Piráti, 2021. Všechna práva vyhlazena. Sdílejte a nechte ostatní
Piráti, 2024. Všechna práva vyhlazena. Sdílejte a nechte ostatní
sdílet za stejných podmínek.
</p>
</section>
......@@ -35,7 +35,7 @@ const Footer = () => {
)}
onClick={() => setShowCfMenu(!showCfMenu)}
>
CF 2021
CF 2024
</span>{" "}
<div className={showCfMenu || isLg ? "" : "hidden"}>
<ul className="mt-6 space-y-2 text-grey-200">
......
......@@ -77,7 +77,7 @@ const Navbar = ({ onGetHelp }) => {
to="/"
className="pl-4 font-bold text-xl lg:border-r lg:border-grey-300 lg:pr-8 hover:no-underline"
>
Celostátní fórum 2021
Celostátní fórum 2024
</NavLink>
</div>
<div className="navbar__menutoggle my-4 flex justify-end lg:hidden">
......
......@@ -9,7 +9,7 @@ const NotYetStarted = ({ startAt }) => (
Jejda ...
</div>
<h1 className="head-alt-base md:head-alt-md lg:head-alt-xl mb-2">
Jednání ještě nebylo zahájeno :(
Jednání ještě nebylo zahájeno
</h1>
<p className="text-xl leading-snug mb-8">
<span>Jednání celostátního fóra ještě nezačalo. </span>
......
import React from "react";
import Chip from "components/Chip";
// import Chip from "components/Chip";
export { default as Beacon } from "./Beacon";
......@@ -9,11 +9,12 @@ export const steps = [
target: "body",
content: (
<>
<h1 className="head-alt-sm mb-4">Vítej na celostátním fóru 2021</h1>
<h1 className="head-alt-sm mb-4">Vítej na celostátním fóru 2024</h1>
<p className="leading-snug text-base">
Letošní Pirátské fórum bude online. Abychom to celé zvládli,
připravili jsme tuhle aplikaci, která se snaží alespoň částečně
nahradit fyzickou přítomnost. Nejprve si vysvětlíme, jak funguje.
Víme, že volebního zasedání se nemohou zúčastnit všichni.
Abychom nepřítomným umožnili zasedání lépe sledovat, připravili
jsme tuhle aplikaci, která umožňuje zasáhnout do rozpravy.
Nejprve si vysvětlíme, jak funguje.
</p>
</>
),
......@@ -60,24 +61,6 @@ export const steps = [
<p>
<strong>Běžné příspěvky</strong> se zobrazí ihned po přidání.
</p>
<p>
<strong>Návrhy postupu</strong> po přidání nejprve zkontroluje
předsedající a pokud sezná, že je takový návrh přípusný, prohlásí ho
za{" "}
<Chip color="blue-300" condensed>
hlasovatelný
</Chip>
. Pro vyjádření podpory používej palce. Na základě míry podpory
předsedající buď návrh označí za{" "}
<Chip color="green-400" condensed>
schválený
</Chip>
, nebo za{" "}
<Chip color="red-600" condensed>
zamítnutý
</Chip>
.
</p>
<p>
U příspěvků se též zobrazuje celková míra podpory. Legenda barevného
odlišení je následující:
......@@ -110,6 +93,11 @@ export const steps = [
je označen příspěvek, který zatím není ohodnocen.
</li>
</ul>
<p>
<strong>Návrhy postupui</strong> po přidání nejprve zkontroluje předsedající a pokud sezná,
že je takový návrh přípusný, prohlásí ho za hlasovatelný a předloží k hlasování
v plénu. Na základě toho návrh předsedající označí za schválený, nebo za zamítnutý.
</p>
</div>
</>
),
......@@ -152,7 +140,7 @@ export const steps = [
<>
<h1 className="head-alt-sm mb-4">To je vše!</h1>
<p className="leading-snug text-base">
Ať se ti letošní „CFko“ líbí i v těchto ztížených podmínkách.
Ať se ti letošní „CFko“ líbí.
</p>
</>
),
......
......@@ -328,7 +328,7 @@ const Post = ({
{labels}
</div>
<div
className="text-sm lg:text-base text-black leading-normal content-block overflow-x-scroll overflow-y-hidden mt-1"
className="text-sm lg:text-base text-black leading-normal content-block overflow-x-auto overflow-y-hidden mt-1"
dangerouslySetInnerHTML={htmlContent}
></div>
</div>
......
......@@ -46,6 +46,8 @@ const AddAnnouncementForm = ({ className }) => {
};
const onAdd = async (evt) => {
evt.preventDefault();
let preventAction = false;
const payload = {
content: text,
......@@ -82,7 +84,7 @@ const AddAnnouncementForm = ({ className }) => {
};
return (
<div className={className}>
<form className={className} onSubmit={onAdd}>
{addingError && (
<ErrorMessage>
Při přidávání oznámení došlo k problému: {addingError}.
......@@ -150,7 +152,7 @@ const AddAnnouncementForm = ({ className }) => {
</div>
<Button
onClick={onAdd}
type="submit"
className="text-sm mt-4"
hoverActive
loading={adding}
......@@ -159,7 +161,7 @@ const AddAnnouncementForm = ({ className }) => {
>
Přidat oznámení
</Button>
</div>
</form>
);
};
......
......@@ -74,6 +74,8 @@ const AddPostForm = ({ className, canAddProposal }) => {
};
const onAdd = async (evt) => {
evt.preventDefault();
if (!!text) {
if (!error) {
const result = await (type === "post" ? addPost : addProposal).run({
......@@ -125,7 +127,7 @@ const AddPostForm = ({ className, canAddProposal }) => {
"p-4 lg:p-8 " + (showAddConfirm || !expanded ? "hidden" : "")
}
>
<div className="space-y-4">
<form className="space-y-4" onSubmit={onAdd}>
{apiError && is429ApiError && (
<div className="alert alert--warning">
<i className="alert__icon ico--clock text-lg" />
......@@ -203,7 +205,7 @@ const AddPostForm = ({ className, canAddProposal }) => {
<div className="space-x-4">
<Button
onClick={onAdd}
type="submit"
disabled={error || addingPost || addingProposal}
loading={addingPost || addingProposal}
fullwidth
......@@ -227,7 +229,7 @@ const AddPostForm = ({ className, canAddProposal }) => {
.
</span>
</div>
</div>
</form>
</CardBody>
</Card>
);
......
......@@ -8,6 +8,10 @@ import { useActionState } from "hooks";
import { AuthStore } from "stores";
const JitsiInviteCard = () => {
// docasne zablokovano
return null;
const { showJitsiInvitePopup, jitsiPopupDismissed } = AuthStore.useState();
const [loading, errorMessage] = useActionState(loadMe);
......
......@@ -22,16 +22,12 @@ const PostFilters = () => {
{ title: "Jen příspěvky", value: "discussionOnly" },
];
const setFilter = (prop, newValue, resetPage = true) => {
const setFilter = (prop, newValue) => {
PostStore.update((state) => {
state.filters[prop] = newValue;
state.window.itemCount = state.window.items.length;
updateWindowPosts(state);
if (resetPage) {
state.window.page = 1;
}
});
};
......
......@@ -13,7 +13,7 @@ export const useItemActionConfirm = (actionFn, actionParamsBuilder = null) => {
if (item) {
const newActionArgs = (actionParamsBuilder || baseActionParamsBuilder)(
item,
args
args,
);
setActionArgs(newActionArgs);
const result = await actionFn.run(newActionArgs);
......@@ -23,7 +23,7 @@ export const useItemActionConfirm = (actionFn, actionParamsBuilder = null) => {
}
}
},
[item, setItem, actionFn, actionParamsBuilder, setActionArgs]
[item, setItem, actionFn, actionParamsBuilder, setActionArgs],
);
const onActionCancel = useCallback(() => {
......
import React from "react";
import ReactDOM from "react-dom";
import ReactDOM from "react-dom/client";
import ReactModal from "react-modal";
import { refreshAccessToken } from "actions/users";
......@@ -7,7 +7,7 @@ import { refreshAccessToken } from "actions/users";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
const root = document.getElementById("root");
const root = ReactDOM.createRoot(document.getElementById("root"));
function handleVisibilityChange() {
if (!document.hidden) {
......@@ -17,14 +17,12 @@ function handleVisibilityChange() {
document.addEventListener("visibilitychange", handleVisibilityChange, false);
ReactDOM.render(
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
root
</React.StrictMode>
);
ReactModal.setAppElement(root);
ReactModal.setAppElement(document.getElementById("root"));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
......
......@@ -2,7 +2,7 @@ import Keycloak from "keycloak-js";
// Setup Keycloak instance as needed
// Pass initialization options as required or leave blank to load from 'keycloak.json'
const keycloak = Keycloak({
const keycloak = new Keycloak({
url: "https://auth.pirati.cz/auth",
realm: "pirati",
clientId: "cf-online",
......
......@@ -6,20 +6,30 @@ import { markdownConverter } from "markdown";
const content = markdownConverter.makeHtml(`
**Celostátní fórum Pirátské strany** je [podle Stanov](https://wiki.pirati.cz/rules/st#cl_8_celostatni_forum) nejvyšším orgánem strany a zasedání se podle možností účastní každý člen strany.
Celostátní fórum ve výlučné působnosti:
> #### Celostátní fórum ve výlučné působnosti:
>
> * a. volí a odvolává republikové předsednictvo,
> * b. volí a odvolává členy republikového výboru volené celostátním fórem,
> * c. zřizuje a ruší komise a odbory na celostátní úrovni,
> * d. volí a odvolává členy komise a vedoucího odboru,
> * e. schvaluje změny stanov,
> * f. projednává a schvaluje výroční zprávu předsedy strany,
> * g. mimořádně přezkoumává rozhodnutí orgánu strany,
> * h. schvaluje zakládací dokument politického institutu,
> * i. může schválit Předpis o institutu,
> * j. může volit a odvolávat některé členy správní rady politického institutu.
>
> #### Celostátní fórum dále
>
> * a. přijímá v mezích stanov další předpisy,
> * b. ukládá úkoly republikovému předsednictvu a republikovému výboru,
> * c. může projednávat a schvalovat základní programové a ideové dokumenty,
> * d. má veškerou působnost, kterou stanovy neurčují jinému orgánu strany.
a. volí a odvolává republikové předsednictvo,
b. volí a odvolává členy republikového výboru volené celostátním fórem,
c. zřizuje a ruší komise a odbory,
d. volí a odvolává členy komise a vedoucího odboru,
e. schvaluje změny stanov,
f. projednává a schvaluje výroční zprávu předsedy strany,
g. projednává a schvaluje výroční finanční zprávu podle ZPS,
h. mimořádně přezkoumává rozhodnutí orgánu strany
### Zasedání na Internetu
Zimní zasedání Celostátního fóru, z důvodu mimořádných okolnosti spojenych s mimořádným stavem, bude probihat **na Internetu**. postup zasedání na Internetu je definovan [§42a](https://wiki.pirati.cz/rules/jdr#zasedani_na_internetu) Jednacího řádu Celostátního fóra v nasledujim znění:
Zasedání Celostátního fóra může z důvodu mimořádných okolností probíhat na Internetu. Postup zasedání na Internetu je definován §42a Jednacího řádu Celostátního fóra v následujícím znění:
> **(1)** Pokud mimořádné okolnosti nedovolují konání běžného zasedání, může, v rámci krizového řízení, republikové předsednictvo pověřit předsedu strany svoláním zasedání na Internetu nebo změnou již svolaného běžného zasedání na zasedání na Internetu.
>
......@@ -37,10 +47,10 @@ Zimní zasedání Celostátního fóru, z důvodu mimořádných okolnosti spoje
>
> **(4)** Právo účasti v jednání zvukem a obrazem mají zejména:
>
> a) předsedající a další činovníci jednání,
> b) osoby s právem na závěrečné slovo v rozpravě k bodům k rozhodnutí,
> c) osoby určené navrhovatelem bodu v rozpravě k jiným bodům,
> d) další osoby, pro něž je schválen takový postup.
> * a) předsedající a další činovníci jednání,
> * b) osoby s právem na závěrečné slovo v rozpravě k bodům k rozhodnutí,
> * c) osoby určené navrhovatelem bodu v rozpravě k jiným bodům,
> * d) další osoby, pro něž je schválen takový postup.
>
> **(5)** Jinak se při zasedání na Internetu postupuje přiměřeně jako při běžném zasedání.
>
......@@ -58,14 +68,14 @@ const About = () => {
return (
<>
<Helmet>
<title>Co je to celostátní fórum? | CF 2021 | Pirátská strana</title>
<title>Co je to celostátní fórum? | CF 2024 | Pirátská strana</title>
<meta
name="description"
content="Nevíte co je to celostátní fórum České pirátské strany? Tady se dočtete vše potřebné."
/>
<meta
property="og:title"
content="Co je to celostátní fórum? | CF 2021 | Pirátská strana"
content="Co je to celostátní fórum? | CF 2024 | Pirátská strana"
/>
<meta
property="og:description"
......
......@@ -136,18 +136,18 @@ const Home = () => {
return (
<>
<Helmet>
<title>Přímý přenos | CF 2021 | Pirátská strana</title>
<title>Přímý přenos | CF 2024 | Pirátská strana</title>
<meta
name="description"
content="Přímý přenos a diskuse z on-line zasedání Celostátního fóra České pirátské strany, 9. 1. 2021."
content="Přímý přenos a diskuse z on-line zasedání Celostátního fóra České pirátské strany, 13. 1. 2024."
/>
<meta
property="og:title"
content="Přímý přenos | CF 2021 | Pirátská strana"
content="Přímý přenos | CF 2024 | Pirátská strana"
/>
<meta
property="og:description"
content="Přímý přenos a diskuse z on-line zasedání Celostátního fóra České pirátské strany, 9. 1. 2021."
content="Přímý přenos a diskuse z on-line zasedání Celostátního fóra České pirátské strany, 13. 1. 2024."
/>
</Helmet>
<Joyride
......
......@@ -6,9 +6,9 @@ import Button from "components/Button";
const NotFound = () => (
<>
<Helmet>
<title>404ka | CF 2021 | Pirátská strana</title>
<title>404ka | CF 2024 | Pirátská strana</title>
<meta name="description" content="Tahle stránka tu není." />
<meta property="og:title" content="404ka | CF 2021 | Pirátská strana" />
<meta property="og:title" content="404ka | CF 2024 | Pirátská strana" />
<meta property="og:description" content="Tahle stránka tu není." />
</Helmet>
<article className="container container--default py-8 lg:py-24">
......
......@@ -28,22 +28,25 @@ const Schedule = () => {
return (
<>
<Helmet>
<title>Program zasedání | CF 2021 | Pirátská strana</title>
<title>Program zasedání | CF 2024 | Pirátská strana</title>
<meta
name="description"
content="Přečtěte si program on-line zasedání Celostátního fóra České pirátské strany, 9. 1. 2021."
content="Přečtěte si program on-line zasedání Celostátního fóra České pirátské strany, 13. 1. 2024."
/>
<meta
property="og:title"
content="Program zasedání | CF 2021 | Pirátská strana"
content="Program zasedání | CF 2024 | Pirátská strana"
/>
<meta
property="og:description"
content="Přečtěte si program on-line zasedání Celostátního fóra České pirátské strany, 9. 1. 2021."
content="Přečtěte si program on-line zasedání Celostátního fóra České pirátské strany, 13. 1. 2024."
/>
</Helmet>
<article className="container container--default py-8 lg:py-24">
<h1 className="head-alt-md lg:head-alt-lg mb-8">Program zasedání</h1>
<div class="my-4">
Program zde neobsahuje z technických důvodů všechny podrobnosti. Kompletní program naleznete na <a href="https://cf2024.pirati.cz/program">webu</a>.
</div>
<div className="flex flex-col">
{scheduleIds.map((id) => {
const isCurrent = id === currentId;
......
......@@ -57,18 +57,18 @@ const Protocol = () => {
return (
<>
<Helmet>
<title>Zápis ze zasedání | CF 2021 | Pirátská strana</title>
<title>Zápis ze zasedání | CF 2024 | Pirátská strana</title>
<meta
name="description"
content="Interaktivní zápis z on-line zasedání Celostátního fóra České pirátské strany, 9. 1. 2021."
content="Interaktivní zápis z on-line zasedání Celostátního fóra České pirátské strany, 13. 1. 2024."
/>
<meta
property="og:title"
content="Zápis ze zasedání | CF 2021 | Pirátská strana"
content="Zápis ze zasedání | CF 2024 | Pirátská strana"
/>
<meta
property="og:description"
content="Interaktivní zápis z on-line zasedání Celostátního fóra České pirátské strany, 9. 1. 2021."
content="Interaktivní zápis z on-line zasedání Celostátního fóra České pirátské strany, 13. 1. 2024."
/>
</Helmet>
<article className="container container--default py-8 lg:py-24">
......