@moontra/moonui-pro 2.20.2 → 2.20.3
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 +8 -3
- package/plugin/index.d.ts +86 -0
- package/plugin/index.js +308 -0
- package/scripts/postinstall.js +176 -23
- package/src/components/advanced-chart/index.tsx +0 -1246
- package/src/components/advanced-forms/index.tsx +0 -585
- package/src/components/animated-button/index.tsx +0 -385
- package/src/components/calendar/event-dialog.tsx +0 -377
- package/src/components/calendar/index.tsx +0 -1220
- package/src/components/calendar-pro/index.tsx +0 -1697
- package/src/components/color-picker/index.tsx +0 -432
- package/src/components/credit-card-input/index.tsx +0 -406
- package/src/components/dashboard/dashboard-grid.tsx +0 -480
- package/src/components/dashboard/demo.tsx +0 -425
- package/src/components/dashboard/index.tsx +0 -1046
- package/src/components/dashboard/time-range-picker.tsx +0 -336
- package/src/components/dashboard/types.ts +0 -225
- package/src/components/dashboard/widgets/activity-feed.tsx +0 -349
- package/src/components/dashboard/widgets/chart-widget.tsx +0 -418
- package/src/components/dashboard/widgets/comparison-widget.tsx +0 -177
- package/src/components/dashboard/widgets/index.ts +0 -5
- package/src/components/dashboard/widgets/metric-card.tsx +0 -363
- package/src/components/dashboard/widgets/progress-widget.tsx +0 -113
- package/src/components/data-table/data-table-bulk-actions.tsx +0 -204
- package/src/components/data-table/data-table-column-toggle.tsx +0 -169
- package/src/components/data-table/data-table-export.ts +0 -156
- package/src/components/data-table/data-table-filter-drawer.tsx +0 -448
- package/src/components/data-table/index.tsx +0 -845
- package/src/components/draggable-list/index.tsx +0 -100
- package/src/components/error-boundary/index.tsx +0 -232
- package/src/components/file-upload/index.tsx +0 -1660
- package/src/components/floating-action-button/index.tsx +0 -206
- package/src/components/form-wizard/form-wizard-context.tsx +0 -335
- package/src/components/form-wizard/form-wizard-navigation.tsx +0 -118
- package/src/components/form-wizard/form-wizard-progress.tsx +0 -329
- package/src/components/form-wizard/form-wizard-step.tsx +0 -111
- package/src/components/form-wizard/index.tsx +0 -102
- package/src/components/form-wizard/types.ts +0 -77
- package/src/components/gesture-drawer/index.tsx +0 -551
- package/src/components/github-stars/github-api.ts +0 -426
- package/src/components/github-stars/hooks.ts +0 -517
- package/src/components/github-stars/index.tsx +0 -375
- package/src/components/github-stars/types.ts +0 -148
- package/src/components/github-stars/variants.tsx +0 -515
- package/src/components/health-check/index.tsx +0 -439
- package/src/components/hover-card-3d/index.tsx +0 -529
- package/src/components/index.ts +0 -130
- package/src/components/internal/index.ts +0 -78
- package/src/components/kanban/add-card-modal.tsx +0 -502
- package/src/components/kanban/card-detail-modal.tsx +0 -761
- package/src/components/kanban/index.ts +0 -13
- package/src/components/kanban/kanban.tsx +0 -1689
- package/src/components/kanban/types.ts +0 -168
- package/src/components/lazy-component/index.tsx +0 -823
- package/src/components/license-error/index.tsx +0 -31
- package/src/components/magnetic-button/index.tsx +0 -216
- package/src/components/memory-efficient-data/index.tsx +0 -1018
- package/src/components/moonui-quiz-form/index.tsx +0 -817
- package/src/components/navbar/index.tsx +0 -781
- package/src/components/optimized-image/index.tsx +0 -425
- package/src/components/performance-debugger/index.tsx +0 -613
- package/src/components/performance-monitor/index.tsx +0 -808
- package/src/components/phone-number-input/index.tsx +0 -343
- package/src/components/phone-number-input/phone-number-input-simple.tsx +0 -167
- package/src/components/pinch-zoom/index.tsx +0 -566
- package/src/components/quiz-form/index.tsx +0 -479
- package/src/components/rich-text-editor/index.tsx +0 -2322
- package/src/components/rich-text-editor/slash-commands-extension.ts +0 -230
- package/src/components/rich-text-editor/slash-commands.css +0 -35
- package/src/components/rich-text-editor/table-styles.css +0 -65
- package/src/components/sidebar/index.tsx +0 -884
- package/src/components/spotlight-card/index.tsx +0 -191
- package/src/components/swipeable-card/index.tsx +0 -100
- package/src/components/timeline/index.tsx +0 -1183
- package/src/components/ui/accordion.tsx +0 -581
- package/src/components/ui/alert-dialog.tsx +0 -141
- package/src/components/ui/alert.tsx +0 -141
- package/src/components/ui/aspect-ratio.tsx +0 -245
- package/src/components/ui/avatar.tsx +0 -155
- package/src/components/ui/badge.tsx +0 -230
- package/src/components/ui/breadcrumb.tsx +0 -216
- package/src/components/ui/button.tsx +0 -228
- package/src/components/ui/calendar.tsx +0 -387
- package/src/components/ui/card.tsx +0 -216
- package/src/components/ui/checkbox.tsx +0 -259
- package/src/components/ui/collapsible.tsx +0 -631
- package/src/components/ui/color-picker.tsx +0 -97
- package/src/components/ui/command.tsx +0 -948
- package/src/components/ui/dialog.tsx +0 -752
- package/src/components/ui/dropdown-menu.tsx +0 -706
- package/src/components/ui/gesture-drawer.tsx +0 -11
- package/src/components/ui/hover-card.tsx +0 -29
- package/src/components/ui/index.ts +0 -222
- package/src/components/ui/input.tsx +0 -224
- package/src/components/ui/label.tsx +0 -29
- package/src/components/ui/lightbox.tsx +0 -606
- package/src/components/ui/magnetic-button.tsx +0 -129
- package/src/components/ui/media-gallery.tsx +0 -611
- package/src/components/ui/navigation-menu.tsx +0 -130
- package/src/components/ui/pagination.tsx +0 -125
- package/src/components/ui/popover.tsx +0 -185
- package/src/components/ui/progress.tsx +0 -30
- package/src/components/ui/radio-group.tsx +0 -257
- package/src/components/ui/scroll-area.tsx +0 -47
- package/src/components/ui/select.tsx +0 -378
- package/src/components/ui/separator.tsx +0 -145
- package/src/components/ui/sheet.tsx +0 -139
- package/src/components/ui/skeleton.tsx +0 -20
- package/src/components/ui/slider.tsx +0 -354
- package/src/components/ui/spotlight-card.tsx +0 -119
- package/src/components/ui/switch.tsx +0 -86
- package/src/components/ui/table.tsx +0 -331
- package/src/components/ui/tabs-pro.tsx +0 -542
- package/src/components/ui/tabs.tsx +0 -54
- package/src/components/ui/textarea.tsx +0 -28
- package/src/components/ui/toast.tsx +0 -317
- package/src/components/ui/toggle.tsx +0 -119
- package/src/components/ui/tooltip.tsx +0 -151
- package/src/components/virtual-list/index.tsx +0 -668
- package/src/hooks/use-chart.ts +0 -205
- package/src/hooks/use-data-table.ts +0 -182
- package/src/hooks/use-docs-pro-access.ts +0 -13
- package/src/hooks/use-license-check.ts +0 -65
- package/src/hooks/use-subscription.ts +0 -19
- package/src/hooks/use-toast.ts +0 -15
- package/src/index.ts +0 -22
- package/src/lib/ai-providers.ts +0 -377
- package/src/lib/component-metadata.ts +0 -18
- package/src/lib/micro-interactions.ts +0 -255
- package/src/lib/paddle.ts +0 -17
- package/src/lib/utils.ts +0 -6
- package/src/patterns/login-form/index.tsx +0 -276
- package/src/patterns/login-form/types.ts +0 -67
- package/src/setupTests.ts +0 -41
- package/src/styles/advanced-chart.css +0 -239
- package/src/styles/calendar.css +0 -35
- package/src/styles/design-system.css +0 -363
- package/src/styles/index.css +0 -681
- package/src/styles/tailwind.css +0 -7
- package/src/styles/tokens.css +0 -455
- package/src/types/next-auth.d.ts +0 -21
- package/src/use-intersection-observer.tsx +0 -154
- package/src/use-local-storage.tsx +0 -71
- package/src/use-paddle.ts +0 -138
- package/src/use-performance-optimizer.ts +0 -389
- package/src/use-pro-access.ts +0 -141
- package/src/use-scroll-animation.ts +0 -219
- package/src/use-subscription.ts +0 -37
- package/src/use-toast.ts +0 -32
- package/src/utils/chart-helpers.ts +0 -357
- package/src/utils/cn.ts +0 -6
- package/src/utils/data-processing.ts +0 -151
- package/src/utils/license-validator.tsx +0 -183
package/src/use-paddle.ts
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
2
|
-
import { getPaddleInstance } from '@/lib/paddle';
|
|
3
|
-
import type { Paddle } from '@paddle/paddle-js';
|
|
4
|
-
|
|
5
|
-
interface UsePaddleReturn {
|
|
6
|
-
paddle: Paddle | null;
|
|
7
|
-
isLoading: boolean;
|
|
8
|
-
error: string | null;
|
|
9
|
-
openCheckout: (planId: string) => Promise<void>;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Custom hook for Paddle integration
|
|
14
|
-
* Handles Paddle initialization and checkout operations
|
|
15
|
-
*/
|
|
16
|
-
export function usePaddle(): UsePaddleReturn {
|
|
17
|
-
const [paddle, setPaddle] = useState<Paddle | null>(null);
|
|
18
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
19
|
-
const [error, setError] = useState<string | null>(null);
|
|
20
|
-
|
|
21
|
-
// Initialize Paddle on mount
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
let mounted = true;
|
|
24
|
-
|
|
25
|
-
const initPaddle = async () => {
|
|
26
|
-
try {
|
|
27
|
-
setIsLoading(true);
|
|
28
|
-
setError(null);
|
|
29
|
-
|
|
30
|
-
const paddleInstance = await getPaddleInstance();
|
|
31
|
-
|
|
32
|
-
if (mounted) {
|
|
33
|
-
if (paddleInstance) {
|
|
34
|
-
setPaddle(paddleInstance);
|
|
35
|
-
} else {
|
|
36
|
-
setError('Failed to initialize Paddle');
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
} catch (err) {
|
|
40
|
-
if (mounted) {
|
|
41
|
-
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
42
|
-
}
|
|
43
|
-
} finally {
|
|
44
|
-
if (mounted) {
|
|
45
|
-
setIsLoading(false);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
initPaddle();
|
|
51
|
-
|
|
52
|
-
return () => {
|
|
53
|
-
mounted = false;
|
|
54
|
-
};
|
|
55
|
-
}, []);
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Open Paddle checkout for a specific plan
|
|
59
|
-
*/
|
|
60
|
-
const openCheckout = async (planId: string) => {
|
|
61
|
-
if (!paddle) {
|
|
62
|
-
throw new Error('Paddle not initialized');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
// Call our API to handle the checkout
|
|
67
|
-
const response = await fetch('/api/paddle', {
|
|
68
|
-
method: 'POST',
|
|
69
|
-
headers: {
|
|
70
|
-
'Content-Type': 'application/json',
|
|
71
|
-
},
|
|
72
|
-
body: JSON.stringify({ plan: planId }),
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
if (!response.ok) {
|
|
76
|
-
const errorText = await response.text();
|
|
77
|
-
throw new Error(errorText || 'Failed to create checkout');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const result = await response.json();
|
|
81
|
-
|
|
82
|
-
if (!result.success) {
|
|
83
|
-
throw new Error(result.message || 'Checkout failed');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// For mock mode, we need to manually trigger checkout
|
|
87
|
-
if (process.env.NEXT_PUBLIC_USE_MOCK_PADDLE === 'true') {
|
|
88
|
-
console.log('🧪 [Hook] Mock mode detected, triggering checkout');
|
|
89
|
-
|
|
90
|
-
// Import paddle config to get price ID
|
|
91
|
-
const { PADDLE_CONFIG } = await import('@/lib/paddle');
|
|
92
|
-
const priceId = PADDLE_CONFIG.priceIds[planId as keyof typeof PADDLE_CONFIG.priceIds];
|
|
93
|
-
|
|
94
|
-
console.log('🧪 [Hook] Using price ID:', priceId, 'for plan:', planId);
|
|
95
|
-
console.log('🧪 [Hook] Paddle instance:', paddle);
|
|
96
|
-
|
|
97
|
-
// Trigger mock checkout
|
|
98
|
-
const checkoutData = {
|
|
99
|
-
items: [{ priceId, quantity: 1 }],
|
|
100
|
-
settings: {
|
|
101
|
-
displayMode: 'overlay' as const,
|
|
102
|
-
theme: 'light' as const,
|
|
103
|
-
locale: 'en' as const,
|
|
104
|
-
successUrl: `${window.location.origin}/dashboard?success=true`
|
|
105
|
-
},
|
|
106
|
-
customData: {
|
|
107
|
-
planId,
|
|
108
|
-
userId: 'test_user',
|
|
109
|
-
timestamp: new Date().toISOString()
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
console.log('🧪 [Hook] Opening checkout with data:', checkoutData);
|
|
114
|
-
|
|
115
|
-
try {
|
|
116
|
-
paddle.Checkout.open(checkoutData);
|
|
117
|
-
console.log('🧪 [Hook] Checkout.open called successfully');
|
|
118
|
-
} catch (checkoutError) {
|
|
119
|
-
console.error('🧪 [Hook] Checkout.open error:', checkoutError);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Checkout is opened inline by Paddle
|
|
124
|
-
console.log('✅ Paddle checkout opened for plan:', planId);
|
|
125
|
-
|
|
126
|
-
} catch (error) {
|
|
127
|
-
console.error('❌ Checkout error:', error);
|
|
128
|
-
throw error;
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
return {
|
|
133
|
-
paddle,
|
|
134
|
-
isLoading,
|
|
135
|
-
error,
|
|
136
|
-
openCheckout,
|
|
137
|
-
};
|
|
138
|
-
}
|
|
@@ -1,389 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
// Performance profiler implementation
|
|
5
|
-
const performanceProfiler = {
|
|
6
|
-
getPerformanceSummary: () => ({}),
|
|
7
|
-
getMeasurements: (type: string) => [] as any[],
|
|
8
|
-
measureComponent: (name: string, callback: () => void) => {
|
|
9
|
-
const start = performance.now();
|
|
10
|
-
callback();
|
|
11
|
-
const duration = performance.now() - start;
|
|
12
|
-
return { name, duration };
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
// Performance optimization hooks
|
|
17
|
-
export function usePerformanceOptimizer() {
|
|
18
|
-
const [optimizations, setOptimizations] = React.useState<OptimizationSuggestion[]>([]);
|
|
19
|
-
const [isAnalyzing, setIsAnalyzing] = React.useState(false);
|
|
20
|
-
|
|
21
|
-
const analyzePerformance = React.useCallback(async () => {
|
|
22
|
-
setIsAnalyzing(true);
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
const metrics = performanceProfiler.getPerformanceSummary();
|
|
26
|
-
const suggestions: OptimizationSuggestion[] = [];
|
|
27
|
-
|
|
28
|
-
// Analyze component render times
|
|
29
|
-
const componentMetrics = performanceProfiler.getMeasurements('component');
|
|
30
|
-
if (componentMetrics.length > 0) {
|
|
31
|
-
const slowComponents = componentMetrics
|
|
32
|
-
.filter(m => m.duration > 16.67) // Slower than 60fps
|
|
33
|
-
.sort((a, b) => b.duration - a.duration)
|
|
34
|
-
.slice(0, 5);
|
|
35
|
-
|
|
36
|
-
slowComponents.forEach(component => {
|
|
37
|
-
suggestions.push({
|
|
38
|
-
type: 'component',
|
|
39
|
-
severity: component.duration > 50 ? 'high' : 'medium',
|
|
40
|
-
title: `Slow component: ${component.name}`,
|
|
41
|
-
description: `Component takes ${component.duration.toFixed(2)}ms to render`,
|
|
42
|
-
solution: 'Consider using React.memo, useMemo, or useCallback',
|
|
43
|
-
impact: 'high',
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Analyze memory usage
|
|
49
|
-
if ('memory' in performance) {
|
|
50
|
-
const memory = (performance as any).memory;
|
|
51
|
-
const usagePercent = (memory.usedJSHeapSize / memory.totalJSHeapSize) * 100;
|
|
52
|
-
|
|
53
|
-
if (usagePercent > 80) {
|
|
54
|
-
suggestions.push({
|
|
55
|
-
type: 'memory',
|
|
56
|
-
severity: 'high',
|
|
57
|
-
title: 'High memory usage',
|
|
58
|
-
description: `Memory usage is at ${usagePercent.toFixed(1)}%`,
|
|
59
|
-
solution: 'Review for memory leaks, optimize data structures, use lazy loading',
|
|
60
|
-
impact: 'high',
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Analyze layout shifts
|
|
66
|
-
const layoutShifts = performanceProfiler.getMeasurements('layout-shift');
|
|
67
|
-
if (layoutShifts.length > 0) {
|
|
68
|
-
const totalShift = layoutShifts.reduce((sum, shift) => sum + (shift.value || 0), 0);
|
|
69
|
-
|
|
70
|
-
if (totalShift > 0.1) {
|
|
71
|
-
suggestions.push({
|
|
72
|
-
type: 'layout',
|
|
73
|
-
severity: 'medium',
|
|
74
|
-
title: 'Layout shifts detected',
|
|
75
|
-
description: `Cumulative Layout Shift: ${totalShift.toFixed(3)}`,
|
|
76
|
-
solution: 'Add dimensions to images, reserve space for dynamic content',
|
|
77
|
-
impact: 'medium',
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Analyze bundle size (if available)
|
|
83
|
-
if (typeof window !== 'undefined' && window.performance) {
|
|
84
|
-
const resources = performance.getEntriesByType('resource') as PerformanceResourceTiming[];
|
|
85
|
-
const jsResources = resources.filter(r => r.name.includes('.js'));
|
|
86
|
-
const largeResources = jsResources.filter(r => r.transferSize > 100000); // >100KB
|
|
87
|
-
|
|
88
|
-
if (largeResources.length > 0) {
|
|
89
|
-
suggestions.push({
|
|
90
|
-
type: 'bundle',
|
|
91
|
-
severity: 'medium',
|
|
92
|
-
title: 'Large JavaScript bundles',
|
|
93
|
-
description: `${largeResources.length} JavaScript files exceed 100KB`,
|
|
94
|
-
solution: 'Implement code splitting, lazy loading, and tree shaking',
|
|
95
|
-
impact: 'medium',
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
setOptimizations(suggestions);
|
|
101
|
-
} catch (error) {
|
|
102
|
-
console.error('Performance analysis failed:', error);
|
|
103
|
-
} finally {
|
|
104
|
-
setIsAnalyzing(false);
|
|
105
|
-
}
|
|
106
|
-
}, []);
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
optimizations,
|
|
110
|
-
isAnalyzing,
|
|
111
|
-
analyzePerformance,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Hook for render optimization
|
|
116
|
-
export function useRenderOptimization<T>(
|
|
117
|
-
value: T,
|
|
118
|
-
dependencies: React.DependencyList
|
|
119
|
-
): T {
|
|
120
|
-
const memoizedValue = React.useMemo(() => value, dependencies);
|
|
121
|
-
|
|
122
|
-
React.useEffect(() => {
|
|
123
|
-
if (process.env.NODE_ENV === 'development') {
|
|
124
|
-
const componentName = 'useRenderOptimization';
|
|
125
|
-
performanceProfiler.measureComponent(componentName, () => {
|
|
126
|
-
// Measure the impact of memoization
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}, dependencies);
|
|
130
|
-
|
|
131
|
-
return memoizedValue;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Hook for debounced performance tracking
|
|
135
|
-
export function useDebouncePerformance<T extends (...args: any[]) => void>(
|
|
136
|
-
callback: T,
|
|
137
|
-
delay: number = 300
|
|
138
|
-
): T {
|
|
139
|
-
const timeoutRef = React.useRef<NodeJS.Timeout | undefined>(undefined);
|
|
140
|
-
|
|
141
|
-
const debouncedCallback = React.useCallback(
|
|
142
|
-
(...args: Parameters<T>) => {
|
|
143
|
-
if (timeoutRef.current) {
|
|
144
|
-
clearTimeout(timeoutRef.current);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
timeoutRef.current = setTimeout(() => {
|
|
148
|
-
performanceProfiler.measureComponent('debounced-callback', () => {
|
|
149
|
-
callback(...args);
|
|
150
|
-
});
|
|
151
|
-
}, delay);
|
|
152
|
-
},
|
|
153
|
-
[callback, delay]
|
|
154
|
-
) as T;
|
|
155
|
-
|
|
156
|
-
React.useEffect(() => {
|
|
157
|
-
return () => {
|
|
158
|
-
if (timeoutRef.current) {
|
|
159
|
-
clearTimeout(timeoutRef.current);
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
}, []);
|
|
163
|
-
|
|
164
|
-
return debouncedCallback;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Hook for throttled performance tracking
|
|
168
|
-
export function useThrottlePerformance<T extends (...args: any[]) => void>(
|
|
169
|
-
callback: T,
|
|
170
|
-
delay: number = 100
|
|
171
|
-
): T {
|
|
172
|
-
const lastCallRef = React.useRef<number>(0);
|
|
173
|
-
|
|
174
|
-
const throttledCallback = React.useCallback(
|
|
175
|
-
(...args: Parameters<T>) => {
|
|
176
|
-
const now = Date.now();
|
|
177
|
-
|
|
178
|
-
if (now - lastCallRef.current >= delay) {
|
|
179
|
-
lastCallRef.current = now;
|
|
180
|
-
performanceProfiler.measureComponent('throttled-callback', () => {
|
|
181
|
-
callback(...args);
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
[callback, delay]
|
|
186
|
-
) as T;
|
|
187
|
-
|
|
188
|
-
return throttledCallback;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Hook for measuring component lifecycle
|
|
192
|
-
export function useComponentLifecycleTracking(componentName: string) {
|
|
193
|
-
const mountTime = React.useRef<number>(0);
|
|
194
|
-
const updateCount = React.useRef<number>(0);
|
|
195
|
-
|
|
196
|
-
React.useEffect(() => {
|
|
197
|
-
// Component mount
|
|
198
|
-
mountTime.current = performance.now();
|
|
199
|
-
|
|
200
|
-
performanceProfiler.measureComponent(`${componentName}-mount`, () => {
|
|
201
|
-
// Measure mount time
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
return () => {
|
|
205
|
-
// Component unmount
|
|
206
|
-
const lifetime = performance.now() - mountTime.current;
|
|
207
|
-
performanceProfiler.measureComponent(`${componentName}-unmount`, () => {
|
|
208
|
-
// Record component lifetime
|
|
209
|
-
});
|
|
210
|
-
};
|
|
211
|
-
}, [componentName]);
|
|
212
|
-
|
|
213
|
-
React.useEffect(() => {
|
|
214
|
-
// Component update
|
|
215
|
-
updateCount.current++;
|
|
216
|
-
|
|
217
|
-
if (updateCount.current > 1) {
|
|
218
|
-
performanceProfiler.measureComponent(`${componentName}-update`, () => {
|
|
219
|
-
// Measure update time
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
mountTime: mountTime.current,
|
|
226
|
-
updateCount: updateCount.current,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Hook for virtual scrolling optimization
|
|
231
|
-
export function useVirtualScrolling<T>(
|
|
232
|
-
items: T[],
|
|
233
|
-
itemHeight: number,
|
|
234
|
-
containerHeight: number,
|
|
235
|
-
overscan: number = 3
|
|
236
|
-
) {
|
|
237
|
-
const [scrollTop, setScrollTop] = React.useState(0);
|
|
238
|
-
|
|
239
|
-
const visibleRange = React.useMemo(() => {
|
|
240
|
-
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
|
|
241
|
-
const endIndex = Math.min(
|
|
242
|
-
items.length - 1,
|
|
243
|
-
Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
return { startIndex, endIndex };
|
|
247
|
-
}, [scrollTop, itemHeight, containerHeight, overscan, items.length]);
|
|
248
|
-
|
|
249
|
-
const visibleItems = React.useMemo(() => {
|
|
250
|
-
return items.slice(visibleRange.startIndex, visibleRange.endIndex + 1);
|
|
251
|
-
}, [items, visibleRange]);
|
|
252
|
-
|
|
253
|
-
const totalHeight = items.length * itemHeight;
|
|
254
|
-
const offsetY = visibleRange.startIndex * itemHeight;
|
|
255
|
-
|
|
256
|
-
return {
|
|
257
|
-
visibleItems,
|
|
258
|
-
totalHeight,
|
|
259
|
-
offsetY,
|
|
260
|
-
startIndex: visibleRange.startIndex,
|
|
261
|
-
endIndex: visibleRange.endIndex,
|
|
262
|
-
setScrollTop,
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Hook for image lazy loading optimization
|
|
267
|
-
export function useLazyImageOptimization(src: string, threshold: number = 0.1) {
|
|
268
|
-
const [isLoaded, setIsLoaded] = React.useState(false);
|
|
269
|
-
const [isIntersecting, setIsIntersecting] = React.useState(false);
|
|
270
|
-
const imgRef = React.useRef<HTMLImageElement>(null);
|
|
271
|
-
|
|
272
|
-
React.useEffect(() => {
|
|
273
|
-
const observer = new IntersectionObserver(
|
|
274
|
-
([entry]) => {
|
|
275
|
-
if (entry.isIntersecting) {
|
|
276
|
-
setIsIntersecting(true);
|
|
277
|
-
|
|
278
|
-
// Measure image load time
|
|
279
|
-
const startTime = performance.now();
|
|
280
|
-
const img = new Image();
|
|
281
|
-
|
|
282
|
-
img.onload = () => {
|
|
283
|
-
const loadTime = performance.now() - startTime;
|
|
284
|
-
performanceProfiler.measureComponent('image-load', () => {
|
|
285
|
-
// Record image load time
|
|
286
|
-
});
|
|
287
|
-
setIsLoaded(true);
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
img.src = src;
|
|
291
|
-
observer.disconnect();
|
|
292
|
-
}
|
|
293
|
-
},
|
|
294
|
-
{ threshold }
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
if (imgRef.current) {
|
|
298
|
-
observer.observe(imgRef.current);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
return () => observer.disconnect();
|
|
302
|
-
}, [src, threshold]);
|
|
303
|
-
|
|
304
|
-
return {
|
|
305
|
-
imgRef,
|
|
306
|
-
isLoaded,
|
|
307
|
-
isIntersecting,
|
|
308
|
-
shouldLoad: isIntersecting,
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// Performance optimization interfaces
|
|
313
|
-
interface OptimizationSuggestion {
|
|
314
|
-
type: 'component' | 'memory' | 'layout' | 'bundle' | 'network';
|
|
315
|
-
severity: 'low' | 'medium' | 'high';
|
|
316
|
-
title: string;
|
|
317
|
-
description: string;
|
|
318
|
-
solution: string;
|
|
319
|
-
impact: 'low' | 'medium' | 'high';
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Hook for automatic performance optimization
|
|
323
|
-
export function useAutoPerformanceOptimization() {
|
|
324
|
-
const [isOptimizing, setIsOptimizing] = React.useState(false);
|
|
325
|
-
const [optimizations, setOptimizations] = React.useState<string[]>([]);
|
|
326
|
-
|
|
327
|
-
const applyOptimizations = React.useCallback(async () => {
|
|
328
|
-
setIsOptimizing(true);
|
|
329
|
-
const applied: string[] = [];
|
|
330
|
-
|
|
331
|
-
try {
|
|
332
|
-
// Apply automatic optimizations
|
|
333
|
-
|
|
334
|
-
// 1. Prefetch critical resources
|
|
335
|
-
const criticalResources = document.querySelectorAll('link[rel="preload"]');
|
|
336
|
-
if (criticalResources.length === 0) {
|
|
337
|
-
// Add preload hints for critical resources
|
|
338
|
-
const cssLinks = document.querySelectorAll('link[rel="stylesheet"]');
|
|
339
|
-
cssLinks.forEach(link => {
|
|
340
|
-
const preloadLink = document.createElement('link');
|
|
341
|
-
preloadLink.rel = 'preload';
|
|
342
|
-
preloadLink.href = (link as HTMLLinkElement).href;
|
|
343
|
-
preloadLink.as = 'style';
|
|
344
|
-
document.head.appendChild(preloadLink);
|
|
345
|
-
});
|
|
346
|
-
applied.push('Added preload hints for CSS');
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// 2. Optimize images
|
|
350
|
-
const images = document.querySelectorAll('img:not([loading])');
|
|
351
|
-
images.forEach(img => {
|
|
352
|
-
(img as HTMLImageElement).loading = 'lazy';
|
|
353
|
-
});
|
|
354
|
-
if (images.length > 0) {
|
|
355
|
-
applied.push(`Applied lazy loading to ${images.length} images`);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// 3. Add intersection observers for below-fold content
|
|
359
|
-
const belowFoldElements = document.querySelectorAll('[data-optimize="lazy"]');
|
|
360
|
-
belowFoldElements.forEach(element => {
|
|
361
|
-
const observer = new IntersectionObserver(
|
|
362
|
-
([entry]) => {
|
|
363
|
-
if (entry.isIntersecting) {
|
|
364
|
-
element.classList.add('optimized');
|
|
365
|
-
observer.disconnect();
|
|
366
|
-
}
|
|
367
|
-
},
|
|
368
|
-
{ threshold: 0.1 }
|
|
369
|
-
);
|
|
370
|
-
observer.observe(element);
|
|
371
|
-
});
|
|
372
|
-
if (belowFoldElements.length > 0) {
|
|
373
|
-
applied.push(`Added intersection observers to ${belowFoldElements.length} elements`);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
setOptimizations(applied);
|
|
377
|
-
} catch (error) {
|
|
378
|
-
console.error('Auto-optimization failed:', error);
|
|
379
|
-
} finally {
|
|
380
|
-
setIsOptimizing(false);
|
|
381
|
-
}
|
|
382
|
-
}, []);
|
|
383
|
-
|
|
384
|
-
return {
|
|
385
|
-
isOptimizing,
|
|
386
|
-
optimizations,
|
|
387
|
-
applyOptimizations,
|
|
388
|
-
};
|
|
389
|
-
}
|
package/src/use-pro-access.ts
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
// Pro Access Hook
|
|
2
|
-
// Kullanıcının pro componentlere erişim durumunu kontrol eder
|
|
3
|
-
|
|
4
|
-
import React from 'react'
|
|
5
|
-
import { useSession } from 'next-auth/react'
|
|
6
|
-
import { useQuery } from '@tanstack/react-query'
|
|
7
|
-
import { getComponentAccess } from '@/lib/component-metadata'
|
|
8
|
-
|
|
9
|
-
interface UserSubscription {
|
|
10
|
-
userId: string
|
|
11
|
-
plan: 'free' | 'pro_monthly' | 'pro_annual' | 'pro_lifetime'
|
|
12
|
-
status: 'active' | 'expired' | 'cancelled' | 'trial'
|
|
13
|
-
expiresAt: Date | null // lifetime için null
|
|
14
|
-
createdAt: Date
|
|
15
|
-
updatedAt: Date
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface ProAccessStatus {
|
|
19
|
-
hasProAccess: boolean
|
|
20
|
-
subscription: UserSubscription | null | undefined
|
|
21
|
-
isLoading: boolean
|
|
22
|
-
error: Error | null
|
|
23
|
-
daysUntilExpiry: number | null
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Subscription durumunu API'den çek
|
|
27
|
-
async function fetchSubscriptionStatus(): Promise<UserSubscription | null> {
|
|
28
|
-
const response = await fetch('/api/user/subscription-status')
|
|
29
|
-
|
|
30
|
-
if (!response.ok) {
|
|
31
|
-
throw new Error('Subscription status fetch failed')
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const data = await response.json()
|
|
35
|
-
return data.subscription
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Pro erişim durumunu hesapla
|
|
39
|
-
function calculateProAccess(subscription: UserSubscription | null | undefined): {
|
|
40
|
-
hasProAccess: boolean
|
|
41
|
-
daysUntilExpiry: number | null
|
|
42
|
-
} {
|
|
43
|
-
if (!subscription) {
|
|
44
|
-
return { hasProAccess: false, daysUntilExpiry: null }
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Lifetime üyelik
|
|
48
|
-
if (subscription.plan === 'pro_lifetime') {
|
|
49
|
-
return { hasProAccess: true, daysUntilExpiry: null }
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Aktif olmayan subscription
|
|
53
|
-
if (subscription.status !== 'active') {
|
|
54
|
-
return { hasProAccess: false, daysUntilExpiry: null }
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Süre kontrolü
|
|
58
|
-
if (subscription.expiresAt) {
|
|
59
|
-
const now = new Date()
|
|
60
|
-
const expiryDate = new Date(subscription.expiresAt)
|
|
61
|
-
const daysUntilExpiry = Math.ceil((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24))
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
hasProAccess: daysUntilExpiry > 0,
|
|
65
|
-
daysUntilExpiry: daysUntilExpiry > 0 ? daysUntilExpiry : 0
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return { hasProAccess: false, daysUntilExpiry: null }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function useProAccess(): ProAccessStatus {
|
|
73
|
-
const { data: session, status } = useSession()
|
|
74
|
-
|
|
75
|
-
const {
|
|
76
|
-
data: subscription,
|
|
77
|
-
isLoading,
|
|
78
|
-
error
|
|
79
|
-
} = useQuery({
|
|
80
|
-
queryKey: ['subscription-status', session?.user?.id],
|
|
81
|
-
queryFn: fetchSubscriptionStatus,
|
|
82
|
-
enabled: !!session?.user?.id && status === 'authenticated',
|
|
83
|
-
staleTime: 5 * 60 * 1000, // 5 dakika cache
|
|
84
|
-
refetchInterval: 10 * 60 * 1000, // 10 dakikada bir otomatik refresh
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
const { hasProAccess, daysUntilExpiry } = calculateProAccess(subscription)
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
hasProAccess,
|
|
91
|
-
subscription,
|
|
92
|
-
isLoading: status === 'loading' || isLoading,
|
|
93
|
-
error: error as Error | null,
|
|
94
|
-
daysUntilExpiry
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Component erişim kontrolü için helper hook
|
|
99
|
-
export function useComponentAccess(componentId: string) {
|
|
100
|
-
const { hasProAccess, isLoading } = useProAccess()
|
|
101
|
-
|
|
102
|
-
const access = React.useMemo(() => {
|
|
103
|
-
return getComponentAccess(componentId, hasProAccess ? 'pro' : 'free')
|
|
104
|
-
}, [componentId, hasProAccess])
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
...access,
|
|
108
|
-
isLoading,
|
|
109
|
-
canUse: access.access === 'unlocked',
|
|
110
|
-
isLocked: access.access === 'locked'
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Subscription durumu için utility functions
|
|
115
|
-
export function getSubscriptionDisplayName(plan: string): string {
|
|
116
|
-
switch (plan) {
|
|
117
|
-
case 'pro_monthly':
|
|
118
|
-
return 'Pro Monthly'
|
|
119
|
-
case 'pro_annual':
|
|
120
|
-
return 'Pro Annual'
|
|
121
|
-
case 'pro_lifetime':
|
|
122
|
-
return 'Pro Lifetime'
|
|
123
|
-
default:
|
|
124
|
-
return 'Free'
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function getSubscriptionColor(status: string): string {
|
|
129
|
-
switch (status) {
|
|
130
|
-
case 'active':
|
|
131
|
-
return 'green'
|
|
132
|
-
case 'trial':
|
|
133
|
-
return 'blue'
|
|
134
|
-
case 'expired':
|
|
135
|
-
return 'red'
|
|
136
|
-
case 'cancelled':
|
|
137
|
-
return 'gray'
|
|
138
|
-
default:
|
|
139
|
-
return 'gray'
|
|
140
|
-
}
|
|
141
|
-
}
|