# Birth Chart Wheel Data Management Guide

This comprehensive guide covers how to effectively manage birth chart wheel data between frontend and backend systems, including data flow, visualization, and best practices.

## 📊 Data Architecture Overview

### Backend Data Structure

Your current system already has a well-structured database schema for astrology charts:

```typescript
// Core Chart Data
interface AstrologyChart {
  id: string;
  userId: string;
  birthDate: Date;
  birthTime?: string;
  birthLocation: string;
  latitude?: number;
  longitude?: number;
  timezone?: string;
  sunSign: string;
  moonSign?: string;
  risingSign?: string;
  chartType: ChartType;
  planetaryPositions: PlanetaryPosition[];
  houseCusps: HouseCusp[];
  chartInterpretation?: string;
  astrologerNotes?: string;
}

// Planetary Positions for Chart Wheel
interface PlanetaryPosition {
  id: string;
  planet: string; // Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto
  sign: string;
  degree: number; // 0-30 degrees within the sign
  house?: number; // 1-12
  isRetrograde: boolean;
  aspects: Aspect[];
}

// House Cusps for Chart Wheel
interface HouseCusp {
  id: string;
  houseNumber: number; // 1-12
  sign: string;
  degree: number; // 0-30 degrees within the sign
}

// Aspects Between Planets
interface Aspect {
  id: string;
  targetPlanet: string;
  aspectType: AspectType; // CONJUNCTION, SEXTILE, SQUARE, TRINE, OPPOSITION, etc.
  orb: number;
  isExact: boolean;
}
```

## 🔄 Data Flow Architecture

### 1. Chart Creation Flow

```
Frontend Form → Backend API → Astrological Calculations → Database Storage → Response
```

### 2. Chart Retrieval Flow

```
Frontend Request → Backend API → Database Query → Data Transformation → Frontend Display
```

### 3. Chart Visualization Flow

```
Raw Chart Data → Data Processing → Wheel Coordinates → SVG/Canvas Rendering → Interactive Chart
```

## 🎨 Frontend Chart Wheel Implementation

### Recommended Libraries

#### 1. **D3.js** (Most Flexible)

```javascript
// Example D3.js implementation for chart wheel
import * as d3 from 'd3';

class AstrologyChartWheel {
  constructor(container, data) {
    this.container = container;
    this.data = data;
    this.width = 600;
    this.height = 600;
    this.radius = Math.min(this.width, this.height) / 2 - 40;
  }

  render() {
    const svg = d3
      .select(this.container)
      .append('svg')
      .attr('width', this.width)
      .attr('height', this.height);

    const g = svg
      .append('g')
      .attr('transform', `translate(${this.width / 2},${this.height / 2})`);

    // Draw outer circle
    g.append('circle')
      .attr('r', this.radius)
      .style('fill', 'none')
      .style('stroke', '#333')
      .style('stroke-width', 2);

    // Draw zodiac signs
    this.drawZodiacSigns(g);

    // Draw houses
    this.drawHouses(g);

    // Draw planets
    this.drawPlanets(g);

    // Draw aspects
    this.drawAspects(g);
  }

  drawZodiacSigns(g) {
    const signs = [
      'Aries',
      'Taurus',
      'Gemini',
      'Cancer',
      'Leo',
      'Virgo',
      'Libra',
      'Scorpio',
      'Sagittarius',
      'Capricorn',
      'Aquarius',
      'Pisces',
    ];

    signs.forEach((sign, i) => {
      const angle = i * 30 - 90; // Start from top
      const x = Math.cos((angle * Math.PI) / 180) * (this.radius - 20);
      const y = Math.sin((angle * Math.PI) / 180) * (this.radius - 20);

      g.append('text')
        .attr('x', x)
        .attr('y', y)
        .attr('text-anchor', 'middle')
        .attr('dominant-baseline', 'middle')
        .style('font-size', '12px')
        .style('font-weight', 'bold')
        .text(sign);
    });
  }

  drawPlanets(g) {
    this.data.planetaryPositions.forEach((planet) => {
      const signIndex = this.getSignIndex(planet.sign);
      const angle = signIndex * 30 + planet.degree - 90;
      const x = Math.cos((angle * Math.PI) / 180) * (this.radius - 60);
      const y = Math.sin((angle * Math.PI) / 180) * (this.radius - 60);

      const planetGroup = g
        .append('g')
        .attr('transform', `translate(${x},${y})`);

      planetGroup
        .append('circle')
        .attr('r', 8)
        .style('fill', this.getPlanetColor(planet.planet))
        .style('stroke', '#333')
        .style('stroke-width', 1);

      planetGroup
        .append('text')
        .attr('y', 3)
        .attr('text-anchor', 'middle')
        .style('font-size', '8px')
        .style('font-weight', 'bold')
        .text(this.getPlanetSymbol(planet.planet));
    });
  }

  getSignIndex(sign) {
    const signs = [
      'Aries',
      'Taurus',
      'Gemini',
      'Cancer',
      'Leo',
      'Virgo',
      'Libra',
      'Scorpio',
      'Sagittarius',
      'Capricorn',
      'Aquarius',
      'Pisces',
    ];
    return signs.indexOf(sign);
  }

  getPlanetColor(planet) {
    const colors = {
      Sun: '#FFD700',
      Moon: '#C0C0C0',
      Mercury: '#87CEEB',
      Venus: '#FFA500',
      Mars: '#FF4500',
      Jupiter: '#4169E1',
      Saturn: '#8B4513',
      Uranus: '#00CED1',
      Neptune: '#0000FF',
      Pluto: '#800080',
    };
    return colors[planet] || '#666';
  }

  getPlanetSymbol(planet) {
    const symbols = {
      Sun: '☉',
      Moon: '☽',
      Mercury: '☿',
      Venus: '♀',
      Mars: '♂',
      Jupiter: '♃',
      Saturn: '♄',
      Uranus: '♅',
      Neptune: '♆',
      Pluto: '♇',
    };
    return symbols[planet] || planet.charAt(0);
  }
}
```

