real-prototypes-skill 2.0.0

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 (60) hide show
  1. package/.claude/skills/agent-browser-skill/SKILL.md +252 -0
  2. package/.claude/skills/real-prototypes-skill/.gitignore +188 -0
  3. package/.claude/skills/real-prototypes-skill/ACCESSIBILITY.md +668 -0
  4. package/.claude/skills/real-prototypes-skill/INSTALL.md +259 -0
  5. package/.claude/skills/real-prototypes-skill/LICENSE +21 -0
  6. package/.claude/skills/real-prototypes-skill/PUBLISH.md +310 -0
  7. package/.claude/skills/real-prototypes-skill/QUICKSTART.md +240 -0
  8. package/.claude/skills/real-prototypes-skill/README.md +442 -0
  9. package/.claude/skills/real-prototypes-skill/SKILL.md +329 -0
  10. package/.claude/skills/real-prototypes-skill/capture/capture-engine.js +1153 -0
  11. package/.claude/skills/real-prototypes-skill/capture/config.schema.json +170 -0
  12. package/.claude/skills/real-prototypes-skill/cli.js +596 -0
  13. package/.claude/skills/real-prototypes-skill/docs/TROUBLESHOOTING.md +278 -0
  14. package/.claude/skills/real-prototypes-skill/docs/schemas/capture-config.md +167 -0
  15. package/.claude/skills/real-prototypes-skill/docs/schemas/design-tokens.md +183 -0
  16. package/.claude/skills/real-prototypes-skill/docs/schemas/manifest.md +169 -0
  17. package/.claude/skills/real-prototypes-skill/examples/CLAUDE.md.example +73 -0
  18. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/CLAUDE.md +136 -0
  19. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/FEATURES.md +222 -0
  20. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/README.md +82 -0
  21. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/references/design-tokens.json +87 -0
  22. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/references/screenshots/homepage-viewport.png +0 -0
  23. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/references/screenshots/prototype-chatbot-final.png +0 -0
  24. package/.claude/skills/real-prototypes-skill/examples/amazon-chatbot/references/screenshots/prototype-fullpage-v2.png +0 -0
  25. package/.claude/skills/real-prototypes-skill/references/accessibility-fixes.md +298 -0
  26. package/.claude/skills/real-prototypes-skill/references/accessibility-report.json +253 -0
  27. package/.claude/skills/real-prototypes-skill/scripts/CAPTURE-ENHANCEMENTS.md +344 -0
  28. package/.claude/skills/real-prototypes-skill/scripts/IMPLEMENTATION-SUMMARY.md +517 -0
  29. package/.claude/skills/real-prototypes-skill/scripts/QUICK-START.md +229 -0
  30. package/.claude/skills/real-prototypes-skill/scripts/QUICKSTART-layout-analysis.md +148 -0
  31. package/.claude/skills/real-prototypes-skill/scripts/README-analyze-layout.md +407 -0
  32. package/.claude/skills/real-prototypes-skill/scripts/analyze-layout.js +880 -0
  33. package/.claude/skills/real-prototypes-skill/scripts/capture-platform.js +203 -0
  34. package/.claude/skills/real-prototypes-skill/scripts/comprehensive-capture.js +597 -0
  35. package/.claude/skills/real-prototypes-skill/scripts/create-manifest.js +338 -0
  36. package/.claude/skills/real-prototypes-skill/scripts/enterprise-pipeline.js +428 -0
  37. package/.claude/skills/real-prototypes-skill/scripts/extract-tokens.js +468 -0
  38. package/.claude/skills/real-prototypes-skill/scripts/full-site-capture.js +738 -0
  39. package/.claude/skills/real-prototypes-skill/scripts/generate-tailwind-config.js +296 -0
  40. package/.claude/skills/real-prototypes-skill/scripts/integrate-accessibility.sh +161 -0
  41. package/.claude/skills/real-prototypes-skill/scripts/manifest-schema.json +302 -0
  42. package/.claude/skills/real-prototypes-skill/scripts/setup-prototype.sh +167 -0
  43. package/.claude/skills/real-prototypes-skill/scripts/test-analyze-layout.js +338 -0
  44. package/.claude/skills/real-prototypes-skill/scripts/test-validation.js +307 -0
  45. package/.claude/skills/real-prototypes-skill/scripts/validate-accessibility.js +598 -0
  46. package/.claude/skills/real-prototypes-skill/scripts/validate-manifest.js +499 -0
  47. package/.claude/skills/real-prototypes-skill/scripts/validate-output.js +361 -0
  48. package/.claude/skills/real-prototypes-skill/scripts/validate-prerequisites.js +319 -0
  49. package/.claude/skills/real-prototypes-skill/scripts/verify-layout-analysis.sh +77 -0
  50. package/.claude/skills/real-prototypes-skill/templates/dashboard-widget.tsx.template +91 -0
  51. package/.claude/skills/real-prototypes-skill/templates/data-table.tsx.template +193 -0
  52. package/.claude/skills/real-prototypes-skill/templates/form-section.tsx.template +250 -0
  53. package/.claude/skills/real-prototypes-skill/templates/modal-dialog.tsx.template +239 -0
  54. package/.claude/skills/real-prototypes-skill/templates/nav-item.tsx.template +265 -0
  55. package/.claude/skills/real-prototypes-skill/validation/validation-engine.js +559 -0
  56. package/.env.example +74 -0
  57. package/LICENSE +21 -0
  58. package/README.md +444 -0
  59. package/bin/cli.js +319 -0
  60. package/package.json +59 -0
