@nestledjs/data-browser 0.1.2 → 0.1.4
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/index.d.ts +15 -0
- package/index.js +64 -0
- package/lib/admin-data.d.ts +2 -0
- package/lib/components/filters/DateRangeFilter.d.ts +7 -0
- package/lib/components/filters/DateRangeFilter.js +105 -0
- package/lib/components/filters/NumberRangeFilter.d.ts +8 -0
- package/lib/components/filters/NumberRangeFilter.js +115 -0
- package/lib/components/filters/RelationComponents.d.ts +32 -0
- package/lib/components/filters/RelationComponents.js +265 -0
- package/lib/components/filters/RelationFilterField.d.ts +8 -0
- package/lib/components/filters/RelationFilterField.js +116 -0
- package/lib/components/filters/index.d.ts +4 -0
- package/lib/components/index.d.ts +2 -0
- package/lib/components/shared/AdminBreadcrumbs.d.ts +13 -0
- package/lib/components/shared/AdminBreadcrumbs.js +103 -0
- package/lib/components/shared/AdminErrorStates.d.ts +30 -0
- package/lib/components/shared/AdminErrorStates.js +214 -0
- package/lib/components/shared/AdminStatusDisplay.d.ts +21 -0
- package/lib/components/shared/AdminStatusDisplay.js +205 -0
- package/lib/components/shared/index.d.ts +3 -0
- package/lib/context/AdminDataContext.d.ts +47 -0
- package/lib/context/AdminDataContext.js +28 -0
- package/lib/hooks/useAdminList.d.ts +5 -0
- package/lib/hooks/useAdminList.js +41 -0
- package/lib/hooks/useClickOutside.d.ts +2 -0
- package/lib/hooks/useClickOutside.js +13 -0
- package/lib/hooks/useDebounce.d.ts +1 -0
- package/lib/hooks/useDebounce.js +13 -0
- package/lib/hooks/useRelationData.d.ts +7 -0
- package/lib/hooks/useRelationData.js +64 -0
- package/lib/layouts/AdminDataLayout.d.ts +5 -0
- package/lib/layouts/AdminDataLayout.js +315 -0
- package/lib/pages/AdminDataCreatePage.d.ts +4 -0
- package/lib/pages/AdminDataCreatePage.js +487 -0
- package/lib/pages/AdminDataEditPage.d.ts +4 -0
- package/lib/pages/AdminDataEditPage.js +903 -0
- package/lib/pages/AdminDataIndexPage.d.ts +4 -0
- package/lib/pages/AdminDataIndexPage.js +91 -0
- package/lib/pages/AdminDataListPage.d.ts +9 -0
- package/lib/pages/AdminDataListPage.js +1218 -0
- package/lib/types/index.d.ts +60 -0
- package/lib/types/index.js +16 -0
- package/lib/utils/graphql-utils.d.ts +25 -0
- package/lib/utils/graphql-utils.js +265 -0
- package/lib/utils/secure-storage.d.ts +50 -0
- package/lib/utils/secure-storage.js +145 -0
- package/lib/utils/string-utils.d.ts +6 -0
- package/lib/utils/string-utils.js +27 -0
- package/package.json +8 -2
- package/.babelrc +0 -12
- package/LICENSE +0 -21
- package/eslint.config.mjs +0 -12
- package/project.json +0 -35
- package/src/index.ts +0 -28
- package/src/lib/admin-data.spec.tsx +0 -10
- package/src/lib/admin-data.tsx +0 -9
- package/src/lib/components/filters/DateRangeFilter.tsx +0 -88
- package/src/lib/components/filters/NumberRangeFilter.tsx +0 -111
- package/src/lib/components/filters/RelationComponents.tsx +0 -176
- package/src/lib/components/filters/RelationFilterField.tsx +0 -106
- package/src/lib/components/filters/index.ts +0 -5
- package/src/lib/components/index.ts +0 -2
- package/src/lib/components/shared/AdminBreadcrumbs.tsx +0 -88
- package/src/lib/components/shared/AdminErrorStates.tsx +0 -180
- package/src/lib/components/shared/AdminStatusDisplay.tsx +0 -292
- package/src/lib/components/shared/index.ts +0 -3
- package/src/lib/context/AdminDataContext.tsx +0 -74
- package/src/lib/hooks/useAdminList.ts +0 -42
- package/src/lib/hooks/useClickOutside.ts +0 -21
- package/src/lib/hooks/useDebounce.ts +0 -16
- package/src/lib/hooks/useRelationData.ts +0 -114
- package/src/lib/layouts/AdminDataLayout.tsx +0 -251
- package/src/lib/pages/AdminDataCreatePage.tsx +0 -415
- package/src/lib/pages/AdminDataEditPage.tsx +0 -777
- package/src/lib/pages/AdminDataIndexPage.tsx +0 -50
- package/src/lib/pages/AdminDataListPage.tsx +0 -921
- package/src/lib/types/index.ts +0 -51
- package/src/lib/utils/graphql-utils.ts +0 -538
- package/src/lib/utils/secure-storage.ts +0 -305
- package/src/lib/utils/string-utils.ts +0 -53
- package/tsconfig.json +0 -17
- package/tsconfig.lib.json +0 -20
- package/vite.config.ts +0 -35
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { ExclamationTriangleIcon, ExclamationCircleIcon, XCircleIcon } from '@heroicons/react/24/outline'
|
|
3
|
-
|
|
4
|
-
export type ErrorSeverity = 'warning' | 'error' | 'critical'
|
|
5
|
-
|
|
6
|
-
export interface AdminErrorStateProps {
|
|
7
|
-
readonly title: string
|
|
8
|
-
readonly message?: string
|
|
9
|
-
readonly severity?: ErrorSeverity
|
|
10
|
-
readonly onRetry?: () => void
|
|
11
|
-
readonly onDismiss?: () => void
|
|
12
|
-
readonly className?: string
|
|
13
|
-
readonly showIcon?: boolean
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface AdminEmptyStateProps {
|
|
17
|
-
readonly title: string
|
|
18
|
-
readonly message?: string
|
|
19
|
-
readonly actionLabel?: string
|
|
20
|
-
readonly onAction?: () => void
|
|
21
|
-
readonly className?: string
|
|
22
|
-
readonly icon?: React.ComponentType<{ className?: string }>
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface AdminLoadingStateProps {
|
|
26
|
-
readonly title?: string
|
|
27
|
-
readonly message?: string
|
|
28
|
-
readonly className?: string
|
|
29
|
-
readonly size?: 'small' | 'medium' | 'large'
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const getSeverityConfig = (severity: ErrorSeverity) => {
|
|
33
|
-
const configs = {
|
|
34
|
-
warning: {
|
|
35
|
-
icon: ExclamationTriangleIcon,
|
|
36
|
-
bgColor: 'bg-yellow-50',
|
|
37
|
-
borderColor: 'border-yellow-200',
|
|
38
|
-
iconColor: 'text-yellow-400',
|
|
39
|
-
titleColor: 'text-yellow-800',
|
|
40
|
-
messageColor: 'text-yellow-700',
|
|
41
|
-
buttonColor: 'bg-yellow-100 text-yellow-800 hover:bg-yellow-200',
|
|
42
|
-
},
|
|
43
|
-
error: {
|
|
44
|
-
icon: ExclamationCircleIcon,
|
|
45
|
-
bgColor: 'bg-red-50',
|
|
46
|
-
borderColor: 'border-red-200',
|
|
47
|
-
iconColor: 'text-red-400',
|
|
48
|
-
titleColor: 'text-red-800',
|
|
49
|
-
messageColor: 'text-red-700',
|
|
50
|
-
buttonColor: 'bg-red-100 text-red-800 hover:bg-red-200',
|
|
51
|
-
},
|
|
52
|
-
critical: {
|
|
53
|
-
icon: XCircleIcon,
|
|
54
|
-
bgColor: 'bg-red-100',
|
|
55
|
-
borderColor: 'border-red-300',
|
|
56
|
-
iconColor: 'text-red-500',
|
|
57
|
-
titleColor: 'text-red-900',
|
|
58
|
-
messageColor: 'text-red-800',
|
|
59
|
-
buttonColor: 'bg-red-200 text-red-900 hover:bg-red-300',
|
|
60
|
-
},
|
|
61
|
-
}
|
|
62
|
-
return configs[severity]
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function AdminErrorState({
|
|
66
|
-
title,
|
|
67
|
-
message,
|
|
68
|
-
severity = 'error',
|
|
69
|
-
onRetry,
|
|
70
|
-
onDismiss,
|
|
71
|
-
className = '',
|
|
72
|
-
showIcon = true,
|
|
73
|
-
}: Readonly<AdminErrorStateProps>) {
|
|
74
|
-
const config = getSeverityConfig(severity)
|
|
75
|
-
const IconComponent = config.icon
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<div className={`rounded-md border p-4 ${config.bgColor} ${config.borderColor} ${className}`}>
|
|
79
|
-
<div className="flex">
|
|
80
|
-
{showIcon && (
|
|
81
|
-
<div className="flex-shrink-0">
|
|
82
|
-
<IconComponent className={`h-5 w-5 ${config.iconColor}`} aria-hidden="true" />
|
|
83
|
-
</div>
|
|
84
|
-
)}
|
|
85
|
-
<div className={showIcon ? 'ml-3' : ''}>
|
|
86
|
-
<h3 className={`text-sm font-medium ${config.titleColor}`}>
|
|
87
|
-
{title}
|
|
88
|
-
</h3>
|
|
89
|
-
{message && (
|
|
90
|
-
<div className={`mt-2 text-sm ${config.messageColor}`}>
|
|
91
|
-
<p>{message}</p>
|
|
92
|
-
</div>
|
|
93
|
-
)}
|
|
94
|
-
{(onRetry || onDismiss) && (
|
|
95
|
-
<div className="mt-4">
|
|
96
|
-
<div className="-mx-2 -my-1.5 flex">
|
|
97
|
-
{onRetry && (
|
|
98
|
-
<button
|
|
99
|
-
type="button"
|
|
100
|
-
onClick={onRetry}
|
|
101
|
-
className={`rounded-md px-2 py-1.5 text-sm font-medium ${config.buttonColor} focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500`}
|
|
102
|
-
>
|
|
103
|
-
Try Again
|
|
104
|
-
</button>
|
|
105
|
-
)}
|
|
106
|
-
{onDismiss && (
|
|
107
|
-
<button
|
|
108
|
-
type="button"
|
|
109
|
-
onClick={onDismiss}
|
|
110
|
-
className={`ml-3 rounded-md px-2 py-1.5 text-sm font-medium ${config.buttonColor} focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500`}
|
|
111
|
-
>
|
|
112
|
-
Dismiss
|
|
113
|
-
</button>
|
|
114
|
-
)}
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
)}
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function AdminEmptyState({
|
|
125
|
-
title,
|
|
126
|
-
message,
|
|
127
|
-
actionLabel,
|
|
128
|
-
onAction,
|
|
129
|
-
className = '',
|
|
130
|
-
icon: IconComponent,
|
|
131
|
-
}: Readonly<AdminEmptyStateProps>) {
|
|
132
|
-
return (
|
|
133
|
-
<div className={`text-center ${className}`}>
|
|
134
|
-
{IconComponent && (
|
|
135
|
-
<IconComponent className="mx-auto h-12 w-12 text-gray-400" />
|
|
136
|
-
)}
|
|
137
|
-
<h3 className="mt-2 text-sm font-medium text-gray-900">{title}</h3>
|
|
138
|
-
{message && (
|
|
139
|
-
<p className="mt-1 text-sm text-gray-500">{message}</p>
|
|
140
|
-
)}
|
|
141
|
-
{actionLabel && onAction && (
|
|
142
|
-
<div className="mt-6">
|
|
143
|
-
<button
|
|
144
|
-
type="button"
|
|
145
|
-
onClick={onAction}
|
|
146
|
-
className="inline-flex items-center rounded-md bg-green-web px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-green-web-800 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-web"
|
|
147
|
-
>
|
|
148
|
-
{actionLabel}
|
|
149
|
-
</button>
|
|
150
|
-
</div>
|
|
151
|
-
)}
|
|
152
|
-
</div>
|
|
153
|
-
)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export function AdminLoadingState({
|
|
157
|
-
title = 'Loading...',
|
|
158
|
-
message,
|
|
159
|
-
className = '',
|
|
160
|
-
size = 'medium',
|
|
161
|
-
}: Readonly<AdminLoadingStateProps>) {
|
|
162
|
-
const getSizeClasses = () => {
|
|
163
|
-
const sizes = {
|
|
164
|
-
small: 'h-4 w-4',
|
|
165
|
-
medium: 'h-8 w-8',
|
|
166
|
-
large: 'h-12 w-12',
|
|
167
|
-
}
|
|
168
|
-
return sizes[size]
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return (
|
|
172
|
-
<div className={`flex flex-col items-center justify-center p-8 ${className}`}>
|
|
173
|
-
<div className={`animate-spin rounded-full border-b-2 border-green-web ${getSizeClasses()}`}></div>
|
|
174
|
-
<h3 className="mt-4 text-sm font-medium text-gray-900">{title}</h3>
|
|
175
|
-
{message && (
|
|
176
|
-
<p className="mt-1 text-sm text-gray-500">{message}</p>
|
|
177
|
-
)}
|
|
178
|
-
</div>
|
|
179
|
-
)
|
|
180
|
-
}
|
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import {
|
|
3
|
-
CheckCircleIcon,
|
|
4
|
-
XCircleIcon,
|
|
5
|
-
ExclamationTriangleIcon,
|
|
6
|
-
ClockIcon,
|
|
7
|
-
QuestionMarkCircleIcon,
|
|
8
|
-
PauseCircleIcon,
|
|
9
|
-
} from '@heroicons/react/24/solid'
|
|
10
|
-
|
|
11
|
-
export type StatusType =
|
|
12
|
-
| 'active'
|
|
13
|
-
| 'inactive'
|
|
14
|
-
| 'pending'
|
|
15
|
-
| 'approved'
|
|
16
|
-
| 'rejected'
|
|
17
|
-
| 'warning'
|
|
18
|
-
| 'error'
|
|
19
|
-
| 'success'
|
|
20
|
-
| 'paused'
|
|
21
|
-
| 'unknown'
|
|
22
|
-
|
|
23
|
-
export type StatusSize = 'small' | 'medium' | 'large'
|
|
24
|
-
export type StatusVariant = 'badge' | 'pill' | 'dot' | 'full'
|
|
25
|
-
|
|
26
|
-
export interface AdminStatusDisplayProps {
|
|
27
|
-
readonly status: StatusType
|
|
28
|
-
readonly label?: string
|
|
29
|
-
readonly size?: StatusSize
|
|
30
|
-
readonly variant?: StatusVariant
|
|
31
|
-
readonly showIcon?: boolean
|
|
32
|
-
readonly className?: string
|
|
33
|
-
readonly onClick?: () => void
|
|
34
|
-
readonly tooltip?: string
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface AdminUserStatusProps {
|
|
38
|
-
readonly status: 'online' | 'offline' | 'away' | 'busy'
|
|
39
|
-
readonly showLabel?: boolean
|
|
40
|
-
readonly size?: StatusSize
|
|
41
|
-
readonly className?: string
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
interface StatusConfig {
|
|
45
|
-
icon: React.ComponentType<{ className?: string }>
|
|
46
|
-
bgColor: string
|
|
47
|
-
textColor: string
|
|
48
|
-
borderColor: string
|
|
49
|
-
defaultLabel: string
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Extract status configuration logic
|
|
53
|
-
const getStatusConfig = (status: StatusType): StatusConfig => {
|
|
54
|
-
const configs: Record<StatusType, StatusConfig> = {
|
|
55
|
-
active: {
|
|
56
|
-
icon: CheckCircleIcon,
|
|
57
|
-
bgColor: 'bg-green-100',
|
|
58
|
-
textColor: 'text-green-800',
|
|
59
|
-
borderColor: 'border-green-200',
|
|
60
|
-
defaultLabel: 'Active',
|
|
61
|
-
},
|
|
62
|
-
inactive: {
|
|
63
|
-
icon: XCircleIcon,
|
|
64
|
-
bgColor: 'bg-gray-100',
|
|
65
|
-
textColor: 'text-gray-800',
|
|
66
|
-
borderColor: 'border-gray-200',
|
|
67
|
-
defaultLabel: 'Inactive',
|
|
68
|
-
},
|
|
69
|
-
pending: {
|
|
70
|
-
icon: ClockIcon,
|
|
71
|
-
bgColor: 'bg-yellow-100',
|
|
72
|
-
textColor: 'text-yellow-800',
|
|
73
|
-
borderColor: 'border-yellow-200',
|
|
74
|
-
defaultLabel: 'Pending',
|
|
75
|
-
},
|
|
76
|
-
approved: {
|
|
77
|
-
icon: CheckCircleIcon,
|
|
78
|
-
bgColor: 'bg-green-100',
|
|
79
|
-
textColor: 'text-green-800',
|
|
80
|
-
borderColor: 'border-green-200',
|
|
81
|
-
defaultLabel: 'Approved',
|
|
82
|
-
},
|
|
83
|
-
rejected: {
|
|
84
|
-
icon: XCircleIcon,
|
|
85
|
-
bgColor: 'bg-red-100',
|
|
86
|
-
textColor: 'text-red-800',
|
|
87
|
-
borderColor: 'border-red-200',
|
|
88
|
-
defaultLabel: 'Rejected',
|
|
89
|
-
},
|
|
90
|
-
warning: {
|
|
91
|
-
icon: ExclamationTriangleIcon,
|
|
92
|
-
bgColor: 'bg-yellow-100',
|
|
93
|
-
textColor: 'text-yellow-800',
|
|
94
|
-
borderColor: 'border-yellow-200',
|
|
95
|
-
defaultLabel: 'Warning',
|
|
96
|
-
},
|
|
97
|
-
error: {
|
|
98
|
-
icon: XCircleIcon,
|
|
99
|
-
bgColor: 'bg-red-100',
|
|
100
|
-
textColor: 'text-red-800',
|
|
101
|
-
borderColor: 'border-red-200',
|
|
102
|
-
defaultLabel: 'Error',
|
|
103
|
-
},
|
|
104
|
-
success: {
|
|
105
|
-
icon: CheckCircleIcon,
|
|
106
|
-
bgColor: 'bg-green-100',
|
|
107
|
-
textColor: 'text-green-800',
|
|
108
|
-
borderColor: 'border-green-200',
|
|
109
|
-
defaultLabel: 'Success',
|
|
110
|
-
},
|
|
111
|
-
paused: {
|
|
112
|
-
icon: PauseCircleIcon,
|
|
113
|
-
bgColor: 'bg-blue-100',
|
|
114
|
-
textColor: 'text-blue-800',
|
|
115
|
-
borderColor: 'border-blue-200',
|
|
116
|
-
defaultLabel: 'Paused',
|
|
117
|
-
},
|
|
118
|
-
unknown: {
|
|
119
|
-
icon: QuestionMarkCircleIcon,
|
|
120
|
-
bgColor: 'bg-gray-100',
|
|
121
|
-
textColor: 'text-gray-800',
|
|
122
|
-
borderColor: 'border-gray-200',
|
|
123
|
-
defaultLabel: 'Unknown',
|
|
124
|
-
},
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return configs[status]
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Extract size configuration logic
|
|
131
|
-
const getSizeClasses = (size: StatusSize) => {
|
|
132
|
-
const sizeClasses = {
|
|
133
|
-
small: {
|
|
134
|
-
text: 'text-xs',
|
|
135
|
-
padding: 'px-2 py-1',
|
|
136
|
-
icon: 'h-3 w-3',
|
|
137
|
-
dot: 'h-2 w-2',
|
|
138
|
-
},
|
|
139
|
-
medium: {
|
|
140
|
-
text: 'text-sm',
|
|
141
|
-
padding: 'px-2.5 py-1.5',
|
|
142
|
-
icon: 'h-4 w-4',
|
|
143
|
-
dot: 'h-3 w-3',
|
|
144
|
-
},
|
|
145
|
-
large: {
|
|
146
|
-
text: 'text-base',
|
|
147
|
-
padding: 'px-3 py-2',
|
|
148
|
-
icon: 'h-5 w-5',
|
|
149
|
-
dot: 'h-4 w-4',
|
|
150
|
-
},
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return sizeClasses[size]
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Extract variant styling logic
|
|
157
|
-
const getVariantClasses = (variant: StatusVariant) => {
|
|
158
|
-
const variantClasses = {
|
|
159
|
-
badge: 'inline-flex items-center rounded border font-medium',
|
|
160
|
-
pill: 'inline-flex items-center rounded-full border font-medium',
|
|
161
|
-
dot: 'inline-flex items-center font-medium',
|
|
162
|
-
full: 'flex items-center justify-center rounded border font-medium w-full',
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return variantClasses[variant]
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Extract user status configuration logic
|
|
169
|
-
const getUserStatusConfig = (status: AdminUserStatusProps['status']) => {
|
|
170
|
-
const configs = {
|
|
171
|
-
online: {
|
|
172
|
-
color: 'bg-green-400',
|
|
173
|
-
label: 'Online',
|
|
174
|
-
},
|
|
175
|
-
offline: {
|
|
176
|
-
color: 'bg-gray-400',
|
|
177
|
-
label: 'Offline',
|
|
178
|
-
},
|
|
179
|
-
away: {
|
|
180
|
-
color: 'bg-yellow-400',
|
|
181
|
-
label: 'Away',
|
|
182
|
-
},
|
|
183
|
-
busy: {
|
|
184
|
-
color: 'bg-red-400',
|
|
185
|
-
label: 'Busy',
|
|
186
|
-
},
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return configs[status]
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
export function AdminStatusDisplay({
|
|
193
|
-
status,
|
|
194
|
-
label,
|
|
195
|
-
size = 'medium',
|
|
196
|
-
variant = 'badge',
|
|
197
|
-
showIcon = true,
|
|
198
|
-
className = '',
|
|
199
|
-
onClick,
|
|
200
|
-
tooltip,
|
|
201
|
-
}: Readonly<AdminStatusDisplayProps>) {
|
|
202
|
-
const config = getStatusConfig(status)
|
|
203
|
-
const sizeClasses = getSizeClasses(size)
|
|
204
|
-
const variantClasses = getVariantClasses(variant)
|
|
205
|
-
const IconComponent = config.icon
|
|
206
|
-
|
|
207
|
-
const displayLabel = label || config.defaultLabel
|
|
208
|
-
|
|
209
|
-
const shouldShowDot = variant === 'dot'
|
|
210
|
-
const shouldShowIcon = showIcon && !shouldShowDot
|
|
211
|
-
const isClickable = !!onClick
|
|
212
|
-
|
|
213
|
-
const baseClasses = `${variantClasses} ${sizeClasses.text} ${sizeClasses.padding} ${config.bgColor} ${config.textColor} ${config.borderColor}`
|
|
214
|
-
const interactiveClasses = isClickable ? 'cursor-pointer hover:opacity-80' : ''
|
|
215
|
-
const finalClasses = `${baseClasses} ${interactiveClasses} ${className}`.trim()
|
|
216
|
-
|
|
217
|
-
const renderDot = () => {
|
|
218
|
-
if (!shouldShowDot) return null
|
|
219
|
-
|
|
220
|
-
return (
|
|
221
|
-
<span className={`inline-block rounded-full mr-2 ${config.bgColor.replace('bg-', 'bg-').replace('-100', '-400')} ${sizeClasses.dot}`} />
|
|
222
|
-
)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const renderIcon = () => {
|
|
226
|
-
if (!shouldShowIcon) return null
|
|
227
|
-
|
|
228
|
-
return (
|
|
229
|
-
<IconComponent className={`${sizeClasses.icon} ${displayLabel ? 'mr-1.5' : ''}`} />
|
|
230
|
-
)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const content = (
|
|
234
|
-
<>
|
|
235
|
-
{renderDot()}
|
|
236
|
-
{renderIcon()}
|
|
237
|
-
{displayLabel}
|
|
238
|
-
</>
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
const handleClick = () => {
|
|
242
|
-
if (onClick) {
|
|
243
|
-
onClick()
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (isClickable) {
|
|
248
|
-
return (
|
|
249
|
-
<button
|
|
250
|
-
type="button"
|
|
251
|
-
onClick={handleClick}
|
|
252
|
-
className={finalClasses}
|
|
253
|
-
title={tooltip}
|
|
254
|
-
>
|
|
255
|
-
{content}
|
|
256
|
-
</button>
|
|
257
|
-
)
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
return (
|
|
261
|
-
<span className={finalClasses} title={tooltip}>
|
|
262
|
-
{content}
|
|
263
|
-
</span>
|
|
264
|
-
)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
export function AdminUserStatus({
|
|
268
|
-
status,
|
|
269
|
-
showLabel = false,
|
|
270
|
-
size = 'medium',
|
|
271
|
-
className = '',
|
|
272
|
-
}: Readonly<AdminUserStatusProps>) {
|
|
273
|
-
const config = getUserStatusConfig(status)
|
|
274
|
-
const sizeClasses = getSizeClasses(size)
|
|
275
|
-
|
|
276
|
-
const dotSizeClass = sizeClasses.dot
|
|
277
|
-
const textSizeClass = showLabel ? sizeClasses.text : ''
|
|
278
|
-
|
|
279
|
-
return (
|
|
280
|
-
<div className={`inline-flex items-center ${className}`}>
|
|
281
|
-
<span
|
|
282
|
-
className={`inline-block rounded-full ${config.color} ${dotSizeClass}`}
|
|
283
|
-
aria-label={config.label}
|
|
284
|
-
/>
|
|
285
|
-
{showLabel && (
|
|
286
|
-
<span className={`ml-2 text-gray-700 ${textSizeClass}`}>
|
|
287
|
-
{config.label}
|
|
288
|
-
</span>
|
|
289
|
-
)}
|
|
290
|
-
</div>
|
|
291
|
-
)
|
|
292
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { createContext, useContext, type ReactNode } from 'react'
|
|
2
|
-
|
|
3
|
-
export interface AdminDataContextValue {
|
|
4
|
-
/** The SDK namespace for dynamic GraphQL document lookups */
|
|
5
|
-
sdk: any
|
|
6
|
-
/** Array of database models from Prisma schema */
|
|
7
|
-
databaseModels: any[]
|
|
8
|
-
/** Base path for admin data routes (e.g., "/admin/data") */
|
|
9
|
-
basePath?: string
|
|
10
|
-
/** Form theme configuration for @nestledjs/forms */
|
|
11
|
-
formTheme: any
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const AdminDataContext = createContext<AdminDataContextValue | null>(null)
|
|
15
|
-
|
|
16
|
-
export interface AdminDataProviderProps {
|
|
17
|
-
children: ReactNode
|
|
18
|
-
/** The entire SDK namespace (e.g., import * as Sdk from '@your-project/shared/sdk') */
|
|
19
|
-
sdk: any
|
|
20
|
-
/** DATABASE_MODELS array from your SDK */
|
|
21
|
-
databaseModels: any[]
|
|
22
|
-
/** Optional base path for routes (defaults to "/admin/data") */
|
|
23
|
-
basePath?: string
|
|
24
|
-
/** Form theme configuration for @nestledjs/forms */
|
|
25
|
-
formTheme: any
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Provider component that supplies SDK and database models to admin data components
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```tsx
|
|
33
|
-
* import * as Sdk from '@your-project/shared/sdk'
|
|
34
|
-
* import { DATABASE_MODELS } from '@your-project/shared/sdk'
|
|
35
|
-
* import { AdminDataProvider } from '@nestledjs/admin-data'
|
|
36
|
-
*
|
|
37
|
-
* <AdminDataProvider
|
|
38
|
-
* sdk={Sdk}
|
|
39
|
-
* databaseModels={DATABASE_MODELS}
|
|
40
|
-
* formTheme={formTheme}
|
|
41
|
-
* basePath="/admin/data"
|
|
42
|
-
* >
|
|
43
|
-
* <AdminDataLayout />
|
|
44
|
-
* </AdminDataProvider>
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
export function AdminDataProvider({
|
|
48
|
-
children,
|
|
49
|
-
sdk,
|
|
50
|
-
databaseModels,
|
|
51
|
-
basePath = '/admin/data',
|
|
52
|
-
formTheme
|
|
53
|
-
}: AdminDataProviderProps) {
|
|
54
|
-
return (
|
|
55
|
-
<AdminDataContext.Provider value={{ sdk, databaseModels, basePath, formTheme }}>
|
|
56
|
-
{children}
|
|
57
|
-
</AdminDataContext.Provider>
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Hook to access the admin data context
|
|
63
|
-
* @throws Error if used outside of AdminDataProvider
|
|
64
|
-
*/
|
|
65
|
-
export function useAdminDataContext() {
|
|
66
|
-
const context = useContext(AdminDataContext)
|
|
67
|
-
if (!context) {
|
|
68
|
-
throw new Error(
|
|
69
|
-
'useAdminDataContext must be used within AdminDataProvider. ' +
|
|
70
|
-
'Make sure to wrap your admin data routes with <AdminDataProvider>.'
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
|
-
return context
|
|
74
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { useReducer } from 'react'
|
|
2
|
-
import { AdminListAction, AdminListState, initialState } from '../types'
|
|
3
|
-
|
|
4
|
-
// State reducer for performance optimization
|
|
5
|
-
function adminListReducer(state: AdminListState, action: AdminListAction): AdminListState {
|
|
6
|
-
switch (action.type) {
|
|
7
|
-
case 'SET_SEARCH':
|
|
8
|
-
return { ...state, search: action.payload }
|
|
9
|
-
case 'SET_DEBOUNCED_SEARCH':
|
|
10
|
-
return { ...state, debouncedSearch: action.payload }
|
|
11
|
-
case 'SET_SKIP':
|
|
12
|
-
return { ...state, skip: action.payload }
|
|
13
|
-
case 'SET_PAGE_SIZE':
|
|
14
|
-
return { ...state, pageSize: action.payload }
|
|
15
|
-
case 'SET_SORT':
|
|
16
|
-
return { ...state, sort: action.payload }
|
|
17
|
-
case 'SET_VISIBLE_COLUMNS':
|
|
18
|
-
return { ...state, visibleColumns: action.payload }
|
|
19
|
-
case 'TOGGLE_COLUMN_SELECTOR':
|
|
20
|
-
return { ...state, showColumnSelector: !state.showColumnSelector }
|
|
21
|
-
case 'SET_SEARCH_FIELDS':
|
|
22
|
-
return { ...state, searchFields: action.payload }
|
|
23
|
-
case 'TOGGLE_SEARCH_FIELD_SELECTOR':
|
|
24
|
-
return { ...state, showSearchFieldSelector: !state.showSearchFieldSelector }
|
|
25
|
-
case 'SET_FILTERS':
|
|
26
|
-
return { ...state, filters: action.payload }
|
|
27
|
-
case 'TOGGLE_FILTERS':
|
|
28
|
-
return { ...state, showFilters: !state.showFilters }
|
|
29
|
-
case 'RESET_PAGINATION':
|
|
30
|
-
return { ...state, skip: 0 }
|
|
31
|
-
case 'RESET_FILTERS':
|
|
32
|
-
return { ...state, filters: {} }
|
|
33
|
-
default:
|
|
34
|
-
return state
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function useAdminList() {
|
|
39
|
-
const [state, dispatch] = useReducer(adminListReducer, initialState)
|
|
40
|
-
|
|
41
|
-
return { state, dispatch }
|
|
42
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import React, { useEffect } from 'react'
|
|
2
|
-
|
|
3
|
-
// Custom hook for click outside detection
|
|
4
|
-
export function useClickOutside(
|
|
5
|
-
ref: React.RefObject<HTMLDivElement | null>,
|
|
6
|
-
handler: () => void,
|
|
7
|
-
isActive = true
|
|
8
|
-
) {
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
if (!isActive) return
|
|
11
|
-
|
|
12
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
13
|
-
if (ref.current && !ref.current.contains(event.target as Node)) {
|
|
14
|
-
handler()
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
document.addEventListener('mousedown', handleClickOutside)
|
|
19
|
-
return () => document.removeEventListener('mousedown', handleClickOutside)
|
|
20
|
-
}, [handler, isActive]) // Removed 'ref' from dependency array as refs are stable
|
|
21
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react'
|
|
2
|
-
|
|
3
|
-
// Custom hook for debouncing values
|
|
4
|
-
export function useDebounce<T>(value: T, delay: number): T {
|
|
5
|
-
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
|
6
|
-
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
const timer = setTimeout(() => {
|
|
9
|
-
setDebouncedValue(value)
|
|
10
|
-
}, delay)
|
|
11
|
-
|
|
12
|
-
return () => clearTimeout(timer)
|
|
13
|
-
}, [value, delay])
|
|
14
|
-
|
|
15
|
-
return debouncedValue
|
|
16
|
-
}
|