#### 2. **Chart.js with Custom Plugin**

```javascript
// Chart.js implementation for astrology wheel
import Chart from 'chart.js/auto';

class AstrologyChartPlugin {
  constructor(chartData) {
    this.chartData = chartData;
  }

  render(containerId) {
    const ctx = document.getElementById(containerId).getContext('2d');

    new Chart(ctx, {
      type: 'doughnut',
      data: {
        datasets: [
          {
            data: Array(12).fill(1), // 12 houses
            backgroundColor: this.generateHouseColors(),
            borderWidth: 2,
            borderColor: '#333',
          },
        ],
      },
      options: {
        responsive: true,
        maintainAspectRatio: true,
        plugins: {
          legend: {
            display: false,
          },
        },
        animation: {
          animateRotate: true,
          animateScale: true,
        },
      },
      plugins: [
        {
          id: 'astrologyWheel',
          afterDraw: (chart) => {
            this.drawPlanets(chart);
            this.drawAspects(chart);
          },
        },
      ],
    });
  }

  drawPlanets(chart) {
    const { ctx, chartArea } = chart;
    const centerX = chartArea.left + chartArea.width / 2;
    const centerY = chartArea.top + chartArea.height / 2;
    const radius = Math.min(chartArea.width, chartArea.height) / 2 - 40;

    this.chartData.planetaryPositions.forEach((planet) => {
      const angle = this.calculatePlanetAngle(planet);
      const x = centerX + Math.cos(angle) * radius;
      const y = centerY + Math.sin(angle) * radius;

      ctx.beginPath();
      ctx.arc(x, y, 8, 0, 2 * Math.PI);
      ctx.fillStyle = this.getPlanetColor(planet.planet);
      ctx.fill();
      ctx.stroke();
    });
  }
}
```

#### 3. **React Component Example**

