nurosys-agents 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.
- package/.agent/INSTRUCTIONS.md +82 -0
- package/.agent/README.md +483 -0
- package/.agent/backend/skills/architect/SKILL.md +436 -0
- package/.agent/backend/skills/auth-and-permissions/SKILL.md +168 -0
- package/.agent/backend/skills/brainstorm/SKILL.md +127 -0
- package/.agent/backend/skills/code-reviewer/SKILL.md +324 -0
- package/.agent/backend/skills/create-blueprint/SKILL.md +650 -0
- package/.agent/backend/skills/debug-issue/SKILL.md +53 -0
- package/.agent/backend/skills/explore-codebase/SKILL.md +45 -0
- package/.agent/backend/skills/quick-execute/SKILL.md +99 -0
- package/.agent/backend/skills/refactor-safely/SKILL.md +46 -0
- package/.agent/backend/skills/security-assessment/SKILL.md +174 -0
- package/.agent/backend/workflows/module-runner.claude.md +226 -0
- package/.agent/backend/workflows/module-runner.codex.md +155 -0
- package/.agent/backend/workflows/module-runner.cursor.md +212 -0
- package/.agent/frontend/skills/architect/SKILL.md +644 -0
- package/.agent/frontend/skills/auth-and-permissions/SKILL.md +43 -0
- package/.agent/frontend/skills/create-blueprint/SKILL.md +635 -0
- package/.agent/frontend/skills/debug-issue/SKILL.md +28 -0
- package/.agent/frontend/skills/explore-codebase/SKILL.md +29 -0
- package/.agent/frontend/skills/feature-workflow/SKILL.md +61 -0
- package/.agent/frontend/skills/react-quality-review/SKILL.md +126 -0
- package/.agent/frontend/skills/react-quality-review/examples.md +24 -0
- package/.agent/frontend/skills/react-quality-review/rules/_sections.md +26 -0
- package/.agent/frontend/skills/react-quality-review/rules/_template.md +28 -0
- package/.agent/frontend/skills/react-quality-review/rules/advanced-event-handler-refs.md +55 -0
- package/.agent/frontend/skills/react-quality-review/rules/advanced-init-once.md +42 -0
- package/.agent/frontend/skills/react-quality-review/rules/react-rules-calling.md +66 -0
- package/.agent/frontend/skills/react-quality-review/rules/react-rules-hooks.md +91 -0
- package/.agent/frontend/skills/react-quality-review/rules/react-rules-purity.md +121 -0
- package/.agent/frontend/skills/react-quality-review/rules/rendering-activity.md +26 -0
- package/.agent/frontend/skills/react-quality-review/rules/rendering-conditional-render.md +40 -0
- package/.agent/frontend/skills/react-quality-review/rules/rendering-content-visibility.md +38 -0
- package/.agent/frontend/skills/react-quality-review/rules/rendering-hoist-jsx.md +46 -0
- package/.agent/frontend/skills/react-quality-review/rules/rendering-usetransition-loading.md +75 -0
- package/.agent/frontend/skills/react-quality-review/rules/rerender-defer-reads.md +39 -0
- package/.agent/frontend/skills/react-quality-review/rules/rerender-dependencies.md +45 -0
- package/.agent/frontend/skills/react-quality-review/rules/rerender-derived-state-no-effect.md +40 -0
- package/.agent/frontend/skills/react-quality-review/rules/rerender-derived-state.md +29 -0
- package/.agent/frontend/skills/react-quality-review/rules/rerender-functional-setstate.md +74 -0
- package/.agent/frontend/skills/react-quality-review/rules/rerender-lazy-state-init.md +58 -0
- package/.agent/frontend/skills/react-quality-review/rules/rerender-memo-with-default-value.md +38 -0
- package/.agent/frontend/skills/react-quality-review/rules/rerender-memo.md +44 -0
- package/.agent/frontend/skills/react-quality-review/rules/rerender-move-effect-to-event.md +45 -0
- package/.agent/frontend/skills/react-quality-review/rules/rerender-no-inline-components.md +82 -0
- package/.agent/frontend/skills/react-quality-review/rules/rerender-simple-expression-in-memo.md +35 -0
- package/.agent/frontend/skills/react-quality-review/rules/rerender-transitions.md +40 -0
- package/.agent/frontend/skills/react-quality-review/rules/rerender-use-ref-transient-values.md +73 -0
- package/.agent/frontend/skills/refactor-safely/SKILL.md +29 -0
- package/.agent/frontend/skills/vuexy-component-guide/SKILL.md +369 -0
- package/.agent/frontend/skills/vuexy-component-guide/examples.md +662 -0
- package/.agent/frontend/skills/vuexy-component-guide/reference.md +1036 -0
- package/.agent/frontend/workflows/build-feature-react.workflow.md +82 -0
- package/.agent/frontend/workflows/feature-module-runner.md +101 -0
- package/.agent/monolith/skills/architect/SKILL.md +648 -0
- package/.agent/monolith/skills/auth-and-permissions/SKILL.md +43 -0
- package/.agent/monolith/skills/code-reviewer/SKILL.md +281 -0
- package/.agent/monolith/skills/create-blueprint/SKILL.md +635 -0
- package/.agent/monolith/skills/debug-issue/SKILL.md +28 -0
- package/.agent/monolith/skills/explore-codebase/SKILL.md +29 -0
- package/.agent/monolith/skills/feature-workflow/SKILL.md +61 -0
- package/.agent/monolith/skills/react-quality-review/SKILL.md +126 -0
- package/.agent/monolith/skills/react-quality-review/examples.md +24 -0
- package/.agent/monolith/skills/react-quality-review/rules/_sections.md +26 -0
- package/.agent/monolith/skills/react-quality-review/rules/_template.md +28 -0
- package/.agent/monolith/skills/react-quality-review/rules/advanced-event-handler-refs.md +55 -0
- package/.agent/monolith/skills/react-quality-review/rules/advanced-init-once.md +42 -0
- package/.agent/monolith/skills/react-quality-review/rules/react-rules-calling.md +66 -0
- package/.agent/monolith/skills/react-quality-review/rules/react-rules-hooks.md +91 -0
- package/.agent/monolith/skills/react-quality-review/rules/react-rules-purity.md +121 -0
- package/.agent/monolith/skills/react-quality-review/rules/rendering-activity.md +26 -0
- package/.agent/monolith/skills/react-quality-review/rules/rendering-conditional-render.md +40 -0
- package/.agent/monolith/skills/react-quality-review/rules/rendering-content-visibility.md +38 -0
- package/.agent/monolith/skills/react-quality-review/rules/rendering-hoist-jsx.md +46 -0
- package/.agent/monolith/skills/react-quality-review/rules/rendering-usetransition-loading.md +75 -0
- package/.agent/monolith/skills/react-quality-review/rules/rerender-defer-reads.md +39 -0
- package/.agent/monolith/skills/react-quality-review/rules/rerender-dependencies.md +45 -0
- package/.agent/monolith/skills/react-quality-review/rules/rerender-derived-state-no-effect.md +40 -0
- package/.agent/monolith/skills/react-quality-review/rules/rerender-derived-state.md +29 -0
- package/.agent/monolith/skills/react-quality-review/rules/rerender-functional-setstate.md +74 -0
- package/.agent/monolith/skills/react-quality-review/rules/rerender-lazy-state-init.md +58 -0
- package/.agent/monolith/skills/react-quality-review/rules/rerender-memo-with-default-value.md +38 -0
- package/.agent/monolith/skills/react-quality-review/rules/rerender-memo.md +44 -0
- package/.agent/monolith/skills/react-quality-review/rules/rerender-move-effect-to-event.md +45 -0
- package/.agent/monolith/skills/react-quality-review/rules/rerender-no-inline-components.md +82 -0
- package/.agent/monolith/skills/react-quality-review/rules/rerender-simple-expression-in-memo.md +35 -0
- package/.agent/monolith/skills/react-quality-review/rules/rerender-transitions.md +40 -0
- package/.agent/monolith/skills/react-quality-review/rules/rerender-use-ref-transient-values.md +73 -0
- package/.agent/monolith/skills/refactor-safely/SKILL.md +29 -0
- package/.agent/monolith/skills/vuexy-component-guide/SKILL.md +369 -0
- package/.agent/monolith/skills/vuexy-component-guide/examples.md +662 -0
- package/.agent/monolith/skills/vuexy-component-guide/reference.md +1036 -0
- package/.agent/monolith/workflows/add-new-api-feature-module.md +63 -0
- package/.agent/monolith/workflows/backend-quality-review.md +27 -0
- package/.agent/monolith/workflows/build-feature-backend.workflow.md +91 -0
- package/.agent/monolith/workflows/build-feature-react.workflow.md +82 -0
- package/.agent/monolith/workflows/feature-module-runner.md +97 -0
- package/.agent/templates/FEATURE_PLAN.md +42 -0
- package/.agent/templates/MODULE.md +45 -0
- package/.agent/templates/REVIEW_REPORT.md +44 -0
- package/.agent/templates/SECURITY_REPORT.md +70 -0
- package/.agent/templates/TEST_PLAN.md +49 -0
- package/README.md +131 -0
- package/package.json +42 -0
- package/scripts/setup-rules.js +224 -0
- package/scripts/setup.js +518 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Don't Define Components Inside Components
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents remount on every render
|
|
5
|
+
tags: rerender, components, remount, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Don't Define Components Inside Components
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (prevents remount on every render)**
|
|
11
|
+
|
|
12
|
+
Defining a component inside another component creates a new component type on every render. React sees a different component each time and fully remounts it, destroying all state and DOM.
|
|
13
|
+
|
|
14
|
+
A common reason developers do this is to access parent variables without passing props. Always pass props instead.
|
|
15
|
+
|
|
16
|
+
**Incorrect (remounts on every render):**
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
function UserProfile({ user, theme }) {
|
|
20
|
+
// Defined inside to access `theme` - BAD
|
|
21
|
+
const Avatar = () => (
|
|
22
|
+
<img
|
|
23
|
+
src={user.avatarUrl}
|
|
24
|
+
className={theme === 'dark' ? 'avatar-dark' : 'avatar-light'}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
// Defined inside to access `user` - BAD
|
|
29
|
+
const Stats = () => (
|
|
30
|
+
<div>
|
|
31
|
+
<span>{user.followers} followers</span>
|
|
32
|
+
<span>{user.posts} posts</span>
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div>
|
|
38
|
+
<Avatar />
|
|
39
|
+
<Stats />
|
|
40
|
+
</div>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Every time `UserProfile` renders, `Avatar` and `Stats` are new component types. React unmounts the old instances and mounts new ones, losing any internal state, running effects again, and recreating DOM nodes.
|
|
46
|
+
|
|
47
|
+
**Correct (pass props instead):**
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
function Avatar({ src, theme }: { src: string; theme: string }) {
|
|
51
|
+
return (
|
|
52
|
+
<img
|
|
53
|
+
src={src}
|
|
54
|
+
className={theme === 'dark' ? 'avatar-dark' : 'avatar-light'}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function Stats({ followers, posts }: { followers: number; posts: number }) {
|
|
60
|
+
return (
|
|
61
|
+
<div>
|
|
62
|
+
<span>{followers} followers</span>
|
|
63
|
+
<span>{posts} posts</span>
|
|
64
|
+
</div>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function UserProfile({ user, theme }) {
|
|
69
|
+
return (
|
|
70
|
+
<div>
|
|
71
|
+
<Avatar src={user.avatarUrl} theme={theme} />
|
|
72
|
+
<Stats followers={user.followers} posts={user.posts} />
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Symptoms of this bug:**
|
|
79
|
+
- Input fields lose focus on every keystroke
|
|
80
|
+
- Animations restart unexpectedly
|
|
81
|
+
- `useEffect` cleanup/setup runs on every parent render
|
|
82
|
+
- Scroll position resets inside the component
|
package/.agent/frontend/skills/react-quality-review/rules/rerender-simple-expression-in-memo.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Do not wrap a simple expression with a primitive result type in useMemo
|
|
3
|
+
impact: LOW-MEDIUM
|
|
4
|
+
impactDescription: wasted computation on every render
|
|
5
|
+
tags: rerender, useMemo, optimization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Do not wrap a simple expression with a primitive result type in useMemo
|
|
9
|
+
|
|
10
|
+
When an expression is simple (few logical or arithmetical operators) and has a primitive result type (boolean, number, string), do not wrap it in `useMemo`.
|
|
11
|
+
Calling `useMemo` and comparing hook dependencies may consume more resources than the expression itself.
|
|
12
|
+
|
|
13
|
+
**Incorrect:**
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
function Header({ user, notifications }: Props) {
|
|
17
|
+
const isLoading = useMemo(() => {
|
|
18
|
+
return user.isLoading || notifications.isLoading
|
|
19
|
+
}, [user.isLoading, notifications.isLoading])
|
|
20
|
+
|
|
21
|
+
if (isLoading) return <Skeleton />
|
|
22
|
+
// return some markup
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Correct:**
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
function Header({ user, notifications }: Props) {
|
|
30
|
+
const isLoading = user.isLoading || notifications.isLoading
|
|
31
|
+
|
|
32
|
+
if (isLoading) return <Skeleton />
|
|
33
|
+
// return some markup
|
|
34
|
+
}
|
|
35
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Transitions for Non-Urgent Updates
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: maintains UI responsiveness
|
|
5
|
+
tags: rerender, transitions, startTransition, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Transitions for Non-Urgent Updates
|
|
9
|
+
|
|
10
|
+
Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness.
|
|
11
|
+
|
|
12
|
+
**Incorrect (blocks UI on every scroll):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
function ScrollTracker() {
|
|
16
|
+
const [scrollY, setScrollY] = useState(0)
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const handler = () => setScrollY(window.scrollY)
|
|
19
|
+
window.addEventListener('scroll', handler, { passive: true })
|
|
20
|
+
return () => window.removeEventListener('scroll', handler)
|
|
21
|
+
}, [])
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Correct (non-blocking updates):**
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { startTransition } from 'react'
|
|
29
|
+
|
|
30
|
+
function ScrollTracker() {
|
|
31
|
+
const [scrollY, setScrollY] = useState(0)
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const handler = () => {
|
|
34
|
+
startTransition(() => setScrollY(window.scrollY))
|
|
35
|
+
}
|
|
36
|
+
window.addEventListener('scroll', handler, { passive: true })
|
|
37
|
+
return () => window.removeEventListener('scroll', handler)
|
|
38
|
+
}, [])
|
|
39
|
+
}
|
|
40
|
+
```
|
package/.agent/frontend/skills/react-quality-review/rules/rerender-use-ref-transient-values.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use useRef for Transient Values
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: avoids unnecessary re-renders on frequent updates
|
|
5
|
+
tags: rerender, useref, state, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use useRef for Transient Values
|
|
9
|
+
|
|
10
|
+
When a value changes frequently and you don't want a re-render on every update (e.g., mouse trackers, intervals, transient flags), store it in `useRef` instead of `useState`. Keep component state for UI; use refs for temporary DOM-adjacent values. Updating a ref does not trigger a re-render.
|
|
11
|
+
|
|
12
|
+
**Incorrect (renders every update):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
function Tracker() {
|
|
16
|
+
const [lastX, setLastX] = useState(0)
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const onMove = (e: MouseEvent) => setLastX(e.clientX)
|
|
20
|
+
window.addEventListener('mousemove', onMove)
|
|
21
|
+
return () => window.removeEventListener('mousemove', onMove)
|
|
22
|
+
}, [])
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div
|
|
26
|
+
style={{
|
|
27
|
+
position: 'fixed',
|
|
28
|
+
top: 0,
|
|
29
|
+
left: lastX,
|
|
30
|
+
width: 8,
|
|
31
|
+
height: 8,
|
|
32
|
+
background: 'black',
|
|
33
|
+
}}
|
|
34
|
+
/>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Correct (no re-render for tracking):**
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
function Tracker() {
|
|
43
|
+
const lastXRef = useRef(0)
|
|
44
|
+
const dotRef = useRef<HTMLDivElement>(null)
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const onMove = (e: MouseEvent) => {
|
|
48
|
+
lastXRef.current = e.clientX
|
|
49
|
+
const node = dotRef.current
|
|
50
|
+
if (node) {
|
|
51
|
+
node.style.transform = `translateX(${e.clientX}px)`
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
window.addEventListener('mousemove', onMove)
|
|
55
|
+
return () => window.removeEventListener('mousemove', onMove)
|
|
56
|
+
}, [])
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div
|
|
60
|
+
ref={dotRef}
|
|
61
|
+
style={{
|
|
62
|
+
position: 'fixed',
|
|
63
|
+
top: 0,
|
|
64
|
+
left: 0,
|
|
65
|
+
width: 8,
|
|
66
|
+
height: 8,
|
|
67
|
+
background: 'black',
|
|
68
|
+
transform: 'translateX(0px)',
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: refactor-safely
|
|
3
|
+
description: Plan and execute safe refactoring using dependency analysis
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Refactor Safely
|
|
8
|
+
|
|
9
|
+
Use the knowledge graph to plan and execute refactoring with confidence.
|
|
10
|
+
|
|
11
|
+
### Steps
|
|
12
|
+
|
|
13
|
+
1. Use `refactor_tool` with mode="suggest" for community-driven refactoring suggestions.
|
|
14
|
+
2. Use `refactor_tool` with mode="dead_code" to find unreferenced code.
|
|
15
|
+
3. For renames, use `refactor_tool` with mode="rename" to preview all affected locations.
|
|
16
|
+
4. Use `apply_refactor_tool` with the refactor_id to apply renames.
|
|
17
|
+
5. After changes, run `detect_changes` to verify the refactoring impact.
|
|
18
|
+
|
|
19
|
+
### Safety Checks
|
|
20
|
+
|
|
21
|
+
- Always preview before applying (rename mode gives you an edit list).
|
|
22
|
+
- Check `get_impact_radius` before major refactors.
|
|
23
|
+
- Use `get_affected_flows` to ensure no critical paths are broken.
|
|
24
|
+
- Run `find_large_functions` to identify decomposition targets.
|
|
25
|
+
|
|
26
|
+
## Token Efficiency Rules
|
|
27
|
+
- ALWAYS start with `get_minimal_context(task="<your task>")` before any other graph tool.
|
|
28
|
+
- Use `detail_level="minimal"` on all calls. Only escalate to "standard" when minimal is insufficient.
|
|
29
|
+
- Target: complete any review/debug/refactor task in ≤5 tool calls and ≤800 total output tokens.
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vuexy-component-guide
|
|
3
|
+
description: >-
|
|
4
|
+
Comprehensive guide to all MUI components used in the Vuexy admin template with MUI v7. Covers 60+ MUI Material and Lab components, 5 custom wrappers (CustomTextField, CustomAvatar, CustomChip, CustomBadge, CustomTabList), and form-building patterns with react-hook-form + valibot. Use when building UI components, creating forms with validation, designing dialogs, drawers, tables, or steppers, or when the user asks about MUI components, form patterns, component configuration, custom wrappers, or how to build interfaces in this Vuexy Next.js project.
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Vuexy MUI Component Guide
|
|
9
|
+
|
|
10
|
+
This skill covers **every MUI component** used in the Vuexy admin template, including custom wrappers in `@core/components/mui/`, form patterns, and layout guidelines.
|
|
11
|
+
|
|
12
|
+
## Quick Decision Flow
|
|
13
|
+
|
|
14
|
+
1. **Building a form?** → See [Form Patterns](#form-patterns) below
|
|
15
|
+
2. **Displaying data in a table?** → See [Data Display](#data-display)
|
|
16
|
+
3. **Showing a dialog/drawer?** → See [Overlays](#overlays)
|
|
17
|
+
4. **Need a card with stats?** → See [Card Statistics Components](#card-statistics-components)
|
|
18
|
+
5. **Navigation/tabs?** → See [Navigation](#navigation)
|
|
19
|
+
|
|
20
|
+
## Core Rule: Use Custom Wrappers
|
|
21
|
+
|
|
22
|
+
Vuexy provides styled wrappers over standard MUI components. **Always prefer the custom versions** from `@core/components/mui/`:
|
|
23
|
+
|
|
24
|
+
| Custom Component | Wraps | Import Path | Added Props |
|
|
25
|
+
|-----------------|-------|-------------|-------------|
|
|
26
|
+
| `CustomTextField` | `TextField` | `@core/components/mui/TextField` | `size='small'` (default), `variant='filled'`, themed borders/colors |
|
|
27
|
+
| `CustomAvatar` | `Avatar` | `@core/components/mui/Avatar` | `color`, `skin` (`'filled'`, `'light'`, `'light-static'`), `size` |
|
|
28
|
+
| `CustomBadge` | `Badge` | `@core/components/mui/Badge` | `tonal` (`'true'` / `'false'`) for light bg badges |
|
|
29
|
+
| `CustomChip` | `Chip` | `@core/components/mui/Chip` | `round` (`'true'` / `'false'`) for pill shape |
|
|
30
|
+
| `CustomTabList` | `TabList` (@mui/lab) | `@core/components/mui/TabList` | `color`, `pill` (`'true'` / `'false'`) for pill-style tabs |
|
|
31
|
+
| `CustomIconButton` | `Button` (styled) | `@core/components/mui/IconButton` | icon font sizes (S:20px, M:22px, L:24px), default color from `action.active` |
|
|
32
|
+
| `CustomAutocomplete` | `Autocomplete` | `@core/components/mui/Autocomplete` | Paper slot styling, generic type support |
|
|
33
|
+
|
|
34
|
+
### All other MUI components → import directly from `@mui/material/ComponentName`
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Form Patterns
|
|
39
|
+
|
|
40
|
+
### Standard Form Template
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
'use client'
|
|
44
|
+
|
|
45
|
+
import { Controller, useForm } from 'react-hook-form'
|
|
46
|
+
import { valibotResolver } from '@hookform/resolvers/valibot'
|
|
47
|
+
import { object, string, nonEmpty, minLength, pipe, email } from 'valibot'
|
|
48
|
+
import Grid from '@mui/material/Grid'
|
|
49
|
+
import Button from '@mui/material/Button'
|
|
50
|
+
import FormHelperText from '@mui/material/FormHelperText'
|
|
51
|
+
import FormLabel from '@mui/material/FormLabel'
|
|
52
|
+
import Radio from '@mui/material/Radio'
|
|
53
|
+
import RadioGroup from '@mui/material/RadioGroup'
|
|
54
|
+
import Checkbox from '@mui/material/Checkbox'
|
|
55
|
+
import Switch from '@mui/material/Switch'
|
|
56
|
+
import MenuItem from '@mui/material/MenuItem'
|
|
57
|
+
import CustomTextField from '@core/components/mui/TextField'
|
|
58
|
+
|
|
59
|
+
const schema = object({
|
|
60
|
+
name: pipe(string(), nonEmpty('Required'), minLength(1)),
|
|
61
|
+
email: pipe(string(), nonEmpty('Required'), email('Invalid email')),
|
|
62
|
+
password: pipe(string(), nonEmpty('Required'), minLength(8, 'Min 8 chars')),
|
|
63
|
+
country: pipe(string(), nonEmpty('Required')),
|
|
64
|
+
agree: pipe(string(), nonEmpty('Required'))
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
type FormValues = {
|
|
68
|
+
name: string
|
|
69
|
+
email: string
|
|
70
|
+
password: string
|
|
71
|
+
country: string
|
|
72
|
+
agree: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default function MyForm() {
|
|
76
|
+
const {
|
|
77
|
+
control,
|
|
78
|
+
handleSubmit,
|
|
79
|
+
formState: { errors }
|
|
80
|
+
} = useForm<FormValues>({
|
|
81
|
+
resolver: valibotResolver(schema),
|
|
82
|
+
defaultValues: { name: '', email: '', password: '', country: '', agree: '' }
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const onSubmit = (data: FormValues) => {
|
|
86
|
+
// handle submission
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
91
|
+
<Grid container spacing={6}>
|
|
92
|
+
{/* Text Input */}
|
|
93
|
+
<Grid size={{ xs: 12, sm: 6 }}>
|
|
94
|
+
<Controller
|
|
95
|
+
name='name'
|
|
96
|
+
control={control}
|
|
97
|
+
rules={{ required: true }}
|
|
98
|
+
render={({ field }) => (
|
|
99
|
+
<CustomTextField
|
|
100
|
+
{...field}
|
|
101
|
+
fullWidth
|
|
102
|
+
label='Name'
|
|
103
|
+
placeholder='John Doe'
|
|
104
|
+
{...(errors.name && { error: true, helperText: errors.name.message })}
|
|
105
|
+
/>
|
|
106
|
+
)}
|
|
107
|
+
/>
|
|
108
|
+
</Grid>
|
|
109
|
+
|
|
110
|
+
{/* Select Dropdown */}
|
|
111
|
+
<Grid size={{ xs: 12, sm: 6 }}>
|
|
112
|
+
<Controller
|
|
113
|
+
name='country'
|
|
114
|
+
control={control}
|
|
115
|
+
rules={{ required: true }}
|
|
116
|
+
render={({ field }) => (
|
|
117
|
+
<CustomTextField select fullWidth label='Country' {...field} error={Boolean(errors.country)}>
|
|
118
|
+
<MenuItem value=''>Select Country</MenuItem>
|
|
119
|
+
<MenuItem value='US'>United States</MenuItem>
|
|
120
|
+
<MenuItem value='UK'>United Kingdom</MenuItem>
|
|
121
|
+
</CustomTextField>
|
|
122
|
+
)}
|
|
123
|
+
/>
|
|
124
|
+
{errors.country && <FormHelperText error>{errors.country.message}</FormHelperText>}
|
|
125
|
+
</Grid>
|
|
126
|
+
|
|
127
|
+
{/* Radio Group */}
|
|
128
|
+
<Grid size={{ xs: 12 }}>
|
|
129
|
+
<FormControl error={Boolean(errors.agree)}>
|
|
130
|
+
<FormLabel>Agreement</FormLabel>
|
|
131
|
+
<Controller
|
|
132
|
+
name='agree'
|
|
133
|
+
control={control}
|
|
134
|
+
rules={{ required: true }}
|
|
135
|
+
render={({ field }) => (
|
|
136
|
+
<RadioGroup row {...field}>
|
|
137
|
+
<FormControlLabel value='yes' control={<Radio />} label='Yes' />
|
|
138
|
+
<FormControlLabel value='no' control={<Radio />} label='No' />
|
|
139
|
+
</RadioGroup>
|
|
140
|
+
)}
|
|
141
|
+
/>
|
|
142
|
+
{errors.agree && <FormHelperText error>This field is required</FormHelperText>}
|
|
143
|
+
</FormControl>
|
|
144
|
+
</Grid>
|
|
145
|
+
|
|
146
|
+
{/* Submit / Reset */}
|
|
147
|
+
<Grid size={{ xs: 12 }} className='flex gap-4'>
|
|
148
|
+
<Button variant='contained' type='submit'>Submit</Button>
|
|
149
|
+
<Button variant='tonal' color='secondary' type='reset' onClick={() => reset()}>Reset</Button>
|
|
150
|
+
</Grid>
|
|
151
|
+
</Grid>
|
|
152
|
+
</form>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Input Patterns Reference
|
|
158
|
+
|
|
159
|
+
| Input Type | Props on `CustomTextField`/Wrapper | Additional Components |
|
|
160
|
+
|-----------|---------------------------|----------------------|
|
|
161
|
+
| Text | `fullWidth`, `label`, `placeholder` | — |
|
|
162
|
+
| Email | `type='email'` | — |
|
|
163
|
+
| Password | `type={isPasswordShown ? 'text' : 'password'}` + `InputAdornment` with toggle icon | `IconButton`, `InputAdornment` |
|
|
164
|
+
| Select | `select` + `<MenuItem>` children | `MenuItem` |
|
|
165
|
+
| Multi-select | `select` + `slotProps={{ select: { multiple: true } }}` | `MenuItem` |
|
|
166
|
+
| Textarea | `multiline`, `rows={4}` | — |
|
|
167
|
+
| Date | Custom wrapper with `AppReactDatepicker` + `customInput={CustomTextField}` | `AppReactDatepicker` from `@/libs/styles/AppReactDatepicker` |
|
|
168
|
+
| Autocomplete | `CustomAutocomplete` wrapping `CustomTextField` via `renderInput` | `CustomAutocomplete` from `@core/components/mui/Autocomplete` |
|
|
169
|
+
| Radio | `Controller` + `RadioGroup` + `FormControlLabel` + `Radio` | `FormLabel`, `FormControl`, `FormHelperText` |
|
|
170
|
+
| Checkbox | `FormControlLabel` + `Checkbox` | `FormControl`, `FormHelperText` |
|
|
171
|
+
| Switch | `FormControlLabel` + `Switch` | — |
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Layout Patterns
|
|
176
|
+
|
|
177
|
+
### Page Layout (Grid-based)
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
import Grid from '@mui/material/Grid'
|
|
181
|
+
import Typography from '@mui/material/Typography'
|
|
182
|
+
|
|
183
|
+
// Page header
|
|
184
|
+
<Typography variant='h4'>Page Title</Typography>
|
|
185
|
+
<Typography variant='body2'>Page description</Typography>
|
|
186
|
+
|
|
187
|
+
// Card grid
|
|
188
|
+
<Grid container spacing={6}>
|
|
189
|
+
<Grid size={{ xs: 12, md: 6 }}>
|
|
190
|
+
<Card>...</Card>
|
|
191
|
+
</Grid>
|
|
192
|
+
<Grid size={{ xs: 12, md: 6 }}>
|
|
193
|
+
<Card>...</Card>
|
|
194
|
+
</Grid>
|
|
195
|
+
</Grid>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Responsive Breakpoints
|
|
199
|
+
|
|
200
|
+
Use MUI sizes in `Grid`: `xs`, `sm`, `md`, `lg`, `xl`
|
|
201
|
+
- Common patterns: `{ xs: 12, sm: 6 }`, `{ xs: 12, md: 4 }`, `{ xs: 12, md: 6 }`
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Component Catalog (Quick Reference)
|
|
206
|
+
|
|
207
|
+
See [reference.md](reference.md) for complete component documentation with all configuration options.
|
|
208
|
+
|
|
209
|
+
### Summary by Category
|
|
210
|
+
|
|
211
|
+
**Inputs** — `CustomTextField`, `CustomAutocomplete` (searchable dropdowns), `Autocomplete`, `Select`, `MenuItem`, `InputAdornment`, `InputBase`, `FormControl`, `FormHelperText`, `FormLabel`, `FormGroup`, `FormControlLabel`, `InputLabel`, `Checkbox`, `Radio`, `RadioGroup`, `Switch`, `Rating`
|
|
212
|
+
|
|
213
|
+
**Data Display** — `Typography`, `Card`, `CardContent`, `CardHeader`, `CardActions`, `CardMedia`, `Chip` (via `CustomChip`), `Avatar` (via `CustomAvatar`), `AvatarGroup`, `Badge` (via `CustomBadge`), `List`, `ListItem`, `ListItemButton`, `ListItemText`, `ListItemIcon`, `ListItemAvatar`, `Divider`, `Table`, `TableBody`, `TableCell`, `TableContainer`, `TableHead`, `TableRow`, `TablePagination`
|
|
214
|
+
|
|
215
|
+
**Feedback** — `Alert`, `AlertTitle`, `CircularProgress`, `LinearProgress`, `Backdrop`, `Snackbar` (though project uses `react-toastify`)
|
|
216
|
+
|
|
217
|
+
**Navigation** — `Button`, `ButtonGroup`, `Breadcrumbs`, `Tabs` (`TabContext`, `TabPanel`, `CustomTabList`), `Tab`, `Step`, `Stepper`, `StepLabel`, `StepContent`, `StepIcon`, `Pagination`
|
|
218
|
+
|
|
219
|
+
**Overlays** — `Dialog`, `DialogTitle`, `DialogContent`, `DialogActions`, `Drawer`, `Popper`, `Fade`, `Grow`, `Zoom`, `Collapse`, `Menu`, `MenuList`, `MenuItem`, `Tooltip`, `ClickAwayListener`, `Paper`
|
|
220
|
+
|
|
221
|
+
**Layout** — `Grid`, `Box`, `Stack`, `CssBaseline`, `Divider`
|
|
222
|
+
|
|
223
|
+
**Surface** — `Card`, `CardActions`, `CardContent`, `CardHeader`, `CardMedia`, `Paper`, `Accordion`, `AccordionSummary`, `AccordionDetails`
|
|
224
|
+
|
|
225
|
+
**Lab Components** — `TabContext`, `TabList` (CustomTabList), `TabPanel`, `Timeline`, `TimelineItem`, `TimelineSeparator`, `TimelineConnector`, `TimelineContent`, `TimelineDot`
|
|
226
|
+
|
|
227
|
+
**Actions** — `Button` (text + icons), `CustomIconButton` (icon-only), `ButtonGroup`
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Dialog / Drawer Patterns
|
|
232
|
+
|
|
233
|
+
### Standard Dialog
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
'use client'
|
|
237
|
+
|
|
238
|
+
import { useState } from 'react'
|
|
239
|
+
import Dialog from '@mui/material/Dialog'
|
|
240
|
+
import DialogTitle from '@mui/material/DialogTitle'
|
|
241
|
+
import DialogContent from '@mui/material/DialogContent'
|
|
242
|
+
import DialogActions from '@mui/material/DialogActions'
|
|
243
|
+
import Button from '@mui/material/Button'
|
|
244
|
+
import Typography from '@mui/material/Typography'
|
|
245
|
+
import Grid from '@mui/material/Grid'
|
|
246
|
+
import CustomTextField from '@core/components/mui/TextField'
|
|
247
|
+
import DialogCloseButton from '@/components/dialogs/DialogCloseButton'
|
|
248
|
+
|
|
249
|
+
export default function MyDialog() {
|
|
250
|
+
const [open, setOpen] = useState(false)
|
|
251
|
+
|
|
252
|
+
return (
|
|
253
|
+
<>
|
|
254
|
+
<Button variant='contained' onClick={() => setOpen(true)}>Open Dialog</Button>
|
|
255
|
+
<Dialog open={open} onClose={() => setOpen(false)} fullWidth maxWidth='sm'>
|
|
256
|
+
<DialogTitle>Dialog Title</DialogTitle>
|
|
257
|
+
<DialogCloseButton onClick={() => setOpen(false)} />
|
|
258
|
+
<DialogContent>
|
|
259
|
+
{/* Form or content */}
|
|
260
|
+
</DialogContent>
|
|
261
|
+
<DialogActions className='dialog-actions-dense'>
|
|
262
|
+
<Button variant='tonal' color='secondary' onClick={() => setOpen(false)}>Cancel</Button>
|
|
263
|
+
<Button variant='contained'>Submit</Button>
|
|
264
|
+
</DialogActions>
|
|
265
|
+
</Dialog>
|
|
266
|
+
</>
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Drawer (Slide-in Panel)
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
import Drawer from '@mui/material/Drawer'
|
|
275
|
+
|
|
276
|
+
<Drawer
|
|
277
|
+
open={open}
|
|
278
|
+
anchor='right'
|
|
279
|
+
variant='temporary'
|
|
280
|
+
onClose={() => setOpen(false)}
|
|
281
|
+
ModalProps={{ keepMounted: true }}
|
|
282
|
+
sx={{ '& .MuiDrawer-paper': { width: { xs: 300, sm: 400 } } }}
|
|
283
|
+
>
|
|
284
|
+
{/* Drawer content */}
|
|
285
|
+
</Drawer>
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Button Variants
|
|
291
|
+
|
|
292
|
+
| Variant | Usage | Example |
|
|
293
|
+
|---------|-------|---------|
|
|
294
|
+
| `contained` | Primary action | Submit, Save, Create |
|
|
295
|
+
| `tonal` (Vuexy custom) | Secondary action | Cancel, Reset, Back |
|
|
296
|
+
| `outlined` | Tertiary action | — |
|
|
297
|
+
| `text` | Least prominent action | — |
|
|
298
|
+
|
|
299
|
+
### CustomIconButton
|
|
300
|
+
|
|
301
|
+
Import from `@core/components/mui/IconButton`. Styled `Button` optimized for icon-only actions.
|
|
302
|
+
|
|
303
|
+
- **Icon sizes auto-scale**: `small` = 20px, `medium` = 22px, `large` = 24px
|
|
304
|
+
- **Default color**: `var(--mui-palette-action-active)` (neutral gray) — no variant-specific color unless `color` prop set
|
|
305
|
+
- **No ripple** when `themeConfig.disableRipple` is true
|
|
306
|
+
- **Use when**: close buttons, menu toggles, edit/delete icon actions
|
|
307
|
+
- **vs `@mui/material/IconButton`**: This project uses `CustomIconButton` which extends `MuiButton`, NOT `@mui/material/IconButton`. The custom version removes min-width constraint, applies specific padding, and integrates with theme config.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## CustomAutocomplete
|
|
312
|
+
|
|
313
|
+
Import from `@core/components/mui/Autocomplete`. Wraps `@mui/material/Autocomplete` with Paper slot pre-configured.
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
import CustomAutocomplete from '@core/components/mui/Autocomplete'
|
|
317
|
+
import CustomTextField from '@core/components/mui/TextField'
|
|
318
|
+
|
|
319
|
+
<CustomAutocomplete
|
|
320
|
+
options={users}
|
|
321
|
+
getOptionLabel={(o) => o.name}
|
|
322
|
+
value={selected}
|
|
323
|
+
onChange={(_, value) => setSelected(value)}
|
|
324
|
+
renderInput={(params) => <CustomTextField {...params} label='Select user' />}
|
|
325
|
+
/>
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## Best Practices
|
|
331
|
+
|
|
332
|
+
1. **Always use `CustomTextField`** for text inputs to maintain theme consistency
|
|
333
|
+
2. **Use `CustomAvatar` with `skin='light'`** for softer appearance in user lists
|
|
334
|
+
3. **Use `CustomChip` with `round='true'`** for pill-shaped tags
|
|
335
|
+
4. **Use `CustomIconButton` for icon-only actions**, `Button` for text-based actions
|
|
336
|
+
5. **Use `Stack` for simple flex layouts**, `Box` for complex layouts
|
|
337
|
+
6. **Use `Grid` for responsive multi-column layouts**
|
|
338
|
+
7. **Always pair `CustomAutocomplete` with `CustomTextField`** using `renderInput` prop
|
|
339
|
+
8. **Use `CustomTabList` with `pill='true'`** for modern pill-style tabs
|
|
340
|
+
9. **Use semantic colors** (primary, success, error) instead of hardcoded colors
|
|
341
|
+
10. **Import only what you use** from `@mui/material` to keep bundle small
|
|
342
|
+
11. **Wrap forms in `Stack` or `Grid`** for consistent spacing between fields
|
|
343
|
+
12. **Use `Card` for content grouping** with `CardHeader`, `CardContent`, `CardActions`
|
|
344
|
+
13. **Always provide `label` on form inputs** for accessibility
|
|
345
|
+
14. **Avoid nesting too many `Box` elements** — use `Grid` for layouts
|
|
346
|
+
|
|
347
|
+
## Accessibility
|
|
348
|
+
|
|
349
|
+
1. **Always use `label` on form inputs** — required for screen readers
|
|
350
|
+
2. **Use semantic HTML** (`button`, `select`, `input`)
|
|
351
|
+
3. **Provide `aria-label` for `CustomIconButton`** icon-only buttons
|
|
352
|
+
4. **Use proper heading hierarchy** (`h1` → `h2` → `h3`)
|
|
353
|
+
5. **Ensure color contrast** meets WCAG standards (Vuexy theme ensures this)
|
|
354
|
+
6. **Support keyboard navigation** — all MUI components are keyboard-accessible by default
|
|
355
|
+
7. **Use error messages** (`helperText`) for validation feedback
|
|
356
|
+
8. **Don't use color alone** to convey information — pair with text or icons
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Color System
|
|
361
|
+
|
|
362
|
+
Use `color` prop on any MUI component: `'primary'`, `'secondary'`, `'error'`, `'warning'`, `'info'`, `'success'`
|
|
363
|
+
|
|
364
|
+
For Avatars/Badges: use `skin` prop (`'filled'`, `'light'`, `'light-static'`) to control background style.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
For the complete component reference with every available prop and configuration option, see [reference.md](reference.md).
|
|
369
|
+
For full interface examples, see [examples.md](examples.md).
|