@umituz/web-dashboard 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@umituz/web-dashboard",
3
- "version": "3.0.0",
4
- "description": "Dashboard Layout System - Comprehensive analytics services, customizable layouts, and config-based architecture",
3
+ "version": "3.1.0",
4
+ "description": "Dashboard Layout System - Comprehensive analytics, calendar, customizable layouts, and config-based architecture",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
7
7
  "sideEffects": false,
@@ -34,6 +34,11 @@
34
34
  "./analytics/utils": "./src/domains/analytics/utils/index.ts",
35
35
  "./analytics/types": "./src/domains/analytics/types/index.ts",
36
36
  "./analytics/services": "./src/domains/analytics/services/index.ts",
37
+ "./calendar": "./src/domains/calendar/index.ts",
38
+ "./calendar/types": "./src/domains/calendar/types/calendar.types",
39
+ "./calendar/services": "./src/domains/calendar/services/index.ts",
40
+ "./calendar/hooks": "./src/domains/calendar/hooks/index.ts",
41
+ "./calendar/utils": "./src/domains/calendar/utils/index.ts",
37
42
  "./config": "./src/domain/config/index.ts",
38
43
  "./billing": "./src/domains/billing/index.ts",
39
44
  "./billing/components": "./src/domains/billing/components/index.ts",
@@ -94,6 +99,11 @@
94
99
  "behavior-prediction",
95
100
  "performance-monitoring",
96
101
  "realtime-metrics",
102
+ "calendar",
103
+ "content-calendar",
104
+ "scheduler",
105
+ "content-planning",
106
+ "drag-drop",
97
107
  "charts",
98
108
  "metrics",
99
109
  "kpi",
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Calendar Configuration
3
+ */
4
+
5
+ import type { CalendarConfig } from '../../domains/calendar/types/calendar.types';
6
+
7
+ /**
8
+ * Default calendar configuration
9
+ */
10
+ export const DEFAULT_CALENDAR_CONFIG: CalendarConfig = {
11
+ defaultView: 'month',
12
+ showWeekends: true,
13
+ startOfWeek: 0,
14
+ hourRange: {
15
+ start: 0,
16
+ end: 23,
17
+ },
18
+ enableDragDrop: true,
19
+ showPlatformFilters: true,
20
+ platforms: ['instagram', 'facebook', 'twitter', 'linkedin', 'tiktok', 'youtube'],
21
+ slotDuration: 30,
22
+ };
@@ -3,3 +3,4 @@
3
3
  */
4
4
 
5
5
  export * from './DashboardConfig';