```jsx
import React, { useEffect, useRef } from 'react';
import * as d3 from 'd3';

const AstrologyChartWheel = ({ chartData }) => {
  const svgRef = useRef();

  useEffect(() => {
    if (chartData && svgRef.current) {
      const wheel = new AstrologyChartWheel(svgRef.current, chartData);
      wheel.render();
    }
  }, [chartData]);

  return (
    <div className="chart-container">
      <svg ref={svgRef} width={600} height={600} />
      <div className="chart-legend">
        {chartData?.planetaryPositions.map((planet) => (
          <div key={planet.id} className="planet-legend-item">
            <span
              className="planet-symbol"
              style={{ color: getPlanetColor(planet.planet) }}
            >
              {getPlanetSymbol(planet.planet)}
            </span>
            <span>
              {planet.planet} in {planet.sign}
            </span>
          </div>
        ))}
      </div>
    </div>
  );
};

export default AstrologyChartWheel;
```

## 🔧 Backend API Enhancements

### 1. Chart Wheel Data Endpoint

```typescript
// Add to your existing AstrologyChartController
getChartWheelData = asyncHandler(async (req: Request, res: Response) => {
  try {
    const { chartId } = req.params;
    const userId = (req as any).user?.userId;

    const chart = await this.astrologyChartService.getDetailedAstrologyChart(chartId, userId);

    // Transform data for wheel visualization
    const wheelData = this.transformForWheel(chart);

    res.status(200).json({
      success: true,
      message: 'Chart wheel data retrieved successfully',
      data: wheelData
    });
  } catch (error) {
    logger.error(`Error in getChartWheelData controller: ${error}`);
    res.status(400).json({
      success: false,
      message: 'Failed to retrieve chart wheel data',
      error: error instanceof Error ? error.message : 'Unknown error'
    });
  }
});

private transformForWheel(chart: AstrologyChartResponse) {
  return {
    id: chart.id,
    birthData: {
      date: chart.birthDate,
      time: chart.birthTime,
      location: chart.birthLocation,
      timezone: chart.timezone
    },
    planetaryPositions: chart.planetaryPositions.map(planet => ({
      ...planet,
      angle: this.calculatePlanetAngle(planet),
      coordinates: this.calculatePlanetCoordinates(planet)
    })),
    houseCusps: chart.houseCusps.map(house => ({
      ...house,
      angle: this.calculateHouseAngle(house),
      coordinates: this.calculateHouseCoordinates(house)
    })),
    aspects: this.calculateAspectLines(chart.planetaryPositions),
    metadata: {
      sunSign: chart.sunSign,
      moonSign: chart.moonSign,
      risingSign: chart.risingSign,
      chartType: chart.chartType
    }
  };
}

private calculatePlanetAngle(planet: PlanetaryPositionResponse): number {
  const signIndex = this.getSignIndex(planet.sign);
  return (signIndex * 30 + planet.degree) * (Math.PI / 180);
}

private calculatePlanetCoordinates(planet: PlanetaryPositionResponse) {
  const angle = this.calculatePlanetAngle(planet);
  const radius = 200; // Base radius for planet positions
  return {
    x: Math.cos(angle) * radius,
    y: Math.sin(angle) * radius
  };
}
```

### 2. Real-time Chart Updates

```typescript
// WebSocket implementation for real-time chart updates
import { Server as SocketIOServer } from 'socket.io';

class AstrologyChartWebSocket {
  private io: SocketIOServer;

  constructor(server: any) {
    this.io = new SocketIOServer(server, {
      cors: {
        origin: process.env.FRONTEND_URL,
        methods: ['GET', 'POST'],
      },
    });

    this.setupEventHandlers();
  }

  private setupEventHandlers() {
    this.io.on('connection', (socket) => {
      console.log('User connected to astrology charts');

      socket.on('join-chart-room', (chartId) => {
        socket.join(`chart-${chartId}`);
        console.log(`User joined chart room: ${chartId}`);
      });

      socket.on('leave-chart-room', (chartId) => {
        socket.leave(`chart-${chartId}`);
        console.log(`User left chart room: ${chartId}`);
      });

      socket.on('disconnect', () => {
        console.log('User disconnected from astrology charts');
      });
    });
  }

  public broadcastChartUpdate(chartId: string, chartData: any) {
    this.io.to(`chart-${chartId}`).emit('chart-updated', chartData);
  }

  public broadcastNewChart(userId: string, chartData: any) {
    this.io.to(`user-${userId}`).emit('new-chart-created', chartData);
  }
}
```

