Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
8472ff9
feat: add settings section and languages index component
timosville Feb 19, 2026
24e888d
feat: add source language component for language settings
timosville Feb 20, 2026
ea16b36
feat: implement language management interface with actions and table …
timosville Feb 20, 2026
8889251
feat: add RingBlock component and story for dashboard progress visual…
timosville Feb 20, 2026
a8192a4
feat: extend LanguageSpecification and update translation progress ha…
timosville Feb 20, 2026
a8d716c
refactor: simplify LanguageSpecification and update SourceLang compon…
timosville Feb 20, 2026
38378d6
fix: remove extraneous closing tag in languages-index.vue
timosville Feb 20, 2026
502f553
refactor: update type definitions in lang-table story to use TableItem
timosville Feb 23, 2026
5f51157
feat: introduce LangStrip component for language display and update l…
timosville Feb 24, 2026
d6744c8
refactor: update lang-table components to use LanguageTableItem and r…
timosville Feb 24, 2026
26b8479
feat: enhance lang-table with action buttons for team management and …
timosville Feb 24, 2026
534caad
fix: adjust positioning of dropdown menu in lang-table component for …
timosville Feb 24, 2026
c228305
feat: implement toggle functionality for action buttons in lang-table…
timosville Feb 24, 2026
95dd533
refactor: update action button functionality in lang-table component …
timosville Feb 24, 2026
5d9d7c4
feat: add source-language component and update related components for…
timosville Feb 24, 2026
1e440a9
feat: add LanguagesProps interface and update language components to …
timosville Feb 24, 2026
455c238
feat: enhance language display by adding truncation for bibleLabel an…
timosville Feb 25, 2026
83066c2
feat: add LanguagesEdit component and corresponding story for languag…
timosville Feb 25, 2026
7730b9c
feat: add language list component and corresponding story for languag…
timosville Feb 25, 2026
713a573
feat: add LanguageListItem component with story for showcasing langua…
timosville Feb 25, 2026
a2caae2
feat: add .cursorignore file to exclude .env from cursor operations
timosville Mar 3, 2026
b741089
feat: enhance language list stories and update language-list-item com…
timosville Mar 3, 2026
4def133
feat: update language-list-item component to improve background color…
timosville Mar 3, 2026
aa13220
feat: add interactive variant to language-list-item story for enhance…
timosville Mar 3, 2026
e77494b
feat: refactor language list component to utilize items prop and enha…
timosville Mar 3, 2026
94f5698
feat: add search and letter filter
timosville Mar 3, 2026
a92844a
feat: improve language selection interface with dynamic updates and e…
timosville Mar 3, 2026
bfbcbb5
feat: update language list button styles for improved accessibility a…
timosville Mar 3, 2026
9c1cbdd
refactor: handle second click
timosville Mar 3, 2026
dae0a1c
feat: sort language list items for improved organization and user exp…
timosville Mar 3, 2026
0bbf2e8
feat: enhance language list layout with additional padding and update…
timosville Mar 3, 2026
ad7cb7b
feat: add Language Add Modal component with story for language select…
timosville Mar 3, 2026
ddd3057
refactor: add language modal in edit page
timosville Mar 3, 2026
0b3065d
refactor: replace button components with PillButton for consistent st…
timosville Mar 3, 2026
7447891
refactor: update Languages Index component with new button actions an…
timosville Mar 3, 2026
8ae6beb
refactor: integrate LanguageModal for language removal and request de…
timosville Mar 3, 2026
876bc36
feat: add Request App Update modal component and integrate it into La…
timosville Mar 3, 2026
5a767d0
feat: add Request Feedback Modal component and integrate it into Lang…
timosville Mar 3, 2026
030c836
feat: add mailFromAddress to CmsMeta and integrate it into Request Fe…
timosville Mar 3, 2026
b92f4c0
refactor: remove simulated API call in submitAppUpdateRequest and add…
timosville Mar 3, 2026
b9e41dc
refactor: restructure LanguageSpecification to extend BibleSpecificat…
timosville Mar 3, 2026
eb89b0c
refactor: add change modal
timosville Mar 4, 2026
13a8591
refactor: change name of function
timosville Mar 4, 2026
8b1f2c9
refactor: replace LanguageModal with dedicated modals for language re…
timosville Mar 4, 2026
93dcdca
refactor: update Bible translations modal to use new BibleVersion str…
timosville Mar 4, 2026
d4639ce
refactor: streamline Bible translations handling by integrating API c…
timosville Mar 4, 2026
dded727
refactor: define APIBibleVersion type for improved type safety in get…
timosville Mar 4, 2026
ecafc2b
refactor: update transformBibleVersions function to utilize APIBibleV…
timosville Mar 4, 2026
0dfd23a
refator: add loading state
timosville Mar 4, 2026
02b2253
refactor: enhance Bible translations modal with error handling and lo…
timosville Mar 4, 2026
22a08b0
refactor: update transformBibleVersions function to return an array o…
timosville Mar 4, 2026
45988e4
refactor: simplify BibleVersion type and enhance default selection lo…
timosville Mar 4, 2026
43b58c1
refactor: improve error handling in Bible translations modal by displ…
timosville Mar 4, 2026
40fb667
refactor: filter languages based on the prop
timosville Mar 4, 2026
47946b0
refactor: add no translations available message and update button sta…
timosville Mar 4, 2026
eceaccb
refactor: optimize loading logic in Bible translations modal to preve…
timosville Mar 4, 2026
e324051
refactor: implement language matching logic to enhance Bible translat…
timosville Mar 4, 2026
43e3bb7
refactor: enhance language table with active languages header and des…
timosville Mar 4, 2026
1ea842d
refactor: integrate Bible translations modal
timosville Mar 4, 2026
9b07b43
refactor: adjust button size and streamline request language button i…
timosville Mar 4, 2026
c6ffa1a
refactor: export language index template
timosville Mar 4, 2026
27f6cb2
feat: add settings icon and link to sidebar
timosville Mar 4, 2026
0244fd7
feat: conditionally display settings and team links in sidebar for ad…
timosville Mar 4, 2026
bb9a3a4
fix: correct import name from LanguageIndex to LanguagesIndex in inde…
timosville Mar 4, 2026
180ee5d
ops: update package-lock.json
timosville Mar 4, 2026
30b7f4e
fix: update button click handler to use dynamic locale in languages i…
timosville Mar 4, 2026
9cb35f7
feat: add LanguagesEdit component to index export
timosville Mar 4, 2026
fa3edf7
feat: introduce LanguagesEditProps interface and update LanguagesEdit…
timosville Mar 4, 2026
2cbc666
refactor: remove white background
timosville Mar 17, 2026
6e24410
refactor: change layout from flex to grid for language list item
timosville Mar 17, 2026
bdfb951
feat: conditionally render pagination component based on items length…
timosville Mar 17, 2026
013bc51
refactor: adjust size of the ring block component
timosville Mar 17, 2026
fb87ddd
feat: add LanguageListItemProps interface and update language-list-it…
timosville Mar 17, 2026
30af455
refactor: update LanguageList component to use status prop and simpli…
timosville Mar 17, 2026
7fb25f6
refactor: update LanguageListItemProps to include 'readonly' and 'ava…
timosville Mar 18, 2026
21f162e
refactor: update props structure in language-list component to correc…
timosville Mar 18, 2026
d8de96a
refactor: update status assignment in language-list component to incl…
timosville Mar 18, 2026
102c653
refactor: enhance availableLanguageItems and addedLanguageItems compu…
timosville Mar 18, 2026
d600943
refactor: update language-list-item component to toggle checkbox visi…
timosville Mar 18, 2026
7dd585f
feat: introduce SettingsIndex component and update import paths for s…
timosville Mar 18, 2026
4c94234
refactor: move templates
timosville Mar 18, 2026
238a78c
refactor: replace bibleVersionId with bibleVersion across components …
timosville Mar 18, 2026
a711f33
refactor: rename LanguagesProps to SettingsPageProps and update relat…
timosville Mar 18, 2026
dfdb482
refactor: improve code readability in languages-edit component by for…
timosville Mar 18, 2026
c64cd62
refactor: streamline language selection logic in languages-edit compo…
timosville Mar 18, 2026
e1bce09
ops: audit fix
timosville Mar 18, 2026
1ebc9d5
refactor: update import and export for SettingsIndex component in ind…
timosville Mar 18, 2026
5d4eb09
feat: add RequestFeedbackModal import to settings-index component
timosville Mar 18, 2026
0024f6e
feat: add providers prop to SettingsPageProps and integrate with widg…
timosville Mar 18, 2026
b7930d2
feat:add feedback message
timosville Mar 19, 2026
044be6f
feat: add BibleTranslationsModal story with multiple variants for lan…
timosville Mar 19, 2026
1cf94c5
feat: update SettingsIndex story to use setupSettings and add provide…
timosville Mar 19, 2026
8348e90
feat: add event handler for Bible translation change in settings-inde…
timosville Mar 19, 2026
696aca6
feat: enhance settings-index component with shared state for source l…
timosville Mar 19, 2026
2e81fd7
feat: implement language removal functionality in settings-index comp…
timosville Mar 19, 2026
c37d69c
feat: add confirmation message for added languages in languages-edit …
timosville Mar 19, 2026
ac53dc2
feat: save languages
timosville Mar 19, 2026
70373dd
feat: refactor Bible translation change handlers in settings-index co…
timosville Mar 19, 2026
5eed30c
Merge branch 'main' into sco-1753-language-ui
timosville Mar 30, 2026
1b28bc3
ops: update lock file
timosville Mar 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cursorignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
4 changes: 4 additions & 0 deletions histoire.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ export default defineConfig({
id: 'team',
title: 'Team',
},
{
id: 'settings',
title: 'Settings',
},
{
id: 'shared',
title: 'Shared',
Expand Down
1 change: 1 addition & 0 deletions src/backend/define_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export function defineConfig(config: Partial<CmsConfig>): CmsConfig {
hasAppPreview: false,
helpUrl: 'https://journeys.helpscoutdocs.com/',
logo: 'https://res.cloudinary.com/journeys/image/upload/v1756295658/logo_g8k7tf.png',
mailFromAddress: 'ops@scoutredeem.co',
...config.meta,
},

Expand Down
5 changes: 5 additions & 0 deletions src/backend/stubs/config/cms.stub
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ const cmsConfig: CmsConfig = defineConfig({
* Are we embedding a Flutter preview?
*/
hasAppPreview: false,

/**
* Contact email for enquiries (from MAIL_FROM_ADDRESS)
*/
mailFromAddress: 'ops@scoutredeem.co',
},

languages: {
Expand Down
51 changes: 51 additions & 0 deletions src/frontend/dashboard/ring-block.story.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<template>
<Story title="Ring block" group="dashboard" :layout="{ type: 'grid', width: 350 }">
<Variant title="Incomplete">
<div class="bg-white p-8">
<RingBlock
:progress="[
{ name: 'Interface', done: 75, draft: 0, total: 100 },
{ name: 'Content', done: 100, draft: 20, total: 100 },
]"
/>
</div>
</Variant>

<Variant title="All done">
<div class="bg-white p-8">
<RingBlock
:progress="[
{ name: 'Interface', done: 100, draft: 50, total: 100 },
{ name: 'Content', done: 300, draft: 10, total: 300 },
]"
/>
</div>
</Variant>

<Variant title="Not started">
<div class="bg-white p-8">
<RingBlock
:progress="[
{ name: 'Interface', done: 0, draft: 0, total: 100 },
{ name: 'Content', done: 0, draft: 0, total: 300 },
]"
/>
</div>
</Variant>

<Variant title="In progress">
<div class="bg-white p-8">
<RingBlock
:progress="[
{ name: 'Interface', done: 50, draft: 10, total: 100 },
{ name: 'Content', done: 150, draft: 0, total: 300 },
]"
/>
</div>
</Variant>
</Story>
</template>

