@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.
- package/dist/index.js +8174 -2834
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8074 -2645
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/ui/accordion.tsx +8 -11
- package/src/components/ui/alert.tsx +10 -13
- package/src/components/ui/analyze-components.txt +4 -0
- package/src/components/ui/aspect-ratio.tsx +6 -9
- package/src/components/ui/avatar.tsx +7 -10
- package/src/components/ui/badge.tsx +4 -7
- package/src/components/ui/breadcrumb.tsx +29 -31
- package/src/components/ui/button.tsx +11 -15
- package/src/components/ui/calendar.tsx +6 -9
- package/src/components/ui/card.tsx +19 -22
- package/src/components/ui/checkbox.tsx +5 -8
- package/src/components/ui/collapsible.tsx +10 -12
- package/src/components/ui/color-picker.tsx +2 -2
- package/src/components/ui/command.tsx +26 -28
- package/src/components/ui/component-analysis-report.md +118 -0
- package/src/components/ui/data-table.tsx +2 -2
- package/src/components/ui/date-picker.tsx +9 -9
- package/src/components/ui/dialog.tsx +21 -23
- package/src/components/ui/draggable-list.tsx +81 -0
- package/src/components/ui/dropdown-menu.tsx +34 -36
- package/src/components/ui/file-upload.tsx +3 -3
- package/src/components/ui/gesture-drawer.tsx +146 -0
- package/src/components/ui/index.ts +43 -9
- package/src/components/ui/input.tsx +6 -9
- package/src/components/ui/label.tsx +4 -7
- package/src/components/ui/moon-logo.tsx +1 -1
- package/src/components/ui/pagination.tsx +5 -7
- package/src/components/ui/popover.tsx +9 -11
- package/src/components/ui/progress.tsx +6 -9
- package/src/components/ui/radio-group.tsx +7 -10
- package/src/components/ui/rich-text-editor/index.tsx +71 -0
- package/src/components/ui/select.tsx +21 -23
- package/src/components/ui/separator.tsx +5 -8
- package/src/components/ui/simple-editor.tsx +4 -4
- package/src/components/ui/skeleton.tsx +7 -10
- package/src/components/ui/slider.tsx +5 -8
- package/src/components/ui/swipeable-card.tsx +69 -0
- package/src/components/ui/switch.tsx +3 -6
- package/src/components/ui/table.tsx +29 -31
- package/src/components/ui/tabs.tsx +9 -12
- package/src/components/ui/textarea.tsx +4 -7
- package/src/components/ui/toast.tsx +7 -9
- package/src/components/ui/toggle.tsx +4 -7
- package/src/components/ui/tooltip.tsx +11 -13
- package/src/index.tsx +13 -7
- package/src/lib/utils.ts +2 -65
- package/src/components/ui/locked-component.tsx +0 -215
- 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
|
|
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
|
|
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
|
-
|
|
44
|
+
Textarea.displayName = "Textarea"
|
|
45
45
|
|
|
46
|
-
export {
|
|
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
|
|
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
|
|
49
|
+
VariantProps<typeof toastVariants>;
|
|
50
50
|
|
|
51
51
|
type ToastActionElement = React.ReactElement<typeof ToastAction>;
|
|
52
52
|
|
|
53
|
-
const
|
|
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 {
|
|
300
|
+
export {
|
|
301
|
+
type ToastProps,
|
|
301
302
|
type ToastActionElement,
|
|
302
303
|
ToastProvider,
|
|
303
304
|
ToastViewport,
|
|
304
|
-
|
|
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
|
|
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
|
|
31
|
+
export interface ToggleProps extends React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root>, VariantProps<typeof toggleVariants> {}
|
|
32
32
|
|
|
33
|
-
const
|
|
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 {
|
|
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
|
|
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
|
|
36
|
+
const Tooltip = TooltipPrimitive.Root
|
|
37
37
|
|
|
38
|
-
const
|
|
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
|
|
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
|
|
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 {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
//
|
|
2
|
-
//
|
|
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
|
|
2
|
-
import { twMerge } from
|
|
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
|
-
}
|
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
|
-
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
|
-
}
|