mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-20 14:49:24 +02:00
style: auto-format codebase with Prettier
Applied formatting to 1487+ files using pnpm format:write - TypeScript/JavaScript files - Svelte components - Astro pages - JSON configs - Markdown docs 13 files still need manual review (Astro JSX comments)
This commit is contained in:
parent
0241f5554c
commit
d36b321d9d
3952 changed files with 661498 additions and 739751 deletions
|
|
@ -1,22 +1,22 @@
|
|||
{
|
||||
"name": "@quote/shared",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./types": "./src/types/index.ts",
|
||||
"./data": "./src/data/index.ts",
|
||||
"./utils": "./src/utils/index.ts",
|
||||
"./services": "./src/services/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"type-check": "tsc --noEmit",
|
||||
"lint": "eslint src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "~5.9.2"
|
||||
},
|
||||
"dependencies": {}
|
||||
"name": "@quote/shared",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./types": "./src/types/index.ts",
|
||||
"./data": "./src/data/index.ts",
|
||||
"./utils": "./src/utils/index.ts",
|
||||
"./services": "./src/services/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"type-check": "tsc --noEmit",
|
||||
"lint": "eslint src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "~5.9.2"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,96 +5,96 @@
|
|||
import type { FullAppConfig, Quote } from '../types';
|
||||
|
||||
export const quotesAppConfig: FullAppConfig<Quote> = {
|
||||
metadata: {
|
||||
name: 'quotes',
|
||||
displayName: 'Zitate',
|
||||
description: 'Inspirierende Zitate von großen Denkern',
|
||||
version: '1.0.0',
|
||||
primaryLanguage: 'de',
|
||||
supportedLanguages: ['de', 'en'],
|
||||
},
|
||||
metadata: {
|
||||
name: 'quotes',
|
||||
displayName: 'Zitate',
|
||||
description: 'Inspirierende Zitate von großen Denkern',
|
||||
version: '1.0.0',
|
||||
primaryLanguage: 'de',
|
||||
supportedLanguages: ['de', 'en'],
|
||||
},
|
||||
|
||||
contentType: 'quote',
|
||||
contentLabel: {
|
||||
singular: 'Zitat',
|
||||
plural: 'Zitate',
|
||||
},
|
||||
authorLabel: {
|
||||
singular: 'Autor',
|
||||
plural: 'Autoren',
|
||||
},
|
||||
contentType: 'quote',
|
||||
contentLabel: {
|
||||
singular: 'Zitat',
|
||||
plural: 'Zitate',
|
||||
},
|
||||
authorLabel: {
|
||||
singular: 'Autor',
|
||||
plural: 'Autoren',
|
||||
},
|
||||
|
||||
colors: {
|
||||
primary: '#667eea',
|
||||
secondary: '#764ba2',
|
||||
accent: '#f093fb',
|
||||
},
|
||||
colors: {
|
||||
primary: '#667eea',
|
||||
secondary: '#764ba2',
|
||||
accent: '#f093fb',
|
||||
},
|
||||
|
||||
features: {
|
||||
favorites: true,
|
||||
lists: true,
|
||||
sharing: true,
|
||||
search: true,
|
||||
filters: true,
|
||||
authors: true,
|
||||
categories: true,
|
||||
tags: true,
|
||||
dailyContent: true,
|
||||
notifications: true,
|
||||
widgets: true,
|
||||
cloudSync: true,
|
||||
premium: true,
|
||||
},
|
||||
features: {
|
||||
favorites: true,
|
||||
lists: true,
|
||||
sharing: true,
|
||||
search: true,
|
||||
filters: true,
|
||||
authors: true,
|
||||
categories: true,
|
||||
tags: true,
|
||||
dailyContent: true,
|
||||
notifications: true,
|
||||
widgets: true,
|
||||
cloudSync: true,
|
||||
premium: true,
|
||||
},
|
||||
|
||||
display: {
|
||||
showAuthor: true,
|
||||
showDate: false,
|
||||
showSource: true,
|
||||
showCategory: true,
|
||||
showTags: true,
|
||||
cardStyle: 'detailed',
|
||||
swipeDirection: 'horizontal',
|
||||
},
|
||||
display: {
|
||||
showAuthor: true,
|
||||
showDate: false,
|
||||
showSource: true,
|
||||
showCategory: true,
|
||||
showTags: true,
|
||||
cardStyle: 'detailed',
|
||||
swipeDirection: 'horizontal',
|
||||
},
|
||||
|
||||
navigation: {
|
||||
tabs: [
|
||||
{
|
||||
id: 'quotes',
|
||||
label: 'Zitate',
|
||||
icon: 'quote',
|
||||
route: '/(tabs)',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 'authors',
|
||||
label: 'Autoren',
|
||||
icon: 'person',
|
||||
route: '/(tabs)/authors',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 'lists',
|
||||
label: 'Listen',
|
||||
icon: 'list',
|
||||
route: '/(tabs)/liste',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 'favorites',
|
||||
label: 'Favoriten',
|
||||
icon: 'heart',
|
||||
route: '/(tabs)/myquotes',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 'search',
|
||||
label: 'Suche',
|
||||
icon: 'search',
|
||||
route: '/(tabs)/search',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
showTabBar: true,
|
||||
tabBarStyle: 'ios',
|
||||
},
|
||||
navigation: {
|
||||
tabs: [
|
||||
{
|
||||
id: 'quotes',
|
||||
label: 'Zitate',
|
||||
icon: 'quote',
|
||||
route: '/(tabs)',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 'authors',
|
||||
label: 'Autoren',
|
||||
icon: 'person',
|
||||
route: '/(tabs)/authors',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 'lists',
|
||||
label: 'Listen',
|
||||
icon: 'list',
|
||||
route: '/(tabs)/liste',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 'favorites',
|
||||
label: 'Favoriten',
|
||||
icon: 'heart',
|
||||
route: '/(tabs)/myquotes',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: 'search',
|
||||
label: 'Suche',
|
||||
icon: 'search',
|
||||
route: '/(tabs)/search',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
showTabBar: true,
|
||||
tabBarStyle: 'ios',
|
||||
},
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -15,110 +15,111 @@ export type ContentType = 'quote' | 'proverb' | 'poem' | 'speech' | 'fable' | 't
|
|||
* Color configuration for the app
|
||||
*/
|
||||
export interface AppColors {
|
||||
primary: string;
|
||||
secondary?: string;
|
||||
accent?: string;
|
||||
background?: string;
|
||||
surface?: string;
|
||||
text?: string;
|
||||
primary: string;
|
||||
secondary?: string;
|
||||
accent?: string;
|
||||
background?: string;
|
||||
surface?: string;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature flags for the app
|
||||
*/
|
||||
export interface AppFeatures {
|
||||
favorites?: boolean;
|
||||
lists?: boolean;
|
||||
sharing?: boolean;
|
||||
search?: boolean;
|
||||
filters?: boolean;
|
||||
authors?: boolean;
|
||||
categories?: boolean;
|
||||
tags?: boolean;
|
||||
dailyContent?: boolean;
|
||||
notifications?: boolean;
|
||||
widgets?: boolean;
|
||||
cloudSync?: boolean;
|
||||
premium?: boolean;
|
||||
favorites?: boolean;
|
||||
lists?: boolean;
|
||||
sharing?: boolean;
|
||||
search?: boolean;
|
||||
filters?: boolean;
|
||||
authors?: boolean;
|
||||
categories?: boolean;
|
||||
tags?: boolean;
|
||||
dailyContent?: boolean;
|
||||
notifications?: boolean;
|
||||
widgets?: boolean;
|
||||
cloudSync?: boolean;
|
||||
premium?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Content display configuration
|
||||
*/
|
||||
export interface ContentDisplayConfig {
|
||||
showAuthor?: boolean;
|
||||
showDate?: boolean;
|
||||
showSource?: boolean;
|
||||
showCategory?: boolean;
|
||||
showTags?: boolean;
|
||||
cardStyle?: 'minimal' | 'detailed' | 'magazine';
|
||||
swipeDirection?: 'horizontal' | 'vertical';
|
||||
showAuthor?: boolean;
|
||||
showDate?: boolean;
|
||||
showSource?: boolean;
|
||||
showCategory?: boolean;
|
||||
showTags?: boolean;
|
||||
cardStyle?: 'minimal' | 'detailed' | 'magazine';
|
||||
swipeDirection?: 'horizontal' | 'vertical';
|
||||
}
|
||||
|
||||
/**
|
||||
* App metadata
|
||||
*/
|
||||
export interface AppMetadata {
|
||||
name: string;
|
||||
displayName: string;
|
||||
description: string;
|
||||
version: string;
|
||||
author?: string;
|
||||
website?: string;
|
||||
icon?: string;
|
||||
primaryLanguage: string;
|
||||
supportedLanguages: string[];
|
||||
name: string;
|
||||
displayName: string;
|
||||
description: string;
|
||||
version: string;
|
||||
author?: string;
|
||||
website?: string;
|
||||
icon?: string;
|
||||
primaryLanguage: string;
|
||||
supportedLanguages: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Main App Configuration
|
||||
*/
|
||||
export interface AppConfig<TContent extends ContentItem = ContentItem> {
|
||||
// Basic Info
|
||||
metadata: AppMetadata;
|
||||
// Basic Info
|
||||
metadata: AppMetadata;
|
||||
|
||||
// Content Type
|
||||
contentType: ContentType;
|
||||
contentLabel: {
|
||||
singular: string;
|
||||
plural: string;
|
||||
};
|
||||
authorLabel?: {
|
||||
singular: string;
|
||||
plural: string;
|
||||
};
|
||||
// Content Type
|
||||
contentType: ContentType;
|
||||
contentLabel: {
|
||||
singular: string;
|
||||
plural: string;
|
||||
};
|
||||
authorLabel?: {
|
||||
singular: string;
|
||||
plural: string;
|
||||
};
|
||||
|
||||
// Branding
|
||||
colors: AppColors;
|
||||
// Branding
|
||||
colors: AppColors;
|
||||
|
||||
// Features
|
||||
features: AppFeatures;
|
||||
// Features
|
||||
features: AppFeatures;
|
||||
|
||||
// Display
|
||||
display: ContentDisplayConfig;
|
||||
// Display
|
||||
display: ContentDisplayConfig;
|
||||
|
||||
// Custom fields (app-specific)
|
||||
custom?: Record<string, any>;
|
||||
// Custom fields (app-specific)
|
||||
custom?: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigation configuration
|
||||
*/
|
||||
export interface NavigationConfig {
|
||||
tabs: {
|
||||
id: string;
|
||||
label: string;
|
||||
icon: string;
|
||||
route: string;
|
||||
enabled: boolean;
|
||||
}[];
|
||||
showTabBar?: boolean;
|
||||
tabBarStyle?: 'ios' | 'android' | 'custom';
|
||||
tabs: {
|
||||
id: string;
|
||||
label: string;
|
||||
icon: string;
|
||||
route: string;
|
||||
enabled: boolean;
|
||||
}[];
|
||||
showTabBar?: boolean;
|
||||
tabBarStyle?: 'ios' | 'android' | 'custom';
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete app configuration including navigation
|
||||
*/
|
||||
export interface FullAppConfig<TContent extends ContentItem = ContentItem> extends AppConfig<TContent> {
|
||||
navigation: NavigationConfig;
|
||||
export interface FullAppConfig<TContent extends ContentItem = ContentItem>
|
||||
extends AppConfig<TContent> {
|
||||
navigation: NavigationConfig;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,29 +14,29 @@
|
|||
* Can represent quote authors, poets, speakers, etc.
|
||||
*/
|
||||
export interface ContentAuthor {
|
||||
id: string;
|
||||
name: string;
|
||||
profession?: string[];
|
||||
biography?: {
|
||||
short: string;
|
||||
long?: string;
|
||||
sections?: { [key: string]: any };
|
||||
keyAchievements?: string[];
|
||||
famousQuote?: string;
|
||||
};
|
||||
lifespan?: {
|
||||
birth: string;
|
||||
death?: string;
|
||||
};
|
||||
verified?: boolean;
|
||||
featured?: boolean;
|
||||
imageUrl?: string;
|
||||
image?: {
|
||||
thumbnail?: string;
|
||||
full?: string;
|
||||
credit?: string;
|
||||
source?: string;
|
||||
};
|
||||
id: string;
|
||||
name: string;
|
||||
profession?: string[];
|
||||
biography?: {
|
||||
short: string;
|
||||
long?: string;
|
||||
sections?: { [key: string]: any };
|
||||
keyAchievements?: string[];
|
||||
famousQuote?: string;
|
||||
};
|
||||
lifespan?: {
|
||||
birth: string;
|
||||
death?: string;
|
||||
};
|
||||
verified?: boolean;
|
||||
featured?: boolean;
|
||||
imageUrl?: string;
|
||||
image?: {
|
||||
thumbnail?: string;
|
||||
full?: string;
|
||||
credit?: string;
|
||||
source?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -44,35 +44,35 @@ export interface ContentAuthor {
|
|||
* Base type for all content (quotes, proverbs, poems, etc.)
|
||||
*/
|
||||
export interface ContentItem<TMetadata = Record<string, any>> {
|
||||
id: string;
|
||||
text: string;
|
||||
authorId: string;
|
||||
author?: ContentAuthor;
|
||||
categories?: string[];
|
||||
tags?: string[];
|
||||
featured?: boolean;
|
||||
likes?: number;
|
||||
isFavorite?: boolean;
|
||||
language?: string;
|
||||
category?: string;
|
||||
metadata?: TMetadata;
|
||||
id: string;
|
||||
text: string;
|
||||
authorId: string;
|
||||
author?: ContentAuthor;
|
||||
categories?: string[];
|
||||
tags?: string[];
|
||||
featured?: boolean;
|
||||
likes?: number;
|
||||
isFavorite?: boolean;
|
||||
language?: string;
|
||||
category?: string;
|
||||
metadata?: TMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic Category type
|
||||
*/
|
||||
export interface ContentCategory {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic Tag type
|
||||
*/
|
||||
export interface ContentTag {
|
||||
id: string;
|
||||
name: string;
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
|
@ -83,98 +83,98 @@ export interface ContentTag {
|
|||
* Quote-specific metadata
|
||||
*/
|
||||
export interface QuoteMetadata {
|
||||
source?: string;
|
||||
year?: number;
|
||||
context?: string;
|
||||
verified?: boolean;
|
||||
source?: string;
|
||||
year?: number;
|
||||
context?: string;
|
||||
verified?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote type - extends ContentItem with quote-specific metadata
|
||||
*/
|
||||
export interface Quote extends ContentItem<QuoteMetadata> {
|
||||
source?: string;
|
||||
year?: number;
|
||||
source?: string;
|
||||
year?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proverb-specific metadata
|
||||
*/
|
||||
export interface ProverbMetadata {
|
||||
origin?: string; // e.g., "Deutsches Sprichwort", "Chinese Proverb"
|
||||
meaning?: string; // Explanation of the proverb
|
||||
variants?: string[]; // Different versions of the proverb
|
||||
relatedProverbs?: string[]; // IDs of related proverbs
|
||||
origin?: string; // e.g., "Deutsches Sprichwort", "Chinese Proverb"
|
||||
meaning?: string; // Explanation of the proverb
|
||||
variants?: string[]; // Different versions of the proverb
|
||||
relatedProverbs?: string[]; // IDs of related proverbs
|
||||
}
|
||||
|
||||
/**
|
||||
* Proverb type - extends ContentItem with proverb-specific metadata
|
||||
*/
|
||||
export interface Proverb extends ContentItem<ProverbMetadata> {
|
||||
origin?: string;
|
||||
meaning?: string;
|
||||
variants?: string[];
|
||||
origin?: string;
|
||||
meaning?: string;
|
||||
variants?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Poem-specific metadata
|
||||
*/
|
||||
export interface PoemMetadata {
|
||||
verses?: string[]; // Individual stanzas/verses
|
||||
rhymeScheme?: string; // e.g., "ABAB", "AABB"
|
||||
form?: string; // e.g., "Sonnet", "Haiku"
|
||||
firstPublished?: number;
|
||||
collection?: string; // Name of the poetry collection
|
||||
verses?: string[]; // Individual stanzas/verses
|
||||
rhymeScheme?: string; // e.g., "ABAB", "AABB"
|
||||
form?: string; // e.g., "Sonnet", "Haiku"
|
||||
firstPublished?: number;
|
||||
collection?: string; // Name of the poetry collection
|
||||
}
|
||||
|
||||
/**
|
||||
* Poem type - extends ContentItem with poem-specific metadata
|
||||
*/
|
||||
export interface Poem extends ContentItem<PoemMetadata> {
|
||||
verses?: string[];
|
||||
rhymeScheme?: string;
|
||||
form?: string;
|
||||
verses?: string[];
|
||||
rhymeScheme?: string;
|
||||
form?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Speech-specific metadata
|
||||
*/
|
||||
export interface SpeechMetadata {
|
||||
date?: string; // Date of the speech
|
||||
location?: string; // Where it was given
|
||||
occasion?: string; // Context/occasion
|
||||
audience?: string; // Who it was for
|
||||
duration?: number; // Length in minutes
|
||||
videoUrl?: string; // Link to video if available
|
||||
date?: string; // Date of the speech
|
||||
location?: string; // Where it was given
|
||||
occasion?: string; // Context/occasion
|
||||
audience?: string; // Who it was for
|
||||
duration?: number; // Length in minutes
|
||||
videoUrl?: string; // Link to video if available
|
||||
}
|
||||
|
||||
/**
|
||||
* Historical Speech type
|
||||
*/
|
||||
export interface Speech extends ContentItem<SpeechMetadata> {
|
||||
date?: string;
|
||||
location?: string;
|
||||
occasion?: string;
|
||||
date?: string;
|
||||
location?: string;
|
||||
occasion?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fable-specific metadata
|
||||
*/
|
||||
export interface FableMetadata {
|
||||
moral?: string; // The moral/lesson of the fable
|
||||
characters?: string[]; // Main characters (e.g., "Rabe", "Fuchs")
|
||||
setting?: string; // Where the fable takes place
|
||||
length?: 'short' | 'medium' | 'long'; // Length classification
|
||||
collection?: string; // Collection name if part of one
|
||||
moral?: string; // The moral/lesson of the fable
|
||||
characters?: string[]; // Main characters (e.g., "Rabe", "Fuchs")
|
||||
setting?: string; // Where the fable takes place
|
||||
length?: 'short' | 'medium' | 'long'; // Length classification
|
||||
collection?: string; // Collection name if part of one
|
||||
}
|
||||
|
||||
/**
|
||||
* Fable type - extends ContentItem with fable-specific metadata
|
||||
*/
|
||||
export interface Fable extends ContentItem<FableMetadata> {
|
||||
moral?: string;
|
||||
characters?: string[];
|
||||
setting?: string;
|
||||
moral?: string;
|
||||
characters?: string[];
|
||||
setting?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -14,16 +14,16 @@ import type { ContentItem, ContentAuthor } from '../types';
|
|||
* Format author/creator name
|
||||
*/
|
||||
export function formatAuthorName(author: ContentAuthor): string {
|
||||
return author.name;
|
||||
return author.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get author/creator lifespan string
|
||||
*/
|
||||
export function getAuthorLifespan(author: ContentAuthor): string {
|
||||
if (!author.lifespan) return '';
|
||||
const { birth, death } = author.lifespan;
|
||||
return death ? `${birth} - ${death}` : `${birth} - Present`;
|
||||
if (!author.lifespan) return '';
|
||||
const { birth, death } = author.lifespan;
|
||||
return death ? `${birth} - ${death}` : `${birth} - Present`;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
|
@ -33,124 +33,98 @@ export function getAuthorLifespan(author: ContentAuthor): string {
|
|||
/**
|
||||
* Filter content items by category
|
||||
*/
|
||||
export function filterContentByCategory<T extends ContentItem>(
|
||||
items: T[],
|
||||
category: string
|
||||
): T[] {
|
||||
return items.filter(item =>
|
||||
item.category === category ||
|
||||
item.categories?.includes(category)
|
||||
);
|
||||
export function filterContentByCategory<T extends ContentItem>(items: T[], category: string): T[] {
|
||||
return items.filter((item) => item.category === category || item.categories?.includes(category));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter content items by tag
|
||||
*/
|
||||
export function filterContentByTag<T extends ContentItem>(
|
||||
items: T[],
|
||||
tag: string
|
||||
): T[] {
|
||||
return items.filter(item => item.tags?.includes(tag));
|
||||
export function filterContentByTag<T extends ContentItem>(items: T[], tag: string): T[] {
|
||||
return items.filter((item) => item.tags?.includes(tag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter content items by author
|
||||
*/
|
||||
export function filterContentByAuthor<T extends ContentItem>(
|
||||
items: T[],
|
||||
authorId: string
|
||||
): T[] {
|
||||
return items.filter(item => item.authorId === authorId);
|
||||
export function filterContentByAuthor<T extends ContentItem>(items: T[], authorId: string): T[] {
|
||||
return items.filter((item) => item.authorId === authorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get random content item from array
|
||||
*/
|
||||
export function getRandomContent<T extends ContentItem>(
|
||||
items: T[]
|
||||
): T | undefined {
|
||||
if (items.length === 0) return undefined;
|
||||
return items[Math.floor(Math.random() * items.length)];
|
||||
export function getRandomContent<T extends ContentItem>(items: T[]): T | undefined {
|
||||
if (items.length === 0) return undefined;
|
||||
return items[Math.floor(Math.random() * items.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Search content items by text
|
||||
*/
|
||||
export function searchContent<T extends ContentItem>(
|
||||
items: T[],
|
||||
searchTerm: string
|
||||
): T[] {
|
||||
const term = searchTerm.toLowerCase();
|
||||
return items.filter(item =>
|
||||
item.text.toLowerCase().includes(term) ||
|
||||
item.tags?.some(tag => tag.toLowerCase().includes(term))
|
||||
);
|
||||
export function searchContent<T extends ContentItem>(items: T[], searchTerm: string): T[] {
|
||||
const term = searchTerm.toLowerCase();
|
||||
return items.filter(
|
||||
(item) =>
|
||||
item.text.toLowerCase().includes(term) ||
|
||||
item.tags?.some((tag) => tag.toLowerCase().includes(term))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unique categories from content items
|
||||
*/
|
||||
export function getUniqueCategories<T extends ContentItem>(
|
||||
items: T[]
|
||||
): string[] {
|
||||
const categories = new Set<string>();
|
||||
items.forEach(item => {
|
||||
if (item.category) categories.add(item.category);
|
||||
item.categories?.forEach(cat => categories.add(cat));
|
||||
});
|
||||
return Array.from(categories).sort();
|
||||
export function getUniqueCategories<T extends ContentItem>(items: T[]): string[] {
|
||||
const categories = new Set<string>();
|
||||
items.forEach((item) => {
|
||||
if (item.category) categories.add(item.category);
|
||||
item.categories?.forEach((cat) => categories.add(cat));
|
||||
});
|
||||
return Array.from(categories).sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unique tags from content items
|
||||
*/
|
||||
export function getUniqueTags<T extends ContentItem>(
|
||||
items: T[]
|
||||
): string[] {
|
||||
const tags = new Set<string>();
|
||||
items.forEach(item => {
|
||||
item.tags?.forEach(tag => tags.add(tag));
|
||||
});
|
||||
return Array.from(tags).sort();
|
||||
export function getUniqueTags<T extends ContentItem>(items: T[]): string[] {
|
||||
const tags = new Set<string>();
|
||||
items.forEach((item) => {
|
||||
item.tags?.forEach((tag) => tags.add(tag));
|
||||
});
|
||||
return Array.from(tags).sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Group content items by category
|
||||
*/
|
||||
export function groupContentByCategory<T extends ContentItem>(
|
||||
items: T[]
|
||||
): Record<string, T[]> {
|
||||
const grouped: Record<string, T[]> = {};
|
||||
export function groupContentByCategory<T extends ContentItem>(items: T[]): Record<string, T[]> {
|
||||
const grouped: Record<string, T[]> = {};
|
||||
|
||||
items.forEach(item => {
|
||||
const categories = item.categories || (item.category ? [item.category] : ['uncategorized']);
|
||||
categories.forEach(category => {
|
||||
if (!grouped[category]) {
|
||||
grouped[category] = [];
|
||||
}
|
||||
grouped[category].push(item);
|
||||
});
|
||||
});
|
||||
items.forEach((item) => {
|
||||
const categories = item.categories || (item.category ? [item.category] : ['uncategorized']);
|
||||
categories.forEach((category) => {
|
||||
if (!grouped[category]) {
|
||||
grouped[category] = [];
|
||||
}
|
||||
grouped[category].push(item);
|
||||
});
|
||||
});
|
||||
|
||||
return grouped;
|
||||
return grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get featured content items
|
||||
*/
|
||||
export function getFeaturedContent<T extends ContentItem>(
|
||||
items: T[]
|
||||
): T[] {
|
||||
return items.filter(item => item.featured === true);
|
||||
export function getFeaturedContent<T extends ContentItem>(items: T[]): T[] {
|
||||
return items.filter((item) => item.featured === true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get favorite content items
|
||||
*/
|
||||
export function getFavoriteContent<T extends ContentItem>(
|
||||
items: T[]
|
||||
): T[] {
|
||||
return items.filter(item => item.isFavorite === true);
|
||||
export function getFavoriteContent<T extends ContentItem>(items: T[]): T[] {
|
||||
return items.filter((item) => item.isFavorite === true);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2022"],
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2022"],
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue