@wondev/dotenv-example 1.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 (34) hide show
  1. package/.claude/README.md +60 -0
  2. package/.claude/commands/business_logic.md +143 -0
  3. package/.claude/commands/generate-prd.md +175 -0
  4. package/.claude/commands/gotobackend.md +569 -0
  5. package/.claude/commands/playwrightMCP_install.md +113 -0
  6. package/.claude/commands/setting_dev.md +731 -0
  7. package/.claude/commands/tech-lead.md +404 -0
  8. package/.claude/commands/user-flow.md +839 -0
  9. package/.claude/settings.local.json +9 -0
  10. package/.cursor/README.md +10 -0
  11. package/.cursor/mcp.json +31 -0
  12. package/.cursor/rules/common/cursor-rules.mdc +53 -0
  13. package/.cursor/rules/common/git-convention.mdc +86 -0
  14. package/.cursor/rules/common/self-improve.mdc +72 -0
  15. package/.cursor/rules/common/tdd.mdc +81 -0
  16. package/.cursor/rules/common/vibe-coding.mdc +114 -0
  17. package/.cursor/rules/supabase/supabase-bootstrap-auth.mdc +236 -0
  18. package/.cursor/rules/supabase/supabase-create-db-functions.mdc +136 -0
  19. package/.cursor/rules/supabase/supabase-create-migration.mdc +50 -0
  20. package/.cursor/rules/supabase/supabase-create-rls-policies.mdc +248 -0
  21. package/.cursor/rules/supabase/supabase-declarative-database-schema.mdc +78 -0
  22. package/.cursor/rules/supabase/supabase-postgres-sql-style-guide.mdc +133 -0
  23. package/.cursor/rules/supabase/supabase-writing-edge-functions.mdc +105 -0
  24. package/.cursor/rules/web/design-rules.mdc +381 -0
  25. package/.cursor/rules/web/nextjs-convention.mdc +237 -0
  26. package/.cursor/rules/web/playwright-test-guide.mdc +176 -0
  27. package/.cursor/rules/web/toss-frontend.mdc +695 -0
  28. package/.env +4 -0
  29. package/CLAUDE.md +40 -0
  30. package/README.md +7 -0
  31. package/bin/index.js +66 -0
  32. package/package.json +32 -0
  33. package/prompts/20250926_175606.md +3 -0
  34. package/prompts/20250926_180205.md +148 -0