6
+ export * from './CalendarConfig';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Calendar Components Index
3
+ */
4
+
5
+ // Placeholder - components will be added in next iteration
6
+ // For now, types, services, hooks, and utils are sufficient
7
+ export {} from './index';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Calendar Hooks Index
3
+ */
4
+
5
+ export * from './useCalendar';
@@ -0,0 +1,232 @@
1
+ /**
2
+ * useCalendar Hook
3
+ *
4
+ * React hook for calendar functionality with config support
5
+ */
6
+
7
+ import { useState, useCallback, useEffect, useMemo } from 'react';
8
+ import type { CalendarConfig, ContentItem, CalendarFilter } from '../types/calendar.types';
9
+ import { calendarService } from '../services';
10
+ import { DEFAULT_CALENDAR_CONFIG } from '../utils';
11
+
12
+ interface UseCalendarOptions {
13
+ /** Calendar configuration */
14
+ config?: Partial<CalendarConfig>;
15
+ /** User ID */
16
+ userId: string;
17
+ /** Error callback */
18
+ onError?: (error: Error) => void;
19
+ }
20
+
21
+ interface UseCalendarReturn {
22
+ // Data
23
+ items: ContentItem[];
24
+ loading: boolean;
25
+ error: string | null;
26
+ currentView: CalendarView;
27
+ currentDate: Date;
28
+ selectedDate: Date;
29
+ filter: CalendarFilter;
30
+
31
+ // Actions
32
+ setCurrentView: (view: CalendarView) => void;
33
+ setCurrentDate: (date: Date) => void;
34
+ setSelectedDate: (date: Date) => void;
35
+ setFilter: (filter: Partial<CalendarFilter>) => void;
36
+ refresh: () => Promise<void>;
37
+
38
+ // CRUD
39
+ createItem: (item: Omit<ContentItem, 'id' | 'created_at' | 'updated_at'>) => Promise<ContentItem>;
40
+ updateItem: (id: string, updates: Partial<ContentItem>) => Promise<void>;
41
+ deleteItem: (id: string) => Promise<void>;
42
+ moveItem: (id: string, newDate: Date) => Promise<void>;
43
+
44
+ // Computed
45
+ filteredItems: ContentItem[];
46
+ itemsForDate: (date: Date) => ContentItem[];
47
+ }
48
+
49
+ type CalendarView = 'month' | 'week' | 'day' | 'timeline';
50
+
51
+ /**
52
+ * useCalendar hook
53
+ *
54
+ * Manages calendar state and operations with config support
55
+ */
56
+ export function useCalendar(options: UseCalendarOptions): UseCalendarReturn {
57
+ const { config: userConfig, userId, onError } = options;
58
+
59
+ // Merge config with defaults
60
+ const config = useMemo(() => ({
61
+ ...DEFAULT_CALENDAR_CONFIG,
62
+ ...userConfig,
63
+ }), [userConfig]);
64
+
65
+ // State
66
+ const [items, setItems] = useState<ContentItem[]>([]);
67
+ const [loading, setLoading] = useState(false);
68
+ const [error, setError] = useState<string | null>(null);
69
+ const [currentView, setCurrentView] = useState<CalendarView>(config.defaultView || 'month');
70
+ const [currentDate, setCurrentDate] = useState(new Date());
71
+ const [selectedDate, setSelectedDate] = useState(new Date());
72
+ const [filter, setFilter] = useState<CalendarFilter>({});
73
+
74
+ // Fetch items
75
+ const refresh = useCallback(async () => {
76
+ if (!userId) return;
77
+
78
+ setLoading(true);
79
+ setError(null);
80
+
81
+ try {
82
+ const data = await calendarService.getContentItems(userId, filter);
83
+ setItems(data);
84
+ } catch (err) {
85
+ const errorMessage = err instanceof Error ? err.message : 'Failed to fetch calendar items';
86
+ setError(errorMessage);
87
+ onError?.(err as Error);
88
+ } finally {
89
+ setLoading(false);
90
+ }
91
+ }, [userId, filter, onError]);
92
+
93
+ // Create item
94
+ const createItem = useCallback(async (item: Omit<ContentItem, 'id' | 'created_at' | 'updated_at'>): Promise<ContentItem> => {
95
+ if (!userId) {
96
+ throw new Error('User ID is required');
97
+ }
98
+
99
+ try {
100
+ const created = await calendarService.createContentItem(userId, item);
101
+ await refresh();
102
+ return created;
103
+ } catch (err) {
104
+ onError?.(err as Error);
105
+ throw err;
106
+ }
107
+ }, [userId, refresh, onError]);
108
+
109
+ // Update item
110
+ const updateItem = useCallback(async (id: string, updates: Partial<ContentItem>): Promise<void> => {
111
+ try {
112
+ await calendarService.updateContentItem(id, updates);
113
+ await refresh();
114
+ } catch (err) {
115
+ onError?.(err as Error);
116
+ throw err;
117
+ }
118
+ }, [refresh, onError]);
119
+
120
+ // Delete item
121
+ const deleteItem = useCallback(async (id: string): Promise<void> => {
122
+ try {
123
+ await calendarService.deleteContentItem(id);
124
+ await refresh();
125
+ } catch (err) {
126
+ onError?.(err as Error);
127
+ throw err;
128
+ }
129
+ }, [refresh, onError]);
130
+
131
+ // Move item
132
+ const moveItem = useCallback(async (id: string, newDate: Date): Promise<void> => {
133
+ try {
134
+ await calendarService.moveContentItem(id, newDate);
135
+ await refresh();
136
+ } catch (err) {
137
+ onError?.(err as Error);
138
+ throw err;
139
+ }
140
+ }, [refresh, onError]);
141
+
142
+ // Filter update
143
+ const updateFilter = useCallback((updates: Partial<CalendarFilter>) => {
144
+ setFilter(prev => ({ ...prev, ...updates }));
145
+ }, []);
146
+
147
+ // Get filtered items
148
+ const filteredItems = useMemo(() => {
149
+ return items.filter(item => {
150
+ // Search filter
151
+ if (filter.search && !item.title.toLowerCase().includes(filter.search.toLowerCase())) {
152
+ return false;
153
+ }
154
+
155
+ // Platform filter
156
+ if (filter.platforms && filter.platforms.length > 0) {
157
+ if (!item.platforms.some(p => filter.platforms?.includes(p))) {
158
+ return false;
159
+ }
160
+ }
161
+
162
+ // Type filter
163
+ if (filter.types && filter.types.length > 0) {
164
+ if (!item.type || !filter.types.includes(item.type)) {
165
+ return false;
166
+ }
167
+ }
168
+
169
+ // Status filter
170
+ if (filter.status && item.status !== filter.status) {
171
+ return false;
172
+ }
173
+
174
+ // Date range filter
175
+ if (filter.dateRange) {
176
+ const itemDate = new Date(item.scheduled_at);
177
+ if (itemDate < filter.dateRange.start || itemDate > filter.dateRange.end) {
178
+ return false;
179
+ }
180
+ }
181
+
182
+ return true;
183
+ });
184
+ }, [items, filter]);
185
+
186
+ // Get items for specific date
187
+ const itemsForDate = useCallback((date: Date): ContentItem[] => {
188
+ const dateStart = new Date(date);
189
+ dateStart.setHours(0, 0, 0, 0);
190
+
191
+ const dateEnd = new Date(date);
192
+ dateEnd.setHours(23, 59, 59, 999);
193
+
194
+ return filteredItems.filter(item => {
195
+ const itemDate = new Date(item.scheduled_at);
196
+ return itemDate >= dateStart && itemDate <= dateEnd;
197
+ });
198
+ }, [filteredItems]);
199
+
200
+ // Initial fetch
201
+ useEffect(() => {
202
+ refresh();
203
+ }, [refresh]);
204
+
205
+ return {
206
+ // Data
207
+ items,
208
+ loading,
209
+ error,
210
+ currentView,
211
+ currentDate,
212
+ selectedDate,
213
+ filter,
214
+
215
+ // Actions
216
+ setCurrentView,
217
+ setCurrentDate,
218
+ setSelectedDate,
219
+ setFilter: updateFilter,
220
+ refresh,
221
+
222
+ // CRUD
223
+ createItem,
224
+ updateItem,
225
+ deleteItem,
226
+ moveItem,
227
+
228
+ // Computed
229
+ filteredItems,
230
+ itemsForDate,
231
+ };
232
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Calendar Domain Index
3
+ */
4
+
5
+ export * from './types/calendar.types';
6
+ export * from './services';
7
+ export * from './hooks';
8
+ export * from './utils';
9
+ export * from './components';
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Calendar Service
3
+ *
4
+ * Firebase-based calendar service for managing content items
5
+ */
6
+
7
+ import type {
8
+ ContentItem,
9
+ CalendarFilter,
10
+ CreateContentItemParams,
11
+ UpdateContentItemParams,
12
+ ICalendarService,
13
+ } from '../types/calendar.types';
14
+
15
+ /**
16
+ * Database interface for calendar operations
17
+ * Implementations can provide different backends
18
+ */
19
+ interface ICalendarDatabase {
20
+ getItems(userId: string): Promise<ContentItem[]>;
21
+ getItemById(id: string): Promise<ContentItem | null>;
22
+ createItem(userId: string, item: CreateContentItemParams): Promise<ContentItem>;
23
+ updateItem(id: string, updates: UpdateContentItemParams): Promise<void>;
24
+ deleteItem(id: string): Promise<void>;
25
+ }
26
+
27
+ /**
28
+ * Firebase implementation of calendar database
29
+ */
30
+ class FirebaseCalendarDatabase implements ICalendarDatabase {
31
+ async getItems(userId: string): Promise<ContentItem[]> {
32
+ // Lazy load Firebase
33
+ const { collection, query, where, getDocs } = await import('firebase/firestore');
34
+ const { db } = await import('@umituz/web-firebase');
35
+
36
+ const calendarQuery = query(
37
+ collection(db as any, 'calendar_items'),
38
+ where("user_id", "==", userId)
39
+ );
40
+
41
+ const postsQuery = query(
42
+ collection(db as any, "posts"),
43
+ where("userId", "==", userId)
44
+ );
45
+
46
+ const [calendarSnap, postsSnap] = await Promise.all([
47
+ getDocs(calendarQuery),
48
+ getDocs(postsQuery)
49
+ ]);
50
+
51
+ const calendarItems = calendarSnap.docs.map((doc: any) => ({
52
+ id: doc.id,
53
+ ...doc.data()
54
+ } as ContentItem));
55
+
56
+ const postItems = postsSnap.docs.map((doc: any) => {
57
+ const data = doc.data();
58
+ return {
59
+ id: doc.id,
60
+ title: data.title || 'Untitled Post',
61
+ description: data.content || '',
62
+ scheduled_at: data.scheduledAt
63
+ ? (typeof data.scheduledAt === 'string' ? data.scheduledAt : (data.scheduledAt as any)?.toDate?.().toISOString())
64
+ : new Date().toISOString(),
65
+ platforms: data.platform ? [data.platform] : [],
66
+ app_name: data.appName || 'My App',
67
+ status: data.status || 'draft',
68
+ type: 'post'
69
+ } as ContentItem;
70
+ });
71
+
72
+ return [...calendarItems, ...postItems].sort((a, b) =>
73
+ new Date(b.scheduled_at).getTime() - new Date(a.scheduled_at).getTime()
74
+ );
75
+ }
76
+
77
+ async getItemById(id: string): Promise<ContentItem | null> {
78
+ const { doc, getDoc } = await import('firebase/firestore');
79
+ const { db } = await import('@umituz/web-firebase');
80
+
81
+ const docRef = doc(db as any, 'calendar_items', id);
82
+ const snap = await getDoc(docRef);
83
+
84
+ if (!snap.exists()) {
85
+ return null;
86
+ }
87
+
88
+ return {
89
+ id: snap.id,
90
+ ...snap.data()
91
+ } as ContentItem;
92
+ }
93
+
94
+ async createItem(userId: string, item: CreateContentItemParams): Promise<ContentItem> {
95
+ const { collection, addDoc, serverTimestamp } = await import('firebase/firestore');
96
+ const { db } = await import('@umituz/web-firebase');
97
+
98
+ const docRef = await addDoc(collection(db as any, 'calendar_items'), {
99
+ ...item,
100
+ scheduled_at: typeof item.scheduled_at === 'string' ? item.scheduled_at : item.scheduled_at.toISOString(),
101
+ user_id: userId,
102
+ created_at: serverTimestamp(),
103
+ updated_at: serverTimestamp()
104
+ });
105
+
106
+ return {
107
+ id: docRef.id,
108
+ ...item,
109
+ scheduled_at: typeof item.scheduled_at === 'string' ? item.scheduled_at : item.scheduled_at.toISOString(),
110
+ user_id: userId,
111
+ created_at: new Date().toISOString()
112
+ } as ContentItem;
113
+ }
114
+
115
+ async updateItem(id: string, updates: UpdateContentItemParams): Promise<void> {
116
+ const { doc, updateDoc, serverTimestamp } = await import('firebase/firestore');
117
+ const { db } = await import('@umituz/web-firebase');
118
+
119
+ const docRef = doc(db as any, 'calendar_items', id);
120
+
121
+ const updateData: any = { ...updates };
122
+ if (updates.scheduled_at) {
123
+ updateData.scheduled_at = typeof updates.scheduled_at === 'string'
124
+ ? updates.scheduled_at
125
+ : updates.scheduled_at.toISOString();
126
+ }
127
+
128
+ await updateDoc(docRef, {
129
+ ...updateData,
130
+ updated_at: serverTimestamp()
131
+ });
132
+ }
133
+
134
+ async deleteItem(id: string): Promise<void> {
135
+ const { doc, deleteDoc } = await import('firebase/firestore');
136
+ const { db } = await import('@umituz/web-firebase');
137
+
138
+ const docRef = doc(db as any, 'calendar_items', id);
139
+ await deleteDoc(docRef);
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Calendar Service Implementation
145
+ *
146
+ * Provides CRUD operations for calendar content items
147
+ * Uses database interface for backend abstraction
148
+ */
149
+ export class CalendarService implements ICalendarService {
150
+ private static instance: CalendarService;
151
+ private database: ICalendarDatabase;
152
+
153
+ private constructor() {
154
+ // Use Firebase implementation by default
155
+ this.database = new FirebaseCalendarDatabase();
156
+ }
157
+
158
+ public static getInstance(): CalendarService {
159
+ if (!CalendarService.instance) {
160
+ CalendarService.instance = new CalendarService();
161
+ }
162
+ return CalendarService.instance;
163
+ }
164
+
165
+ /**
166
+ * Set database implementation (for testing or custom backends)
167
+ */
168
+ setDatabase(database: ICalendarDatabase): void {
169
+ this.database = database;
170
+ }
171
+
172
+ /**
173
+ * Get all content items for a user
174
+ */
175
+ async getContentItems(userId: string, filter?: CalendarFilter): Promise<ContentItem[]> {
176
+ const items = await this.database.getItems(userId);
177
+
178
+ if (!filter) return items;
179
+
180
+ // Apply filters
181
+ return items.filter(item => {
182
+ // Search filter
183
+ if (filter.search && !item.title.toLowerCase().includes(filter.search.toLowerCase())) {
184
+ return false;
185
+ }
186
+
187
+ // Platform filter
188
+ if (filter.platforms && filter.platforms.length > 0) {
189
+ if (!item.platforms.some(p => filter.platforms?.includes(p))) {
190
+ return false;
191
+ }
192
+ }
193
+
194
+ // Type filter
195
+ if (filter.types && filter.types.length > 0) {
196
+ if (!item.type || !filter.types.includes(item.type)) {
197
+ return false;
198
+ }
199
+ }
200
+
201
+ // Status filter
202
+ if (filter.status && item.status !== filter.status) {
203
+ return false;
204
+ }
205
+
206
+ // Date range filter
207
+ if (filter.dateRange) {
208
+ const itemDate = new Date(item.scheduled_at);
209
+ if (itemDate < filter.dateRange.start || itemDate > filter.dateRange.end) {
210
+ return false;
211
+ }
212
+ }
213
+
214
+ return true;
215
+ });
216
+ }
217
+
218
+ /**
219
+ * Get a single content item by ID
220
+ */
221
+ async getContentItemById(id: string): Promise<ContentItem | null> {
222
+ return this.database.getItemById(id);
223
+ }
224
+
225
+ /**
226
+ * Create a new content item
227
+ */
228
+ async createContentItem(userId: string, item: CreateContentItemParams): Promise<ContentItem> {
229
+ return this.database.createItem(userId, item);
230
+ }
231
+
232
+ /**
233
+ * Update an existing content item
234
+ */
235
+ async updateContentItem(id: string, updates: UpdateContentItemParams): Promise<void> {
236
+ await this.database.updateItem(id, updates);
237
+ }
238
+
239
+ /**
240
+ * Delete a content item
241
+ */
242
+ async deleteContentItem(id: string): Promise<void> {
243
+ await this.database.deleteItem(id);
244
+ }
245
+
246
+ /**
247
+ * Move content item to a new date
248
+ */
249
+ async moveContentItem(id: string, newDate: Date): Promise<void> {
250
+ await this.updateContentItem(id, {
251
+ scheduled_at: newDate
252
+ });
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Singleton instance
258
+ */
259
+ export const calendarService = CalendarService.getInstance();
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Calendar Services Index
3
+ */
4
+
5
+ export * from './CalendarService';
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Calendar Domain Types
3
+ */
4
+
5
+ /**
6
+ * Content item status
7
+ */
8
+ export type ContentStatus = 'draft' | 'scheduled' | 'published' | 'failed';
9
+
10
+ /**
11
+ * Content item type
12
+ */
13
+ export type ContentType = 'post' | 'story' | 'reel' | 'tweet' | 'article';
14
+
15
+ /**
16
+ * Calendar view type
17
+ */
18
+ export type CalendarView = 'month' | 'week' | 'day' | 'timeline';
19
+
20
+ /**
21
+ * Content item
22
+ */
23
+ export interface ContentItem {
24
+ id: string;
25
+ title: string;
26
+ description: string;
27
+ type?: ContentType;
28
+ status: ContentStatus;
29
+ scheduled_at: string;
30
+ platforms: string[];
31
+ app_name: string;
32
+ user_id?: string;
33
+ media_url?: string;
34
+ tags?: string[];
35
+ created_at?: string;
36
+ updated_at?: string;
37
+ }
38
+
39
+ /**
40
+ * Calendar event
41
+ */
42
+ export interface CalendarEvent {
43
+ id: string;
44
+ title: string;
45
+ date: Date;
46
+ type: 'content' | 'campaign' | 'reminder';
47
+ status: string;
48
+ color?: string;
49
+ }
50
+
51
+ /**
52
+ * Calendar filter
53
+ */
54
+ export interface CalendarFilter {
55
+ search?: string;
56
+ platforms?: string[];
57
+ types?: ContentType[];
58
+ status?: ContentStatus;
59
+ dateRange?: {
60
+ start: Date;
61
+ end: Date;
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Calendar configuration
67
+ */
68
+ export interface CalendarConfig {
69
+ /** Default view */
70
+ defaultView?: CalendarView;
71
+ /** Show weekends */
72
+ showWeekends?: boolean;
73
+ /** Start of week (0-6, 0 = Sunday) */
74
+ startOfWeek?: number;
75
+ /** Hour range for day/week view */
76
+ hourRange?: {
77
+ start: number; // 0-23
78
+ end: number; // 0-23
79
+ };
80
+ /** Enable drag and drop */
81
+ enableDragDrop?: boolean;
82
+ /** Show platform filters */
83
+ showPlatformFilters?: boolean;
84
+ /** Available platforms */
85
+ platforms?: string[];
86
+ /** Time slot duration in minutes */
87
+ slotDuration?: number;
88
+ }
89
+
90
+ /**
91
+ * Content item creation params
92
+ */
93
+ export interface CreateContentItemParams {
94
+ title: string;
95
+ description: string;
96
+ scheduled_at: string | Date;
97
+ platforms: string[];
98
+ app_name: string;
99
+ type?: ContentType;
100
+ status?: ContentStatus;
101
+ media_url?: string;
102
+ tags?: string[];
103
+ }
104
+
105
+ /**
106
+ * Content item update params
107
+ */
108
+ export interface UpdateContentItemParams {
109
+ title?: string;
110
+ description?: string;
111
+ scheduled_at?: string | Date;
112
+ platforms?: string[];
113
+ type?: ContentType;
114
+ status?: ContentStatus;
115
+ media_url?: string;
116
+ tags?: string[];
117
+ }
118
+
119
+ /**
120
+ * Calendar service interface
121
+ */
122
+ export interface ICalendarService {
123
+ getContentItems(userId: string, filter?: CalendarFilter): Promise<ContentItem[]>;
124
+ getContentItemById(id: string): Promise<ContentItem | null>;
125
+ createContentItem(userId: string, item: CreateContentItemParams): Promise<ContentItem>;
126
+ updateContentItem(id: string, updates: UpdateContentItemParams): Promise<void>;
127
+ deleteContentItem(id: string): Promise<void>;
128
+ moveContentItem(id: string, newDate: Date): Promise<void>;
129
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Calendar Domain Utilities
3
+ */
4
+
5
+ import type { CalendarConfig } from '../types/calendar.types';
6
+
7
+ /**
8
+ * Default calendar configuration
9
+ */
10
+ export const DEFAULT_CALENDAR_CONFIG: CalendarConfig = {
11
+ defaultView: 'month',
12
+ showWeekends: true,
13
+ startOfWeek: 0, // Sunday
14
+ hourRange: {
15
+ start: 0,
16
+ end: 23,
17
+ },
18
+ enableDragDrop: true,
19
+ showPlatformFilters: true,
20
+ platforms: ['instagram', 'facebook', 'twitter', 'linkedin', 'tiktok'],
21
+ slotDuration: 30, // 30 minutes
22
+ };
23
+
24
+ /**
25
+ * Get week dates for a given date
26
+ */
27
+ export function getWeekDates(date: Date, startOfWeek: number = 0): Date[] {
28
+ const week = [];
29
+ const current = new Date(date);
30
+
31
+ // Find the first day of the week
32
+ const day = current.getDay();
33
+ const diff = (day < startOfWeek ? 7 : 0) + day - startOfWeek;
34
+ current.setDate(current.getDate() - diff);
35
+
36
+ // Get 7 days
37
+ for (let i = 0; i < 7; i++) {
38
+ week.push(new Date(current));
39
+ current.setDate(current.getDate() + 1);
40
+ }
41
+
42
+ return week;
43
+ }
44
+
45
+ /**
46
+ * Get month days
47
+ */
48
+ export function getMonthDays(date: Date): Date[] {
49
+ const year = date.getFullYear();
50
+ const month = date.getMonth();
51
+ const days: Date[] = [];
52
+
53
+ const firstDay = new Date(year, month, 1);
54
+ const lastDay = new Date(year, month + 1, 0);
55
+
56
+ for (let d = new Date(firstDay); d <= lastDay; d.setDate(d.getDate() + 1)) {
57
+ days.push(new Date(d));
58
+ }
59
+
60
+ return days;
61
+ }
62
+
63
+ /**
64
+ * Check if date is in range
65
+ */
66
+ export function isDateInRange(date: Date, range: { start: Date; end: Date }): boolean {
67
+ return date >= range.start && date <= range.end;
68
+ }
69
+
70
+ /**
71
+ * Format date for display
72
+ */
73
+ export function formatDate(date: Date, format: 'short' | 'long' | 'time' = 'short'): string {
74
+ if (format === 'time') {
75
+ return date.toLocaleTimeString('en-US', {
76
+ hour: '2-digit',
77
+ minute: '2-digit',
78
+ });
79
+ }
80
+
81
+ return date.toLocaleDateString('en-US', {
82
+ month: format === 'long' ? 'long' : 'short',
83
+ day: 'numeric',
84
+ year: 'numeric',
85
+ });
86
+ }
87
+
88
+ /**
89
+ * Check if dates are same day
90
+ */
91
+ export function isSameDay(date1: Date, date2: Date): boolean {
92
+ return (
93
+ date1.getFullYear() === date2.getFullYear() &&
94
+ date1.getMonth() === date2.getMonth() &&
95
+ date1.getDate() === date2.getDate()
96
+ );
97
+ }
98
+
99
+ /**
100
+ * Get date range for a view
101
+ */
102
+ export function getViewRange(view: 'month' | 'week', date: Date): { start: Date; end: Date } {
103
+ if (view === 'month') {
104
+ const start = new Date(date.getFullYear(), date.getMonth(), 1);
105
+ const end = new Date(date.getFullYear(), date.getMonth() + 1, 0);
106
+ return { start, end };
107
+ }
108
+
109
+ // Week view
110
+ const week = getWeekDates(date);
111
+ return {
112
+ start: new Date(week[0].setHours(0, 0, 0, 0)),
113
+ end: new Date(week[6].setHours(23, 59, 59, 999)),
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Generate time slots
119
+ */
120
+ export function generateTimeSlots(startHour: number, endHour: number, slotDuration: number): string[] {
121
+ const slots: string[] = [];
122
+
123
+ for (let hour = startHour; hour < endHour; hour++) {
124
+ for (let minute = 0; minute < 60; minute += slotDuration) {
125
+ const time = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
126
+ slots.push(time);
127
+ }
128
+ }
129
+
130
+ return slots;
131
+ }
package/src/index.ts CHANGED
@@ -12,7 +12,11 @@ export * from './domains/layouts';
12
12
  export * from './domains/settings';
13
13
  export * from './domains/billing';
14
14
 
15
- // Auth and Onboarding with conflict resolution
15
+ // Domains - using selective exports to avoid conflicts
16
+ export * from './domains/layouts';
17
+ export * from './domains/settings';
18
+ export * from './domains/billing';
19
+ export * from './domains/calendar';
16
20
  export {
17
21
  OnboardingWizard,
18
22
  useOnboarding,
@@ -95,9 +99,37 @@ export {
95
99
  type RealtimeMetrics,
96
100
  } from './domains/analytics';
97
101
 
102
+ // Calendar
103
+ export {
104
+ useCalendar,
105
+ calendarService,
106
+ // Services
107
+ CalendarService,
108
+ // Utils
109
+ getWeekDates,
110
+ getMonthDays,
111
+ isDateInRange,
112
+ formatDate,
113
+ isSameDay,
114
+ getViewRange,
115
+ generateTimeSlots,
116
+ // Types
117
+ type ContentItem,
118
+ type CalendarEvent,
119
+ type CalendarFilter,
120
+ type CalendarConfig,
121
+ type ContentStatus,
122
+ type ContentType,
123
+ type CalendarView,
124
+ type CreateContentItemParams,
125
+ type UpdateContentItemParams,
126
+ type ICalendarService,
127
+ } from './domains/calendar';
128
+
98
129
  // Config with renamed exports to avoid conflicts
99
130
  export {
100
131
  DEFAULT_DASHBOARD_CONFIG,
132
+ DEFAULT_CALENDAR_CONFIG,
101
133
  type AnalyticsServiceType,
102
134
  type ExportFormat as DashboardExportFormat,
103
135
  type AnalyticsConfig as DashboardAnalyticsConfig,