@umituz/react-native-design-system 2.3.12 → 2.3.14

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.
Files changed (81) hide show
  1. package/package.json +14 -12
  2. package/src/atoms/AtomicAvatar.tsx +1 -1
  3. package/src/atoms/AtomicBadge.tsx +1 -1
  4. package/src/atoms/AtomicButton.tsx +1 -1
  5. package/src/atoms/AtomicCard.tsx +1 -1
  6. package/src/atoms/AtomicChip.tsx +1 -1
  7. package/src/atoms/AtomicDatePicker.tsx +1 -1
  8. package/src/atoms/AtomicFab.tsx +2 -2
  9. package/src/atoms/AtomicIcon.tsx +1 -1
  10. package/src/atoms/AtomicInput.tsx +1 -1
  11. package/src/atoms/AtomicPicker.tsx +1 -1
  12. package/src/atoms/AtomicProgress.tsx +1 -1
  13. package/src/atoms/AtomicText.tsx +3 -3
  14. package/src/atoms/datepicker/components/DatePickerButton.tsx +1 -1
  15. package/src/atoms/datepicker/components/DatePickerModal.tsx +1 -1
  16. package/src/atoms/input/styles/inputStylesHelper.ts +1 -1
  17. package/src/atoms/picker/components/PickerChips.tsx +1 -1
  18. package/src/atoms/picker/components/PickerModal.tsx +1 -1
  19. package/src/atoms/picker/styles/pickerStyles.ts +1 -1
  20. package/src/atoms/skeleton/AtomicSkeleton.tsx +1 -1
  21. package/src/device/detection/deviceDetection.ts +2 -2
  22. package/src/device/detection/iPadDetection.ts +1 -1
  23. package/src/device/detection/iPadLayoutUtils.ts +1 -1
  24. package/src/index.ts +25 -0
  25. package/src/layouts/AppHeader/AppHeader.tsx +2 -2
  26. package/src/layouts/Container/Container.tsx +2 -2
  27. package/src/layouts/FormLayout/FormLayout.tsx +2 -2
  28. package/src/layouts/Grid/Grid.tsx +2 -2
  29. package/src/layouts/ScreenHeader/ScreenHeader.tsx +2 -2
  30. package/src/layouts/ScreenLayout/ScreenLayout.tsx +3 -3
  31. package/src/molecules/BaseModal.tsx +2 -2
  32. package/src/molecules/ConfirmationModalContent.tsx +1 -1
  33. package/src/molecules/ConfirmationModalMain.tsx +1 -1
  34. package/src/molecules/Divider/Divider.tsx +2 -2
  35. package/src/molecules/FormField.tsx +2 -2
  36. package/src/molecules/IconContainer.tsx +1 -1
  37. package/src/molecules/List/List.tsx +1 -1
  38. package/src/molecules/ListItem.tsx +2 -2
  39. package/src/molecules/SearchBar/SearchBar.tsx +2 -2
  40. package/src/molecules/SearchBar/SearchHistory.tsx +3 -3
  41. package/src/molecules/SearchBar/SearchSuggestions.tsx +2 -2
  42. package/src/molecules/StepProgress/StepProgress.tsx +1 -1
  43. package/src/molecules/alerts/AlertBanner.tsx +2 -2
  44. package/src/molecules/alerts/AlertContainer.tsx +1 -1
  45. package/src/molecules/alerts/AlertInline.tsx +2 -2
  46. package/src/molecules/alerts/AlertModal.tsx +2 -2
  47. package/src/molecules/alerts/AlertToast.tsx +2 -2
  48. package/src/molecules/avatar/Avatar.tsx +2 -2
  49. package/src/molecules/avatar/AvatarGroup.tsx +2 -2
  50. package/src/molecules/bottom-sheet/components/BottomSheet.tsx +1 -1
  51. package/src/molecules/bottom-sheet/components/BottomSheetModal.tsx +1 -1
  52. package/src/molecules/bottom-sheet/components/filter/FilterBottomSheet.tsx +2 -2
  53. package/src/molecules/bottom-sheet/components/filter/FilterSheet.tsx +2 -2
  54. package/src/molecules/bottom-sheet/components/filter/FilterSheetComponents/FilterSheetHeader.tsx +2 -2
  55. package/src/molecules/bottom-sheet/components/filter/FilterSheetComponents/FilterSheetOption.tsx +2 -2
  56. package/src/molecules/calendar/domain/entities/CalendarDay.entity.ts +115 -0
  57. package/src/molecules/calendar/domain/entities/CalendarEvent.entity.ts +202 -0
  58. package/src/molecules/calendar/domain/repositories/ICalendarRepository.ts +120 -0
  59. package/src/molecules/calendar/index.ts +98 -0
  60. package/src/molecules/calendar/infrastructure/services/CalendarEvents.ts +196 -0
  61. package/src/molecules/calendar/infrastructure/services/CalendarGeneration.ts +172 -0
  62. package/src/molecules/calendar/infrastructure/services/CalendarPermissions.ts +92 -0
  63. package/src/molecules/calendar/infrastructure/services/CalendarService.ts +161 -0
  64. package/src/molecules/calendar/infrastructure/services/CalendarSync.ts +205 -0
  65. package/src/molecules/calendar/infrastructure/storage/CalendarStore.ts +307 -0
  66. package/src/molecules/calendar/infrastructure/utils/DateUtilities.ts +128 -0
  67. package/src/molecules/calendar/presentation/components/AtomicCalendar.tsx +279 -0
  68. package/src/molecules/calendar/presentation/hooks/useCalendar.ts +356 -0
  69. package/src/molecules/confirmation-modal/components.tsx +2 -2
  70. package/src/molecules/confirmation-modal/styles/confirmationModalStyles.ts +1 -1
  71. package/src/molecules/index.ts +3 -0
  72. package/src/molecules/listitem/styles/listItemStyles.ts +1 -1
  73. package/src/organisms/FormContainer.tsx +2 -2
  74. package/src/responsive/gridUtils.ts +1 -1
  75. package/src/responsive/responsiveLayout.ts +1 -1
  76. package/src/responsive/responsiveModal.ts +1 -1
  77. package/src/responsive/responsiveSizing.ts +1 -1
  78. package/src/responsive/useResponsive.ts +1 -1
  79. package/src/theme/hooks/useResponsiveDesignTokens.ts +1 -1
  80. package/src/typography/presentation/utils/textColorUtils.ts +1 -1
  81. package/src/typography/presentation/utils/textStyleUtils.ts +1 -1
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Calendar Sync Service
3
+ *
4
+ * Handles synchronization with system calendar.
5
+ *
6
+ * SOLID: Single Responsibility - Only sync operations
7
+ * DRY: Centralized sync logic
8
+ * KISS: Simple sync interface
9
+ */
10
+
11
+ import * as Calendar from 'expo-calendar';
12
+ import { Platform } from 'react-native';
13
+ import { CalendarPermissions } from './CalendarPermissions';
14
+ import type { CalendarEvent, SystemCalendar } from '../../domain/entities/CalendarEvent.entity';
15
+
16
+ export class CalendarSync {
17
+ /**
18
+ * Sync event to system calendar
19
+ */
20
+ static async syncToSystemCalendar(
21
+ event: CalendarEvent
22
+ ): Promise<{
23
+ success: boolean;
24
+ eventId?: string;
25
+ calendarId?: string;
26
+ error?: string;
27
+ }> {
28
+ try {
29
+ if (Platform.OS === 'web') {
30
+ return { success: false, error: 'Calendar sync not supported on web' };
31
+ }
32
+
33
+ const permission = await CalendarPermissions.requestPermissions();
34
+ if (!permission.granted) {
35
+ return { success: false, error: 'Calendar permission not granted' };
36
+ }
37
+
38
+ const primaryCal = await this.getPrimaryCalendar();
39
+ if (!primaryCal) {
40
+ return { success: false, error: 'No writable calendar found' };
41
+ }
42
+
43
+ const eventData = this.buildSystemEventData(event);
44
+ const systemEventId = await Calendar.createEventAsync(primaryCal.id, eventData);
45
+
46
+ return {
47
+ success: true,
48
+ eventId: systemEventId,
49
+ calendarId: primaryCal.id
50
+ };
51
+ } catch (error) {
52
+ return {
53
+ success: false,
54
+ error: error instanceof Error ? error.message : 'Failed to sync to calendar'
55
+ };
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Update system calendar event
61
+ */
62
+ static async updateSystemCalendarEvent(
63
+ event: CalendarEvent
64
+ ): Promise<{ success: boolean; error?: string }> {
65
+ try {
66
+ if (Platform.OS === 'web' || !event.systemCalendar) {
67
+ return { success: false, error: 'No system calendar data' };
68
+ }
69
+
70
+ const permission = await CalendarPermissions.requestPermissions();
71
+ if (!permission.granted) {
72
+ return { success: false, error: 'Calendar permission not granted' };
73
+ }
74
+
75
+ const eventData = this.buildSystemEventData(event);
76
+ await Calendar.updateEventAsync(event.systemCalendar.eventId, eventData);
77
+
78
+ return { success: true };
79
+ } catch (error) {
80
+ return {
81
+ success: false,
82
+ error: error instanceof Error ? error.message : 'Failed to update event'
83
+ };
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Remove event from system calendar
89
+ */
90
+ static async removeFromSystemCalendar(
91
+ eventId: string
92
+ ): Promise<{ success: boolean; error?: string }> {
93
+ try {
94
+ if (Platform.OS === 'web') {
95
+ return { success: false, error: 'Calendar sync not supported on web' };
96
+ }
97
+
98
+ const permission = await CalendarPermissions.requestPermissions();
99
+ if (!permission.granted) {
100
+ return { success: false, error: 'Calendar permission not granted' };
101
+ }
102
+
103
+ await Calendar.deleteEventAsync(eventId);
104
+ return { success: true };
105
+ } catch (error) {
106
+ return {
107
+ success: false,
108
+ error: error instanceof Error ? error.message : 'Failed to remove from calendar'
109
+ };
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Get primary writable calendar
115
+ */
116
+ static async getPrimaryCalendar(): Promise<SystemCalendar | null> {
117
+ try {
118
+ if (Platform.OS === 'web') {
119
+ return null;
120
+ }
121
+
122
+ const calendars = await Calendar.getCalendarsAsync();
123
+ const writableCalendars = calendars.filter((cal: Calendar.Calendar) =>
124
+ cal.allowsModifications &&
125
+ (cal.source.type === 'local' || cal.source.type === 'caldav')
126
+ );
127
+
128
+ // Prefer default calendar, fallback to first writable
129
+ const primaryCal = writableCalendars.find((cal: Calendar.Calendar) => cal.isPrimary) || writableCalendars[0];
130
+ if (!primaryCal) return null;
131
+
132
+ return {
133
+ id: primaryCal.id,
134
+ title: primaryCal.title,
135
+ color: primaryCal.color,
136
+ allowsModifications: primaryCal.allowsModifications,
137
+ source: primaryCal.source.name,
138
+ isPrimary: primaryCal.isPrimary || false
139
+ };
140
+ } catch {
141
+ return null;
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Get all system calendars
147
+ */
148
+ static async getSystemCalendars(): Promise<SystemCalendar[]> {
149
+ try {
150
+ if (Platform.OS === 'web') {
151
+ return [];
152
+ }
153
+
154
+ const calendars = await Calendar.getCalendarsAsync();
155
+ return calendars.map((cal: Calendar.Calendar) => ({
156
+ id: cal.id,
157
+ title: cal.title,
158
+ color: cal.color,
159
+ allowsModifications: cal.allowsModifications,
160
+ source: cal.source.name,
161
+ isPrimary: cal.isPrimary || false
162
+ }));
163
+ } catch {
164
+ return [];
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Build system calendar event data
170
+ */
171
+ private static buildSystemEventData(event: CalendarEvent) {
172
+ const [year, month, day] = event.date.split('-').map(Number);
173
+ let startDate = new Date(year, month - 1, day);
174
+ let endDate = new Date(startDate);
175
+
176
+ // Set time if provided
177
+ if (event.time) {
178
+ const [hours, minutes] = event.time.split(':').map(Number);
179
+ startDate.setHours(hours, minutes, 0, 0);
180
+ endDate.setHours(hours, minutes, 0, 0);
181
+ }
182
+
183
+ // Set duration
184
+ if (event.duration) {
185
+ endDate.setMinutes(endDate.getMinutes() + event.duration);
186
+ } else {
187
+ endDate.setHours(endDate.getHours() + 1); // Default 1 hour
188
+ }
189
+
190
+ // Create reminders
191
+ const alarms = event.reminders?.map(minutesBefore => ({
192
+ relativeOffset: -minutesBefore
193
+ }));
194
+
195
+ return {
196
+ title: event.title,
197
+ startDate,
198
+ endDate,
199
+ notes: event.description,
200
+ location: event.location,
201
+ alarms,
202
+ timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone
203
+ };
204
+ }
205
+ }
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Calendar Store (Zustand)
3
+ *
4
+ * Global state management for calendar functionality.
5
+ * Manages calendar view state, selected date, and events.
6
+ *
7
+ * Design Philosophy:
8
+ * - Zustand for lightweight state
9
+ * - AsyncStorage for persistence
10
+ * - Generic event handling
11
+ * - Timezone-aware via CalendarService
12
+ */
13
+
14
+ import { create } from 'zustand';
15
+ import { persist, createJSONStorage } from 'zustand/middleware';
16
+ import AsyncStorage from '@react-native-async-storage/async-storage';
17
+ import type { CalendarEvent, CreateCalendarEventRequest, UpdateCalendarEventRequest } from '../../domain/entities/CalendarEvent.entity';
18
+ import { CalendarService } from '../services/CalendarService';
19
+
20
+ /**
21
+ * Calendar view mode
22
+ */
23
+ export type CalendarViewMode = 'month' | 'week' | 'day' | 'list';
24
+
25
+ /**
26
+ * Calendar state interface
27
+ */
28
+ interface CalendarState {
29
+ // State
30
+ events: CalendarEvent[];
31
+ selectedDate: Date;
32
+ currentMonth: Date;
33
+ viewMode: CalendarViewMode;
34
+ isLoading: boolean;
35
+ error: string | null;
36
+
37
+ // Actions
38
+ actions: {
39
+ // Event CRUD
40
+ loadEvents: () => Promise<void>;
41
+ addEvent: (request: CreateCalendarEventRequest) => Promise<void>;
42
+ updateEvent: (request: UpdateCalendarEventRequest) => Promise<void>;
43
+ deleteEvent: (id: string) => Promise<void>;
44
+ completeEvent: (id: string) => Promise<void>;
45
+ uncompleteEvent: (id: string) => Promise<void>;
46
+
47
+ // Navigation
48
+ setSelectedDate: (date: Date) => void;
49
+ goToToday: () => void;
50
+ navigateMonth: (direction: 'prev' | 'next') => void;
51
+ navigateWeek: (direction: 'prev' | 'next') => void;
52
+ setCurrentMonth: (date: Date) => void;
53
+
54
+ // View mode
55
+ setViewMode: (mode: CalendarViewMode) => void;
56
+
57
+ // Utilities
58
+ getEventsForDate: (date: Date) => CalendarEvent[];
59
+ getEventsForMonth: (year: number, month: number) => CalendarEvent[];
60
+ clearError: () => void;
61
+ clearAllEvents: () => Promise<void>;
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Storage key for calendar events
67
+ */
68
+ const STORAGE_KEY = 'calendar_events';
69
+
70
+ /**
71
+ * Generate unique ID for events
72
+ */
73
+ const generateId = (): string => {
74
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
75
+ };
76
+
77
+ /**
78
+ * Calendar Store
79
+ */
80
+ export const useCalendarStore = create<CalendarState>()(
81
+ persist(
82
+ (set, get) => ({
83
+ // Initial State
84
+ events: [],
85
+ selectedDate: new Date(),
86
+ currentMonth: new Date(),
87
+ viewMode: 'month',
88
+ isLoading: false,
89
+ error: null,
90
+
91
+ // Actions
92
+ actions: {
93
+ /**
94
+ * Load events from storage
95
+ */
96
+ loadEvents: async () => {
97
+ set({ isLoading: true, error: null });
98
+ try {
99
+ const stored = await AsyncStorage.getItem(STORAGE_KEY);
100
+ if (stored) {
101
+ const events = JSON.parse(stored);
102
+ // Restore Date objects
103
+ events.forEach((event: CalendarEvent) => {
104
+ event.createdAt = new Date(event.createdAt);
105
+ event.updatedAt = new Date(event.updatedAt);
106
+ });
107
+ set({ events, isLoading: false });
108
+ } else {
109
+ set({ isLoading: false });
110
+ }
111
+ } catch (error) {
112
+ set({
113
+ error: error instanceof Error ? error.message : 'Failed to load events',
114
+ isLoading: false
115
+ });
116
+ }
117
+ },
118
+
119
+ /**
120
+ * Add a new event
121
+ */
122
+ addEvent: async (request: CreateCalendarEventRequest) => {
123
+ set({ isLoading: true, error: null });
124
+ try {
125
+ const newEvent: CalendarEvent = {
126
+ id: generateId(),
127
+ ...request,
128
+ isCompleted: false,
129
+ createdAt: new Date(),
130
+ updatedAt: new Date(),
131
+ };
132
+
133
+ const events = [...get().events, newEvent];
134
+ await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(events));
135
+ set({ events, isLoading: false });
136
+ } catch (error) {
137
+ set({
138
+ error: error instanceof Error ? error.message : 'Failed to add event',
139
+ isLoading: false
140
+ });
141
+ }
142
+ },
143
+
144
+ /**
145
+ * Update an existing event
146
+ */
147
+ updateEvent: async (request: UpdateCalendarEventRequest) => {
148
+ set({ isLoading: true, error: null });
149
+ try {
150
+ const events = get().events.map(event => {
151
+ if (event.id === request.id) {
152
+ return {
153
+ ...event,
154
+ ...request,
155
+ updatedAt: new Date(),
156
+ };
157
+ }
158
+ return event;
159
+ });
160
+
161
+ await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(events));
162
+ set({ events, isLoading: false });
163
+ } catch (error) {
164
+ set({
165
+ error: error instanceof Error ? error.message : 'Failed to update event',
166
+ isLoading: false
167
+ });
168
+ }
169
+ },
170
+
171
+ /**
172
+ * Delete an event
173
+ */
174
+ deleteEvent: async (id: string) => {
175
+ set({ isLoading: true, error: null });
176
+ try {
177
+ const events = get().events.filter(event => event.id !== id);
178
+ await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(events));
179
+ set({ events, isLoading: false });
180
+ } catch (error) {
181
+ set({
182
+ error: error instanceof Error ? error.message : 'Failed to delete event',
183
+ isLoading: false
184
+ });
185
+ }
186
+ },
187
+
188
+ /**
189
+ * Mark event as completed
190
+ */
191
+ completeEvent: async (id: string) => {
192
+ await get().actions.updateEvent({ id, isCompleted: true });
193
+ },
194
+
195
+ /**
196
+ * Mark event as incomplete
197
+ */
198
+ uncompleteEvent: async (id: string) => {
199
+ await get().actions.updateEvent({ id, isCompleted: false });
200
+ },
201
+
202
+ /**
203
+ * Set selected date
204
+ */
205
+ setSelectedDate: (date: Date) => {
206
+ set({ selectedDate: date });
207
+ },
208
+
209
+ /**
210
+ * Go to today's date
211
+ */
212
+ goToToday: () => {
213
+ const today = new Date();
214
+ set({
215
+ selectedDate: today,
216
+ currentMonth: today,
217
+ });
218
+ },
219
+
220
+ /**
221
+ * Navigate to previous/next month
222
+ */
223
+ navigateMonth: (direction: 'prev' | 'next') => {
224
+ const currentMonth = get().currentMonth;
225
+ const newMonth = direction === 'prev'
226
+ ? CalendarService.getPreviousMonth(currentMonth)
227
+ : CalendarService.getNextMonth(currentMonth);
228
+
229
+ set({ currentMonth: newMonth });
230
+ },
231
+
232
+ /**
233
+ * Navigate to previous/next week
234
+ */
235
+ navigateWeek: (direction: 'prev' | 'next') => {
236
+ const selectedDate = get().selectedDate;
237
+ const newDate = direction === 'prev'
238
+ ? CalendarService.getPreviousWeek(selectedDate)
239
+ : CalendarService.getNextWeek(selectedDate);
240
+
241
+ set({ selectedDate: newDate });
242
+ },
243
+
244
+ /**
245
+ * Set current month directly
246
+ */
247
+ setCurrentMonth: (date: Date) => {
248
+ set({ currentMonth: date });
249
+ },
250
+
251
+ /**
252
+ * Set view mode
253
+ */
254
+ setViewMode: (mode: CalendarViewMode) => {
255
+ set({ viewMode: mode });
256
+ },
257
+
258
+ /**
259
+ * Get events for a specific date
260
+ */
261
+ getEventsForDate: (date: Date) => {
262
+ const events = get().events;
263
+ return CalendarService.getEventsForDate(date, events);
264
+ },
265
+
266
+ /**
267
+ * Get events for a specific month
268
+ */
269
+ getEventsForMonth: (year: number, month: number) => {
270
+ const events = get().events;
271
+ const firstDay = new Date(year, month, 1);
272
+ const lastDay = new Date(year, month + 1, 0);
273
+ return CalendarService.getEventsInRange(firstDay, lastDay, events);
274
+ },
275
+
276
+ /**
277
+ * Clear error state
278
+ */
279
+ clearError: () => {
280
+ set({ error: null });
281
+ },
282
+
283
+ /**
284
+ * Clear all events (for testing/reset)
285
+ */
286
+ clearAllEvents: async () => {
287
+ set({ isLoading: true, error: null });
288
+ try {
289
+ await AsyncStorage.removeItem(STORAGE_KEY);
290
+ set({ events: [], isLoading: false });
291
+ } catch (error) {
292
+ set({
293
+ error: error instanceof Error ? error.message : 'Failed to clear events',
294
+ isLoading: false
295
+ });
296
+ }
297
+ },
298
+ },
299
+ }),
300
+ {
301
+ name: 'calendar-storage',
302
+ storage: createJSONStorage(() => AsyncStorage),
303
+ // Only persist events, not UI state
304
+ partialize: (state) => ({ events: state.events }),
305
+ }
306
+ )
307
+ );
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Date Utilities
3
+ *
4
+ * Pure functions for date operations and formatting.
5
+ * No side effects, timezone-aware operations.
6
+ *
7
+ * SOLID: Single Responsibility - Only date operations
8
+ * DRY: Reusable date utility functions
9
+ * KISS: Simple, focused functions
10
+ */
11
+
12
+ export class DateUtilities {
13
+ /**
14
+ * Format date to string (YYYY-MM-DD)
15
+ */
16
+ static formatDateToString(date: Date): string {
17
+ return date.toISOString().split('T')[0];
18
+ }
19
+
20
+ /**
21
+ * Check if two dates are the same day
22
+ */
23
+ static isSameDay(date1: Date, date2: Date): boolean {
24
+ return this.formatDateToString(date1) === this.formatDateToString(date2);
25
+ }
26
+
27
+ /**
28
+ * Add days to a date
29
+ */
30
+ static addDays(date: Date, days: number): Date {
31
+ const result = new Date(date);
32
+ result.setDate(result.getDate() + days);
33
+ return result;
34
+ }
35
+
36
+ /**
37
+ * Check if date is today
38
+ */
39
+ static isToday(date: Date): boolean {
40
+ return this.isSameDay(date, new Date());
41
+ }
42
+
43
+ /**
44
+ * Get current timezone
45
+ */
46
+ static getCurrentTimezone(): string {
47
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
48
+ }
49
+
50
+ /**
51
+ * Get start of month
52
+ */
53
+ static getStartOfMonth(date: Date): Date {
54
+ return new Date(date.getFullYear(), date.getMonth(), 1);
55
+ }
56
+
57
+ /**
58
+ * Get end of month
59
+ */
60
+ static getEndOfMonth(date: Date): Date {
61
+ return new Date(date.getFullYear(), date.getMonth() + 1, 0);
62
+ }
63
+
64
+ /**
65
+ * Get number of days in month
66
+ */
67
+ static getDaysInMonth(date: Date): number {
68
+ return this.getEndOfMonth(date).getDate();
69
+ }
70
+
71
+ /**
72
+ * Get start of week (Sunday)
73
+ */
74
+ static getStartOfWeek(date: Date): Date {
75
+ const result = new Date(date);
76
+ const day = result.getDay();
77
+ const diff = result.getDate() - day;
78
+ return new Date(result.setDate(diff));
79
+ }
80
+
81
+ /**
82
+ * Get end of week (Saturday)
83
+ */
84
+ static getEndOfWeek(date: Date): Date {
85
+ const result = this.getStartOfWeek(date);
86
+ result.setDate(result.getDate() + 6);
87
+ return result;
88
+ }
89
+
90
+ /**
91
+ * Parse date from string (YYYY-MM-DD)
92
+ */
93
+ static parseDate(dateString: string): Date {
94
+ const [year, month, day] = dateString.split('-').map(Number);
95
+ return new Date(year, month - 1, day);
96
+ }
97
+
98
+ /**
99
+ * Format time to string (HH:MM)
100
+ */
101
+ static formatTimeToString(date: Date): string {
102
+ return date.toTimeString().slice(0, 5);
103
+ }
104
+
105
+ /**
106
+ * Check if date is in the past
107
+ */
108
+ static isPast(date: Date): boolean {
109
+ return date < new Date();
110
+ }
111
+
112
+ /**
113
+ * Check if date is in the future
114
+ */
115
+ static isFuture(date: Date): boolean {
116
+ return date > new Date();
117
+ }
118
+ }
119
+
120
+
121
+
122
+
123
+
124
+
125
+
126
+
127
+
128
+