<script setup lang="ts">
import RingBlock from './ring-block.vue';
</script>
116 changes: 116 additions & 0 deletions src/frontend/dashboard/ring-block.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<template>
<div class="flex flex-col items-center text-gray-500">
<template v-if="hasCompleteRings">
<div class="relative mx-auto size-20 rounded-full border-[3px] border-teal-500">
<div class="absolute inset-0 flex items-center justify-center">
<Icon name="check-large" class="h-auto w-6 text-teal-500" />
</div>
</div>
<p class="mt-2 text-center text-xs font-medium leading-4">All done</p>
</template>
<template v-else>
<div class="flex justify-between gap-8">
<div v-for="item in ringData" :key="item.name" class="flex flex-col items-center">
<p class="mb-2 text-center text-sm font-medium leading-4">
{{ item.name }}
</p>
<div class="relative flex flex-col items-center justify-center">
<svg
v-if="item.label !== ''"
class="-rotate-90"
:height="circleWidth"
:width="circleWidth"
>
<circle
class="fill-transparent stroke-teal-500 stroke-[6px]"
stroke="currentColor"
:r="circleRadius"
:cx="center"
:cy="center"
:stroke-dasharray="`${item.tealSegment} ${circumference}`"
stroke-dashoffset="0"
/>
<circle
class="fill-transparent stroke-blue-500 stroke-[6px]"
stroke="currentColor"
:r="circleRadius"
:cx="center"
:cy="center"
:stroke-dasharray="`${item.blueSegment} ${circumference}`"
:stroke-dashoffset="`-${item.tealSegment}`"
/>
<circle
class="fill-transparent stroke-gray-200 stroke-[6px]"
stroke="currentColor"
:r="circleRadius"
:cx="center"
:cy="center"
:stroke-dasharray="`${item.unfilledSegment} ${circumference}`"
:stroke-dashoffset="`-${item.tealSegment + item.blueSegment}`"
/>
</svg>
<div class="absolute inset-0 flex items-center justify-center">
<div v-if="item.donePercentage === 100 && item.draftPercentage === 0">
<Icon name="check" class="h-auto w-4 text-teal-500" />
</div>
<span v-else class="text-sm font-bold leading-none text-gray-800">
{{ item.label }}
</span>
</div>
</div>
</div>
</div>
</template>
</div>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import Icon from '../shared/icon.vue';
import type { Progress } from '../../types';

