skills/meriley/claude-code-skills/vendure-admin-ui-reviewing

vendure-admin-ui-reviewing

SKILL.md

Vendure Admin UI Reviewing

Purpose

Audit Vendure Admin UI extensions for violations and anti-patterns.

Review Workflow

Step 1: Identify UI Files

# Find UI extension files
find . -path "*/ui/*.ts" -o -path "*/ui/*.tsx"

# Find component files
find . -name "*.tsx" -path "*/components/*"

# Find hook files
find . -name "use*.ts" -path "*/hooks/*"

Step 2: Run Automated Checks

# === CRITICAL VIOLATIONS ===

# Direct fetch calls (should use Vendure hooks)
grep -rn "fetch(" --include="*.tsx" --include="*.ts" | grep -v "node_modules"

# Missing useInjector for services
grep -rn "NotificationService" --include="*.tsx" | grep -v "useInjector"

# Angular patterns in React code
grep -rn "@Component\|@Injectable\|ngOnInit" --include="*.tsx"

# Missing page metadata
grep -rn "export function.*List\|export function.*Detail" --include="*.tsx" -A 20 | grep -v "usePageMetadata"

# === HIGH PRIORITY ===

# Missing loading states
grep -rn "useQuery" --include="*.tsx" -A 10 | grep -v "loading"

# Missing error states
grep -rn "useQuery" --include="*.tsx" -A 10 | grep -v "error"

# Direct state mutation
grep -rn "\.push(\|\.splice(\|\.pop(" --include="*.tsx"

# Missing useCallback/useMemo
grep -rn "onClick.*=.*async" --include="*.tsx" | grep -v "useCallback"

# === MEDIUM PRIORITY ===

# Inline styles (should use CSS variables)
grep -rn 'style={{' --include="*.tsx"

# Missing TypeScript types on GraphQL queries
grep -rn "useQuery(" --include="*.tsx" | grep -v "useQuery<"

# Console.log statements
grep -rn "console.log\|console.error" --include="*.tsx" --include="*.ts"

Step 3: Manual Review Checklist

Extension Structure

  • index.ts exports AdminUiExtension
  • routes.ts uses registerReactRouteComponent
  • providers.ts uses addNavMenuSection
  • Translations file exists if needed
  • GraphQL codegen configured

Components

  • usePageMetadata for title/breadcrumbs
  • useInjector(NotificationService) for notifications
  • Loading state handled
  • Error state handled
  • useCallback for event handlers
  • useMemo for expensive computations

GraphQL Integration

  • Queries in separate files
  • TypeScript types from codegen
  • Proper refetch after mutations
  • Error handling on mutations

Styling

  • CSS variables for colors/spacing
  • Responsive design considered
  • No hardcoded pixel values
  • Theme consistency with Vendure

Severity Classification

CRITICAL (Must Fix)

  • Direct fetch calls bypassing Vendure hooks
  • Angular patterns in React code
  • Missing error handling
  • No loading states

HIGH (Should Fix)

  • Missing usePageMetadata
  • Missing useInjector for services
  • Direct state mutation
  • Inline styles
  • Missing TypeScript types

MEDIUM (Should Fix)

  • Missing useCallback/useMemo
  • Console statements
  • Hardcoded strings (no translations)
  • Missing accessibility attributes

Common Violations

1. Missing Loading State

Violation:

export function ItemList() {
    const { data } = useQuery(GET_ITEMS);

    return (
        <table>
            {data?.items.map(item => (
                <tr key={item.id}>{item.name}</tr>
            ))}
        </table>
    );
}

Fix:

export function ItemList() {
    const { data, loading, error } = useQuery(GET_ITEMS);

    if (loading) return <div>Loading...</div>;
    if (error) return <div>Error: {error.message}</div>;

    return (
        <table>
            {data?.items.map(item => (
                <tr key={item.id}>{item.name}</tr>
            ))}
        </table>
    );
}

2. Missing useInjector

Violation:

// Directly importing and using service
import { NotificationService } from "@vendure/admin-ui/core";

export function ItemForm() {
  const handleSave = async () => {
    NotificationService.success("Saved!"); // WRONG
  };
}

Fix:

import { NotificationService } from "@vendure/admin-ui/core";
import { useInjector } from "@vendure/admin-ui/react";