## 📱 Frontend State Management

### 1. Redux/Zustand Store

```typescript
// Zustand store for astrology charts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface AstrologyChartState {
  charts: AstrologyChartResponse[];
  currentChart: AstrologyChartResponse | null;
  wheelData: any | null;
  loading: boolean;
  error: string | null;

  // Actions
  fetchUserCharts: () => Promise<void>;
  fetchChartById: (chartId: string) => Promise<void>;
  fetchChartWheelData: (chartId: string) => Promise<void>;
  createChart: (chartData: CreateAstrologyChartRequest) => Promise<void>;
  updateChart: (
    chartId: string,
    updateData: UpdateAstrologyChartRequest
  ) => Promise<void>;
  deleteChart: (chartId: string) => Promise<void>;
  setCurrentChart: (chart: AstrologyChartResponse | null) => void;
  clearError: () => void;
}

export const useAstrologyChartStore = create<AstrologyChartState>()(
  persist(
    (set, get) => ({
      charts: [],
      currentChart: null,
      wheelData: null,
      loading: false,
      error: null,

      fetchUserCharts: async () => {
        set({ loading: true, error: null });
        try {
          const response = await fetch('/api/v1/astrology-charts/user', {
            headers: {
              Authorization: `Bearer ${localStorage.getItem('token')}`,
            },
          });

          if (!response.ok) throw new Error('Failed to fetch charts');

          const data = await response.json();
          set({ charts: data.data, loading: false });
        } catch (error) {
          set({ error: error.message, loading: false });
        }
      },

      fetchChartWheelData: async (chartId: string) => {
        set({ loading: true, error: null });
        try {
          const response = await fetch(
            `/api/v1/astrology-charts/${chartId}/wheel-data`,
            {
              headers: {
                Authorization: `Bearer ${localStorage.getItem('token')}`,
              },
            }
          );

          if (!response.ok) throw new Error('Failed to fetch wheel data');

          const data = await response.json();
          set({ wheelData: data.data, loading: false });
        } catch (error) {
          set({ error: error.message, loading: false });
        }
      },

      createChart: async (chartData: CreateAstrologyChartRequest) => {
        set({ loading: true, error: null });
        try {
          const response = await fetch('/api/v1/astrology-charts', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
              Authorization: `Bearer ${localStorage.getItem('token')}`,
            },
            body: JSON.stringify(chartData),
          });

          if (!response.ok) throw new Error('Failed to create chart');

          const data = await response.json();
          set((state) => ({
            charts: [...state.charts, data.data],
            currentChart: data.data,
            loading: false,
          }));
        } catch (error) {
          set({ error: error.message, loading: false });
        }
      },

      setCurrentChart: (chart) => set({ currentChart: chart }),
      clearError: () => set({ error: null }),
    }),
    {
      name: 'astrology-chart-store',
      partialize: (state) => ({
        charts: state.charts,
        currentChart: state.currentChart,
      }),
    }
  )
);
```

### 2. React Hook for Chart Management

```typescript
// Custom hook for astrology chart operations
import { useCallback } from 'react';
import { useAstrologyChartStore } from './store';

export const useAstrologyChart = () => {
  const {
    charts,
    currentChart,
    wheelData,
    loading,
    error,
    fetchUserCharts,
    fetchChartById,
    fetchChartWheelData,
    createChart,
    updateChart,
    deleteChart,
    setCurrentChart,
    clearError,
  } = useAstrologyChartStore();

  const handleCreateChart = useCallback(
    async (chartData: CreateAstrologyChartRequest) => {
      await createChart(chartData);
    },
    [createChart]
  );

  const handleFetchWheelData = useCallback(
    async (chartId: string) => {
      await fetchChartWheelData(chartId);
    },
    [fetchChartWheelData]
  );

  const handleUpdateChart = useCallback(
    async (chartId: string, updateData: UpdateAstrologyChartRequest) => {
      await updateChart(chartId, updateData);
    },
    [updateChart]
  );

  return {
    // State
    charts,
    currentChart,
    wheelData,
    loading,
    error,

    // Actions
    fetchUserCharts,
    fetchChartById,
    handleFetchWheelData,
    handleCreateChart,
    handleUpdateChart,
    deleteChart,
    setCurrentChart,
    clearError,
  };
};
```

