diff --git a/backend/app/Controllers/ElementController.php b/backend/app/Controllers/ElementController.php
index 0101dea..8665880 100644
--- a/backend/app/Controllers/ElementController.php
+++ b/backend/app/Controllers/ElementController.php
@@ -55,6 +55,9 @@ class ElementController
$result->getChildElements(),
),
'element' => $this->buildElementPayload($element),
+ 'parentElement' => $this->buildElementSummaryPayload(
+ $element->getParentElement(),
+ ),
'siblingElements' => $this->buildElementSummaryPayloads(
$result->getSiblingElements(),
),
@@ -328,16 +331,35 @@ class ElementController
{
$payloads = [];
foreach ($elements as $element) {
- $payloads[] = [
- 'id' => $element->getId(),
- 'title' => $element->getTitle(),
- 'description' => $element->getDescription(),
- ];
+ $payload = $this->buildElementSummaryPayload($element);
+ if ($payload !== null) {
+ $payloads[] = $payload;
+ }
}
return $payloads;
}
+ /**
+ * @return array{
+ * id: int,
+ * title: string,
+ * description: string,
+ * }|null
+ */
+ private function buildElementSummaryPayload(?Element $element): ?array
+ {
+ if ($element === null) {
+ return null;
+ }
+
+ return [
+ 'id' => $element->getId(),
+ 'title' => $element->getTitle(),
+ 'description' => $element->getDescription(),
+ ];
+ }
+
private function storageUrl(?string $path, string $folderPrefix): ?string
{
if ($path === null) {
diff --git a/frontend/rabbi_gerzi/src/components/ElementParentLink.vue b/frontend/rabbi_gerzi/src/components/ElementParentLink.vue
new file mode 100644
index 0000000..f105b68
--- /dev/null
+++ b/frontend/rabbi_gerzi/src/components/ElementParentLink.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+ Parent: {{ parentElement.title }}
+
+
+
+
diff --git a/frontend/rabbi_gerzi/src/stores/elements.ts b/frontend/rabbi_gerzi/src/stores/elements.ts
index 074ba04..870eb2e 100644
--- a/frontend/rabbi_gerzi/src/stores/elements.ts
+++ b/frontend/rabbi_gerzi/src/stores/elements.ts
@@ -20,6 +20,7 @@ type ElementFileType = 'iconImage' | 'shortPdf' | 'longPdf'
interface ElementResponse {
element: Element
childElements: ChildElement[]
+ parentElement: ChildElement | null
siblingElements: ChildElement[]
}
@@ -63,6 +64,7 @@ const ELEMENT_UPDATE_URL = `${API_BASE_URL}/api/element/update`
export const useElementsStore = defineStore('elements', () => {
const element = ref(null)
const childElements = ref([])
+ const parentElement = ref(null)
const siblingElements = ref([])
const isLoading = ref(false)
const error = ref(null)
@@ -77,6 +79,7 @@ export const useElementsStore = defineStore('elements', () => {
async function fetchElement(elementId: string): Promise {
element.value = null
childElements.value = []
+ parentElement.value = null
siblingElements.value = []
error.value = null
saveError.value = null
@@ -101,9 +104,11 @@ export const useElementsStore = defineStore('elements', () => {
const data: ElementResponse = await response.json()
element.value = data.element
childElements.value = data.childElements
+ parentElement.value = data.parentElement
siblingElements.value = data.siblingElements
} catch {
childElements.value = []
+ parentElement.value = null
siblingElements.value = []
error.value = 'Network error - could not load element'
} finally {
@@ -451,6 +456,7 @@ export const useElementsStore = defineStore('elements', () => {
return {
element,
childElements,
+ parentElement,
siblingElements,
isLoading,
error,
diff --git a/frontend/rabbi_gerzi/src/views/AdminElementPage.vue b/frontend/rabbi_gerzi/src/views/AdminElementPage.vue
index 2c66cfc..3d529ba 100644
--- a/frontend/rabbi_gerzi/src/views/AdminElementPage.vue
+++ b/frontend/rabbi_gerzi/src/views/AdminElementPage.vue
@@ -6,6 +6,7 @@ import {
ChevronUp as ChevronUpIcon,
} from 'lucide-vue-next'
import { useRoute, useRouter } from 'vue-router'
+import ElementParentLink from '@/components/ElementParentLink.vue'
import ElementSiblingNavigation from '@/components/ElementSiblingNavigation.vue'
import RichTextEditor from '@/components/RichTextEditor.vue'
import SiteHeader from '@/components/SiteHeader.vue'
@@ -30,6 +31,7 @@ const elementsStore = useElementsStore()
const {
element,
childElements,
+ parentElement,
siblingElements,
isLoading,
error,
@@ -352,6 +354,12 @@ function resetInput(changeEvent: Event): void {
@@ -730,7 +738,7 @@ function resetInput(changeEvent: Event): void {
}
.admin-element-page__heading {
- margin: 0;
+ margin: 0.35rem 0 0;
color: #2c2c2c;
font-family: var(--font-serif);
font-size: 2rem;
diff --git a/frontend/rabbi_gerzi/src/views/ElementPage.vue b/frontend/rabbi_gerzi/src/views/ElementPage.vue
index fbf4dee..c2ed707 100644
--- a/frontend/rabbi_gerzi/src/views/ElementPage.vue
+++ b/frontend/rabbi_gerzi/src/views/ElementPage.vue
@@ -2,6 +2,7 @@
import { storeToRefs } from 'pinia'
import { computed, watch } from 'vue'
import { useRoute } from 'vue-router'
+import ElementParentLink from '@/components/ElementParentLink.vue'
import ElementSiblingNavigation from '@/components/ElementSiblingNavigation.vue'
import SiteHeader from '@/components/SiteHeader.vue'
import { useElementsStore } from '@/stores/elements'
@@ -10,8 +11,14 @@ type TimestampPart = string | undefined
const route = useRoute()
const elementsStore = useElementsStore()
-const { element, childElements, siblingElements, isLoading, error } =
- storeToRefs(elementsStore)
+const {
+ element,
+ childElements,
+ parentElement,
+ siblingElements,
+ isLoading,
+ error,
+} = storeToRefs(elementsStore)
const youtubeIframeAllow = [
'accelerometer',
@@ -221,6 +228,17 @@ function isShortYoutubeHost(hostname: string): boolean {
data-cy="element-icon"
/>
+
+
+
+
{{ element.title }}
@@ -335,6 +353,12 @@ function isShortYoutubeHost(hostname: string): boolean {
object-fit: contain;
}
+.element-page__parent-navigation {
+ display: flex;
+ justify-content: center;
+ margin: 0 0 0.5rem;
+}
+
.element-page__heading {
margin: 0;
color: #2c2c2c;