@@ -0,0 +1,695 @@
1
+ ---
2
+ description: This rule defines Toss Frontend's design principles and coding standards, focusing on readability, predictability, cohesion, and loose coupling for maintainable React applications.
3
+ globs: **/*.js,**/*.jsx,**/*.ts,**/*.tsx
4
+ alwaysApply: false
5
+ ---
6
+ # Frontend Design Guideline
7
+
8
+ This document summarizes key frontend design principles and rules, showcasing
9
+ recommended patterns. Follow these guidelines when writing frontend code.
10
+
11
+ # Readability
12
+
13
+ Improving the clarity and ease of understanding code.
14
+
15
+ ## Naming Magic Numbers
16
+
17
+ **Rule:** Replace magic numbers with named constants for clarity.
18
+
19
+ **Reasoning:**
20
+
21
+ - Improves clarity by giving semantic meaning to unexplained values.
22
+ - Enhances maintainability.
23
+
24
+ #### Recommended Pattern:
25
+
26
+ ```typescript
27
+ const ANIMATION_DELAY_MS = 300;
28
+
29
+ async function onLikeClick() {
30
+ await postLike(url);
31
+ await delay(ANIMATION_DELAY_MS); // Clearly indicates waiting for animation
32
+ await refetchPostLike();
33
+ }
34
+ ```
35
+
36
+ ## Abstracting Implementation Details
37
+
38
+ **Rule:** Abstract complex logic/interactions into dedicated components/HOCs.
39
+
40
+ **Reasoning:**
41
+
42
+ - Reduces cognitive load by separating concerns.
43
+ - Improves readability, testability, and maintainability of components.
44
+
45
+ #### Recommended Pattern 1: Auth Guard
46
+
47
+ (Login check abstracted to a wrapper/guard component)
48
+
49
+ ```tsx
50
+ // App structure
51
+ function App() {
52
+ return (
53
+ <AuthGuard>
54
+ {" "}
55
+ {/* Wrapper handles auth check */}
56
+ <LoginStartPage />
57
+ </AuthGuard>
58
+ );
59
+ }
60
+
61
+ // AuthGuard component encapsulates the check/redirect logic
62
+ function AuthGuard({ children }) {
63
+ const status = useCheckLoginStatus();
64
+ useEffect(() => {
65
+ if (status === "LOGGED_IN") {
66
+ location.href = "/home";
67
+ }
68
+ }, [status]);
69
+
70
+ // Render children only if not logged in, otherwise render null (or loading)
71
+ return status !== "LOGGED_IN" ? children : null;
72
+ }
73
+
74
+ // LoginStartPage is now simpler, focused only on login UI/logic
75
+ function LoginStartPage() {
76
+ // ... login related logic ONLY ...
77
+ return <>{/* ... login related components ... */}</>;
78
+ }
79
+ ```
80
+
81
+ #### Recommended Pattern 2: Dedicated Interaction Component
82
+
83
+ (Dialog logic abstracted into a dedicated `InviteButton` component)
84
+
85
+ ```tsx
86
+ export function FriendInvitation() {
87
+ const { data } = useQuery(/* ... */);
88
+
89
+ return (
90
+ <>
91
+ {/* Use the dedicated button component */}
92
+ <InviteButton name={data.name} />
93
+ {/* ... other UI ... */}
94
+ </>
95
+ );
96
+ }
97
+
98
+ // InviteButton handles the confirmation flow internally
99
+ function InviteButton({ name }) {
100
+ const handleClick = async () => {
101
+ const canInvite = await overlay.openAsync(({ isOpen, close }) => (
102
+ <ConfirmDialog
103
+ title={`Share with ${name}`}
104
+ // ... dialog setup ...
105
+ />
106
+ ));
107
+
108
+ if (canInvite) {
109
+ await sendPush();
110
+ }
111
+ };
112
+
113
+ return <Button onClick={handleClick}>Invite</Button>;
114
+ }
115
+ ```
116
+
117
+ ## Separating Code Paths for Conditional Rendering
118
+
119
+ **Rule:** Separate significantly different conditional UI/logic into distinct
120
+ components.
121
+
122
+ **Reasoning:**
123
+
124
+ - Improves readability by avoiding complex conditionals within one component.
125
+ - Ensures each specialized component has a clear, single responsibility.
126
+
127
+ #### Recommended Pattern:
128
+
129
+ (Separate components for each role)
130
+
131
+ ```tsx
132
+ function SubmitButton() {
133
+ const isViewer = useRole() === "viewer";
134
+
135
+ // Delegate rendering to specialized components
136
+ return isViewer ? <ViewerSubmitButton /> : <AdminSubmitButton />;
137
+ }
138
+
139
+ // Component specifically for the 'viewer' role
140
+ function ViewerSubmitButton() {
141
+ return <TextButton disabled>Submit</TextButton>;
142
+ }
143
+
144
+ // Component specifically for the 'admin' (or non-viewer) role
145
+ function AdminSubmitButton() {
146
+ useEffect(() => {
147
+ showAnimation(); // Animation logic isolated here
148
+ }, []);
149
+
150
+ return <Button type="submit">Submit</Button>;
151
+ }
152
+ ```
153
+
154
+ ## Simplifying Complex Ternary Operators
155
+
156
+ **Rule:** Replace complex/nested ternaries with `if`/`else` or IIFEs for
157
+ readability.
158
+
159
+ **Reasoning:**
160
+
161
+ - Makes conditional logic easier to follow quickly.
162
+ - Improves overall code maintainability.
163
+
164
+ #### Recommended Pattern:
165
+
166
+ (Using an IIFE with `if` statements)
167
+
168
+ ```typescript
169
+ const status = (() => {
170
+ if (ACondition && BCondition) return "BOTH";
171
+ if (ACondition) return "A";
172
+ if (BCondition) return "B";
173
+ return "NONE";
174
+ })();
175
+ ```
176
+
177
+ ## Reducing Eye Movement (Colocating Simple Logic)
178
+
179
+ **Rule:** Colocate simple, localized logic or use inline definitions to reduce
180
+ context switching.
181
+
182
+ **Reasoning:**
183
+
184
+ - Allows top-to-bottom reading and faster comprehension.
185
+ - Reduces cognitive load from context switching (eye movement).
186
+
187
+ #### Recommended Pattern A: Inline `switch`
188
+
189
+ ```tsx
190
+ function Page() {
191
+ const user = useUser();
192
+
193
+ // Logic is directly visible here
194
+ switch (user.role) {
195
+ case "admin":
196
+ return (
197
+ <div>
198
+ <Button disabled={false}>Invite</Button>
199
+ <Button disabled={false}>View</Button>
200
+ </div>
201
+ );
202
+ case "viewer":
203
+ return (
204
+ <div>
205
+ <Button disabled={true}>Invite</Button> {/* Example for viewer */}
206
+ <Button disabled={false}>View</Button>
207
+ </div>
208
+ );
209
+ default:
210
+ return null;
211
+ }
212
+ }
213
+ ```
214
+
215
+ #### Recommended Pattern B: Colocated simple policy object
216
+
217
+ ```tsx
218
+ function Page() {
219
+ const user = useUser();
220
+ // Simple policy defined right here, easy to see
221
+ const policy = {
222
+ admin: { canInvite: true, canView: true },
223
+ viewer: { canInvite: false, canView: true },
224
+ }[user.role];
225
+
226
+ // Ensure policy exists before accessing properties if role might not match
227
+ if (!policy) return null;
228
+
229
+ return (
230
+ <div>
231
+ <Button disabled={!policy.canInvite}>Invite</Button>
232
+ <Button disabled={!policy.canView}>View</Button>
233
+ </div>
234
+ );
235
+ }
236
+ ```
237
+
238
+ ## Naming Complex Conditions
239
+
240
+ **Rule:** Assign complex boolean conditions to named variables.
241
+
242
+ **Reasoning:**
243
+
244
+ - Makes the _meaning_ of the condition explicit.
245
+ - Improves readability and self-documentation by reducing cognitive load.
246
+
247
+ #### Recommended Pattern:
248
+
249
+ (Conditions assigned to named variables)
250
+
251
+ ```typescript
252
+ const matchedProducts = products.filter((product) => {
253
+ // Check if product belongs to the target category
254
+ const isSameCategory = product.categories.some(
255
+ (category) => category.id === targetCategory.id
256
+ );
257
+
258
+ // Check if any product price falls within the desired range
259
+ const isPriceInRange = product.prices.some(
260
+ (price) => price >= minPrice && price <= maxPrice
261
+ );
262
+
263
+ // The overall condition is now much clearer
264
+ return isSameCategory && isPriceInRange;
265
+ });
266
+ ```
267
+
268
+ **Guidance:** Name conditions when the logic is complex, reused, or needs unit
269
+ testing. Avoid naming very simple, single-use conditions.
270
+
271
+ # Predictability
272
+
273
+ Ensuring code behaves as expected based on its name, parameters, and context.
274
+
275
+ ## Standardizing Return Types
276
+
277
+ **Rule:** Use consistent return types for similar functions/hooks.
278
+
279
+ **Reasoning:**
280
+
281
+ - Improves code predictability; developers can anticipate return value shapes.
282
+ - Reduces confusion and potential errors from inconsistent types.
283
+
284
+ #### Recommended Pattern 1: API Hooks (React Query)
285
+
286
+ ```typescript
287
+ // Always return the Query object
288
+ import { useQuery, UseQueryResult } from "@tanstack/react-query";
289
+
290
+ // Assuming fetchUser returns Promise<UserType>
291
+ function useUser(): UseQueryResult<UserType, Error> {
292
+ const query = useQuery({ queryKey: ["user"], queryFn: fetchUser });
293
+ return query;
294
+ }
295
+
296
+ // Assuming fetchServerTime returns Promise<Date>
297
+ function useServerTime(): UseQueryResult<Date, Error> {
298
+ const query = useQuery({
299
+ queryKey: ["serverTime"],
300
+ queryFn: fetchServerTime,
301
+ });
302
+ return query;
303
+ }
304
+ ```
305
+
306
+ #### Recommended Pattern 2: Validation Functions
307
+
308
+ (Using a consistent type, ideally a Discriminated Union)
309
+
310
+ ```typescript
311
+ type ValidationResult = { ok: true } | { ok: false; reason: string };
312
+
313
+ function checkIsNameValid(name: string): ValidationResult {
314
+ if (name.length === 0) return { ok: false, reason: "Name cannot be empty." };
315
+ if (name.length >= 20)
316
+ return { ok: false, reason: "Name cannot be longer than 20 characters." };
317
+ return { ok: true };
318
+ }
319
+
320
+ function checkIsAgeValid(age: number): ValidationResult {
321
+ if (!Number.isInteger(age))
322
+ return { ok: false, reason: "Age must be an integer." };
323
+ if (age < 18) return { ok: false, reason: "Age must be 18 or older." };
324
+ if (age > 99) return { ok: false, reason: "Age must be 99 or younger." };
325
+ return { ok: true };
326
+ }
327
+
328
+ // Usage allows safe access to 'reason' only when ok is false
329
+ const nameValidation = checkIsNameValid(name);
330
+ if (!nameValidation.ok) {
331
+ console.error(nameValidation.reason);
332
+ }
333
+ ```
334
+
335
+ ## Revealing Hidden Logic (Single Responsibility)
336
+
337
+ **Rule:** Avoid hidden side effects; functions should only perform actions
338
+ implied by their signature (SRP).
339
+
340
+ **Reasoning:**
341
+
342
+ - Leads to predictable behavior without unintended side effects.
343
+ - Creates more robust, testable code through separation of concerns (SRP).
344
+
345
+ #### Recommended Pattern:
346
+
347
+ ```typescript
348
+ // Function *only* fetches balance
349
+ async function fetchBalance(): Promise<number> {
350
+ const balance = await http.get<number>("...");
351
+ return balance;
352
+ }
353
+
354
+ // Caller explicitly performs logging where needed
355
+ async function handleUpdateClick() {
356
+ const balance = await fetchBalance(); // Fetch
357
+ logging.log("balance_fetched"); // Log (explicit action)
358
+ await syncBalance(balance); // Another action
359
+ }
360
+ ```
361
+
362
+ ## Using Unique and Descriptive Names (Avoiding Ambiguity)
363
+
364
+ **Rule:** Use unique, descriptive names for custom wrappers/functions to avoid
365
+ ambiguity.
366
+
367
+ **Reasoning:**
368
+
369
+ - Avoids ambiguity and enhances predictability.
370
+ - Allows developers to understand specific actions (e.g., adding auth) directly
371
+ from the name.
372
+
373
+ #### Recommended Pattern:
374
+
375
+ ```typescript
376
+ // In httpService.ts - Clearer module name
377
+ import { http as httpLibrary } from "@some-library/http";
378
+
379
+ export const httpService = {
380
+ // Unique module name
381
+ async getWithAuth(url: string) {
382
+ // Descriptive function name
383
+ const token = await fetchToken();
384
+ return httpLibrary.get(url, {
385
+ headers: { Authorization: `Bearer ${token}` },
386
+ });
387
+ },
388
+ };
389
+
390
+ // In fetchUser.ts - Usage clearly indicates auth
391
+ import { httpService } from "./httpService";
392
+ export async function fetchUser() {
393
+ // Name 'getWithAuth' makes the behavior explicit
394
+ return await httpService.getWithAuth("...");
395
+ }
396
+ ```
397
+
398
+ # Cohesion
399
+
400
+ Keeping related code together and ensuring modules have a well-defined, single
401
+ purpose.
402
+
403
+ ## Considering Form Cohesion
404
+
405
+ **Rule:** Choose field-level or form-level cohesion based on form requirements.
406
+
407
+ **Reasoning:**
408
+
409
+ - Balances field independence (field-level) vs. form unity (form-level).
410
+ - Ensures related form logic is appropriately grouped based on requirements.
411
+
412
+ #### Recommended Pattern (Field-Level Example):
413
+
414
+ ```tsx
415
+ // Each field uses its own `validate` function
416
+ import { useForm } from "react-hook-form";
417
+
418
+ export function Form() {
419
+ const {
420
+ register,
421
+ formState: { errors },
422
+ handleSubmit,
423
+ } = useForm({
424
+ /* defaultValues etc. */
425
+ });
426
+
427
+ const onSubmit = handleSubmit((formData) => {
428
+ console.log("Form submitted:", formData);
429
+ });
430
+
431
+ return (
432
+ <form onSubmit={onSubmit}>
433
+ <div>
434
+ <input
435
+ {...register("name", {
436
+ validate: (value) =>
437
+ value.trim() === "" ? "Please enter your name." : true, // Example validation
438
+ })}
439
+ placeholder="Name"
440
+ />
441
+ {errors.name && <p>{errors.name.message}</p>}
442
+ </div>
443
+ <div>
444
+ <input
445
+ {...register("email", {
446
+ validate: (value) =>
447
+ /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)
448
+ ? true
449
+ : "Invalid email address.", // Example validation
450
+ })}
451
+ placeholder="Email"
452
+ />
453
+ {errors.email && <p>{errors.email.message}</p>}
454
+ </div>
455
+ <button type="submit">Submit</button>
456
+ </form>
457
+ );
458
+ }
459
+ ```
460
+
461
+ #### Recommended Pattern (Form-Level Example):
462
+
463
+ ```tsx
464
+ // A single schema defines validation for the whole form
465
+ import * as z from "zod";
466
+ import { useForm } from "react-hook-form";
467
+ import { zodResolver } from "@hookform/resolvers/zod";
468
+
469
+ const schema = z.object({
470
+ name: z.string().min(1, "Please enter your name."),
471
+ email: z.string().min(1, "Please enter your email.").email("Invalid email."),
472
+ });
473
+
474
+ export function Form() {
475
+ const {
476
+ register,
477
+ formState: { errors },
478
+ handleSubmit,
479
+ } = useForm({
480
+ resolver: zodResolver(schema),
481
+ defaultValues: { name: "", email: "" },
482
+ });
483
+
484
+ const onSubmit = handleSubmit((formData) => {
485
+ console.log("Form submitted:", formData);
486
+ });
487
+
488
+ return (
489
+ <form onSubmit={onSubmit}>
490
+ <div>
491
+ <input {...register("name")} placeholder="Name" />
492
+ {errors.name && <p>{errors.name.message}</p>}
493
+ </div>
494
+ <div>
495
+ <input {...register("email")} placeholder="Email" />
496
+ {errors.email && <p>{errors.email.message}</p>}
497
+ </div>
498
+ <button type="submit">Submit</button>
499
+ </form>
500
+ );
501
+ }
502
+ ```
503
+
504
+ **Guidance:** Choose **field-level** for independent validation, async checks,
505
+ or reusable fields. Choose **form-level** for related fields, wizard forms, or
506
+ interdependent validation.
507
+
508
+ ## Organizing Code by Feature/Domain
509
+
510
+ **Rule:** Organize directories by feature/domain, not just by code type.
511
+
512
+ **Reasoning:**
513
+
514
+ - Increases cohesion by keeping related files together.
515
+ - Simplifies feature understanding, development, maintenance, and deletion.
516
+
517
+ #### Recommended Pattern:
518
+
519
+ (Organized by feature/domain)
520
+
521
+ ```
522
+ src/
523
+ ├── components/ # Shared/common components
524
+ ├── hooks/ # Shared/common hooks
525
+ ├── utils/ # Shared/common utils
526
+ ├── domains/
527
+ │ ├── user/
528
+ │ │ ├── components/
529
+ │ │ │ └── UserProfileCard.tsx
530
+ │ │ ├── hooks/
531
+ │ │ │ └── useUser.ts
532
+ │ │ └── index.ts # Optional barrel file
533
+ │ ├── product/
534
+ │ │ ├── components/
535
+ │ │ │ └── ProductList.tsx
536
+ │ │ ├── hooks/
537
+ │ │ │ └── useProducts.ts
538
+ │ │ └── ...
539
+ │ └── order/
540
+ │ ├── components/
541
+ │ │ └── OrderSummary.tsx
542
+ │ ├── hooks/
543
+ │ │ └── useOrder.ts
544
+ │ └── ...
545
+ └── App.tsx
546
+ ```
547
+
548
+ ## Relating Magic Numbers to Logic
549
+
550
+ **Rule:** Define constants near related logic or ensure names link them clearly.
551
+
552
+ **Reasoning:**
553
+
554
+ - Improves cohesion by linking constants to the logic they represent.
555
+ - Prevents silent failures caused by updating logic without updating related
556
+ constants.
557
+
558
+ #### Recommended Pattern:
559
+
560
+ ```typescript
561
+ // Constant clearly named and potentially defined near animation logic
562
+ const ANIMATION_DELAY_MS = 300;
563
+
564
+ async function onLikeClick() {
565
+ await postLike(url);
566
+ // Delay uses the constant, maintaining the link to the animation
567
+ await delay(ANIMATION_DELAY_MS);
568
+ await refetchPostLike();
569
+ }
570
+ ```
571
+
572
+ _Ensure constants are maintained alongside the logic they depend on or clearly
573
+ named to show the relationship._
574
+
575
+ # Coupling
576
+
577
+ Minimizing dependencies between different parts of the codebase.
578
+
579
+ ## Balancing Abstraction and Coupling (Avoiding Premature Abstraction)
580
+
581
+ **Rule:** Avoid premature abstraction of duplicates if use cases might diverge;
582
+ prefer lower coupling.
583
+
584
+ **Reasoning:**
585
+
586
+ - Avoids tight coupling from forcing potentially diverging logic into one
587
+ abstraction.
588
+ - Allowing some duplication can improve decoupling and maintainability when
589
+ future needs are uncertain.
590
+
591
+ #### Guidance:
592
+
593
+ Before abstracting, consider if the logic is truly identical and likely to
594
+ _stay_ identical across all use cases. If divergence is possible (e.g.,
595
+ different pages needing slightly different behavior from a shared hook like
596
+ `useOpenMaintenanceBottomSheet`), keeping the logic separate initially (allowing
597
+ duplication) can lead to more maintainable, decoupled code. Discuss trade-offs
598
+ with the team. _[No specific 'good' code example here, as the recommendation is
599
+ situational awareness rather than a single pattern]._
600
+
601
+ ## Scoping State Management (Avoiding Overly Broad Hooks)
602
+
603
+ **Rule:** Break down broad state management into smaller, focused
604
+ hooks/contexts.
605
+
606
+ **Reasoning:**
607
+
608
+ - Reduces coupling by ensuring components only depend on necessary state slices.
609
+ - Improves performance by preventing unnecessary re-renders from unrelated state
610
+ changes.
611
+
612
+ #### Recommended Pattern:
613
+
614
+ (Focused hooks, low coupling)
615
+
616
+ ```typescript
617
+ // Hook specifically for cardId query param
618
+ import { useQueryParam, NumberParam } from "use-query-params";
619
+ import { useCallback } from "react";
620
+
621
+ export function useCardIdQueryParam() {
622
+ // Assuming 'query' provides the raw param value
623
+ const [cardIdParam, setCardIdParam] = useQueryParam("cardId", NumberParam);
624
+
625
+ const setCardId = useCallback(
626
+ (newCardId: number | undefined) => {
627
+ setCardIdParam(newCardId, "replaceIn"); // Or 'push' depending on desired history behavior
628
+ },
629
+ [setCardIdParam]
630
+ );
631
+
632
+ // Provide a stable return tuple
633
+ return [cardIdParam ?? undefined, setCardId] as const;
634
+ }
635
+
636
+ // Separate hook for date range, etc.
637
+ // export function useDateRangeQueryParam() { /* ... */ }
638
+ ```
639
+
640
+ Components now only import and use `useCardIdQueryParam` if they need `cardId`,
641
+ decoupling them from date range state, etc.
642
+
643
+ ## Eliminating Props Drilling with Composition
644
+
645
+ **Rule:** Use Component Composition instead of Props Drilling.
646
+
647
+ **Reasoning:**
648
+
649
+ - Significantly reduces coupling by eliminating unnecessary intermediate
650
+ dependencies.
651
+ - Makes refactoring easier and clarifies data flow in flatter component trees.
652
+
653
+ #### Recommended Pattern:
654
+
655
+ ```tsx
656
+ import React, { useState } from "react";
657
+
658
+ // Assume Modal, Input, Button, ItemEditList components exist
659
+
660
+ function ItemEditModal({ open, items, recommendedItems, onConfirm, onClose }) {
661
+ const [keyword, setKeyword] = useState("");
662
+
663
+ // Render children directly within Modal, passing props only where needed
664
+ return (
665
+ <Modal open={open} onClose={onClose}>
666
+ {/* Input and Button rendered directly */}
667
+ <div
668
+ style={{
669
+ display: "flex",
670
+ justifyContent: "space-between",
671
+ marginBottom: "1rem",
672
+ }}
673
+ >
674
+ <Input
675
+ value={keyword}
676
+ onChange={(e) => setKeyword(e.target.value)} // State managed here
677
+ placeholder="Search items..."
678
+ />
679
+ <Button onClick={onClose}>Close</Button>
680
+ </div>
681
+ {/* ItemEditList rendered directly, gets props it needs */}
682
+ <ItemEditList
683
+ keyword={keyword} // Passed directly
684
+ items={items} // Passed directly
685
+ recommendedItems={recommendedItems} // Passed directly
686
+ onConfirm={onConfirm} // Passed directly
687
+ />
688
+ </Modal>
689
+ );
690
+ }
691
+
692
+ // The intermediate ItemEditBody component is eliminated, reducing coupling.
693
+ ```
694
+
695
+ <!-- https://gist.github.com/toy-crane/dde6258997519d954063a536fc72d055 -->
package/.env ADDED
@@ -0,0 +1,4 @@
1
+ NOTION_DATABASE_ID="da341231dasmdsa.."
2
+ NOTION_API_KEY="ntn-1231231232.."
3
+
4
+ GEMINI_API_KEY="glg-2313123..."