diff --git a/apps/web/src/routes/(app)/collective/manage/+page.svelte b/apps/web/src/routes/(app)/collective/manage/+page.svelte index 23075e0..a45ecca 100644 --- a/apps/web/src/routes/(app)/collective/manage/+page.svelte +++ b/apps/web/src/routes/(app)/collective/manage/+page.svelte @@ -5,6 +5,9 @@ import { currentUser } from '$lib/stores/auth'; import { currentCollective, collectiveMembers, userCollectives } from '$lib/stores/collective'; import Avatar from '$lib/components/Avatar.svelte'; + import type { FeatureFlags, SectionKey } from '@colectivo/types'; + import { SECTION_KEYS } from '@colectivo/types'; + import { setCollectiveFeature } from '$lib/stores/features'; import * as m from '$lib/paraglide/messages'; type Member = { @@ -236,6 +239,31 @@ userCollectives.update((list) => list.map((it) => (it.id === c.id ? c : it))); } + // ── Fase 12.4.2 — collective section visibility (admin only) ──────────── + function sectionLabel(section: SectionKey): string { + if (section === 'lists') return m.section_label_lists(); + if (section === 'tasks') return m.section_label_tasks(); + if (section === 'notes') return m.section_label_notes(); + return m.section_label_search(); + } + + function collectiveFlag(section: SectionKey): boolean { + const flags = (($currentCollective?.feature_flags ?? {}) as FeatureFlags); + // Missing key → ON (the collective has "no opinion" and the user layer + // can take over). Equivalent to section_enabled's collective coalesce. + return flags[section] ?? true; + } + + async function toggleCollectiveSection(section: SectionKey) { + if (!isAdmin) return; + const next = !collectiveFlag(section); + try { + await setCollectiveFeature(section, next); + } catch { + // optimistic mutation already rolled back inside the helper + } + } + // ── Fase 10.3: dissolve collective (CU-H08) ───────────────────────────── let dissolveOpen = $state(false); let dissolveConfirmInput = $state(''); @@ -511,6 +539,35 @@ {/if} + +
+

+ {m.collective_section_visibility_title()} +

+

+ {m.collective_section_visibility_blurb()} +

+ +
+

diff --git a/apps/web/src/routes/(app)/settings/+page.svelte b/apps/web/src/routes/(app)/settings/+page.svelte index e0dc28f..44b7c62 100644 --- a/apps/web/src/routes/(app)/settings/+page.svelte +++ b/apps/web/src/routes/(app)/settings/+page.svelte @@ -17,7 +17,12 @@ recolorTag, deleteTag } from '$lib/stores/tags'; - import type { ItemTag, ItemTagColor } from '@colectivo/types'; + import type { ItemTag, ItemTagColor, FeatureFlags, SectionKey } from '@colectivo/types'; + import { SECTION_KEYS } from '@colectivo/types'; + import { + currentUserFeatures, + setUserFeature + } from '$lib/stores/features'; import { languageTag, setLanguageTag } from '$lib/paraglide/runtime'; import * as m from '$lib/paraglide/messages'; import type { ThemePreference } from '$lib/theme'; @@ -205,6 +210,39 @@ .eq('id', $currentUser.id); } + // ── Fase 12.4.1 — section visibility toggles ──────────────────────────── + // The "effective" map is derived from the user + active-collective flags + // (see $lib/stores/features). The disabled state of each toggle reflects + // whether the collective has explicitly hidden the section — when it has, + // the user toggle is moot and we surface that with a one-line hint. + function sectionLabel(section: SectionKey): string { + if (section === 'lists') return m.section_label_lists(); + if (section === 'tasks') return m.section_label_tasks(); + if (section === 'notes') return m.section_label_notes(); + return m.section_label_search(); + } + + function userFlag(section: SectionKey): boolean { + const flags = ($currentUserFeatures?.feature_flags ?? {}) as FeatureFlags; + const v = flags[section]; + // Default ON when the key is missing or null — matches section_enabled(). + return v ?? true; + } + + function collectiveHidden(section: SectionKey): boolean { + const flags = ($currentCollective?.feature_flags ?? {}) as FeatureFlags; + return flags[section] === false; + } + + async function toggleUserSection(section: SectionKey) { + const next = !userFlag(section); + try { + await setUserFeature(section, next); + } catch { + // optimistic mutation already rolled back inside the helper + } + } + async function switchLanguage(lang: 'en' | 'es') { language = lang; setLanguageTag(lang); @@ -420,6 +458,40 @@

+ +
+

+ {m.settings_section_visibility_title()} +

+

{m.settings_section_visibility_blurb()}

+ +
+