@moontra/moonui 2.1.4 → 2.1.7

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 (53) hide show
  1. package/dist/index.js +8174 -2834
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +8074 -2645
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/src/components/ui/accordion.tsx +8 -11
  7. package/src/components/ui/alert.tsx +10 -13
  8. package/src/components/ui/analyze-components.txt +4 -0
  9. package/src/components/ui/aspect-ratio.tsx +6 -9
  10. package/src/components/ui/avatar.tsx +7 -10
  11. package/src/components/ui/badge.tsx +4 -7
  12. package/src/components/ui/breadcrumb.tsx +29 -31
  13. package/src/components/ui/button.tsx +11 -15
  14. package/src/components/ui/calendar.tsx +6 -9
  15. package/src/components/ui/card.tsx +19 -22
  16. package/src/components/ui/checkbox.tsx +5 -8
  17. package/src/components/ui/collapsible.tsx +10 -12
  18. package/src/components/ui/color-picker.tsx +2 -2
  19. package/src/components/ui/command.tsx +26 -28
  20. package/src/components/ui/component-analysis-report.md +118 -0
  21. package/src/components/ui/data-table.tsx +2 -2
  22. package/src/components/ui/date-picker.tsx +9 -9
  23. package/src/components/ui/dialog.tsx +21 -23
  24. package/src/components/ui/draggable-list.tsx +81 -0
  25. package/src/components/ui/dropdown-menu.tsx +34 -36
  26. package/src/components/ui/file-upload.tsx +3 -3
  27. package/src/components/ui/gesture-drawer.tsx +146 -0
  28. package/src/components/ui/index.ts +43 -9
  29. package/src/components/ui/input.tsx +6 -9
  30. package/src/components/ui/label.tsx +4 -7
  31. package/src/components/ui/moon-logo.tsx +1 -1
  32. package/src/components/ui/pagination.tsx +5 -7
  33. package/src/components/ui/popover.tsx +9 -11
  34. package/src/components/ui/progress.tsx +6 -9
  35. package/src/components/ui/radio-group.tsx +7 -10
  36. package/src/components/ui/rich-text-editor/index.tsx +71 -0
  37. package/src/components/ui/select.tsx +21 -23
  38. package/src/components/ui/separator.tsx +5 -8
  39. package/src/components/ui/simple-editor.tsx +4 -4
  40. package/src/components/ui/skeleton.tsx +7 -10
  41. package/src/components/ui/slider.tsx +5 -8
  42. package/src/components/ui/swipeable-card.tsx +69 -0
  43. package/src/components/ui/switch.tsx +3 -6
  44. package/src/components/ui/table.tsx +29 -31
  45. package/src/components/ui/tabs.tsx +9 -12
  46. package/src/components/ui/textarea.tsx +4 -7
  47. package/src/components/ui/toast.tsx +7 -9
  48. package/src/components/ui/toggle.tsx +4 -7
  49. package/src/components/ui/tooltip.tsx +11 -13
  50. package/src/index.tsx +13 -7
  51. package/src/lib/utils.ts +2 -65
  52. package/src/components/ui/locked-component.tsx +0 -215
  53. package/src/use-pro-access.ts +0 -141
@@ -2,7 +2,7 @@ import * as React from "react"
2
2
 
3
3
  import { cn } from "../../lib/utils"
4
4
 
5
- export interface MoonUITextareaProps
5
+ export interface TextareaProps
6
6
  extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
7
7
  // Enhanced props that shouldn't be passed to DOM
8
8
  variant?: "default" | "outline" | "ghost" | "underline"
@@ -15,7 +15,7 @@ export interface MoonUITextareaProps
15
15
  characterCount?: boolean
16
16
  }
17
17
 
18
- const MoonUITextarea = React.forwardRef<HTMLTextAreaElement, MoonUITextareaProps>(
18
+ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
19
19
  ({
20
20
  className,
21
21
  // Extract enhanced props to prevent them from being passed to DOM
@@ -41,9 +41,6 @@ const MoonUITextarea = React.forwardRef<HTMLTextAreaElement, MoonUITextareaProps
41
41
  )
42
42
  }
43
43
  )
44
- MoonUITextarea.displayName = "MoonUITextarea"
44
+ Textarea.displayName = "Textarea"
45
45
 