const props = defineProps<{ progress: Omit<Progress, 'lastUpdated'>[] }>();

const hasCompleteRings = computed(() => {
return props.progress.every((stat) => stat.done === stat.total);
});

const circleWidth = 80;
const center = circleWidth / 2;
const circleRadius = circleWidth / 2 - 6;
const circumference = 2 * Math.PI * circleRadius;

function computeRingData(item: Omit<Progress, 'lastUpdated'>) {
const isOverdone = item.done + item.draft > item.total;
const workingDone = isOverdone
? item.done - (item.done + item.draft - item.total)
: item.done;
const donePercentage =
item.total === 0 ? 0 : Math.round((workingDone / item.total) * 100);
const draftPercentage =
item.total === 0 ? 0 : Math.round((item.draft / item.total) * 100);
const unfilledPercentage = 100 - (donePercentage + draftPercentage);

let label = '';
const nr = donePercentage + draftPercentage;
if (!isNaN(nr)) {
label = nr > 100 ? '100%' : `${nr}%`;
}

const tealSegment = circumference * (donePercentage / 100);
const blueSegment = circumference * (draftPercentage / 100);
const unfilledSegment = circumference * (unfilledPercentage / 100);

return {
name: item.name,
label,
donePercentage,
draftPercentage,
tealSegment,
blueSegment,
unfilledSegment,
};
}

