diff --git a/base/blocks.py b/base/blocks.py
new file mode 100644
index 0000000..a109031
--- /dev/null
+++ b/base/blocks.py
@@ -0,0 +1,47 @@
+from wagtail.blocks import (
+ CharBlock,
+ ChoiceBlock,
+ RichTextBlock,
+ StreamBlock,
+ StructBlock,
+)
+from wagtail.embeds.blocks import EmbedBlock
+from wagtail.images.blocks import ImageChooserBlock
+
+
+class ImageBlock(StructBlock):
+ image = ImageChooserBlock(required=True)
+ caption = CharBlock(required=False)
+ attribution = CharBlock(required=False)
+
+ class Meta:
+ icon = "image"
+ template = "base/blocks/image_block.html"
+
+
+class HeadingBlock(StructBlock):
+ heading_text = CharBlock(classname="title", required=True)
+ size = ChoiceBlock(
+ choices=[
+ ("", "Select a heading size"),
+ ("h2", "H2"),
+ ("h3", "H3"),
+ ("h4", "H4"),
+ ],
+ blank=True,
+ required=False,
+ )
+
+ class Meta:
+ icon = "title"
+ template = "base/blocks/heading_block.html"
+
+
+class BaseStreamBlock(StreamBlock):
+ heading_block = HeadingBlock()
+ paragraph_block = RichTextBlock(icon="pilcrow")
+ image_block = ImageBlock()
+ embed_block = EmbedBlock(
+ help_text="Insert a URL to embed. For example, https://www.youtube.com/watch?v=SGJFWirQ3ks",
+ icon="media",
+ )
\ No newline at end of file
diff --git a/base/templates/base/blocks/heading_block.html b/base/templates/base/blocks/heading_block.html
new file mode 100644
index 0000000..e3776a9
--- /dev/null
+++ b/base/templates/base/blocks/heading_block.html
@@ -0,0 +1,7 @@
+{% if self.size == 'h2' %}
+
{{ self.heading_text }}
+{% elif self.size == 'h3' %}
+ {{ self.heading_text }}
+{% elif self.size == 'h4' %}
+ {{ self.heading_text }}
+{% endif %}
\ No newline at end of file
diff --git a/base/templates/base/blocks/image_block.html b/base/templates/base/blocks/image_block.html
new file mode 100644
index 0000000..5355897
--- /dev/null
+++ b/base/templates/base/blocks/image_block.html
@@ -0,0 +1,6 @@
+{% load wagtailimages_tags %}
+
+
+ {% image self.image fill-600x338 loading="lazy" %}
+ {{ self.caption }} - {{ self.attribution }}
+
\ No newline at end of file
diff --git a/iamkonstantin_web/settings/base.py b/iamkonstantin_web/settings/base.py
index 4df5c76..d2b2316 100644
--- a/iamkonstantin_web/settings/base.py
+++ b/iamkonstantin_web/settings/base.py
@@ -24,6 +24,7 @@ BASE_DIR = os.path.dirname(PROJECT_DIR)
# Application definition
INSTALLED_APPS = [
+ "portfolio",
"base",
"blog",
"home",
diff --git a/media/images/moon_and_planets_design.2e16d0ba.fill-600x338.jpg b/media/images/moon_and_planets_design.2e16d0ba.fill-600x338.jpg
new file mode 100644
index 0000000..9f8798a
Binary files /dev/null and b/media/images/moon_and_planets_design.2e16d0ba.fill-600x338.jpg differ
diff --git a/portfolio/__init__.py b/portfolio/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/portfolio/admin.py b/portfolio/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/portfolio/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/portfolio/apps.py b/portfolio/apps.py
new file mode 100644
index 0000000..bbc7dbd
--- /dev/null
+++ b/portfolio/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class PortfolioConfig(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'portfolio'
diff --git a/portfolio/blocks.py b/portfolio/blocks.py
new file mode 100644
index 0000000..5f9e492
--- /dev/null
+++ b/portfolio/blocks.py
@@ -0,0 +1,39 @@
+# import CharBlock, ListBlock, PageChooserBlock, PageChooserBlock, RichTextBlock, and StructBlock:
+from wagtail.blocks import (
+ CharBlock,
+ ListBlock,
+ PageChooserBlock,
+ RichTextBlock,
+ StructBlock,
+)
+
+# import ImageChooserBlock:
+from wagtail.images.blocks import ImageChooserBlock
+
+from base.blocks import BaseStreamBlock
+
+# add CardBlock:
+class CardBlock(StructBlock):
+ heading = CharBlock()
+ text = RichTextBlock(features=["bold", "italic", "link"])
+ image = ImageChooserBlock(required=False)
+
+ class Meta:
+ icon = "form"
+ template = "portfolio/blocks/card_block.html"
+
+# add FeaturedPostsBlock:
+class FeaturedPostsBlock(StructBlock):
+ heading = CharBlock()
+ text = RichTextBlock(features=["bold", "italic", "link"], required=False)
+ posts = ListBlock(PageChooserBlock(page_type="blog.BlogPage"))
+
+ class Meta:
+ icon = "folder-open-inverse"
+ template = "portfolio/blocks/featured_posts_block.html"
+
+class PortfolioStreamBlock(BaseStreamBlock):
+ # delete the pass statement
+
+ card = CardBlock(group="Sections")
+ featured_posts = FeaturedPostsBlock(group="Sections")
\ No newline at end of file
diff --git a/portfolio/migrations/0001_initial.py b/portfolio/migrations/0001_initial.py
new file mode 100644
index 0000000..75051c8
--- /dev/null
+++ b/portfolio/migrations/0001_initial.py
@@ -0,0 +1,31 @@
+# Generated by Django 5.0.2 on 2024-03-01 18:40
+
+import django.db.models.deletion
+import wagtail.blocks
+import wagtail.embeds.blocks
+import wagtail.fields
+import wagtail.images.blocks
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('wagtailcore', '0091_remove_revision_submitted_for_moderation'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='PortfolioPage',
+ fields=[
+ ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
+ ('body', wagtail.fields.StreamField([('heading_block', wagtail.blocks.StructBlock([('heading_text', wagtail.blocks.CharBlock(form_classname='title', required=True)), ('size', wagtail.blocks.ChoiceBlock(blank=True, choices=[('', 'Select a heading size'), ('h2', 'H2'), ('h3', 'H3'), ('h4', 'H4')], required=False))])), ('paragraph_block', wagtail.blocks.RichTextBlock(icon='pilcrow')), ('image_block', wagtail.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('caption', wagtail.blocks.CharBlock(required=False)), ('attribution', wagtail.blocks.CharBlock(required=False))])), ('embed_block', wagtail.embeds.blocks.EmbedBlock(help_text='Insert a URL to embed. For example, https://www.youtube.com/watch?v=SGJFWirQ3ks', icon='media'))], blank=True, help_text='Use this section to list your projects and skills.')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('wagtailcore.page',),
+ ),
+ ]
diff --git a/portfolio/migrations/0002_alter_portfoliopage_body.py b/portfolio/migrations/0002_alter_portfoliopage_body.py
new file mode 100644
index 0000000..9bfeb55
--- /dev/null
+++ b/portfolio/migrations/0002_alter_portfoliopage_body.py
@@ -0,0 +1,22 @@
+# Generated by Django 5.0.2 on 2024-03-01 18:41
+
+import wagtail.blocks
+import wagtail.embeds.blocks
+import wagtail.fields
+import wagtail.images.blocks
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('portfolio', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='portfoliopage',
+ name='body',
+ field=wagtail.fields.StreamField([('heading_block', wagtail.blocks.StructBlock([('heading_text', wagtail.blocks.CharBlock(form_classname='title', required=True)), ('size', wagtail.blocks.ChoiceBlock(blank=True, choices=[('', 'Select a heading size'), ('h2', 'H2'), ('h3', 'H3'), ('h4', 'H4')], required=False))])), ('paragraph_block', wagtail.blocks.RichTextBlock(icon='pilcrow')), ('image_block', wagtail.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('caption', wagtail.blocks.CharBlock(required=False)), ('attribution', wagtail.blocks.CharBlock(required=False))])), ('embed_block', wagtail.embeds.blocks.EmbedBlock(help_text='Insert a URL to embed. For example, https://www.youtube.com/watch?v=SGJFWirQ3ks', icon='media')), ('card', wagtail.blocks.StructBlock([('heading', wagtail.blocks.CharBlock()), ('text', wagtail.blocks.RichTextBlock(features=['bold', 'italic', 'link'])), ('image', wagtail.images.blocks.ImageChooserBlock(required=False))], group='Sections')), ('featured_posts', wagtail.blocks.StructBlock([('heading', wagtail.blocks.CharBlock()), ('text', wagtail.blocks.RichTextBlock(features=['bold', 'italic', 'link'], required=False)), ('posts', wagtail.blocks.ListBlock(wagtail.blocks.PageChooserBlock(page_type=['blog.BlogPage'])))], group='Sections'))], blank=True, help_text='Use this section to list your projects and skills.'),
+ ),
+ ]
diff --git a/portfolio/migrations/__init__.py b/portfolio/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/portfolio/models.py b/portfolio/models.py
new file mode 100644
index 0000000..d674a23
--- /dev/null
+++ b/portfolio/models.py
@@ -0,0 +1,20 @@
+from wagtail.models import Page
+from wagtail.fields import StreamField
+from wagtail.admin.panels import FieldPanel
+
+from portfolio.blocks import PortfolioStreamBlock
+
+
+class PortfolioPage(Page):
+ parent_page_types = ["home.HomePage"]
+
+ body = StreamField(
+ PortfolioStreamBlock(),
+ blank=True,
+ use_json_field=True,
+ help_text="Use this section to list your projects and skills.",
+ )
+
+ content_panels = Page.content_panels + [
+ FieldPanel("body"),
+ ]
\ No newline at end of file
diff --git a/portfolio/templates/portfolio/blocks/card_block.html b/portfolio/templates/portfolio/blocks/card_block.html
new file mode 100644
index 0000000..dc211e4
--- /dev/null
+++ b/portfolio/templates/portfolio/blocks/card_block.html
@@ -0,0 +1,8 @@
+{% load wagtailcore_tags wagtailimages_tags %}
+
+
{{ self.heading }}
+
{{ self.text|richtext }}
+ {% if self.image %}
+ {% image self.image width-480 %}
+ {% endif %}
+
\ No newline at end of file
diff --git a/portfolio/templates/portfolio/blocks/featured_posts_block.html b/portfolio/templates/portfolio/blocks/featured_posts_block.html
new file mode 100644
index 0000000..d3a234e
--- /dev/null
+++ b/portfolio/templates/portfolio/blocks/featured_posts_block.html
@@ -0,0 +1,16 @@
+{% load wagtailcore_tags %}
+
+
{{ self.heading }}
+ {% if self.text %}
+
{{ self.text|richtext }}
+ {% endif %}
+
+
+ {% for page in self.posts %}
+
+ {% endfor %}
+
+
\ No newline at end of file
diff --git a/portfolio/templates/portfolio/portfolio_page.html b/portfolio/templates/portfolio/portfolio_page.html
new file mode 100644
index 0000000..8e395fe
--- /dev/null
+++ b/portfolio/templates/portfolio/portfolio_page.html
@@ -0,0 +1,11 @@
+{% extends "base.html" %}
+
+{% load wagtailcore_tags wagtailimages_tags %}
+
+{% block body_class %}template-portfolio{% endblock %}
+
+{% block content %}
+ {{ page.title }}
+
+ {{ page.body }}
+{% endblock %}
\ No newline at end of file
diff --git a/portfolio/tests.py b/portfolio/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/portfolio/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/portfolio/views.py b/portfolio/views.py
new file mode 100644
index 0000000..91ea44a
--- /dev/null
+++ b/portfolio/views.py
@@ -0,0 +1,3 @@
+from django.shortcuts import render
+
+# Create your views here.