46
- export { MoonUITextarea };
47
-
48
- // Backward compatibility exports
49
- export { MoonUITextarea as Textarea }
46
+ export { Textarea }
@@ -23,7 +23,7 @@ const ToastViewport = React.forwardRef<
23
23
  ));
24
24
  ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
25
25
 
26
- const moonUIToastVariants = cva(
26
+ const toastVariants = cva(
27
27
  "group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-8 shadow-sm transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
28
28
  {
29
29
  variants: {
@@ -46,11 +46,11 @@ const moonUIToastVariants = cva(
46
46
  );
47
47
 
48
48
  type ToastProps = React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
49
- VariantProps<typeof moonUIToastVariants>;
49
+ VariantProps<typeof toastVariants>;
50
50
 
51
51
  type ToastActionElement = React.ReactElement<typeof ToastAction>;
52
52
 
53
- const MoonUIToast = React.forwardRef<
53
+ const Toast = React.forwardRef<
54
54
  React.ElementRef<typeof ToastPrimitives.Root>,
55
55
  ToastProps
56
56
  >(({ className, variant, ...props }, ref) => {
@@ -297,11 +297,12 @@ function Toaster() {
297
297
  );
298
298
  }
299
299
 
300
- export { type ToastProps,
300
+ export {
301
+ type ToastProps,
301
302
  type ToastActionElement,
302
303
  ToastProvider,
303
304
  ToastViewport,
304
- MoonUIToast,
305
+ Toast,
305
306
  ToastTitle,
306
307
  ToastDescription,
307
308
  ToastAction,
@@ -309,7 +310,4 @@ export { type ToastProps,
309
310
  Toaster,
310
311
  toast,
311
312
  useToast,
312
- };
313
-
314
- // Backward compatibility exports
315
- export { MoonUIToast as Toast };
313
+ };
@@ -6,7 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority"
6
6
 
7
7
  import { cn } from "../../lib/utils"
8
8
 
9
- const moonUIToggleVariants = cva(
9
+ const toggleVariants = cva(
10
10
  "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground",
11
11
  {
12
12
  variants: {
@@ -28,9 +28,9 @@ const moonUIToggleVariants = cva(
28
28
  }
29
29
  )
30
30
 
31
- export interface MoonUIToggleProps extends React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root>, VariantProps<typeof moonUIToggleVariants> {}
31
+ export interface ToggleProps extends React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root>, VariantProps<typeof toggleVariants> {}
32
32
 
33
- const MoonUIToggle = React.forwardRef<
33
+ const Toggle = React.forwardRef<
34
34
  React.ElementRef<typeof TogglePrimitive.Root>,
35
35
  ToggleProps
36
36
  >(({ className, variant, size, ...props }, ref) => (
@@ -43,8 +43,5 @@ const MoonUIToggle = React.forwardRef<
43
43
 
44
44
  Toggle.displayName = TogglePrimitive.Root.displayName
45
45
 
46
- export { MoonUIToggle, moonUIToggleVariants };
47
-
48
- // Backward compatibility exports
49
- export { MoonUIToggle as Toggle, moonUIToggleVariants as toggleVariants }
46
+ export { Toggle, toggleVariants }
50
47
  export type { ToggleProps }
@@ -6,7 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority"
6
6
 
7
7
  import { cn } from "../../lib/utils"
8
8
 
9
- const MoonUITooltipProvider = TooltipPrimitive.Provider
9
+ const TooltipProvider = TooltipPrimitive.Provider
10
10
 
11
11
  const tooltipVariants = cva(
12
12
  "z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
@@ -33,9 +33,9 @@ const tooltipVariants = cva(
33
33
  }
34
34
  )
35
35
 
36
- const MoonUITooltip = TooltipPrimitive.Root
36
+ const Tooltip = TooltipPrimitive.Root
37
37
 
38
- const MoonUITooltipTrigger = TooltipPrimitive.Trigger
38
+ const TooltipTrigger = TooltipPrimitive.Trigger
39
39
 
40
40
  const TooltipArrow = React.forwardRef<
41
41
  React.ElementRef<typeof TooltipPrimitive.Arrow>,
@@ -49,13 +49,13 @@ const TooltipArrow = React.forwardRef<
49
49
  ))
50
50
  TooltipArrow.displayName = TooltipPrimitive.Arrow.displayName
51
51
 
52
- export interface MoonUITooltipContentProps
52
+ export interface TooltipContentProps
53
53
  extends React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>,
54
54
  VariantProps<typeof tooltipVariants> {
55
55
  showArrow?: boolean
56
56
  }
57
57
 
58
- const MoonUITooltipContent = React.forwardRef<
58
+ const TooltipContent = React.forwardRef<
59
59
  React.ElementRef<typeof TooltipPrimitive.Content>,
60
60
  TooltipContentProps
61
61
  >(({ className, variant, size, showArrow = false, sideOffset = 4, ...props }, ref) => (
@@ -141,14 +141,12 @@ const SimpleTooltip = React.forwardRef<
141
141
  )
142
142
  SimpleTooltip.displayName = "SimpleTooltip"
143
143
 
144
- export { MoonUITooltip,
145
- MoonUITooltipProvider,
146
- MoonUITooltipTrigger,
147
- MoonUITooltipContent,
144
+ export {
145
+ Tooltip,
146
+ TooltipProvider,
147
+ TooltipTrigger,
148
+ TooltipContent,
148
149
  TooltipArrow,
149
150
  SimpleTooltip,
150
151
  tooltipVariants,
151
- };
152
-
153
- // Backward compatibility exports
154
- export { MoonUITooltip as Tooltip, MoonUITooltipTrigger as TooltipTrigger, MoonUITooltipContent as TooltipContent, MoonUITooltipProvider as TooltipProvider }
152
+ }
package/src/index.tsx CHANGED
@@ -1,7 +1,6 @@
1
- // CSS import kaldırıldı - Style conflict'leri önlemek için
2
- // Component'ler Tailwind class'ları kullanacak
1
+ // Core UI Components Export
2
+ // Premium components with advanced features
3
3
 
4
- // FREE UI Components Export - Source of Truth
5
4
  export * from "./components/ui/accordion"
6
5
  export * from "./components/ui/alert"
7
6
  export * from "./components/ui/aspect-ratio"
@@ -13,27 +12,34 @@ export * from "./components/ui/calendar"
13
12
  export * from "./components/ui/card"
14
13
  export * from "./components/ui/checkbox"
15
14
  export * from "./components/ui/collapsible"
15
+ export * from "./components/ui/color-picker"
16
16
  export * from "./components/ui/command"
17
+ export * from "./components/ui/data-table"
17
18
  export * from "./components/ui/date-picker"
18
19
  export * from "./components/ui/dialog"
20
+ export * from "./components/ui/draggable-list"
19
21
  export * from "./components/ui/dropdown-menu"
22
+ export * from "./components/ui/file-upload"
23
+ export * from "./components/ui/gesture-drawer"
24
+ export * from "./components/ui/github-stars"
20
25
  export * from "./components/ui/input"
21
26
  export * from "./components/ui/label"
27
+ export * from "./components/ui/moon-logo"
22
28
  export * from "./components/ui/pagination"
23
29
  export * from "./components/ui/popover"
24
30
  export * from "./components/ui/progress"
25
31
  export * from "./components/ui/radio-group"
32
+ export * from "./components/ui/rich-text-editor"
26
33
  export * from "./components/ui/select"
27
34
  export * from "./components/ui/separator"
35
+ export * from "./components/ui/simple-editor"
28
36
  export * from "./components/ui/skeleton"
29
37
  export * from "./components/ui/slider"
38
+ export * from "./components/ui/swipeable-card"
30
39
  export * from "./components/ui/switch"
31
40
  export * from "./components/ui/table"
32
41
  export * from "./components/ui/tabs"
33
42
  export * from "./components/ui/textarea"
34
43
  export * from "./components/ui/toast"
35
44
  export * from "./components/ui/toggle"
36
- export * from "./components/ui/tooltip"
37
-
38
- // Utility exports
39
- export * from "./lib/utils"
45
+ export * from "./components/ui/tooltip"
package/src/lib/utils.ts CHANGED
@@ -1,69 +1,6 @@
1
- import { type ClassValue, clsx } from "clsx";
2
- import { twMerge } from "tailwind-merge";
1
+ import { type ClassValue, clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
3
 
4
- // Utility function using clsx and tailwind-merge
5
4
  export function cn(...inputs: ClassValue[]) {
6
5
  return twMerge(clsx(inputs));
7
6
  }
8
-
9
- // Date formatting utility
10
- export function formatDate(date: Date): string {
11
- if (isNaN(date.getTime())) {
12
- return 'Invalid Date'
13
- }
14
- return date.toLocaleDateString('en-US', {
15
- year: 'numeric',
16
- month: 'short',
17
- day: 'numeric'
18
- })
19
- }
20
-
21
- // Currency formatting utility
22
- export function formatCurrency(amount: number): string {
23
- return new Intl.NumberFormat('en-US', {
24
- style: 'currency',
25
- currency: 'USD'
26
- }).format(amount)
27
- }
28
-
29
- // Text truncation utility
30
- export function truncateText(text: string, maxLength: number): string {
31
- if (text.length <= maxLength) {
32
- return text
33
- }
34
- return text.slice(0, maxLength) + '...'
35
- }
36
-
37
- // ID generation utility
38
- export function generateId(length: number = 8): string {
39
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
40
- let result = ''
41
- for (let i = 0; i < length; i++) {
42
- result += chars.charAt(Math.floor(Math.random() * chars.length))
43
- }
44
- return result
45
- }
46
-
47
- // Get client IP address from request
48
- export function getClientIP(request: Request): string {
49
- // Check various headers for IP address
50
- const forwarded = request.headers.get('x-forwarded-for')
51
- const realIP = request.headers.get('x-real-ip')
52
- const remoteAddr = request.headers.get('x-remote-addr')
53
-
54
- if (forwarded) {
55
- // x-forwarded-for can contain multiple IPs, take the first one
56
- return forwarded.split(',')[0].trim()
57
- }
58
-
59
- if (realIP) {
60
- return realIP
61
- }
62
-
63
- if (remoteAddr) {
64
- return remoteAddr
65
- }
66
-
67
- // Fallback to a default IP if none found
68
- return '127.0.0.1'
69
- }
@@ -1,215 +0,0 @@
1
- // Locked Component UI
2
- // Pro componentlerin locked state'ini gösteren wrapper component
3
-
4
- import React, { useState } from 'react'
5
- import { cn } from '../../lib/utils'
6
- import { Button } from './button'
7
- import { Badge } from './badge'
8
- import { Lock, Crown, Zap } from 'lucide-react'
9
- import Link from 'next/link'
10
- import { useComponentAccess } from '../../use-pro-access'
11
-
12
- interface MoonUILockedComponentProps {
13
- children: React.ReactNode
14
- componentName: string
15
- className?: string
16
- showPreview?: boolean
17
- previewDuration?: number // ms
18
- }
19
-
20
- export function LockedComponent({
21
- children,
22
- componentName,
23
- className,
24
- showPreview = true,
25
- previewDuration = 2000
26
- }: LockedComponentProps) {
27
- const [isPreviewActive, setIsPreviewActive] = useState(false)
28
- const [previewTimeout, setPreviewTimeout] = useState<NodeJS.Timeout | null>(null)
29
-
30
- const handleMouseEnter = () => {
31
- if (!showPreview) return
32
-
33
- setIsPreviewActive(true)
34
-
35
- // Clear existing timeout
36
- if (previewTimeout) {
37
- clearTimeout(previewTimeout)
38
- }
39
-
40
- // Set new timeout
41
- const timeout = setTimeout(() => {
42
- setIsPreviewActive(false)
43
- }, previewDuration)
44
-
45
- setPreviewTimeout(timeout)
46
- }
47
-
48
- const handleMouseLeave = () => {
49
- if (previewTimeout) {
50
- clearTimeout(previewTimeout)
51
- setPreviewTimeout(null)
52
- }
53
- setIsPreviewActive(false)
54
- }
55
-
56
- return (
57
- <div
58
- className={cn(
59
- "relative group cursor-pointer transition-all duration-300",
60
- className
61
- )}
62
- onMouseEnter={handleMouseEnter}
63
- onMouseLeave={handleMouseLeave}
64
- >
65
- {/* Component Content */}
66
- <div
67
- className={cn(
68
- "transition-all duration-300",
69
- !isPreviewActive && "blur-sm grayscale-[0.3] opacity-60"
70
- )}
71
- >
72
- {children}
73
- </div>
74
-
75
- {/* Overlay */}
76
- <div
77
- className={cn(
78
- "absolute inset-0 bg-gradient-to-br from-amber-500/10 via-transparent to-purple-500/10",
79
- "border-2 border-dashed border-amber-400/30 rounded-lg",
80
- "flex items-center justify-center",
81
- "transition-all duration-300",
82
- isPreviewActive ? "opacity-0" : "opacity-100"
83
- )}
84
- >
85
- <div className="text-center space-y-3 p-6">
86
- {/* Pro Badge */}
87
- <div className="flex justify-center">
88
- <Badge
89
- variant="secondary"
90
- className="bg-gradient-to-r from-amber-500 to-orange-500 text-white border-0 px-3 py-1"
91
- >
92
- <Crown className="w-3 h-3 mr-1" />
93
- PRO ONLY
94
- </Badge>
95
- </div>
96
-
97
- {/* Lock Icon */}
98
- <div className="flex justify-center">
99
- <div className="p-3 rounded-full bg-amber-100 dark:bg-amber-900/30">
100
- <Lock className="w-6 h-6 text-amber-600 dark:text-amber-400" />
101
- </div>
102
- </div>
103
-
104
- {/* Component Name */}
105
- <h3 className="font-semibold text-gray-900 dark:text-gray-100">
106
- {componentName}
107
- </h3>
108
-
109
- {/* Description */}
110
- <p className="text-sm text-gray-600 dark:text-gray-400 max-w-xs">
111
- This premium component is available with Pro subscription
112
- </p>
113
-
114
- {/* Upgrade Button */}
115
- <Link href="/pricing">
116
- <Button
117
- size="sm"
118
- className="bg-gradient-to-r from-amber-500 to-orange-500 hover:from-amber-600 hover:to-orange-600 border-0"
119
- >
120
- <Zap className="w-4 h-4 mr-2" />
121
- Upgrade to Pro
122
- </Button>
123
- </Link>
124
- </div>
125
- </div>
126
-
127
- {/* Preview Hint */}
128
- {showPreview && !isPreviewActive && (
129
- <div className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
130
- <div className="bg-black/80 text-white text-xs px-2 py-1 rounded">
131
- Hover to preview
132
- </div>
133
- </div>
134
- )}
135
- </div>
136
- )
137
- }
138
-
139
- // Pro Component Wrapper - Otomatik erişim kontrolü ile
140
- interface ProComponentWrapperProps {
141
- children: React.ReactNode
142
- componentId: string
143
- componentName?: string
144
- className?: string
145
- fallback?: React.ReactNode
146
- }
147
-
148
- export function ProComponentWrapper({
149
- children,
150
- componentId,
151
- componentName,
152
- className,
153
- fallback
154
- }: ProComponentWrapperProps) {
155
- const { canUse, isLocked, isLoading } = useComponentAccess(componentId)
156
-
157
- // Loading state
158
- if (isLoading) {
159
- return (
160
- <div className={cn("animate-pulse bg-gray-200 dark:bg-gray-800 rounded-lg h-32", className)}>
161
- <div className="flex items-center justify-center h-full">
162
- <div className="text-sm text-gray-500">Loading...</div>
163
- </div>
164
- </div>
165
- )
166
- }
167
-
168
- // Unlocked - show actual component
169
- if (canUse) {
170
- return <div className={className}>{children}</div>
171
- }
172
-
173
- // Locked - show locked state
174
- if (isLocked) {
175
- return (
176
- <LockedComponent
177
- componentName={componentName || componentId}
178
- className={className}
179
- >
180
- {fallback || children}
181
- </LockedComponent>
182
- )
183
- }
184
-
185
- // Default fallback
186
- return <div className={className}>{children}</div>
187
- }
188
-
189
- // Pro Badge Component
190
- export function ProBadge({ className }: { className?: string }) {
191
- return (
192
- <Badge
193
- variant="secondary"
194
- className={cn(
195
- "bg-gradient-to-r from-amber-500 to-orange-500 text-white border-0 text-xs",
196
- className
197
- )}
198
- >
199
- <Crown className="w-3 h-3 mr-1" />
200
- PRO
201
- </Badge>
202
- )
203
- }
204
-
205
- // Free Badge Component
206
- export function FreeBadge({ className }: { className?: string }) {
207
- return (
208
- <Badge
209
- variant="outline"
210
- className={cn("border-green-500 text-green-600 dark:text-green-400 text-xs", className)}
211
- >
212
- FREE
213
- </Badge>
214
- )
215
- }
@@ -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
- export 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
- export 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
- }