mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-18 18:01:23 +02:00
feat: major update with network graphs, themes, todo extensions, and more
## 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>
This commit is contained in:
parent
e84371aa94
commit
ee42b6cc76
381 changed files with 39284 additions and 6275 deletions
152
packages/shared-help-types/src/content.ts
Normal file
152
packages/shared-help-types/src/content.ts
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/**
|
||||
* Help Content Type Definitions
|
||||
* Defines the structure for all help content types
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// Base Types
|
||||
// ============================================================================
|
||||
|
||||
export type SupportedLanguage = 'en' | 'de' | 'fr' | 'it' | 'es';
|
||||
|
||||
export type FAQCategory = 'general' | 'account' | 'billing' | 'features' | 'technical' | 'privacy';
|
||||
|
||||
export type FeatureCategory = 'getting-started' | 'core' | 'advanced' | 'integration';
|
||||
|
||||
export type GuideDifficulty = 'beginner' | 'intermediate' | 'advanced';
|
||||
|
||||
export type ChangelogType = 'major' | 'minor' | 'patch' | 'beta';
|
||||
|
||||
export type ShortcutCategory = 'navigation' | 'editing' | 'general' | 'app-specific';
|
||||
|
||||
// ============================================================================
|
||||
// Content Item Types
|
||||
// ============================================================================
|
||||
|
||||
export interface BaseContentItem {
|
||||
id: string;
|
||||
language: SupportedLanguage;
|
||||
order?: number;
|
||||
appSpecific?: boolean;
|
||||
apps?: string[];
|
||||
lastUpdated?: Date;
|
||||
}
|
||||
|
||||
export interface FAQItem extends BaseContentItem {
|
||||
question: string;
|
||||
answer: string;
|
||||
category: FAQCategory;
|
||||
featured?: boolean;
|
||||
tags?: string[];
|
||||
relatedFaqs?: string[];
|
||||
}
|
||||
|
||||
export interface FeatureItem extends BaseContentItem {
|
||||
title: string;
|
||||
description: string;
|
||||
content: string;
|
||||
icon?: string;
|
||||
category: FeatureCategory;
|
||||
available?: boolean;
|
||||
comingSoon?: boolean;
|
||||
highlights?: string[];
|
||||
learnMoreUrl?: string;
|
||||
}
|
||||
|
||||
export interface KeyboardShortcut {
|
||||
shortcut: string;
|
||||
action: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface ShortcutsItem extends BaseContentItem {
|
||||
category: ShortcutCategory;
|
||||
title?: string;
|
||||
shortcuts: KeyboardShortcut[];
|
||||
}
|
||||
|
||||
export interface GuideStep {
|
||||
title: string;
|
||||
content: string;
|
||||
duration?: string;
|
||||
}
|
||||
|
||||
export interface GettingStartedItem extends BaseContentItem {
|
||||
title: string;
|
||||
description: string;
|
||||
content: string;
|
||||
difficulty: GuideDifficulty;
|
||||
estimatedTime?: string;
|
||||
prerequisites?: string[];
|
||||
steps?: GuideStep[];
|
||||
}
|
||||
|
||||
export interface ChangelogChange {
|
||||
title: string;
|
||||
description?: string;
|
||||
category?: string;
|
||||
}
|
||||
|
||||
export interface ChangelogItem extends BaseContentItem {
|
||||
version: string;
|
||||
title: string;
|
||||
releaseDate: Date;
|
||||
type: ChangelogType;
|
||||
summary?: string;
|
||||
content: string;
|
||||
highlighted?: boolean;
|
||||
changes?: {
|
||||
features?: ChangelogChange[];
|
||||
improvements?: ChangelogChange[];
|
||||
bugfixes?: ChangelogChange[];
|
||||
};
|
||||
platforms?: string[];
|
||||
}
|
||||
|
||||
export interface ContactInfo extends BaseContentItem {
|
||||
title: string;
|
||||
content: string;
|
||||
supportEmail?: string;
|
||||
supportUrl?: string;
|
||||
discordUrl?: string;
|
||||
twitterUrl?: string;
|
||||
documentationUrl?: string;
|
||||
responseTime?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Aggregated Content Types
|
||||
// ============================================================================
|
||||
|
||||
export interface HelpContent {
|
||||
faq: FAQItem[];
|
||||
features: FeatureItem[];
|
||||
shortcuts: ShortcutsItem[];
|
||||
gettingStarted: GettingStartedItem[];
|
||||
changelog: ChangelogItem[];
|
||||
contact: ContactInfo | null;
|
||||
}
|
||||
|
||||
export interface AppHelpContent {
|
||||
appId: string;
|
||||
appName: string;
|
||||
content: HelpContent;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Configuration Types
|
||||
// ============================================================================
|
||||
|
||||
export interface HelpContentConfig {
|
||||
appId: string;
|
||||
locale: SupportedLanguage;
|
||||
fallbackLocale?: SupportedLanguage;
|
||||
includeAppSpecific?: boolean;
|
||||
}
|
||||
|
||||
export interface MergeContentOptions {
|
||||
appId: string;
|
||||
locale: SupportedLanguage;
|
||||
/** If true, app-specific content replaces central content with same ID */
|
||||
overrideById?: boolean;
|
||||
}
|
||||
13
packages/shared-help-types/src/index.ts
Normal file
13
packages/shared-help-types/src/index.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* @manacore/shared-help-types
|
||||
* Shared TypeScript types and Zod schemas for Help content
|
||||
*/
|
||||
|
||||
// Content types
|
||||
export * from './content.js';
|
||||
|
||||
// Zod schemas for validation
|
||||
export * from './schemas.js';
|
||||
|
||||
// Search types
|
||||
export * from './search.js';
|
||||
130
packages/shared-help-types/src/schemas.ts
Normal file
130
packages/shared-help-types/src/schemas.ts
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/**
|
||||
* Zod Schemas for Help Content Validation
|
||||
* Used to validate Markdown frontmatter
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
// ============================================================================
|
||||
// Base Schemas
|
||||
// ============================================================================
|
||||
|
||||
export const supportedLanguageSchema = z.enum(['en', 'de', 'fr', 'it', 'es']);
|
||||
|
||||
export const faqCategorySchema = z.enum([
|
||||
'general',
|
||||
'account',
|
||||
'billing',
|
||||
'features',
|
||||
'technical',
|
||||
'privacy',
|
||||
]);
|
||||
|
||||
export const featureCategorySchema = z.enum(['getting-started', 'core', 'advanced', 'integration']);
|
||||
|
||||
export const guideDifficultySchema = z.enum(['beginner', 'intermediate', 'advanced']);
|
||||
|
||||
export const changelogTypeSchema = z.enum(['major', 'minor', 'patch', 'beta']);
|
||||
|
||||
export const shortcutCategorySchema = z.enum(['navigation', 'editing', 'general', 'app-specific']);
|
||||
|
||||
// ============================================================================
|
||||
// Content Item Schemas (for Frontmatter)
|
||||
// ============================================================================
|
||||
|
||||
const baseContentSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
language: supportedLanguageSchema,
|
||||
order: z.number().optional().default(0),
|
||||
appSpecific: z.boolean().optional().default(false),
|
||||
apps: z.array(z.string()).optional().default([]),
|
||||
lastUpdated: z.coerce.date().optional(),
|
||||
});
|
||||
|
||||
export const faqFrontmatterSchema = baseContentSchema.extend({
|
||||
question: z.string().min(1),
|
||||
category: faqCategorySchema,
|
||||
featured: z.boolean().optional().default(false),
|
||||
tags: z.array(z.string()).optional().default([]),
|
||||
relatedFaqs: z.array(z.string()).optional().default([]),
|
||||
});
|
||||
|
||||
export const featureFrontmatterSchema = baseContentSchema.extend({
|
||||
title: z.string().min(1),
|
||||
description: z.string().min(1),
|
||||
icon: z.string().optional(),
|
||||
category: featureCategorySchema,
|
||||
available: z.boolean().optional().default(true),
|
||||
comingSoon: z.boolean().optional().default(false),
|
||||
highlights: z.array(z.string()).optional().default([]),
|
||||
learnMoreUrl: z.string().url().optional(),
|
||||
});
|
||||
|
||||
export const shortcutSchema = z.object({
|
||||
shortcut: z.string().min(1),
|
||||
action: z.string().min(1),
|
||||
description: z.string().optional(),
|
||||
});
|
||||
|
||||
export const shortcutsFrontmatterSchema = baseContentSchema.extend({
|
||||
category: shortcutCategorySchema,
|
||||
title: z.string().optional(),
|
||||
});
|
||||
|
||||
export const guideStepSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
content: z.string().min(1),
|
||||
duration: z.string().optional(),
|
||||
});
|
||||
|
||||
export const gettingStartedFrontmatterSchema = baseContentSchema.extend({
|
||||
title: z.string().min(1),
|
||||
description: z.string().min(1),
|
||||
difficulty: guideDifficultySchema,
|
||||
estimatedTime: z.string().optional(),
|
||||
prerequisites: z.array(z.string()).optional().default([]),
|
||||
});
|
||||
|
||||
export const changelogChangeSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
description: z.string().optional(),
|
||||
category: z.string().optional(),
|
||||
});
|
||||
|
||||
export const changelogFrontmatterSchema = baseContentSchema.extend({
|
||||
version: z.string().min(1),
|
||||
title: z.string().min(1),
|
||||
releaseDate: z.coerce.date(),
|
||||
type: changelogTypeSchema,
|
||||
summary: z.string().optional(),
|
||||
highlighted: z.boolean().optional().default(false),
|
||||
changes: z
|
||||
.object({
|
||||
features: z.array(changelogChangeSchema).optional(),
|
||||
improvements: z.array(changelogChangeSchema).optional(),
|
||||
bugfixes: z.array(changelogChangeSchema).optional(),
|
||||
})
|
||||
.optional(),
|
||||
platforms: z.array(z.string()).optional().default(['all']),
|
||||
});
|
||||
|
||||
export const contactFrontmatterSchema = baseContentSchema.extend({
|
||||
title: z.string().min(1),
|
||||
supportEmail: z.string().email().optional(),
|
||||
supportUrl: z.string().url().optional(),
|
||||
discordUrl: z.string().url().optional(),
|
||||
twitterUrl: z.string().url().optional(),
|
||||
documentationUrl: z.string().url().optional(),
|
||||
responseTime: z.string().optional(),
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Type Exports from Schemas
|
||||
// ============================================================================
|
||||
|
||||
export type FAQFrontmatter = z.infer<typeof faqFrontmatterSchema>;
|
||||
export type FeatureFrontmatter = z.infer<typeof featureFrontmatterSchema>;
|
||||
export type ShortcutsFrontmatter = z.infer<typeof shortcutsFrontmatterSchema>;
|
||||
export type GettingStartedFrontmatter = z.infer<typeof gettingStartedFrontmatterSchema>;
|
||||
export type ChangelogFrontmatter = z.infer<typeof changelogFrontmatterSchema>;
|
||||
export type ContactFrontmatter = z.infer<typeof contactFrontmatterSchema>;
|
||||
71
packages/shared-help-types/src/search.ts
Normal file
71
packages/shared-help-types/src/search.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* Search-related Type Definitions
|
||||
*/
|
||||
|
||||
import type { FAQItem, FeatureItem, GettingStartedItem, ChangelogItem } from './content.js';
|
||||
|
||||
// ============================================================================
|
||||
// Searchable Item Types
|
||||
// ============================================================================
|
||||
|
||||
export type SearchableContentType = 'faq' | 'feature' | 'guide' | 'changelog';
|
||||
|
||||
export interface SearchableItem {
|
||||
id: string;
|
||||
type: SearchableContentType;
|
||||
title: string;
|
||||
content: string;
|
||||
tags?: string[];
|
||||
question?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Search Result Types
|
||||
// ============================================================================
|
||||
|
||||
export interface SearchResult {
|
||||
id: string;
|
||||
type: SearchableContentType;
|
||||
title: string;
|
||||
excerpt: string;
|
||||
score: number;
|
||||
highlight?: string;
|
||||
/** Original item reference */
|
||||
item: FAQItem | FeatureItem | GettingStartedItem | ChangelogItem;
|
||||
}
|
||||
|
||||
export interface SearchOptions {
|
||||
/** Maximum number of results to return */
|
||||
limit?: number;
|
||||
/** Minimum score threshold (0-1, lower is more strict) */
|
||||
threshold?: number;
|
||||
/** Filter by content type */
|
||||
types?: SearchableContentType[];
|
||||
/** Filter by app ID (for app-specific content) */
|
||||
appId?: string;
|
||||
}
|
||||
|
||||
export interface SearchIndexConfig {
|
||||
/** Weight for title/question field */
|
||||
titleWeight?: number;
|
||||
/** Weight for content field */
|
||||
contentWeight?: number;
|
||||
/** Weight for tags field */
|
||||
tagsWeight?: number;
|
||||
/** Fuzzy match threshold (0-1, lower is more strict) */
|
||||
threshold?: number;
|
||||
/** Minimum characters to start searching */
|
||||
minMatchCharLength?: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Search State Types (for UI)
|
||||
// ============================================================================
|
||||
|
||||
export interface SearchState {
|
||||
query: string;
|
||||
results: SearchResult[];
|
||||
isSearching: boolean;
|
||||
hasSearched: boolean;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue