@djangocfg/ui-nextjs 1.4.45

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 (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -0
  3. package/package.json +110 -0
  4. package/src/animations/AnimatedBackground.tsx +645 -0
  5. package/src/animations/index.ts +2 -0
  6. package/src/blocks/ArticleCard.tsx +94 -0
  7. package/src/blocks/ArticleList.tsx +95 -0
  8. package/src/blocks/CTASection.tsx +136 -0
  9. package/src/blocks/FeatureSection.tsx +104 -0
  10. package/src/blocks/Hero.tsx +102 -0
  11. package/src/blocks/NewsletterSection.tsx +119 -0
  12. package/src/blocks/StatsSection.tsx +103 -0
  13. package/src/blocks/SuperHero.tsx +328 -0
  14. package/src/blocks/TestimonialSection.tsx +122 -0
  15. package/src/blocks/index.ts +9 -0
  16. package/src/components/README.md +2018 -0
  17. package/src/components/breadcrumb-navigation.tsx +127 -0
  18. package/src/components/breadcrumb.tsx +132 -0
  19. package/src/components/button-download.tsx +275 -0
  20. package/src/components/dropdown-menu.tsx +219 -0
  21. package/src/components/index.ts +86 -0
  22. package/src/components/markdown/MarkdownMessage.tsx +338 -0
  23. package/src/components/markdown/index.ts +5 -0
  24. package/src/components/menubar.tsx +274 -0
  25. package/src/components/multi-select-pro/async.tsx +608 -0
  26. package/src/components/multi-select-pro/helpers.tsx +84 -0
  27. package/src/components/multi-select-pro/index.tsx +622 -0
  28. package/src/components/navigation-menu.tsx +153 -0
  29. package/src/components/pagination-static.tsx +348 -0
  30. package/src/components/pagination.tsx +138 -0
  31. package/src/components/phone-input.tsx +276 -0
  32. package/src/components/sidebar.tsx +866 -0
  33. package/src/components/sonner.tsx +31 -0
  34. package/src/components/ssr-pagination.tsx +237 -0
  35. package/src/hooks/index.ts +19 -0
  36. package/src/hooks/useCfgRouter.ts +153 -0
  37. package/src/hooks/useLocalStorage.ts +221 -0
  38. package/src/hooks/useQueryParams.ts +73 -0
  39. package/src/hooks/useSessionStorage.ts +188 -0
  40. package/src/hooks/useTheme.ts +57 -0
  41. package/src/index.ts +24 -0
  42. package/src/lib/index.ts +2 -0
  43. package/src/styles/index.css +2 -0
  44. package/src/theme/ForceTheme.tsx +115 -0
  45. package/src/theme/ThemeProvider.tsx +82 -0
  46. package/src/theme/ThemeToggle.tsx +52 -0
  47. package/src/theme/index.ts +3 -0
  48. package/src/tools/JsonForm/JsonSchemaForm.tsx +199 -0
  49. package/src/tools/JsonForm/examples/BotConfigExample.tsx +245 -0
  50. package/src/tools/JsonForm/examples/RealBotConfigExample.tsx +157 -0
  51. package/src/tools/JsonForm/index.ts +46 -0
  52. package/src/tools/JsonForm/templates/ArrayFieldItemTemplate.tsx +46 -0
  53. package/src/tools/JsonForm/templates/ArrayFieldTemplate.tsx +73 -0
  54. package/src/tools/JsonForm/templates/BaseInputTemplate.tsx +106 -0
  55. package/src/tools/JsonForm/templates/ErrorListTemplate.tsx +34 -0
  56. package/src/tools/JsonForm/templates/FieldTemplate.tsx +61 -0
  57. package/src/tools/JsonForm/templates/ObjectFieldTemplate.tsx +43 -0
  58. package/src/tools/JsonForm/templates/index.ts +12 -0
  59. package/src/tools/JsonForm/types.ts +83 -0
  60. package/src/tools/JsonForm/utils.ts +212 -0
  61. package/src/tools/JsonForm/widgets/CheckboxWidget.tsx +36 -0
  62. package/src/tools/JsonForm/widgets/NumberWidget.tsx +88 -0
  63. package/src/tools/JsonForm/widgets/SelectWidget.tsx +100 -0
  64. package/src/tools/JsonForm/widgets/SwitchWidget.tsx +34 -0
  65. package/src/tools/JsonForm/widgets/TextWidget.tsx +95 -0
  66. package/src/tools/JsonForm/widgets/index.ts +12 -0
  67. package/src/tools/JsonTree/index.tsx +252 -0
  68. package/src/tools/LottiePlayer/LottiePlayer.client.tsx +212 -0
  69. package/src/tools/LottiePlayer/index.tsx +54 -0
  70. package/src/tools/LottiePlayer/types.ts +108 -0
  71. package/src/tools/LottiePlayer/useLottie.ts +163 -0
  72. package/src/tools/Mermaid/Mermaid.client.tsx +341 -0
  73. package/src/tools/Mermaid/index.tsx +40 -0
  74. package/src/tools/OpenapiViewer/components/EndpointInfo.tsx +144 -0
  75. package/src/tools/OpenapiViewer/components/EndpointsLibrary.tsx +255 -0
  76. package/src/tools/OpenapiViewer/components/PlaygroundLayout.tsx +123 -0
  77. package/src/tools/OpenapiViewer/components/PlaygroundStepper.tsx +98 -0
  78. package/src/tools/OpenapiViewer/components/RequestBuilder.tsx +164 -0
  79. package/src/tools/OpenapiViewer/components/RequestParametersForm.tsx +253 -0
  80. package/src/tools/OpenapiViewer/components/ResponseViewer.tsx +169 -0
  81. package/src/tools/OpenapiViewer/components/VersionSelector.tsx +64 -0
  82. package/src/tools/OpenapiViewer/components/index.ts +14 -0
  83. package/src/tools/OpenapiViewer/constants.ts +39 -0
  84. package/src/tools/OpenapiViewer/context/PlaygroundContext.tsx +338 -0
  85. package/src/tools/OpenapiViewer/hooks/index.ts +8 -0
  86. package/src/tools/OpenapiViewer/hooks/useMobile.ts +10 -0
  87. package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +203 -0
  88. package/src/tools/OpenapiViewer/index.tsx +36 -0
  89. package/src/tools/OpenapiViewer/types.ts +152 -0
  90. package/src/tools/OpenapiViewer/utils/apiKeyManager.ts +149 -0
  91. package/src/tools/OpenapiViewer/utils/formatters.ts +71 -0
  92. package/src/tools/OpenapiViewer/utils/index.ts +9 -0
  93. package/src/tools/OpenapiViewer/utils/versionManager.ts +161 -0
  94. package/src/tools/PrettyCode/PrettyCode.client.tsx +217 -0
  95. package/src/tools/PrettyCode/index.tsx +43 -0
  96. package/src/tools/VideoPlayer/README.md +239 -0
  97. package/src/tools/VideoPlayer/VideoControls.tsx +138 -0
  98. package/src/tools/VideoPlayer/VideoPlayer.tsx +230 -0
  99. package/src/tools/VideoPlayer/index.ts +9 -0
  100. package/src/tools/VideoPlayer/types.ts +62 -0
  101. package/src/tools/index.ts +43 -0
@@ -0,0 +1,253 @@
1
+ 'use client';
2
+
3
+ import React, { useState, useEffect } from 'react';
4
+ import {
5
+ Card, CardContent, CardHeader, CardTitle,
6
+ Input, Label, Textarea, Button, Badge,
7
+ Tabs, TabsContent, TabsList, TabsTrigger,
8
+ Accordion, AccordionContent, AccordionItem, AccordionTrigger,
9
+ Tooltip, TooltipContent, TooltipProvider, TooltipTrigger
10
+ } from '@djangocfg/ui-core/components';
11
+ import { HelpCircle } from 'lucide-react';
12
+ import { usePlaygroundContext } from '../context/PlaygroundContext';
13
+
14
+ interface Parameter {
15
+ name: string;
16
+ type: string;
17
+ required: boolean;
18
+ description?: string;
19
+ }
20
+
21
+ export const RequestParametersForm: React.FC = () => {
22
+ const { state, setParameters } = usePlaygroundContext();
23
+ const [formData, setFormData] = useState<Record<string, string>>({});
24
+ const [activeTab, setActiveTab] = useState('required');
25
+
26
+ // Reset form data when endpoint changes
27
+ useEffect(() => {
28
+ if (state.selectedEndpoint?.parameters) {
29
+ const initialData: Record<string, string> = {};
30
+ state.selectedEndpoint.parameters.forEach(param => {
31
+ initialData[param.name] = '';
32
+ });
33
+ setFormData(initialData);
34
+ }
35
+ }, [state.selectedEndpoint?.path, state.selectedEndpoint?.parameters]);
36
+
37
+ const handleInputChange = (name: string, value: string) => {
38
+ const newFormData = {
39
+ ...formData,
40
+ [name]: value
41
+ };
42
+ setFormData(newFormData);
43
+
44
+ // Update parameters in context for URL substitution
45
+ setParameters(newFormData);
46
+ };
47
+
48
+ const handleClearForm = () => {
49
+ setFormData({});
50
+ setParameters({});
51
+ };
52
+
53
+ const handleGenerateJson = () => {
54
+ const hasData = Object.values(formData).some(value => value !== '' && value !== null && value !== undefined);
55
+ if (hasData) {
56
+ const jsonString = JSON.stringify(formData, null, 2);
57
+ navigator.clipboard.writeText(jsonString);
58
+ }
59
+ };
60
+
61
+ if (!state.selectedEndpoint) return null;
62
+
63
+ const requiredParams = state.selectedEndpoint.parameters?.filter(param => param.required) || [];
64
+ const optionalParams = state.selectedEndpoint.parameters?.filter(param => !param.required) || [];
65
+ const hasParameters = (state.selectedEndpoint.parameters?.length || 0) > 0;
66
+
67
+ if (!hasParameters) {
68
+ return null;
69
+ }
70
+
71
+ const hasFormData = Object.values(formData).some(value => value !== '' && value !== null && value !== undefined);
72
+
73
+ // Component for rendering parameter fields
74
+ const ParameterField = ({ param }: { param: Parameter }) => (
75
+ <div className="space-y-2">
76
+ <div className="flex items-center space-x-1">
77
+ <Label htmlFor={param.name} className="text-xs font-medium text-foreground">
78
+ {param.name}
79
+ {param.required && <span className="text-destructive ml-1">*</span>}
80
+ </Label>
81
+ {param.description && (
82
+ <TooltipProvider>
83
+ <Tooltip>
84
+ <TooltipTrigger asChild>
85
+ <HelpCircle className="h-3 w-3 text-muted-foreground hover:text-foreground cursor-help" />
86
+ </TooltipTrigger>
87
+ <TooltipContent side="top" className="max-w-xs">
88
+ <p className="text-xs">{param.description}</p>
89
+ </TooltipContent>
90
+ </Tooltip>
91
+ </TooltipProvider>
92
+ )}
93
+ </div>
94
+ <Input
95
+ id={param.name}
96
+ value={formData[param.name] || ''}
97
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleInputChange(param.name, e.target.value)}
98
+ placeholder={`Enter ${param.name}${param.required ? '' : ' (optional)'}`}
99
+ className="text-sm"
100
+ />
101
+ </div>
102
+ );
103
+
104
+ // Component for rendering parameters in grid or list layout
105
+ const ParametersGrid = ({ params, title, badgeVariant }: {
106
+ params: Parameter[],
107
+ title: string,
108
+ badgeVariant: 'destructive' | 'secondary'
109
+ }) => {
110
+ if (params.length === 0) return null;
111
+
112
+ return (
113
+ <div className="space-y-3">
114
+ <div className="flex items-center justify-between">
115
+ <div className="flex items-center space-x-2">
116
+ <Badge variant={badgeVariant} className="text-xs">{title}</Badge>
117
+ <span className="text-xs text-muted-foreground">
118
+ {params.length} parameter{params.length !== 1 ? 's' : ''}
119
+ </span>
120
+ </div>
121
+ </div>
122
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
123
+ {params.map((param) => (
124
+ <ParameterField key={param.name} param={param} />
125
+ ))}
126
+ </div>
127
+ </div>
128
+ );
129
+ };
130
+
131
+ return (
132
+ <TooltipProvider>
133
+ <Card>
134
+ <CardHeader className="pb-3">
135
+ <div className="flex items-center justify-between">
136
+ <CardTitle className="text-sm text-foreground">Request Parameters</CardTitle>
137
+ <div className="flex items-center space-x-2">
138
+ {hasFormData && (
139
+ <Button
140
+ variant="default"
141
+ size="sm"
142
+ onClick={handleGenerateJson}
143
+ className="text-xs"
144
+ >
145
+ Copy JSON
146
+ </Button>
147
+ )}
148
+ <Button
149
+ variant="outline"
150
+ size="sm"
151
+ onClick={handleClearForm}
152
+ className="text-xs"
153
+ >
154
+ Clear
155
+ </Button>
156
+ </div>
157
+ </div>
158
+ </CardHeader>
159
+
160
+ <CardContent className="space-y-4">
161
+ {/* Use Tabs for better organization when there are many parameters */}
162
+ {(requiredParams.length + optionalParams.length) > 10 ? (
163
+ <Tabs value={activeTab} onValueChange={setActiveTab}>
164
+ <TabsList className="grid w-full grid-cols-2">
165
+ <TabsTrigger value="required" className="text-xs">
166
+ Required ({requiredParams.length})
167
+ </TabsTrigger>
168
+ <TabsTrigger value="optional" className="text-xs">
169
+ Optional ({optionalParams.length})
170
+ </TabsTrigger>
171
+ </TabsList>
172
+
173
+ <TabsContent value="required" className="space-y-4">
174
+ <ParametersGrid
175
+ params={requiredParams}
176
+ title="Required"
177
+ badgeVariant="destructive"
178
+ />
179
+ </TabsContent>
180
+
181
+ <TabsContent value="optional" className="space-y-4">
182
+ <ParametersGrid
183
+ params={optionalParams}
184
+ title="Optional"
185
+ badgeVariant="secondary"
186
+ />
187
+ </TabsContent>
188
+ </Tabs>
189
+ ) : (
190
+ /* Use Accordion for smaller parameter sets */
191
+ <Accordion type="multiple" defaultValue={requiredParams.length > 0 ? ["required"] : ["optional"]}>
192
+ {requiredParams.length > 0 && (
193
+ <AccordionItem value="required">
194
+ <AccordionTrigger className="text-sm text-foreground">
195
+ <div className="flex items-center space-x-2">
196
+ <Badge variant="destructive" className="text-xs">Required</Badge>
197
+ <span className="text-xs text-muted-foreground">
198
+ {requiredParams.length} parameter{requiredParams.length !== 1 ? 's' : ''}
199
+ </span>
200
+ </div>
201
+ </AccordionTrigger>
202
+ <AccordionContent>
203
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 pt-2">
204
+ {requiredParams.map((param) => (
205
+ <ParameterField key={param.name} param={param} />
206
+ ))}
207
+ </div>
208
+ </AccordionContent>
209
+ </AccordionItem>
210
+ )}
211
+
212
+ {optionalParams.length > 0 && (
213
+ <AccordionItem value="optional">
214
+ <AccordionTrigger className="text-sm text-foreground">
215
+ <div className="flex items-center space-x-2">
216
+ <Badge variant="secondary" className="text-xs">Optional</Badge>
217
+ <span className="text-xs text-muted-foreground">
218
+ {optionalParams.length} parameter{optionalParams.length !== 1 ? 's' : ''}
219
+ </span>
220
+ </div>
221
+ </AccordionTrigger>
222
+ <AccordionContent>
223
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 pt-2">
224
+ {optionalParams.map((param) => (
225
+ <ParameterField key={param.name} param={param} />
226
+ ))}
227
+ </div>
228
+ </AccordionContent>
229
+ </AccordionItem>
230
+ )}
231
+ </Accordion>
232
+ )}
233
+
234
+ {/* JSON Preview */}
235
+ {hasFormData && (
236
+ <div className="space-y-2 mt-6 pt-4 border-t">
237
+ <Label className="text-xs text-foreground">Generated JSON</Label>
238
+ <Textarea
239
+ value={JSON.stringify(formData, null, 2)}
240
+ readOnly
241
+ className="text-xs font-mono"
242
+ rows={4}
243
+ />
244
+ <p className="text-xs text-muted-foreground">
245
+ Click &quot;Copy JSON&quot; to copy this to clipboard, then paste it into the Request Body field above.
246
+ </p>
247
+ </div>
248
+ )}
249
+ </CardContent>
250
+ </Card>
251
+ </TooltipProvider>
252
+ );
253
+ };
@@ -0,0 +1,169 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from '@djangocfg/ui-core/components';
5
+ import { Copy, XCircle, Download } from 'lucide-react';
6
+ import { usePlaygroundContext } from '../context/PlaygroundContext';
7
+ import { getStatusColor } from '../utils';
8
+ import JsonTree from '../../JsonTree';
9
+
10
+ export const ResponseViewer: React.FC = () => {
11
+ const { state, copyToClipboard } = usePlaygroundContext();
12
+ const { response } = state;
13
+
14
+ if (!response) {
15
+ return (
16
+ <div className="space-y-4">
17
+ <div className="flex items-center justify-between">
18
+ <h2 className="text-lg font-semibold text-foreground">Response</h2>
19
+ </div>
20
+ <Card>
21
+ <CardContent className="flex items-center justify-center py-8">
22
+ <p className="text-sm text-muted-foreground">No response yet. Send a request to see the response here.</p>
23
+ </CardContent>
24
+ </Card>
25
+ </div>
26
+ );
27
+ }
28
+
29
+ const handleCopyResponse = () => {
30
+ const responseText = typeof response.data === 'string' ? response.data : JSON.stringify(response.data, null, 2);
31
+ copyToClipboard(responseText);
32
+ };
33
+
34
+ const handleDownloadResponse = () => {
35
+ const responseText = typeof response.data === 'string' ? response.data : JSON.stringify(response.data, null, 2);
36
+ const blob = new Blob([responseText], { type: 'application/json' });
37
+ const url = URL.createObjectURL(blob);
38
+ const a = document.createElement('a');
39
+ a.href = url;
40
+ a.download = 'response.json';
41
+ document.body.appendChild(a);
42
+ a.click();
43
+ document.body.removeChild(a);
44
+ URL.revokeObjectURL(url);
45
+ };
46
+
47
+ return (
48
+ <div className="space-y-4">
49
+ <div className="flex items-center justify-between">
50
+ <h2 className="text-lg font-semibold text-foreground">Response</h2>
51
+ <div className="flex items-center space-x-2">
52
+ <Button variant="outline" size="sm" onClick={handleCopyResponse}>
53
+ <Copy className="h-4 w-4" />
54
+ <span className="hidden sm:inline">Copy</span>
55
+ </Button>
56
+ <Button variant="outline" size="sm" onClick={handleDownloadResponse}>
57
+ <Download className="h-4 w-4" />
58
+ <span className="hidden sm:inline">Download</span>
59
+ </Button>
60
+ </div>
61
+ </div>
62
+
63
+ {/* Response Status */}
64
+ <Card>
65
+ <CardHeader>
66
+ <CardTitle className="text-sm text-foreground">Response Information</CardTitle>
67
+ </CardHeader>
68
+ <CardContent className="space-y-4">
69
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
70
+ <div className="space-y-2">
71
+ <div className="flex items-center justify-between">
72
+ <span className="text-sm font-medium text-foreground">Status Code:</span>
73
+ <Badge
74
+ variant={getStatusColor(response.status || 0) === 'success' ? 'default' :
75
+ getStatusColor(response.status || 0) === 'error' ? 'destructive' : 'secondary'}
76
+ className="text-xs"
77
+ >
78
+ {response.status}
79
+ </Badge>
80
+ </div>
81
+ {response.statusText && (
82
+ <div className="flex items-center justify-between">
83
+ <span className="text-sm font-medium text-foreground">Status Text:</span>
84
+ <span className="text-sm text-muted-foreground">{response.statusText}</span>
85
+ </div>
86
+ )}
87
+ </div>
88
+
89
+ <div className="space-y-2">
90
+ <div className="flex items-center justify-between">
91
+ <span className="text-sm font-medium text-foreground">Response Type:</span>
92
+ <span className="text-sm text-muted-foreground">
93
+ {typeof response.data === 'string' ? 'Text' : 'JSON'}
94
+ </span>
95
+ </div>
96
+ <div className="flex items-center justify-between">
97
+ <span className="text-sm font-medium text-foreground">Data Size:</span>
98
+ <span className="text-sm text-muted-foreground">
99
+ {typeof response.data === 'string' ? response.data.length : JSON.stringify(response.data).length} chars
100
+ </span>
101
+ </div>
102
+ </div>
103
+ </div>
104
+
105
+ {response.error && (
106
+ <div className="bg-destructive/10 border border-destructive/20 rounded p-3">
107
+ <div className="flex items-center space-x-2">
108
+ <XCircle className="h-4 w-4 text-destructive" />
109
+ <p className="text-sm text-destructive">{response.error}</p>
110
+ </div>
111
+ </div>
112
+ )}
113
+ </CardContent>
114
+ </Card>
115
+
116
+ {/* Response Body */}
117
+ <Card>
118
+ <CardContent className="p-0">
119
+ {(() => {
120
+ // Check if response body should be hidden
121
+ const is404 = response.status === 404;
122
+ const isNonJson = typeof response.data === 'string';
123
+
124
+ if (is404 || isNonJson) {
125
+ return (
126
+ <div className="p-8 text-center">
127
+ <p className="text-sm text-muted-foreground">
128
+ {is404
129
+ ? 'Response body hidden for 404 Not Found status'
130
+ : 'Response body hidden for non-JSON responses'}
131
+ </p>
132
+ </div>
133
+ );
134
+ }
135
+
136
+ if (response.data) {
137
+ return (
138
+ <JsonTree
139
+ title="Response Body"
140
+ data={response.data}
141
+ config={{
142
+ // Smart defaults for API responses
143
+ maxAutoExpandDepth: 2,
144
+ maxAutoExpandArrayItems: 10,
145
+ maxAutoExpandObjectKeys: 5,
146
+ maxStringLength: 200,
147
+ collectionLimit: 50,
148
+ showCollectionInfo: true,
149
+ showExpandControls: true,
150
+ showActionButtons: false, // We have our own copy/download buttons above
151
+ preserveKeyOrder: true,
152
+ className: "border-0 rounded-none"
153
+ }}
154
+ />
155
+ );
156
+ }
157
+
158
+ return (
159
+ <div className="p-8 text-center">
160
+ <p className="text-sm text-muted-foreground">No response body available</p>
161
+ </div>
162
+ );
163
+ })()}
164
+ </CardContent>
165
+ </Card>
166
+
167
+ </div>
168
+ );
169
+ };
@@ -0,0 +1,64 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { Badge, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@djangocfg/ui-core/components';
5
+ import { GitBranch, Info } from 'lucide-react';
6
+ import { usePlaygroundContext } from '../context/PlaygroundContext';
7
+ import { API_VERSIONS, getVersionById, getVersionStats } from '../utils/versionManager';
8
+ import useOpenApiSchema from '../hooks/useOpenApiSchema';
9
+
10
+ export const VersionSelector: React.FC = () => {
11
+ const { state, config, setSelectedVersion } = usePlaygroundContext();
12
+ const { endpoints } = useOpenApiSchema({
13
+ schemas: config.schemas,
14
+ defaultSchemaId: config.defaultSchemaId,
15
+ });
16
+
17
+ const currentVersion = getVersionById(state.selectedVersion);
18
+ const versionStats = getVersionStats(endpoints);
19
+
20
+ const handleVersionChange = (versionId: string) => {
21
+ setSelectedVersion(versionId);
22
+ };
23
+
24
+ return (
25
+ <div className="flex items-center space-x-3">
26
+ <div className="flex items-center space-x-2">
27
+ <GitBranch className="h-4 w-4 text-muted-foreground" />
28
+ <span className="text-sm font-medium text-foreground">API Version:</span>
29
+ </div>
30
+
31
+ <Select value={state.selectedVersion} onValueChange={handleVersionChange}>
32
+ <SelectTrigger className="w-48">
33
+ <SelectValue />
34
+ </SelectTrigger>
35
+ <SelectContent>
36
+ {API_VERSIONS.map((version) => (
37
+ <SelectItem key={version.id} value={version.id}>
38
+ <div className="flex items-center justify-between w-full">
39
+ <div className="flex items-center space-x-2">
40
+ <span className="font-medium">{version.name}</span>
41
+ {version.isDefault && (
42
+ <Badge variant="secondary" className="text-xs">
43
+ Default
44
+ </Badge>
45
+ )}
46
+ </div>
47
+ <span className="text-xs text-muted-foreground ml-2">
48
+ {versionStats[version.id] || 0} endpoints
49
+ </span>
50
+ </div>
51
+ </SelectItem>
52
+ ))}
53
+ </SelectContent>
54
+ </Select>
55
+
56
+ {currentVersion && (
57
+ <div className="flex items-center space-x-1 text-xs text-muted-foreground">
58
+ <Info className="h-3 w-3" />
59
+ <span>{currentVersion.description}</span>
60
+ </div>
61
+ )}
62
+ </div>
63
+ );
64
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Playground Components
3
+ *
4
+ * Centralized exports for all playground components
5
+ */
6
+
7
+ export { PlaygroundLayout } from './PlaygroundLayout';
8
+ export { PlaygroundStepper } from './PlaygroundStepper';
9
+ export { EndpointsLibrary } from './EndpointsLibrary';
10
+ export { EndpointInfo } from './EndpointInfo';
11
+ export { RequestBuilder } from './RequestBuilder';
12
+ export { RequestParametersForm } from './RequestParametersForm';
13
+ export { ResponseViewer } from './ResponseViewer';
14
+ export { VersionSelector } from './VersionSelector';
@@ -0,0 +1,39 @@
1
+ // HTTP method colors mapping
2
+ export const HTTP_METHOD_COLORS = {
3
+ GET: 'success',
4
+ POST: 'primary',
5
+ PUT: 'warning',
6
+ DELETE: 'error',
7
+ PATCH: 'default',
8
+ } as const;
9
+
10
+ // HTTP status code colors mapping
11
+ export const HTTP_STATUS_COLORS = {
12
+ '2': 'success',
13
+ '3': 'default',
14
+ '4': 'warning',
15
+ '5': 'error',
16
+ } as const;
17
+
18
+ // Default request headers
19
+ export const DEFAULT_HEADERS = {
20
+ 'Content-Type': 'application/json',
21
+ } as const;
22
+
23
+ // Playground steps configuration
24
+ export const PLAYGROUND_STEPS = ['endpoints', 'request', 'response'] as const;
25
+
26
+ export const STEP_CONFIG = {
27
+ endpoints: {
28
+ title: 'API Endpoints',
29
+ description: 'Browse and select API endpoints',
30
+ },
31
+ request: {
32
+ title: 'Request Builder',
33
+ description: 'Configure your API request',
34
+ },
35
+ response: {
36
+ title: 'Response Viewer',
37
+ description: 'View API response and details',
38
+ },
39
+ } as const;