mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 18:39:40 +02:00
## New Features ### Network Graph Visualization (Contacts, Calendar, Todo) - D3.js force simulation for physics-based layout - Zoom & pan with mouse/touchpad - Keyboard shortcuts: +/- zoom, 0 reset, Esc deselect, / search, F focus - Filtering by tags, company/location/project, connection strength - Shared components in @manacore/shared-ui ### Central Tags API (mana-core-auth) - CRUD endpoints for tags - Schema: tags table with userId, name, color, app - Shared tag components in @manacore/shared-ui ### Custom Themes System - Theme editor with live preview and color picker - Community theme gallery - Theme sharing (public, unlisted, private) - Backend API in mana-core-auth ### Todo App Extensions - Glass-pill design for task input and items - Settings page with 20+ preferences - Task edit modal with inline editing - Statistics page with visualizations - PWA support with offline capabilities - Multiple kanban boards ### Contacts App Features - Duplicate detection - Photo upload - Batch operations - Enhanced favorites page with multiple view modes - Alphabet view improvements - Search modal ### Help System - @manacore/shared-help-content - @manacore/shared-help-ui - @manacore/shared-help-types ### Other Features - Themes page for all apps - Referral system frontend - CommandBar (global search) - Skeleton loaders - Settings page improvements ## Bug Fixes - Network graph simulation initialization - Database schema TEXT for user_id columns (Better Auth compatibility) - Various styling fixes ## Documentation - Daily report for 2025-12-10 - CI/CD deployment guide 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
119 lines
3.4 KiB
TypeScript
119 lines
3.4 KiB
TypeScript
/**
|
|
* Content Merger
|
|
* Merges central help content with app-specific content
|
|
*/
|
|
|
|
import type { HelpContent, MergeContentOptions } from '@manacore/shared-help-types';
|
|
|
|
/**
|
|
* Filter content items by locale and app
|
|
*/
|
|
function filterItems<T extends { language: string; appSpecific?: boolean; apps?: string[] }>(
|
|
items: T[],
|
|
locale: string,
|
|
appId: string
|
|
): T[] {
|
|
return items.filter((item) => {
|
|
// Filter by language
|
|
if (item.language !== locale) {
|
|
return false;
|
|
}
|
|
|
|
// Include non-app-specific items
|
|
if (!item.appSpecific) {
|
|
return true;
|
|
}
|
|
|
|
// Include app-specific items for this app
|
|
return item.apps?.includes(appId) ?? false;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Merge two arrays, optionally replacing items with same ID
|
|
*/
|
|
function mergeArrays<T extends { id: string }>(
|
|
central: T[],
|
|
appSpecific: T[],
|
|
overrideById: boolean
|
|
): T[] {
|
|
if (!overrideById) {
|
|
return [...central, ...appSpecific];
|
|
}
|
|
|
|
const appIds = new Set(appSpecific.map((item) => item.id));
|
|
const filtered = central.filter((item) => !appIds.has(item.id));
|
|
return [...filtered, ...appSpecific];
|
|
}
|
|
|
|
/**
|
|
* Sort items by order property
|
|
*/
|
|
function sortByOrder<T extends { order?: number }>(items: T[]): T[] {
|
|
return [...items].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
}
|
|
|
|
/**
|
|
* Merge central help content with app-specific content
|
|
*/
|
|
export function mergeContent(
|
|
central: HelpContent,
|
|
appSpecific: Partial<HelpContent>,
|
|
options: MergeContentOptions
|
|
): HelpContent {
|
|
const { appId, locale, overrideById = true } = options;
|
|
|
|
// Filter central content by locale and app
|
|
const filteredCentral: HelpContent = {
|
|
faq: filterItems(central.faq, locale, appId),
|
|
features: filterItems(central.features, locale, appId),
|
|
shortcuts: filterItems(central.shortcuts, locale, appId),
|
|
gettingStarted: filterItems(central.gettingStarted, locale, appId),
|
|
changelog: filterItems(central.changelog, locale, appId),
|
|
contact: central.contact?.language === locale ? central.contact : null,
|
|
};
|
|
|
|
// Filter app-specific content
|
|
const filteredApp: Partial<HelpContent> = {
|
|
faq: appSpecific.faq ? filterItems(appSpecific.faq, locale, appId) : [],
|
|
features: appSpecific.features ? filterItems(appSpecific.features, locale, appId) : [],
|
|
shortcuts: appSpecific.shortcuts ? filterItems(appSpecific.shortcuts, locale, appId) : [],
|
|
gettingStarted: appSpecific.gettingStarted
|
|
? filterItems(appSpecific.gettingStarted, locale, appId)
|
|
: [],
|
|
changelog: appSpecific.changelog ? filterItems(appSpecific.changelog, locale, appId) : [],
|
|
contact: appSpecific.contact?.language === locale ? appSpecific.contact : null,
|
|
};
|
|
|
|
// Merge and sort
|
|
return {
|
|
faq: sortByOrder(mergeArrays(filteredCentral.faq, filteredApp.faq ?? [], overrideById)),
|
|
features: sortByOrder(
|
|
mergeArrays(filteredCentral.features, filteredApp.features ?? [], overrideById)
|
|
),
|
|
shortcuts: sortByOrder(
|
|
mergeArrays(filteredCentral.shortcuts, filteredApp.shortcuts ?? [], overrideById)
|
|
),
|
|
gettingStarted: sortByOrder(
|
|
mergeArrays(filteredCentral.gettingStarted, filteredApp.gettingStarted ?? [], overrideById)
|
|
),
|
|
changelog: sortByOrder(
|
|
mergeArrays(filteredCentral.changelog, filteredApp.changelog ?? [], overrideById)
|
|
),
|
|
contact: filteredApp.contact ?? filteredCentral.contact,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Create an empty HelpContent object
|
|
*/
|
|
export function createEmptyContent(): HelpContent {
|
|
return {
|
|
faq: [],
|
|
features: [],
|
|
shortcuts: [],
|
|
gettingStarted: [],
|
|
changelog: [],
|
|
contact: null,
|
|
};
|
|
}
|