Skip to content

Commit 87e618a

Browse files
committed
feat: Update component prop handling and add TypeScript types
- Moved TypeScript types and interfaces to a dedicated file (`types/wizard.ts`) for better organization. - Added a pre-commit hook to validate question IDs before committing. - Enhanced the `InstructionsWizard` component to create a JSON object of questions and answers upon closing. - Updated documentation to reflect new development workflow practices regarding TypeScript types and reusable effect logic. - Added a new script (`validate-question-ids.ts`) to check for duplicate question IDs across data files.
1 parent fece1dc commit 87e618a

File tree

8 files changed

+843
-95
lines changed

8 files changed

+843
-95
lines changed

.github/copilot-instructions.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1+
12
When adding props to a component, always place the props interface at the top of the component file, above the component definition.
3+
4+
Development Workflow:
5+
- Always move TypeScript types and interfaces to a dedicated file (e.g., `types/wizard.ts`) instead of declaring them inside components.
6+
- When you identify reusable effect logic, wrap it in a custom hook under `hooks/` (e.g., `use-window-click-dismiss.ts`) and consume that hook rather than repeating `useEffect` blocks.

.husky/pre-commit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
npm run validate:ids

agents.md

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,8 @@
3535
3. The wizard automatically consumes new questions when they follow the existing schema.
3636

3737
## Development Workflow
38-
- Lint with `npm run lint` (already part of standard checks).
39-
- When editing, maintain ASCII files, Tailwind utility classes, and ShadCN UI conventions.
40-
- Tests are lint-only at present; consider adding React Testing Library coverage if the wizard logic expands.
41-
42-
## Notes for Agents
43-
- Respect the "enabled" flag semantics: `false` means the option renders disabled with a "Soon" badge.
44-
- When adding tooltip content, prefer concise prose and keep hover cards scannable.
45-
- Keep documentation links trustworthy; avoid marketing splash pages when direct references exist.
46-
- If adding new output formats, update both the data definition in `data/files.json` and any generation logic (currently beyond this repo).
47-
- When page-level components need reusable calculations or datasets, extract those helpers into `lib/utils.ts` and import them instead of defining inline.
48-
- For complex UI blocks reused across the wizard, lift them into dedicated components (e.g., `components/instructions-answer-card.tsx`) and consume them in the wizard instead of duplicating markup.
49-
- Create new components under `components/MyComponentName/MyComponentName.tsx` with an accompanying `index.tsx` that default-exports the component.
38+
39+
- **Always move TypeScript types and interfaces to a dedicated file (e.g., `types/wizard.ts`) instead of declaring them inside components.**
40+
41+
5042
- When you identify reusable effect logic, wrap it in a custom hook under `hooks/` (e.g., `use-window-click-dismiss.ts`) and consume that hook rather than repeating `useEffect` blocks.

components/instructions-wizard.tsx

Lines changed: 78 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as simpleIcons from "simple-icons"
77
import type { SimpleIcon } from "simple-icons"
88

99
import rawIdes from "@/data/ides.json"
10+
import type { DataAnswerSource, DataQuestionSource, FileOutputConfig, FrameworkConfig, IdeConfig, InstructionsWizardProps, Responses, WizardAnswer, WizardQuestion, WizardResponses, WizardStep } from "@/types/wizard"
1011
import rawFrameworks from "@/data/frameworks.json"
1112
import generalData from "@/data/general.json"
1213
import architectureData from "@/data/architecture.json"
@@ -16,86 +17,6 @@ import commitsData from "@/data/commits.json"
1617
import filesData from "@/data/files.json"
1718
import { InstructionsAnswerCard } from "./instructions-answer-card"
1819