@@ -0,0 +1,77 @@
1
+ #!/bin/bash
2
+
3
+ # Verification script for layout analysis output
4
+ # Checks that all expected files are present and valid
5
+
6
+ set -e
7
+
8
+ echo "🔍 Verifying Layout Analysis Output"
9
+ echo "===================================="
10
+ echo ""
11
+
12
+ # Get the references directory (passed as argument or use default)
13
+ REFERENCES_DIR="${1:-../../../references}"
14
+
15
+ # Check if references directory exists
16
+ if [ ! -d "$REFERENCES_DIR" ]; then
17
+ echo "❌ Error: References directory not found at $REFERENCES_DIR"
18
+ exit 1
19
+ fi
20
+
21
+ echo "✓ References directory exists: $REFERENCES_DIR"
22
+
23
+ # Check for structure-manifest.json
24
+ if [ ! -f "$REFERENCES_DIR/structure-manifest.json" ]; then
25
+ echo "❌ Error: structure-manifest.json not found"
26
+ echo " Run: node analyze-layout.js"
27
+ exit 1
28
+ fi
29
+
30
+ echo "✓ structure-manifest.json exists"
31
+
32
+ # Validate JSON structure (use absolute path)
33
+ MANIFEST_PATH="$(cd "$REFERENCES_DIR" && pwd)/structure-manifest.json"
34
+ if ! node -e "require('$MANIFEST_PATH')" 2>/dev/null; then
35
+ echo "❌ Error: structure-manifest.json is not valid JSON"
36
+ exit 1
37
+ fi
38
+
39
+ echo "✓ structure-manifest.json is valid JSON"
40
+
41
+ # Check for component maps
42
+ COMPONENT_MAPS=$(ls "$REFERENCES_DIR"/component-map-*.json 2>/dev/null | wc -l)
43
+ if [ "$COMPONENT_MAPS" -eq 0 ]; then
44
+ echo "❌ Error: No component-map-*.json files found"
45
+ echo " Run: node analyze-layout.js"
46
+ exit 1
47
+ fi
48
+
49
+ echo "✓ Found $COMPONENT_MAPS component map files"
50
+
51
+ # Extract key metrics from structure-manifest.json
52
+ echo ""
53
+ echo "📊 Analysis Metrics:"
54
+ echo "-------------------"
55
+
56
+ PAGES_ANALYZED=$(node -e "console.log(require('$MANIFEST_PATH').pagesAnalyzed)")
57
+ echo "Pages analyzed: $PAGES_ANALYZED"
58
+
59
+ UI_LIBRARY=$(node -e "console.log(require('$MANIFEST_PATH').library)")
60
+ echo "UI library: $UI_LIBRARY"
61
+
62
+ LAYOUT_TYPE=$(node -e "console.log(require('$MANIFEST_PATH').layout.type)")
63
+ echo "Layout type: $LAYOUT_TYPE"
64
+
65
+ COMPONENT_TYPES=$(node -e "console.log(Object.keys(require('$MANIFEST_PATH').components).join(', '))")
66
+ echo "Component types: $COMPONENT_TYPES"
67
+
68
+ TOTAL_COMPONENTS=$(node -e "const m=require('$MANIFEST_PATH'); console.log(Object.values(m.components).reduce((sum,c)=>sum+c.totalCount,0))")
69
+ echo "Total components: $TOTAL_COMPONENTS"
70
+
71
+ echo ""
72
+ echo "✅ All verification checks passed!"
73
+ echo ""
74
+ echo "💡 Next steps:"
75
+ echo " 1. Review structure-manifest.json for layout patterns"
76
+ echo " 2. Review component-map-*.json for page-specific details"
77
+ echo " 3. Run: node test-analyze-layout.js $REFERENCES_DIR"
@@ -0,0 +1,91 @@
1
+ import React from "react";
2
+ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
3
+ import { Button } from "@/components/ui/button";
4
+
5
+ interface DashboardWidgetProps {
6
+ title?: string;
7
+ description?: string;
8
+ actions?: React.ReactNode;
9
+ children?: React.ReactNode;
10
+ className?: string;
11
+ }
12
+
13
+ export function DashboardWidget({
14
+ title = "{/* FEATURE_TITLE */}",
15
+ description,
16
+ actions,
17
+ children,
18
+ className = "",
19
+ }: DashboardWidgetProps) {
20
+ return (
21
+ <Card
22
+ className={`bg-platform-background border-platform-secondary/20 shadow-sm hover:shadow-md transition-shadow ${className}`}
23
+ role="region"
24
+ aria-labelledby="widget-title"
25
+ >
26
+ <CardHeader className="pb-3">
27
+ <div className="flex items-center justify-between">
28
+ <div>
29
+ <CardTitle
30
+ id="widget-title"
31
+ className="text-lg font-semibold text-platform-foreground"
32
+ >
33
+ {title}
34
+ </CardTitle>
35
+ {description && (
36
+ <CardDescription className="text-platform-foreground/60 mt-1">
37
+ {description}
38
+ </CardDescription>
39
+ )}
40
+ </div>
41
+ {actions && (
42
+ <div className="flex items-center gap-2" role="toolbar" aria-label="Widget actions">
43
+ {actions}
44
+ </div>
45
+ )}
46
+ </div>
47
+ </CardHeader>
48
+
49
+ <CardContent className="text-platform-foreground">
50
+ {/* FEATURE_CONTENT */}
51
+ {children}
52
+ </CardContent>
53
+
54
+ {/* Optional footer for additional actions */}
55
+ {/*
56
+ <CardFooter className="pt-3 border-t border-platform-secondary/10">
57
+ <div className="flex items-center justify-end gap-2 w-full">
58
+ <Button
59
+ variant="outline"
60
+ size="sm"
61
+ className="border-platform-secondary text-platform-foreground hover:bg-platform-secondary/10"
62
+ >
63
+ View Details
64
+ </Button>
65
+ <Button
66
+ size="sm"
67
+ className="bg-platform-primary text-white hover:bg-platform-primary/90"
68
+ >
69
+ Take Action
70
+ </Button>
71
+ </div>
72
+ </CardFooter>
73
+ */}
74
+ </Card>
75
+ );
76
+ }
77
+
78
+ // Example usage:
79
+ // <DashboardWidget
80
+ // title="Sales Overview"
81
+ // description="Monthly sales performance"
82
+ // actions={
83
+ // <Button variant="ghost" size="sm">
84
+ // <RefreshIcon className="h-4 w-4" />
85
+ // </Button>
86
+ // }
87
+ // >
88
+ // <div className="space-y-4">
89
+ // <p>Your content here</p>
90
+ // </div>
91
+ // </DashboardWidget>
@@ -0,0 +1,193 @@
1
+ import React, { useState, useMemo } from "react";
2
+ import {
3
+ Table,
4
+ TableBody,
5
+ TableCell,
6
+ TableHead,
7
+ TableHeader,
8
+ TableRow,
9
+ } from "@/components/ui/table";
10
+ import { Button } from "@/components/ui/button";
11
+ import { ChevronUp, ChevronDown, ChevronsUpDown } from "lucide-react";
12
+
13
+ type SortDirection = "asc" | "desc" | null;
14
+
15
+ interface Column<T> {
16
+ key: keyof T;
17
+ header: string;
18
+ sortable?: boolean;
19
+ render?: (value: T[keyof T], row: T) => React.ReactNode;
20
+ className?: string;
21
+ }
22
+
23
+ interface DataTableProps<T extends Record<string, unknown>> {
24
+ data: T[];
25
+ columns: Column<T>[];
26
+ caption?: string;
27
+ emptyMessage?: string;
28
+ onRowClick?: (row: T) => void;
29
+ className?: string;
30
+ }
31
+
32
+ export function DataTable<T extends Record<string, unknown>>({
33
+ data,
34
+ columns,
35
+ caption = "{/* FEATURE_TITLE */}",
36
+ emptyMessage = "No data available",
37
+ onRowClick,
38
+ className = "",
39
+ }: DataTableProps<T>) {
40
+ const [sortKey, setSortKey] = useState<keyof T | null>(null);
41
+ const [sortDirection, setSortDirection] = useState<SortDirection>(null);
42
+
43
+ const handleSort = (key: keyof T) => {
44
+ if (sortKey === key) {
45
+ if (sortDirection === "asc") {
46
+ setSortDirection("desc");
47
+ } else if (sortDirection === "desc") {
48
+ setSortDirection(null);
49
+ setSortKey(null);
50
+ } else {
51
+ setSortDirection("asc");
52
+ }
53
+ } else {
54
+ setSortKey(key);
55
+ setSortDirection("asc");
56
+ }
57
+ };
58
+
59
+ const sortedData = useMemo(() => {
60
+ if (!sortKey || !sortDirection) return data;
61
+
62
+ return [...data].sort((a, b) => {
63
+ const aVal = a[sortKey];
64
+ const bVal = b[sortKey];
65
+
66
+ if (aVal === bVal) return 0;
67
+ if (aVal === null || aVal === undefined) return 1;
68
+ if (bVal === null || bVal === undefined) return -1;
69
+
70
+ const comparison = aVal < bVal ? -1 : 1;
71
+ return sortDirection === "asc" ? comparison : -comparison;
72
+ });
73
+ }, [data, sortKey, sortDirection]);
74
+
75
+ const getSortIcon = (key: keyof T) => {
76
+ if (sortKey !== key) {
77
+ return <ChevronsUpDown className="ml-2 h-4 w-4 text-platform-foreground/40" />;
78
+ }
79
+ if (sortDirection === "asc") {
80
+ return <ChevronUp className="ml-2 h-4 w-4 text-platform-primary" />;
81
+ }
82
+ return <ChevronDown className="ml-2 h-4 w-4 text-platform-primary" />;
83
+ };
84
+
85
+ return (
86
+ <div className={`w-full overflow-auto rounded-lg border border-platform-secondary/20 ${className}`}>
87
+ <Table>
88
+ {caption && (
89
+ <caption className="sr-only">{caption}</caption>
90
+ )}
91
+ <TableHeader>
92
+ <TableRow className="bg-platform-secondary/5 hover:bg-platform-secondary/10">
93
+ {columns.map((column) => (
94
+ <TableHead
95
+ key={String(column.key)}
96
+ className={`text-platform-foreground font-semibold ${column.className || ""}`}
97
+ >
98
+ {column.sortable ? (
99
+ <Button
100
+ variant="ghost"
101
+ size="sm"
102
+ className="h-8 px-2 -ml-2 hover:bg-platform-secondary/10 text-platform-foreground"
103
+ onClick={() => handleSort(column.key)}
104
+ aria-label={`Sort by ${column.header}`}
105
+ >
106
+ {column.header}
107
+ {getSortIcon(column.key)}
108
+ </Button>
109
+ ) : (
110
+ column.header
111
+ )}
112
+ </TableHead>
113
+ ))}
114
+ </TableRow>
115
+ </TableHeader>
116
+ <TableBody>
117
+ {/* FEATURE_CONTENT */}
118
+ {sortedData.length === 0 ? (
119
+ <TableRow>
120
+ <TableCell
121
+ colSpan={columns.length}
122
+ className="h-24 text-center text-platform-foreground/60"
123
+ >
124
+ {emptyMessage}
125
+ </TableCell>
126
+ </TableRow>
127
+ ) : (
128
+ sortedData.map((row, index) => (
129
+ <TableRow
130
+ key={index}
131
+ className={`
132
+ bg-platform-background
133
+ hover:bg-platform-secondary/5
134
+ transition-colors
135
+ ${onRowClick ? "cursor-pointer" : ""}
136
+ `}
137
+ onClick={() => onRowClick?.(row)}
138
+ tabIndex={onRowClick ? 0 : undefined}
139
+ onKeyDown={(e) => {
140
+ if (onRowClick && (e.key === "Enter" || e.key === " ")) {
141
+ e.preventDefault();
142
+ onRowClick(row);
143
+ }
144
+ }}
145
+ role={onRowClick ? "button" : undefined}
146
+ >
147
+ {columns.map((column) => (
148
+ <TableCell
149
+ key={String(column.key)}
150
+ className={`text-platform-foreground ${column.className || ""}`}
151
+ >
152
+ {column.render
153
+ ? column.render(row[column.key], row)
154
+ : String(row[column.key] ?? "")}
155
+ </TableCell>
156
+ ))}
157
+ </TableRow>
158
+ ))
159
+ )}
160
+ </TableBody>
161
+ </Table>
162
+ </div>
163
+ );
164
+ }
165
+
166
+ // Example usage:
167
+ // interface User {
168
+ // id: number;
169
+ // name: string;
170
+ // email: string;
171
+ // status: string;
172
+ // }
173
+ //
174
+ // const columns: Column<User>[] = [
175
+ // { key: "name", header: "Name", sortable: true },
176
+ // { key: "email", header: "Email", sortable: true },
177
+ // {
178
+ // key: "status",
179
+ // header: "Status",
180
+ // render: (value) => (
181
+ // <Badge variant={value === "active" ? "default" : "secondary"}>
182
+ // {value}
183
+ // </Badge>
184
+ // ),
185
+ // },
186
+ // ];
187
+ //
188
+ // <DataTable
189
+ // data={users}
190
+ // columns={columns}
191
+ // caption="User list"
192
+ // onRowClick={(user) => console.log(user)}
193
+ // />
@@ -0,0 +1,250 @@
1
+ import React from "react";
2
+ import { useForm } from "react-hook-form";
3
+ import { Button } from "@/components/ui/button";
4
+ import { Input } from "@/components/ui/input";
5
+ import { Label } from "@/components/ui/label";
6
+ import { Checkbox } from "@/components/ui/checkbox";
7
+ import {
8
+ Select,
9
+ SelectContent,
10
+ SelectItem,
11
+ SelectTrigger,
12
+ SelectValue,
13
+ } from "@/components/ui/select";
14
+ import { Textarea } from "@/components/ui/textarea";
15
+
16
+ interface FormData {
17
+ textField: string;
18
+ emailField: string;
19
+ selectField: string;
20
+ textareaField: string;
21
+ checkboxField: boolean;
22
+ }
23
+
24
+ interface FormSectionProps {
25
+ title?: string;
26
+ description?: string;
27
+ onSubmit?: (data: FormData) => void;
28
+ onCancel?: () => void;
29
+ isLoading?: boolean;
30
+ className?: string;
31
+ }
32
+
33
+ export function FormSection({
34
+ title = "{/* FEATURE_TITLE */}",
35
+ description,
36
+ onSubmit,
37
+ onCancel,
38
+ isLoading = false,
39
+ className = "",
40
+ }: FormSectionProps) {
41
+ const {
42
+ register,
43
+ handleSubmit,
44
+ setValue,
45
+ watch,
46
+ formState: { errors },
47
+ } = useForm<FormData>({
48
+ defaultValues: {
49
+ textField: "",
50
+ emailField: "",
51
+ selectField: "",
52
+ textareaField: "",
53
+ checkboxField: false,
54
+ },
55
+ });
56
+
57
+ const checkboxValue = watch("checkboxField");
58
+
59
+ const handleFormSubmit = (data: FormData) => {
60
+ onSubmit?.(data);
61
+ };
62
+
63
+ return (
64
+ <div className={`bg-platform-background p-6 rounded-lg border border-platform-secondary/20 ${className}`}>
65
+ {(title || description) && (
66
+ <div className="mb-6">
67
+ {title && (
68
+ <h2 className="text-xl font-semibold text-platform-foreground">
69
+ {title}
70
+ </h2>
71
+ )}
72
+ {description && (
73
+ <p className="mt-1 text-sm text-platform-foreground/60">
74
+ {description}
75
+ </p>
76
+ )}
77
+ </div>
78
+ )}
79
+
80
+ <form onSubmit={handleSubmit(handleFormSubmit)} className="space-y-6">
81
+ {/* FEATURE_CONTENT */}
82
+
83
+ {/* Text Input */}
84
+ <div className="space-y-2">
85
+ <Label
86
+ htmlFor="textField"
87
+ className="text-platform-foreground font-medium"
88
+ >
89
+ Text Field
90
+ <span className="text-red-500 ml-1" aria-hidden="true">*</span>
91
+ </Label>
92
+ <Input
93
+ id="textField"
94
+ type="text"
95
+ placeholder="Enter text..."
96
+ className="bg-platform-background border-platform-secondary/30 text-platform-foreground placeholder:text-platform-foreground/40 focus:border-platform-primary focus:ring-platform-primary"
97
+ aria-required="true"
98
+ aria-invalid={!!errors.textField}
99
+ aria-describedby={errors.textField ? "textField-error" : undefined}
100
+ {...register("textField", { required: "This field is required" })}
101
+ />
102
+ {errors.textField && (
103
+ <p id="textField-error" className="text-sm text-red-500" role="alert">
104
+ {errors.textField.message}
105
+ </p>
106
+ )}
107
+ </div>
108
+
109
+ {/* Email Input */}
110
+ <div className="space-y-2">
111
+ <Label
112
+ htmlFor="emailField"
113
+ className="text-platform-foreground font-medium"
114
+ >
115
+ Email Field
116
+ </Label>
117
+ <Input
118
+ id="emailField"
119
+ type="email"
120
+ placeholder="email@example.com"
121
+ className="bg-platform-background border-platform-secondary/30 text-platform-foreground placeholder:text-platform-foreground/40 focus:border-platform-primary focus:ring-platform-primary"
122
+ aria-invalid={!!errors.emailField}
123
+ aria-describedby={errors.emailField ? "emailField-error" : undefined}
124
+ {...register("emailField", {
125
+ pattern: {
126
+ value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
127
+ message: "Invalid email address",
128
+ },
129
+ })}
130
+ />
131
+ {errors.emailField && (
132
+ <p id="emailField-error" className="text-sm text-red-500" role="alert">
133
+ {errors.emailField.message}
134
+ </p>
135
+ )}
136
+ </div>
137
+
138
+ {/* Select Input */}
139
+ <div className="space-y-2">
140
+ <Label
141
+ htmlFor="selectField"
142
+ className="text-platform-foreground font-medium"
143
+ >
144
+ Select Field
145
+ </Label>
146
+ <Select
147
+ onValueChange={(value) => setValue("selectField", value)}
148
+ >
149
+ <SelectTrigger
150
+ id="selectField"
151
+ className="bg-platform-background border-platform-secondary/30 text-platform-foreground focus:border-platform-primary focus:ring-platform-primary"
152
+ aria-label="Select an option"
153
+ >
154
+ <SelectValue placeholder="Select an option..." />
155
+ </SelectTrigger>
156
+ <SelectContent className="bg-platform-background border-platform-secondary/30">
157
+ <SelectItem
158
+ value="option1"
159
+ className="text-platform-foreground focus:bg-platform-secondary/10"
160
+ >
161
+ Option 1
162
+ </SelectItem>
163
+ <SelectItem
164
+ value="option2"
165
+ className="text-platform-foreground focus:bg-platform-secondary/10"
166
+ >
167
+ Option 2
168
+ </SelectItem>
169
+ <SelectItem
170
+ value="option3"
171
+ className="text-platform-foreground focus:bg-platform-secondary/10"
172
+ >
173
+ Option 3
174
+ </SelectItem>
175
+ </SelectContent>
176
+ </Select>
177
+ </div>
178
+
179
+ {/* Textarea */}
180
+ <div className="space-y-2">
181
+ <Label
182
+ htmlFor="textareaField"
183
+ className="text-platform-foreground font-medium"
184
+ >
185
+ Description
186
+ </Label>
187
+ <Textarea
188
+ id="textareaField"
189
+ placeholder="Enter a description..."
190
+ rows={4}
191
+ className="bg-platform-background border-platform-secondary/30 text-platform-foreground placeholder:text-platform-foreground/40 focus:border-platform-primary focus:ring-platform-primary resize-none"
192
+ {...register("textareaField")}
193
+ />
194
+ </div>
195
+
196
+ {/* Checkbox */}
197
+ <div className="flex items-start space-x-3">
198
+ <Checkbox
199
+ id="checkboxField"
200
+ checked={checkboxValue}
201
+ onCheckedChange={(checked) => setValue("checkboxField", !!checked)}
202
+ className="border-platform-secondary/50 data-[state=checked]:bg-platform-primary data-[state=checked]:border-platform-primary mt-0.5"
203
+ />
204
+ <div className="space-y-1">
205
+ <Label
206
+ htmlFor="checkboxField"
207
+ className="text-platform-foreground font-medium cursor-pointer"
208
+ >
209
+ I agree to the terms
210
+ </Label>
211
+ <p className="text-sm text-platform-foreground/60">
212
+ By checking this box, you agree to our Terms of Service and Privacy Policy.
213
+ </p>
214
+ </div>
215
+ </div>
216
+
217
+ {/* Form Actions */}
218
+ <div className="flex items-center justify-end gap-3 pt-4 border-t border-platform-secondary/10">
219
+ {onCancel && (
220
+ <Button
221
+ type="button"
222
+ variant="outline"
223
+ onClick={onCancel}
224
+ disabled={isLoading}
225
+ className="border-platform-secondary text-platform-foreground hover:bg-platform-secondary/10"
226
+ >
227
+ Cancel
228
+ </Button>
229
+ )}
230
+ <Button
231
+ type="submit"
232
+ disabled={isLoading}
233
+ className="bg-platform-primary text-white hover:bg-platform-primary/90 disabled:opacity-50"
234
+ >
235
+ {isLoading ? "Submitting..." : "Submit"}
236
+ </Button>
237
+ </div>
238
+ </form>
239
+ </div>
240
+ );
241
+ }
242
+
243
+ // Example usage:
244
+ // <FormSection
245
+ // title="Contact Information"
246
+ // description="Please fill out the form below"
247
+ // onSubmit={(data) => console.log(data)}
248
+ // onCancel={() => console.log("Cancelled")}
249
+ // isLoading={false}
250
+ // />