Skip to main content
ClaudeWave
Skill556 estrellas del repoactualizado 11d ago

phase-6-ui-integration

Phase 6 transforms design mockups into functional screens by implementing UI components from the design system and connecting them to backend APIs. Use this phase when ready to build actual user-facing pages with state management, API integration, error handling, and loading states across features like authentication, products, and orders.

Instalar en Claude Code
Copiar
git clone --depth 1 https://github.com/popup-studio-ai/bkit-claude-code /tmp/phase-6-ui-integration && cp -r /tmp/phase-6-ui-integration/skills/phase-6-ui-integration ~/.claude/skills/phase-6-ui-integration
Después abre una sesión nueva de Claude Code; el skill carga automáticamente.

SKILL.md

# Phase 6: UI Implementation + API Integration

> Actual UI implementation and API integration

## Purpose

Implement actual screens using design system components and integrate with APIs.

## What to Do in This Phase

1. **Page Implementation**: Develop each screen
2. **State Management**: Handle client state
3. **API Integration**: Call backend APIs
4. **Error Handling**: Handle loading and error states

## Deliverables

```
src/
├── pages/              # Page components
│   ├── index.tsx
│   ├── login.tsx
│   └── ...
├── features/           # Feature-specific components
│   ├── auth/
│   ├── product/
│   └── ...
└── hooks/              # API call hooks
    ├── useAuth.ts
    └── useProducts.ts

docs/03-analysis/
└── ui-qa.md            # QA results
```

## PDCA Application

- **Plan**: Define screens/features to implement
- **Design**: Component structure, state management design
- **Do**: UI implementation + API integration
- **Check**: Zero Script QA
- **Act**: Fix bugs and proceed to Phase 7

## Level-wise Application

| Level | Application Method |
|-------|-------------------|
| Starter | Static UI only (no API integration) |
| Dynamic | Full integration |
| Enterprise | Full integration + optimization |

## API Client Architecture

### Why is a Centralized API Client Needed?

| Problem (Scattered API Calls) | Solution (Centralized Client) |
|------------------------------|------------------------------|
| Duplicate error handling logic | Common error handler |
| Distributed auth token handling | Automatic token injection |
| Inconsistent response formats | Standardized response types |
| Multiple changes when endpoint changes | Single point of management |
| Difficult testing/mocking | Easy mock replacement |

### 3-Layer API Client Structure

```
┌─────────────────────────────────────────────────────────┐
│                    UI Components                         │
│              (pages, features, hooks)                    │
├─────────────────────────────────────────────────────────┤
│                    Service Layer                         │
│         (Domain-specific API call functions)             │
│    authService, productService, orderService, ...        │
├─────────────────────────────────────────────────────────┤
│                    API Client Layer                      │
│         (Common settings, interceptors, error handling)  │
│              apiClient (axios/fetch wrapper)             │
└─────────────────────────────────────────────────────────┘
```

### Folder Structure

```
src/
├── lib/
│   └── api/
│       ├── client.ts           # API client (axios/fetch wrapper)
│       ├── interceptors.ts     # Request/response interceptors
│       └── error-handler.ts    # Error handling logic
├── services/
│   ├── auth.service.ts         # Auth-related APIs
│   ├── product.service.ts      # Product-related APIs
│   └── order.service.ts        # Order-related APIs
├── types/
│   ├── api.types.ts            # Common API types
│   ├── auth.types.ts           # Auth domain types
│   └── product.types.ts        # Product domain types
└── hooks/
    ├── useAuth.ts              # Hooks using Service
    └── useProducts.ts
```

---

## API Client Implementation

### 1. Basic API Client (lib/api/client.ts)

```typescript
// lib/api/client.ts
import { ApiError, ApiResponse } from '@/types/api.types';

const BASE_URL = process.env.NEXT_PUBLIC_API_URL || '/api';

interface RequestConfig extends RequestInit {
  params?: Record<string, string>;
}

class ApiClient {
  private baseUrl: string;

  constructor(baseUrl: string) {
    this.baseUrl = baseUrl;
  }

  private async request<T>(
    endpoint: string,
    config: RequestConfig = {}
  ): Promise<ApiResponse<T>> {
    const { params, ...init } = config;

    // URL parameter handling
    const url = new URL(`${this.baseUrl}${endpoint}`);
    if (params) {
      Object.entries(params).forEach(([key, value]) => {
        url.searchParams.append(key, value);
      });
    }

    // Default header settings
    const headers = new Headers(init.headers);
    if (!headers.has('Content-Type')) {
      headers.set('Content-Type', 'application/json');
    }

    // Automatic auth token injection
    const token = this.getAuthToken();
    if (token) {
      headers.set('Authorization', `Bearer ${token}`);
    }

    try {
      const response = await fetch(url.toString(), {
        ...init,
        headers,
      });

      return this.handleResponse<T>(response);
    } catch (error) {
      throw this.handleNetworkError(error);
    }
  }

  private async handleResponse<T>(response: Response): Promise<ApiResponse<T>> {
    const data = await response.json();

    if (!response.ok) {
      throw new ApiError(
        data.error?.code || 'UNKNOWN_ERROR',
        data.error?.message || 'An error occurred',
        response.status,
        data.error?.details
      );
    }

    return data as ApiResponse<T>;
  }

  private handleNetworkError(error: unknown): ApiError {
    if (error instanceof TypeError && error.message === 'Failed to fetch') {
      return new ApiError('NETWORK_ERROR', 'Please check your network connection.', 0);
    }
    return new ApiError('UNKNOWN_ERROR', 'An unknown error occurred.', 0);
  }

  private getAuthToken(): string | null {
    if (typeof window === 'undefined') return null;
    return localStorage.getItem('auth_token');
  }

  // HTTP method wrappers
  get<T>(endpoint: string, params?: Record<string, string>) {
    return this.request<T>(endpoint, { method: 'GET', params });
  }

  post<T>(endpoint: string, body?: unknown) {
    return this.request<T>(endpoint, {
      method: 'POST',
      body: JSON.stringify(body),
    });
  }

  put<T>(endpoint: string, body?: unknown) {
    return this.request<T>(endpoint, {
      method: 'PUT',
      body: JSON.stringify(body),
    });
  }

  patch<T>(endpoint: string, body?: unknown) {
    return this.request<T>(endpoint, {
      method: 'PATCH',
      body: JSON.stringi