19-
type IdeConfig = {
20-
id: string
21-
label: string
22-
icon?: string
23-
enabled?: boolean
24-
outputFiles?: string[]
25-
docs?: string
26-
}
27-
28-
type FrameworkConfig = {
29-
id: string
30-
label: string
31-
icon?: string
32-
enabled?: boolean
33-
docs?: string
34-
}
35-
36-
type DataAnswerSource = {
37-
value: string
38-
label: string
39-
icon?: string
40-
example?: string
41-
docs?: string
42-
pros?: string[]
43-
cons?: string[]
44-
tags?: string[]
45-
isDefault?: boolean
46-
disabled?: boolean
47-
disabledLabel?: string
48-
}
49-
50-
type DataQuestionSource = {
51-
id: string
52-
question: string
53-
allowMultiple?: boolean
54-
answers: DataAnswerSource[]
55-
}
56-
57-
type FileOutputConfig = {
58-
id: string
59-
label: string
60-
filename: string
61-
format: string
62-
enabled?: boolean
63-
icon?: string
64-
docs?: string
65-
}
66-
67-
type WizardAnswer = {
68-
value: string
69-
label: string
70-
icon?: string
71-
example?: string
72-
infoLines?: string[]
73-
tags?: string[]
74-
isDefault?: boolean
75-
disabled?: boolean
76-
disabledLabel?: string
77-
docs?: string
78-
}
79-
80-
type WizardQuestion = {
81-
id: string
82-
question: string
83-
allowMultiple?: boolean
84-
answers: WizardAnswer[]
85-
}
86-
87-
type WizardStep = {
88-
id: string
89-
title: string
90-
questions: WizardQuestion[]
91-
}
92-
93-
type InstructionsWizardProps = {
94-
onClose?: () => void
95-
}
96-
97-
type Responses = Record<string, string | string[] | null | undefined>
98-
9920
const FRAMEWORK_STEP_ID = "frameworks"
10021
const FRAMEWORK_QUESTION_ID = "frameworkSelection"
10122

@@ -642,7 +563,83 @@ export function InstructionsWizard({ onClose }: InstructionsWizardProps) {
642563
<Button variant="ghost" onClick={requestResetWizard}>
643564
Start Over
644565
</Button>
645-
<Button onClick={() => onClose?.()}>
566+
<Button onClick={() => {
567+
// Create a JSON object with question IDs as keys and their answers as values
568+
const questionsAndAnswers: WizardResponses = {
569+
preferredIde: null,
570+
frameworkSelection: null,
571+
tooling: null,
572+
language: null,
573+
fileStructure: null,
574+
styling: null,
575+
testingUT: null,
576+
testingE2E: null,
577+
projectPriority: null,
578+
codeStyle: null,
579+
variableNaming: null,
580+
fileNaming: null,
581+
componentNaming: null,
582+
exports: null,
583+
comments: null,
584+
collaboration: null,
585+
stateManagement: null,
586+
apiLayer: null,
587+
folders: null,
588+
dataFetching: null,
589+
reactPerf: null,
590+
auth: null,
591+
validation: null,
592+
logging: null,
593+
commitStyle: null,
594+
prRules: null,
595+
outputFile: null,
596+
}
597+
598+
wizardSteps.forEach((step) => {
599+
step.questions.forEach((question) => {
600+
let key = question.id
601+
let answer = responses[question.id]
602+
603+
// Special handling for preferredIdes and outputFiles
604+
if (key === "preferredIdes") {
605+
key = "preferredIde"
606+
if (Array.isArray(answer) && answer.length > 0) {
607+
answer = answer[0]
608+
} else if (typeof answer === "string") {
609+
// already a string
610+
} else {
611+
answer = null
612+
}
613+
}
614+
if (key === "outputFiles") {
615+
key = "outputFile"
616+
if (Array.isArray(answer) && answer.length > 0) {
617+
answer = answer[0]
618+
} else if (typeof answer === "string") {
619+
// already a string
620+
} else {
621+
answer = null
622+
}
623+
}
624+
625+
if (answer !== null && answer !== undefined) {
626+
if (question.allowMultiple && Array.isArray(answer)) {
627+
// For all other multi-selects, keep as array
628+
questionsAndAnswers[key as keyof WizardResponses] = Array.isArray(answer) ? answer.join(", ") : answer
629+
} else if (!question.allowMultiple && typeof answer === 'string') {
630+
questionsAndAnswers[key as keyof WizardResponses] = Array.isArray(answer) ? answer.join(", ") : answer
631+
} else {
632+
questionsAndAnswers[key as keyof WizardResponses] = Array.isArray(answer) ? answer.join(", ") : answer
633+
}
634+
} else {
635+
questionsAndAnswers[key as keyof WizardResponses] = null
636+
}
637+
})
638+
})
639+
640+
console.log('Questions and Answers JSON:', JSON.stringify(questionsAndAnswers, null, 2))
641+
onClose?.()
642+
}}>
646643
Generate My Instructions
647644
</Button>
648645
</div>

0 commit comments

Comments
 (0)