From c6556a0ae52533e6b04a1ca386ff8e278534ac68 Mon Sep 17 00:00:00 2001 From: Timothy Koech Date: Wed, 13 May 2026 07:34:33 +0300 Subject: [PATCH 01/22] feat: add NavigationPane component with icon support and corresponding story --- src/frontend/shared/icon.vue | 88 +++++++++++++++++++ src/frontend/shared/navigation-pane.story.vue | 39 ++++++++ src/frontend/shared/navigation-pane.vue | 53 +++++++++++ src/types.ts | 7 ++ 4 files changed, 187 insertions(+) create mode 100644 src/frontend/shared/navigation-pane.story.vue create mode 100644 src/frontend/shared/navigation-pane.vue diff --git a/src/frontend/shared/icon.vue b/src/frontend/shared/icon.vue index 91997306..64750129 100644 --- a/src/frontend/shared/icon.vue +++ b/src/frontend/shared/icon.vue @@ -3219,6 +3219,94 @@ fill="currentColor" /> + + + + + + + + + + + + + + + diff --git a/src/frontend/shared/navigation-pane.story.vue b/src/frontend/shared/navigation-pane.story.vue new file mode 100644 index 00000000..db589b58 --- /dev/null +++ b/src/frontend/shared/navigation-pane.story.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/frontend/shared/navigation-pane.vue b/src/frontend/shared/navigation-pane.vue new file mode 100644 index 00000000..b6c864a3 --- /dev/null +++ b/src/frontend/shared/navigation-pane.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/types.ts b/src/types.ts index 34928441..b155fcd5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -398,6 +398,13 @@ export interface TabItem { count: number; } +/** Tabs for `navigation-pane` (icon + label). */ +export interface NavigationPaneTab { + label: string; + /** Registered `Icon` name; omitted for label-only tabs. */ + icon?: string; +} + export interface StatMetric { name: string; stat: number; From 442bf0e7b5392d8bc30312662e3eecbd849c518a Mon Sep 17 00:00:00 2001 From: Timothy Koech Date: Wed, 13 May 2026 07:34:40 +0300 Subject: [PATCH 02/22] feat: refactor story edit page to use tabbed navigation and modular components for details, sections, and resources --- .../stories/components/story-edit-details.vue | 61 ++++++++++++++ .../components/story-edit-resources.vue | 5 ++ .../components/story-edit-sections.vue | 5 ++ src/frontend/stories/story-edit.vue | 83 +++++++------------ 4 files changed, 99 insertions(+), 55 deletions(-) create mode 100644 src/frontend/stories/components/story-edit-details.vue create mode 100644 src/frontend/stories/components/story-edit-resources.vue create mode 100644 src/frontend/stories/components/story-edit-sections.vue diff --git a/src/frontend/stories/components/story-edit-details.vue b/src/frontend/stories/components/story-edit-details.vue new file mode 100644 index 00000000..a98544b4 --- /dev/null +++ b/src/frontend/stories/components/story-edit-details.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/frontend/stories/components/story-edit-resources.vue b/src/frontend/stories/components/story-edit-resources.vue new file mode 100644 index 00000000..d9767791 --- /dev/null +++ b/src/frontend/stories/components/story-edit-resources.vue @@ -0,0 +1,5 @@ + diff --git a/src/frontend/stories/components/story-edit-sections.vue b/src/frontend/stories/components/story-edit-sections.vue new file mode 100644 index 00000000..f9542ec1 --- /dev/null +++ b/src/frontend/stories/components/story-edit-sections.vue @@ -0,0 +1,5 @@ + diff --git a/src/frontend/stories/story-edit.vue b/src/frontend/stories/story-edit.vue index 9ecc311f..791f9a80 100644 --- a/src/frontend/stories/story-edit.vue +++ b/src/frontend/stories/story-edit.vue @@ -7,6 +7,15 @@ + @@ -21,55 +30,9 @@ ]" >
- - - - - + + + @@ -93,20 +56,19 @@ import { onMounted, ref } from 'vue'; import { DateTime } from 'luxon'; import { router } from '@inertiajs/vue3'; -import { SharedPageProps } from '../../types'; +import type { NavigationPaneTab, SharedPageProps } from '../../types'; import AppLayout from '../shared/app-layout.vue'; import ContentHeader from '../shared/content-header.vue'; import { useSharedStore, useWidgetsStore, useModelStore } from '../store'; -import MarkdownField from '../fields/markdown-field.vue'; import { ResponseStatus, StoryEditProps } from '../../types'; import LabelButton from '../shared/label-button.vue'; import MetaMetaBox from './components/meta-meta-box.vue'; import ActionButton from '../shared/action-button.vue'; -import StringField from '../fields/string-field.vue'; -import ImageField from '../fields/image-field.vue'; -import NumberField from '../fields/number-field.vue'; -import TagField from '../fields/tag-field.vue'; import ContentSidebar from '../shared/content-sidebar.vue'; +import StoryEditDetails from './components/story-edit-details.vue'; +import StoryEditResources from './components/story-edit-resources.vue'; +import StoryEditSections from './components/story-edit-sections.vue'; +import NavigationPane from '../shared/navigation-pane.vue'; const props = defineProps(); const shared = useSharedStore(); @@ -120,6 +82,17 @@ model.setModel(props.story); const title = ref(props.story.name); const savedAt = ref(props.story.updatedAt as unknown as string); +const storyEditTabs: NavigationPaneTab[] = [ + { label: 'Details', icon: 'book-open' }, + { label: 'Sections', icon: 'list-bullet' }, + { label: 'Resources', icon: 'folder' }, +]; +const currentStoryTab = ref('Details'); + +const onStoryTabChange = (tab: string) => { + currentStoryTab.value = tab; +}; + onMounted(() => { model.$subscribe(() => { title.value = model.getField('name', 'New Story'); From 2493845d9f55313fbe23df1b5d0f98e8caf0be30 Mon Sep 17 00:00:00 2001 From: Timothy Koech Date: Wed, 13 May 2026 08:10:34 +0300 Subject: [PATCH 03/22] feat: add content classification panel with story, section, and chapter type fields to story edit details --- .../stories/components/story-edit-details.vue | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/frontend/stories/components/story-edit-details.vue b/src/frontend/stories/components/story-edit-details.vue index a98544b4..ddc8fafe 100644 --- a/src/frontend/stories/components/story-edit-details.vue +++ b/src/frontend/stories/components/story-edit-details.vue @@ -49,6 +49,32 @@ :is-nested="true" class="px-8" /> + @@ -58,4 +84,5 @@ import StringField from '../../fields/string-field.vue'; import ImageField from '../../fields/image-field.vue'; import NumberField from '../../fields/number-field.vue'; import TagField from '../../fields/tag-field.vue'; +import PanelField from '../../fields/panel-field.vue'; From cf975d6b56b67694ce11498367492f60d747589f Mon Sep 17 00:00:00 2001 From: Timothy Koech Date: Wed, 13 May 2026 08:16:29 +0300 Subject: [PATCH 04/22] feat: add visibility select field to story edit details component --- .../stories/components/story-edit-details.vue | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/frontend/stories/components/story-edit-details.vue b/src/frontend/stories/components/story-edit-details.vue index ddc8fafe..286f75c7 100644 --- a/src/frontend/stories/components/story-edit-details.vue +++ b/src/frontend/stories/components/story-edit-details.vue @@ -75,6 +75,20 @@ :is-nested="true" class="px-8" /> + @@ -85,4 +99,5 @@ import ImageField from '../../fields/image-field.vue'; import NumberField from '../../fields/number-field.vue'; import TagField from '../../fields/tag-field.vue'; import PanelField from '../../fields/panel-field.vue'; +import SelectField from '../../fields/select-field.vue'; From 3163a3855c170aa11216bfa13ae0f96b1bc86c6b Mon Sep 17 00:00:00 2001 From: Timothy Koech Date: Wed, 13 May 2026 15:24:15 +0300 Subject: [PATCH 05/22] wip: add section-panel --- .../fields/section-panel-field.story.vue | 66 +++++ src/frontend/fields/section-panel-field.vue | 236 ++++++++++++++++++ src/frontend/fields/widget-fields.ts | 3 + src/frontend/shared/icon.vue | 2 +- .../components/story-edit-resources.vue | 51 +++- .../components/story-edit-sections.vue | 31 ++- src/frontend/stories/story-edit.story.vue | 4 +- 7 files changed, 387 insertions(+), 6 deletions(-) create mode 100644 src/frontend/fields/section-panel-field.story.vue create mode 100644 src/frontend/fields/section-panel-field.vue diff --git a/src/frontend/fields/section-panel-field.story.vue b/src/frontend/fields/section-panel-field.story.vue new file mode 100644 index 00000000..0e979336 --- /dev/null +++ b/src/frontend/fields/section-panel-field.story.vue @@ -0,0 +1,66 @@ + + + diff --git a/src/frontend/fields/section-panel-field.vue b/src/frontend/fields/section-panel-field.vue new file mode 100644 index 00000000..b6bceb30 --- /dev/null +++ b/src/frontend/fields/section-panel-field.vue @@ -0,0 +1,236 @@ + + + diff --git a/src/frontend/fields/widget-fields.ts b/src/frontend/fields/widget-fields.ts index 2667fda8..5ab35941 100644 --- a/src/frontend/fields/widget-fields.ts +++ b/src/frontend/fields/widget-fields.ts @@ -17,6 +17,7 @@ import DateField from './date-field.vue'; import DateRangeField from './date-range-field.vue'; import TagField from './tag-field.vue'; import RegionField from './region-field.vue'; +import SectionPanelField from './section-panel-field.vue'; export const widgetField = (widget: string) => { const up = widget[0].toUpperCase() + widget.substring(1); @@ -62,6 +63,8 @@ export const widgetField = (widget: string) => { return TagField; case 'RegionField': return RegionField; + case 'SectionPanelField': + return SectionPanelField; default: return NullField; } diff --git a/src/frontend/shared/icon.vue b/src/frontend/shared/icon.vue index 64750129..da04c809 100644 --- a/src/frontend/shared/icon.vue +++ b/src/frontend/shared/icon.vue @@ -143,7 +143,7 @@ > diff --git a/src/frontend/stories/components/story-edit-resources.vue b/src/frontend/stories/components/story-edit-resources.vue index d9767791..808396af 100644 --- a/src/frontend/stories/components/story-edit-resources.vue +++ b/src/frontend/stories/components/story-edit-resources.vue @@ -1,5 +1,52 @@ + + diff --git a/src/frontend/stories/components/story-edit-sections.vue b/src/frontend/stories/components/story-edit-sections.vue index f9542ec1..47e6c311 100644 --- a/src/frontend/stories/components/story-edit-sections.vue +++ b/src/frontend/stories/components/story-edit-sections.vue @@ -1,5 +1,32 @@ + + diff --git a/src/frontend/stories/story-edit.story.vue b/src/frontend/stories/story-edit.story.vue index 520b0e0e..5da2393e 100644 --- a/src/frontend/stories/story-edit.story.vue +++ b/src/frontend/stories/story-edit.story.vue @@ -30,7 +30,7 @@ diff --git a/src/frontend/stories/story-edit.story.vue b/src/frontend/stories/story-edit.story.vue index 5da2393e..702ef2f4 100644 --- a/src/frontend/stories/story-edit.story.vue +++ b/src/frontend/stories/story-edit.story.vue @@ -48,6 +48,19 @@ const storyData = { createdAt: '2024-01-15T10:30:00Z', updatedAt: '2024-01-20T14:45:00Z', sections: listModel.sections, + resources: [ + { + title: 'Study companion PDF', + url: 'https://example.com/guides/john-study.pdf', + summary: + '# About this resource\n\nA printable **study guide** with discussion questions for small groups.', + }, + { + title: 'Sermon series', + url: 'https://example.com/series/gospel-of-john', + summary: 'Weekly audio and outlines covering *John* chapters 1–12.', + }, + ], }; const newStoryData = { @@ -62,6 +75,7 @@ const newStoryData = { createdAt: '2024-01-25T09:00:00Z', updatedAt: '2024-01-25T09:00:00Z', sections: [], + resources: [], }; const loadNormalData: StoryHandler = ({ app, story, variant }): void => { diff --git a/src/frontend/stories/story-edit.vue b/src/frontend/stories/story-edit.vue index 791f9a80..9c1adcef 100644 --- a/src/frontend/stories/story-edit.vue +++ b/src/frontend/stories/story-edit.vue @@ -29,7 +29,7 @@ }, ]" > -
+ From 0b4b9faa0ff08772029f5436c3aa82b545775c8f Mon Sep 17 00:00:00 2001 From: Timothy Koech Date: Wed, 13 May 2026 16:46:37 +0300 Subject: [PATCH 09/22] feat: enhance story edit components with tab icons and improved section panel field integration --- src/frontend/fields/section-panel-field.vue | 13 ++++++++++- .../stories/components/story-edit-details.vue | 9 +++++++- .../components/story-edit-resources.vue | 13 ++++++++++- .../components/story-edit-sections.vue | 15 +++++++----- src/frontend/stories/story-edit.vue | 23 +++++++++++++++---- 5 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/frontend/fields/section-panel-field.vue b/src/frontend/fields/section-panel-field.vue index 07e35b0e..9561e483 100644 --- a/src/frontend/fields/section-panel-field.vue +++ b/src/frontend/fields/section-panel-field.vue @@ -1,5 +1,5 @@ + From db46533863da70a4890cef200546bd3b68bcde07 Mon Sep 17 00:00:00 2001 From: Jannie Theunissen Date: Fri, 19 Jun 2026 17:01:57 +0200 Subject: [PATCH 22/22] increment: screen props --- src/backend/services/story_service.ts | 44 +++++++++++++++++++++++++++ src/types.ts | 8 ++--- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/backend/services/story_service.ts b/src/backend/services/story_service.ts index 6ccf6c13..dd2c8db0 100644 --- a/src/backend/services/story_service.ts +++ b/src/backend/services/story_service.ts @@ -1,6 +1,7 @@ import config from '@adonisjs/core/services/config'; import { errors, HttpContext } from '@adonisjs/core/http'; import db from '@adonisjs/lucid/services/db'; +import { randomUUID } from 'crypto'; import StoryDeleteException from '../exceptions/story_delete_exception.js'; import Draft from '../models/draft.js'; import Chapter from '../models/chapter.js'; @@ -16,6 +17,7 @@ import type { StoryVersion, Providers, StoryCreateProps, + StorySection, } from '../../types.js'; import { slugify } from './helpers.js'; @@ -253,6 +255,42 @@ export class StoryService { }); } + public async prepSections( + version: StoryVersion, + sections?: PostedSection[], + ): Promise { + if (!sections) return []; + const sourceLocale = this.config.languages[0].locale; + if (version.locale === sourceLocale) return this.sourceSections(sections); + + const sourceLocalisation = await StoryLocalisation.query() + .where('storyId', version.storyId) + .where('locale', sourceLocale) + .first(); + + const source = sourceLocalisation?.sections ?? []; + if (!source.length) return []; + + return source.map((section, index) => { + const posted = sections[index]; + return { + id: section.id, + title: posted?.title ?? '', + description: posted?.description ?? '', + }; + }); + } + + sourceSections(sections: PostedSection[]): StorySection[] { + return sections.map((section) => { + return { + id: section.id?.trim() ? section.id : randomUUID(), + title: section.title, + description: section.description ?? '', + }; + }); + } + private async hasNoContent(id: number): Promise { const index = await Index.query().where('storyId', id).first(); if (index === null) return true; @@ -346,3 +384,9 @@ export class StoryService { return template.fields; } } + +export type PostedSection = { + id?: string; + title: string; + description?: string; +}; diff --git a/src/types.ts b/src/types.ts index 07de87ba..bc8ee2b4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -263,7 +263,7 @@ export interface JournaStoryEditProps { isNew: boolean; } -export interface StoryCreateProps { +export type StoryCreateProps = { model: { title: string; coverImage: string; @@ -279,9 +279,9 @@ export interface StoryCreateProps { }; templates: BundleTemplate[]; providers: Providers; -} +}; -export interface StoryEditProps { +export type StoryEditProps = { model: { id: number; title: string; @@ -308,7 +308,7 @@ export interface StoryEditProps { }; hasNoContent: boolean; providers: Providers; -} +}; export interface StoryIndexProps { index: IndexReadyItem[];