const ringData = computed(() => props.progress.map(computeRingData));
</script>
4 changes: 4 additions & 0 deletions src/frontend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import ImageField from './fields/image-field.vue';
import IndexCard from './stories/components/index-card.vue';
import IndexFilter from './shared/index-filter.vue';
import LabelButton from './shared/label-button.vue';
import LanguagesEdit from './settings/languages/languages-edit.vue';
import SettingsIndex from './settings/settings-index.vue';
import ListField from './fields/list-field.vue';
import ListSwitcher from './shared/list-switcher.vue';
import Login from './auth/login-index.vue';
Expand Down Expand Up @@ -91,6 +93,8 @@ export {
IndexCard,
IndexFilter,
LabelButton,
LanguagesEdit,
SettingsIndex,
ListField,
ListSwitcher,
Login,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<template>
<Story title="Bible Translations Modal" group="settings">
<Variant title="With translations" :setup-app="setupWithTranslations">
<button
class="rounded bg-blue-500 px-4 py-2 text-white"
@click="showModal = true"
>
Choose Bible translation
</button>
<BibleTranslationsModal
:open="showModal"
:item="englishItem"
@close="showModal = false"
@confirm="onConfirm"
/>
</Variant>
<Variant title="No translations for language" :setup-app="setupWithTranslations">
<button
class="rounded bg-blue-500 px-4 py-2 text-white"
@click="showEmptyModal = true"
>
Choose Bible translation
</button>
<BibleTranslationsModal
:open="showEmptyModal"
:item="obscureLanguageItem"
@close="showEmptyModal = false"
@confirm="onConfirm"
/>
</Variant>
<Variant title="Multiple versions" :setup-app="setupWithMultipleVersions">
<button
class="rounded bg-blue-500 px-4 py-2 text-white"
@click="showMultipleModal = true"
>
Choose Bible translation
</button>
<BibleTranslationsModal
:open="showMultipleModal"
:item="spanishItem"
@close="showMultipleModal = false"
@confirm="onConfirm"
/>
</Variant>
</Story>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import BibleTranslationsModal from './bible-translations-modal.vue';
import { useSharedStore, useWidgetsStore } from '../../../store';
import type { StoryHandler } from '../../../shared/helpers';
import type { LanguageTableItem } from '../../../../types';

const showModal = ref(false);
const showEmptyModal = ref(false);
const showMultipleModal = ref(false);

const englishItem: LanguageTableItem = {
language: 'English',
languageDirection: 'ltr',
locale: 'en',
bibleVersion: 'de4e12af7f28f599-01',
bibleLabel: '(KJV) King James Version',
};

const obscureLanguageItem: LanguageTableItem = {
language: 'Klingon',
languageDirection: 'ltr',
locale: 'tlh',
};

const spanishItem: LanguageTableItem = {
language: 'Spanish',
languageDirection: 'ltr',
locale: 'es',
bibleVersion: '592420522e16049f-01',
bibleLabel: '(RVR1960) Reina-Valera 1960',
};

const mockEnglishTranslations = [
{
language: 'English',
bibleVersion: 'de4e12af7f28f599-01',
bibleLabel: '(KJV) King James Version',
},
{
language: 'English',
bibleVersion: '9879dbb7cfe39e4d-01',
bibleLabel: '(ERV) Easy-to-Read Version',
},
{
language: 'English',
bibleVersion: 'abc123-01',
bibleLabel: '(ESV) English Standard Version',
},
];

const mockTranslationsWithMultiple = [
...mockEnglishTranslations,
{
language: 'Spanish',
bibleVersion: '592420522e16049f-01',
bibleLabel: '(RVR1960) Reina-Valera 1960',
},
{
language: 'Spanish',
bibleVersion: 'spa002-01',
bibleLabel: '(NTV) Nueva Traducción Viviente',
},
{ language: 'Spanish', bibleVersion: 'spa003-01', bibleLabel: '(DHH) Dios Habla Hoy' },
];

const setupWithTranslations: StoryHandler = () => {
const shared = useSharedStore();
const widgets = useWidgetsStore();
shared.setBibleTranslations(mockEnglishTranslations);
widgets.setProviders({
...widgets.providers,
scripture: { bibleApiKey: 'mock-key-for-story' },
});
};

const setupWithMultipleVersions: StoryHandler = () => {
const shared = useSharedStore();
const widgets = useWidgetsStore();
shared.setBibleTranslations(mockTranslationsWithMultiple);
widgets.setProviders({
...widgets.providers,
scripture: { bibleApiKey: 'mock-key-for-story' },
});
};

const onConfirm = (bibleVersion: string, bibleVersionName: string) => {
alert(`Confirmed: ${bibleVersionName} (${bibleVersion})`);
};
</script>
Loading
Loading