diff --git a/backend/api/cms/page/blocks/communities_section.py b/backend/api/cms/page/blocks/communities_section.py
new file mode 100644
index 0000000000..5849b8b95c
--- /dev/null
+++ b/backend/api/cms/page/blocks/communities_section.py
@@ -0,0 +1,62 @@
+from typing import Self
+import strawberry
+from api.cms.page.registry import register_page_block
+
+
+@strawberry.type
+class Community:
+ name: str
+ description: str
+ logo: str | None
+ banner_photo: str | None
+ banner_background_color: str | None
+
+ mastodon_url: str | None
+ facebook_url: str | None
+ instagram_url: str | None
+ linkedin_url: str | None
+ twitter_url: str | None
+
+ @classmethod
+ def from_block(cls, block) -> Self:
+ logo_url = None
+ if block["logo"]:
+ logo_url = block["logo"].get_rendition("width-300|jpegquality-60").full_url
+
+ banner_photo_url = None
+ if block["banner_photo"]:
+ banner_photo_url = (
+ block["banner_photo"].get_rendition("width-300|jpegquality-60").full_url
+ )
+
+ return cls(
+ name=block["name"],
+ description=block["description"],
+ logo=logo_url,
+ banner_photo=banner_photo_url,
+ banner_background_color=block["banner_background_color"],
+ mastodon_url=block["mastodon_url"],
+ facebook_url=block["facebook_url"],
+ instagram_url=block["instagram_url"],
+ linkedin_url=block["linkedin_url"],
+ twitter_url=block["twitter_url"],
+ )
+
+
+@register_page_block()
+@strawberry.type
+class CommunitiesSection:
+ id: strawberry.ID
+ title: str
+ communities: list[Community]
+
+ @classmethod
+ def from_block(cls, block) -> Self:
+ return cls(
+ id=block.id,
+ title=block.value["title"],
+ communities=[
+ Community.from_block(community)
+ for community in block.value["communities"]
+ ],
+ )
diff --git a/backend/api/cms/page/blocks/dynamic_content_display_section.py b/backend/api/cms/page/blocks/dynamic_content_display_section.py
index 072803a2a9..e989149164 100644
--- a/backend/api/cms/page/blocks/dynamic_content_display_section.py
+++ b/backend/api/cms/page/blocks/dynamic_content_display_section.py
@@ -9,6 +9,7 @@ class DynamicContentDisplaySectionSource(enum.Enum):
speakers = "speakers"
keynoters = "keynoters"
proposals = "proposals"
+ local_communities = "local_communities"
@register_page_block()
diff --git a/backend/cms/components/page/blocks/communities_section.py b/backend/cms/components/page/blocks/communities_section.py
new file mode 100644
index 0000000000..0aa62c22ca
--- /dev/null
+++ b/backend/cms/components/page/blocks/communities_section.py
@@ -0,0 +1,25 @@
+from wagtail.images.blocks import ImageChooserBlock
+from wagtail import blocks
+
+
+class Community(blocks.StructBlock):
+ name = blocks.CharBlock(required=True)
+ description = blocks.RichTextBlock(required=True)
+ logo = ImageChooserBlock(required=True)
+ banner_photo = ImageChooserBlock(required=False)
+ banner_background_color = blocks.CharBlock(required=False)
+
+ mastodon_url = blocks.URLBlock(required=False)
+ facebook_url = blocks.URLBlock(required=False)
+ instagram_url = blocks.URLBlock(required=False)
+ linkedin_url = blocks.URLBlock(required=False)
+ twitter_url = blocks.URLBlock(required=False)
+
+
+class CommunitiesSection(blocks.StructBlock):
+ title = blocks.CharBlock(required=True)
+ communities = blocks.ListBlock(Community)
+
+ class Meta:
+ label = "Communities Section"
+ icon = "crosshairs"
diff --git a/backend/cms/components/page/blocks/dynamic_content_display_section.py b/backend/cms/components/page/blocks/dynamic_content_display_section.py
index fc87d47799..27a9945aeb 100644
--- a/backend/cms/components/page/blocks/dynamic_content_display_section.py
+++ b/backend/cms/components/page/blocks/dynamic_content_display_section.py
@@ -7,5 +7,6 @@ class DynamicContentDisplaySection(blocks.StructBlock):
("speakers", "Speakers"),
("keynoters", "Keynoters"),
("proposals", "Proposals"),
+ ("local_communities", "Local Communities"),
],
)
diff --git a/backend/cms/components/page/models.py b/backend/cms/components/page/models.py
index b3bf3e9a2f..8316bf3d38 100644
--- a/backend/cms/components/page/models.py
+++ b/backend/cms/components/page/models.py
@@ -1,3 +1,4 @@
+from cms.components.page.blocks.communities_section import CommunitiesSection
from cms.components.page.blocks.dynamic_content_display_section import (
DynamicContentDisplaySection,
)
@@ -41,6 +42,7 @@ class BodyBlock(blocks.StreamBlock):
live_streaming_section = LiveStreamingSection()
homepage_hero = HomepageHero()
dynamic_content_display_section = DynamicContentDisplaySection()
+ communities_section = CommunitiesSection()
class GenericPage(CustomHeadlessMixin, Page):
diff --git a/frontend/src/components/blocks-renderer/index.tsx b/frontend/src/components/blocks-renderer/index.tsx
index f3a3d6ddf5..721d6a6e0d 100644
--- a/frontend/src/components/blocks-renderer/index.tsx
+++ b/frontend/src/components/blocks-renderer/index.tsx
@@ -5,6 +5,7 @@ import { TextSection } from "~/components/blocks/text-section";
import type { Block } from "~/types";
import { CheckoutSection } from "../blocks/checkout-section";
+import { CommunitiesSection } from "../blocks/communities-section";
import { DynamicContentDisplaySection } from "../blocks/dynamic-content-display-section";
import { HomeIntroSection } from "../blocks/home-intro-section";
import { HomepageHero } from "../blocks/homepage-hero";
@@ -38,6 +39,7 @@ const REGISTRY: Registry = {
LiveStreamingSection,
HomepageHero,
DynamicContentDisplaySection,
+ CommunitiesSection,
};
type Props = {
diff --git a/frontend/src/components/blocks/communities-section.tsx b/frontend/src/components/blocks/communities-section.tsx
new file mode 100644
index 0000000000..92dc7e9623
--- /dev/null
+++ b/frontend/src/components/blocks/communities-section.tsx
@@ -0,0 +1,86 @@
+import {
+ CardPart,
+ Grid,
+ Heading,
+ MultiplePartsCard,
+ MultiplePartsCardCollection,
+ SocialLinks,
+ Spacer,
+ StyledHTMLText,
+ Text,
+ VerticalStack,
+} from "@python-italia/pycon-styleguide";
+import { Section } from "@python-italia/pycon-styleguide";
+import type { Community } from "~/types";
+
+type Props = {
+ title: string;
+ communities: Community[];
+};
+
+export const CommunitiesSection = ({ title, communities }: Props) => {
+ return (
+
+
+ {communities.map((community) => {
+ const socialLinks = [];
+ if (community.mastodonUrl) {
+ socialLinks.push({
+ icon: "mastodon",
+ link: community.mastodonUrl,
+ });
+ }
+ if (community.facebookUrl) {
+ socialLinks.push({
+ icon: "facebook",
+ link: community.facebookUrl,
+ });
+ }
+ if (community.instagramUrl) {
+ socialLinks.push({
+ icon: "instagram",
+ link: community.instagramUrl,
+ });
+ }
+ if (community.linkedinUrl) {
+ socialLinks.push({
+ icon: "linkedin",
+ link: community.linkedinUrl,
+ });
+ }
+ if (community.twitterUrl) {
+ socialLinks.push({
+ icon: "twitter",
+ link: community.twitterUrl,
+ });
+ }
+
+ return (
+
+
+ {community.name}
+
+
+ {community.logo && (
+ <>
+
+
+
+
+ >
+ )}
+
+
+
+
+
+
+
+
+
+ );
+ })}
+
+
+ );
+};
diff --git a/frontend/src/components/blocks/dynamic-content-display-section/index.tsx b/frontend/src/components/blocks/dynamic-content-display-section/index.tsx
index 37f9bb3fa1..4187bf5ac4 100644
--- a/frontend/src/components/blocks/dynamic-content-display-section/index.tsx
+++ b/frontend/src/components/blocks/dynamic-content-display-section/index.tsx
@@ -5,6 +5,7 @@ import {
queryKeynotesSection,
} from "~/types";
import { KeynotersContent } from "./keynoters-content";
+import { LocalCommunitiesContent } from "./local-communities-content";
import { ProposalsContent } from "./proposals-content";
import { SpeakersContent } from "./speakers-content";
@@ -24,6 +25,9 @@ export const DynamicContentDisplaySection = ({
{source === DynamicContentDisplaySectionSource.Proposals && (
)}
+ {source === DynamicContentDisplaySectionSource.LocalCommunities && (
+
+ )}
);
};
@@ -56,6 +60,9 @@ DynamicContentDisplaySection.dataFetching = (client, language, block) => {
}),
];
}
+ case DynamicContentDisplaySectionSource.LocalCommunities: {
+ return [];
+ }
}
return [];
diff --git a/frontend/src/components/blocks/dynamic-content-display-section/local-communities-content.tsx b/frontend/src/components/blocks/dynamic-content-display-section/local-communities-content.tsx
new file mode 100644
index 0000000000..339974e619
--- /dev/null
+++ b/frontend/src/components/blocks/dynamic-content-display-section/local-communities-content.tsx
@@ -0,0 +1,85 @@
+import {
+ CardPart,
+ Grid,
+ Heading,
+ MultiplePartsCard,
+ MultiplePartsCardCollection,
+ SocialLinks,
+ Spacer,
+ Text,
+ VerticalStack,
+} from "@python-italia/pycon-styleguide";
+import { Section } from "@python-italia/pycon-styleguide";
+
+export const LocalCommunitiesContent = () => {
+ return (
+
+
+ {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((item) => (
+
+
+
+
+
+
+ Python Bari
+
+
+
+
+
+
+
+ Python Bari è una community di Python che si occupa di
+ sviluppare software per la città di Bari.
+
+
+
+
+
+
+
+
+ ))}
+
+
+ );
+};
diff --git a/frontend/src/pages/schedule/fragments/blocks.graphql b/frontend/src/pages/schedule/fragments/blocks.graphql
index d96579185f..54ba628288 100644
--- a/frontend/src/pages/schedule/fragments/blocks.graphql
+++ b/frontend/src/pages/schedule/fragments/blocks.graphql
@@ -135,6 +135,23 @@ fragment Blocks on Block {
id
source
}
+
+ ... on CommunitiesSection {
+ id
+ title
+ communities {
+ name
+ description
+ logo
+ bannerPhoto
+ bannerBackgroundColor
+ mastodonUrl
+ facebookUrl
+ instagramUrl
+ linkedinUrl
+ twitterUrl
+ }
+ }
}
fragment CTAInfo on CTA {
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
index 4dcdb1cd32..0e481c04b3 100644
--- a/frontend/tsconfig.json
+++ b/frontend/tsconfig.json
@@ -13,7 +13,7 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
- "jsx": "preserve",
+ "jsx": "react-jsx",
"baseUrl": ".",
"paths": {
"~/*": ["./src/*"],