export function ItemForm() {
  const notificationService = useInjector(NotificationService);

  const handleSave = async () => {
    notificationService.success("Saved!"); // CORRECT
  };
}

3. Missing Page Metadata

Violation:

export function ItemDetail() {
    // No page metadata!
    return (
        <div>
            <h1>Item Details</h1>
            {/* content */}
        </div>
    );
}

Fix:

export function ItemDetail() {
    const { setTitle, setBreadcrumb } = usePageMetadata();

    React.useEffect(() => {
        setTitle('Item Details');
        setBreadcrumb([
            { label: 'Items', link: ['/extensions/my-plugin/items'] },
            { label: 'Details', link: [] }
        ]);
    }, [setTitle, setBreadcrumb]);

    return (
        <PageBlock>
            {/* content */}
        </PageBlock>
    );
}

4. Direct State Mutation

Violation:

const [items, setItems] = React.useState<Item[]>([]);

const addItem = (item: Item) => {
  items.push(item); // WRONG - mutating state
  setItems(items);
};

Fix:

const [items, setItems] = React.useState<Item[]>([]);

const addItem = React.useCallback((item: Item) => {
  setItems((prev) => [...prev, item]); // CORRECT - new array
}, []);

5. Missing TypeScript Types on Query

Violation:

const { data } = useQuery(GET_ITEMS); // No type!
// data is 'any'

Fix:

const { data } = useQuery<GetItemsQuery>(GET_ITEMS);
// data is properly typed

6. Missing useCallback on Event Handlers

Violation:

export function ItemList() {
    const handleDelete = async (id: string) => {
        // Creates new function on every render
    };

    return items.map(item => (
        <button onClick={() => handleDelete(item.id)}>Delete</button>
    ));
}

Fix:

export function ItemList() {
    const handleDelete = React.useCallback(async (id: string) => {
        // Memoized function
    }, [/* dependencies */]);

    return items.map(item => (
        <button onClick={() => handleDelete(item.id)}>Delete</button>
    ));
}

Quick Detection Commands

# All-in-one UI audit
echo "=== CRITICAL: Direct fetch calls ===" && \
grep -rn "fetch(" --include="*.tsx" | grep -v "node_modules" | head -10 && \
echo "" && \
echo "=== HIGH: Missing loading states ===" && \
grep -rn "useQuery" --include="*.tsx" -l | xargs -I{} sh -c 'grep -L "loading" {} 2>/dev/null' | head -10 && \
echo "" && \
echo "=== MEDIUM: Missing TypeScript types ===" && \
grep -rn "useQuery(" --include="*.tsx" | grep -v "useQuery<" | head -10

Review Output Template

## Admin UI Review: [Component/Feature Name]

### Summary

[Overview of UI quality]

### Critical Issues (Must Fix)

- [ ] [Issue] - `file:line`

### High Priority

- [ ] [Issue] - `file:line`

### Passed Checks

- [x] Extension structure correct
- [x] Routes properly registered
- [x] Navigation items configured
- [x] Loading states handled

### Recommendations

- [Suggestions]

Extension Structure Checklist

## Extension Structure Review

### Required Files

- [ ] ui/index.ts - AdminUiExtension export
- [ ] ui/routes.ts - registerReactRouteComponent
- [ ] ui/providers.ts - addNavMenuSection

### GraphQL Setup

- [ ] ui/graphql/queries.ts - Query definitions
- [ ] ui/graphql/mutations.ts - Mutation definitions
- [ ] ui/gql/graphql.ts - Generated types
- [ ] ui/codegen.yml - Codegen configuration

### Component Organization

- [ ] components/ - React components
- [ ] hooks/ - Custom hooks
- [ ] styles/ - CSS files
- [ ] translations/ - i18n files

Cross-Reference

All rules match patterns in vendure-admin-ui-writing skill.


Related Skills

  • vendure-admin-ui-writing - UI patterns
  • vendure-plugin-reviewing - Plugin-level review
  • vendure-graphql-reviewing - GraphQL review
Weekly Installs
8
GitHub Stars
2
First Seen
Jan 27, 2026
Installed on
opencode7
gemini-cli7
codex7
cursor7
antigravity6
github-copilot6