## 🎯 Best Practices

### 1. Data Validation

```typescript
// Frontend validation
const validateChartData = (data: CreateAstrologyChartRequest): string[] => {
  const errors: string[] = [];

  if (!data.birthDate) errors.push('Birth date is required');
  if (!data.birthLocation) errors.push('Birth location is required');

  if (
    data.birthTime &&
    !/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/.test(data.birthTime)
  ) {
    errors.push('Invalid birth time format. Use HH:MM format');
  }

  if (data.latitude && (data.latitude < -90 || data.latitude > 90)) {
    errors.push('Invalid latitude. Must be between -90 and 90');
  }

  if (data.longitude && (data.longitude < -180 || data.longitude > 180)) {
    errors.push('Invalid longitude. Must be between -180 and 180');
  }

  return errors;
};
```

### 2. Error Handling

```typescript
// Global error handler for chart operations
export const handleChartError = (error: any): string => {
  if (error.response?.data?.message) {
    return error.response.data.message;
  }

  if (error.message) {
    return error.message;
  }

  return 'An unexpected error occurred while processing your chart';
};
```

### 3. Performance Optimization

```typescript
// Memoized chart calculations
import { useMemo } from 'react';

export const useChartCalculations = (chartData: AstrologyChartResponse) => {
  return useMemo(() => {
    if (!chartData) return null;

    return {
      planetaryPositions: chartData.planetaryPositions.map((planet) => ({
        ...planet,
        angle: calculatePlanetAngle(planet),
        coordinates: calculatePlanetCoordinates(planet),
      })),
      houseCusps: chartData.houseCusps.map((house) => ({
        ...house,
        angle: calculateHouseAngle(house),
        coordinates: calculateHouseCoordinates(house),
      })),
      aspects: calculateAspectLines(chartData.planetaryPositions),
    };
  }, [chartData]);
};
```

### 4. Caching Strategy

```typescript
// Redis caching for chart data
export class ChartCacheService {
  private redis: Redis;

  constructor() {
    this.redis = new Redis(process.env.REDIS_URL);
  }

  async getChartWheelData(chartId: string): Promise<any | null> {
    const cached = await this.redis.get(`chart-wheel:${chartId}`);
    return cached ? JSON.parse(cached) : null;
  }

  async setChartWheelData(
    chartId: string,
    data: any,
    ttl: number = 3600
  ): Promise<void> {
    await this.redis.setex(`chart-wheel:${chartId}`, ttl, JSON.stringify(data));
  }

  async invalidateChartCache(chartId: string): Promise<void> {
    await this.redis.del(`chart-wheel:${chartId}`);
  }
}
```

## 🚀 Implementation Steps

### Phase 1: Backend Enhancements

1. Add chart wheel data endpoint
2. Implement WebSocket for real-time updates
3. Add caching layer for performance
4. Enhance data validation

### Phase 2: Frontend Foundation

1. Set up state management (Zustand/Redux)
2. Create base chart wheel component
3. Implement data fetching hooks
4. Add error handling and loading states

### Phase 3: Visualization

1. Implement D3.js chart wheel
2. Add interactive features (zoom, pan, tooltips)
3. Create responsive design
4. Add chart customization options

### Phase 4: Advanced Features

1. Real-time chart updates
2. Chart sharing and export
3. Mobile optimization
4. Performance monitoring

## 📚 Additional Resources

- [D3.js Documentation](https://d3js.org/)
- [Chart.js Documentation](https://www.chartjs.org/)
- [Swiss Ephemeris Library](https://www.astro.com/swisseph/)
- [Astrology.js Library](https://github.com/astrology-js/astrology-js)
- [React D3 Examples](https://observablehq.com/@d3/gallery)

This guide provides a comprehensive foundation for managing birth chart wheel data between frontend and backend systems. The modular approach allows for incremental implementation and easy maintenance.
