# Frontend Integration Guide

**How to use versioning in React components**

## Overview

The versioning system provides React hooks and components for displaying version history, comparing versions, and rolling back changes. All components are fully typed with TypeScript discriminated unions.

## Type System

### Core Types

```typescript
// Entity type union
type EntityType = 'park' | 'ride' | 'company' | 'ride_model';

// Change type enum
type ChangeType = 'created' | 'updated' | 'deleted' | 'restored' | 'archived';

// Base version metadata
interface BaseVersion {
  version_id: string;
  version_number: number;
  created_at: string;
  created_by: string | null;
  change_type: ChangeType;
  change_reason: string | null;
  submission_id: string | null;
  is_current: boolean;
}
```

### Entity-Specific Versions

The system uses discriminated unions for type-safe version handling:

```typescript
// Discriminated union based on entity type
type EntityVersion = 
  | ParkVersion 
  | RideVersion 
  | CompanyVersion 
  | RideModelVersion;

// Example: Park version
interface ParkVersion extends BaseVersion {
  park_id: string;
  name: string;
  slug: string;
  description: string | null;
  park_type: string;
  status: string;
  opening_date: string | null;
  closing_date: string | null;
  location_id: string | null;
  operator_id: string | null;
  property_owner_id: string | null;
  // ... all park fields
}
```

### Version Diff

```typescript
interface VersionDiff {
  [fieldName: string]: {
    from: any;
    to: any;
    changed: boolean;
  };
}
```

## React Hooks

### useEntityVersions

**Purpose:** Fetch and manage version history for an entity.

#### Usage

```typescript
import { useEntityVersions } from '@/hooks/useEntityVersions';

const MyComponent = ({ parkId }: { parkId: string }) => {
  const {
    versions,
    currentVersion,
    loading,
    fieldHistory,
    fetchVersions,
    fetchFieldHistory,
    compareVersions,
    rollbackToVersion,
  } = useEntityVersions('park', parkId);

  // versions: Array of all versions
  // currentVersion: Latest version with is_current = true
  // loading: Boolean loading state
  // fieldHistory: Array of field changes for selected version
};
```

#### Return Type

```typescript
interface UseEntityVersionsReturn {
  versions: EntityVersion[];
  currentVersion: EntityVersion | null;
  loading: boolean;
  fieldHistory: FieldChange[];
  fetchVersions: () => Promise<void>;
  fetchFieldHistory: (versionId: string) => Promise<void>;
  compareVersions: (fromId: string, toId: string) => Promise<VersionDiff | null>;
  rollbackToVersion: (versionId: string, reason: string) => Promise<boolean>;
}
```

#### Methods

**fetchVersions()**
- Fetches all versions for the entity
- Automatically called on mount and entity ID change
- Returns: `Promise<void>`

**fetchFieldHistory(versionId)**
- Fetches detailed field changes for a specific version
- Populates `fieldHistory` state
- Parameters:
  - `versionId: string` - Version to analyze
- Returns: `Promise<void>`

**compareVersions(fromId, toId)**
- Compares two versions using database function
- Parameters:
  - `fromId: string` - Older version ID
  - `toId: string` - Newer version ID
- Returns: `Promise<VersionDiff | null>`

**rollbackToVersion(versionId, reason)**
- Restores entity to a previous version
- Creates new version with `change_type='restored'`
- Requires moderator role
- Parameters:
  - `versionId: string` - Target version to restore
  - `reason: string` - Reason for rollback
- Returns: `Promise<boolean>` - Success status

### useVersionComparison

**Purpose:** Compare two specific versions and get diff.

#### Usage

```typescript
import { useVersionComparison } from '@/hooks/useVersionComparison';

const ComparisonView = () => {
  const { diff, loading, error } = useVersionComparison(
    'park',
    fromVersionId,
    toVersionId
  );

  if (loading) return <Spinner />;
  if (error) return <Error message={error} />;
  
  return (
    <div>
      {Object.entries(diff || {}).map(([field, change]) => (
        <FieldDiff
          key={field}
          field={field}
          from={change.from}
          to={change.to}
        />
      ))}
    </div>
  );
};
```

#### Return Type

```typescript
interface UseVersionComparisonReturn {
  diff: VersionDiff | null;
  loading: boolean;
  error: string | null;
}
```

## Components

### VersionIndicator

**Purpose:** Display current version badge on entity pages.

#### Props

```typescript
interface VersionIndicatorProps {
  entityType: EntityType;
  entityId: string;
  entityName: string;
  compact?: boolean;  // Compact mode shows only version number
}
```

#### Usage

```typescript
import { VersionIndicator } from '@/components/versioning/VersionIndicator';

// Full mode (with "Last edited" timestamp)
<VersionIndicator 
  entityType="park" 
  entityId={park.id} 
  entityName={park.name}
/>

// Compact mode (badge only)
<VersionIndicator 
  entityType="park" 
  entityId={park.id} 
  entityName={park.name}
  compact
/>
```

#### Output

- Displays: "Version 3 · Last edited 2 hours ago"
- Clicking opens `EntityVersionHistory` dialog
- Compact mode: Just "v3" badge

### EntityVersionHistory

**Purpose:** Display complete version timeline with comparison and rollback.

#### Props

```typescript
interface EntityVersionHistoryProps {
  entityType: EntityType;
  entityId: string;
  entityName: string;
}
```

#### Usage

```typescript
import { EntityVersionHistory } from '@/components/versioning/EntityVersionHistory';

<Dialog>
  <DialogContent>
    <EntityVersionHistory
      entityType="park"
      entityId={park.id}
      entityName={park.name}
    />
  </DialogContent>
</Dialog>
```

#### Features

- Timeline of all versions
- Select two versions to compare
- Rollback to any previous version
- User attribution and timestamps
- Change type badges (created, updated, restored)

### VersionComparisonDialog

**Purpose:** Side-by-side diff view of two versions.

#### Props

```typescript
interface VersionComparisonDialogProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  entityType: EntityType;
  fromVersionId: string | null;
  toVersionId: string | null;
}
```

#### Usage

```typescript
import { VersionComparisonDialog } from '@/components/versioning/VersionComparisonDialog';

<VersionComparisonDialog
  open={showComparison}
  onOpenChange={setShowComparison}
  entityType="park"
  fromVersionId={selectedVersions[0]}
  toVersionId={selectedVersions[1]}
/>
```

#### Output

- Field-by-field comparison
- Highlights added, removed, and modified fields
- Color-coded changes (green=added, red=removed, yellow=modified)
- Formatted values (dates, JSON, etc.)

### RollbackDialog

**Purpose:** Confirm and execute version rollback.

#### Props

```typescript
interface RollbackDialogProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  entityType: EntityType;
  entityId: string;
  targetVersion: EntityVersion;
  onRollback: (versionId: string, reason: string) => Promise<boolean>;
}
```

#### Usage

```typescript
import { RollbackDialog } from '@/components/versioning/RollbackDialog';

<RollbackDialog
  open={showRollback}
  onOpenChange={setShowRollback}
  entityType="park"
  entityId={park.id}
  targetVersion={selectedVersion}
  onRollback={rollbackToVersion}
/>
```

#### Features

- Requires rollback reason (text input)
- Shows preview of target version
- Confirms action with user
- Displays success/error toast

## Integration Examples

### Adding Version History to Entity Pages

```typescript
// Example: ParkDetail.tsx

import { VersionIndicator } from '@/components/versioning/VersionIndicator';
import { EntityHistoryTabs } from '@/components/history/EntityHistoryTabs';

const ParkDetail = () => {
  const { park } = usePark();

  return (
    <div>
      {/* Header with version indicator */}
      <div className="flex justify-between items-center">
        <h1>{park.name}</h1>
        <VersionIndicator 
          entityType="park" 
          entityId={park.id} 
          entityName={park.name}
        />
      </div>

      {/* Tabs with history */}
      <Tabs>
        <TabsList>
          <TabsTrigger value="details">Details</TabsTrigger>
          <TabsTrigger value="history">History</TabsTrigger>
        </TabsList>

        <TabsContent value="details">
          {/* Entity details */}
        </TabsContent>

        <TabsContent value="history">
          <EntityHistoryTabs
            entityType="park"
            entityId={park.id}
            entityName={park.name}
            events={[]} // Event history
            formerNames={park.former_names}
            currentName={park.name}
          />
        </TabsContent>
      </Tabs>
    </div>
  );
};
```

### Custom Version List

```typescript
const CustomVersionList = ({ parkId }: { parkId: string }) => {
  const { versions, loading } = useEntityVersions('park', parkId);

  if (loading) return <Skeleton />;

  return (
    <ul>
      {versions.map((version) => (
        <li key={version.version_id}>
          <Badge>{version.change_type}</Badge>
          <span>Version {version.version_number}</span>
          <span>{formatDate(version.created_at)}</span>
          <UserAvatar userId={version.created_by} />
        </li>
      ))}
    </ul>
  );
};
```

### Version Comparison Widget

```typescript
const VersionComparison = ({ 
  entityType, 
  versionIds 
}: { 
  entityType: EntityType; 
  versionIds: [string, string] 
}) => {
  const { diff, loading } = useVersionComparison(
    entityType,
    versionIds[0],
    versionIds[1]
  );

  if (loading) return <Spinner />;

  const changedFields = Object.entries(diff || {}).filter(
    ([_, change]) => change.changed
  );

  return (
    <Card>
      <CardHeader>
        <CardTitle>{changedFields.length} fields changed</CardTitle>
      </CardHeader>
      <CardContent>
        {changedFields.map(([field, change]) => (
          <div key={field} className="grid grid-cols-2 gap-4">
            <div>
              <Label>{field}</Label>
              <pre>{JSON.stringify(change.from, null, 2)}</pre>
            </div>
            <div>
              <Label>{field}</Label>
              <pre>{JSON.stringify(change.to, null, 2)}</pre>
            </div>
          </div>
        ))}
      </CardContent>
    </Card>
  );
};
```

## Real-Time Updates

The `useEntityVersions` hook automatically subscribes to real-time changes via Supabase:

```typescript
// Automatic subscription in useEntityVersions
useEffect(() => {
  const channel = supabase
    .channel(`${tableName}:${entityId}`)
    .on(
      'postgres_changes',
      {
        event: '*',
        schema: 'public',
        table: tableName,
        filter: `${entityType}_id=eq.${entityId}`,
      },
      () => {
        fetchVersions(); // Refetch on change
      }
    )
    .subscribe();

  return () => {
    supabase.removeChannel(channel);
  };
}, [entityType, entityId]);
```

## Type Guards

Use type guards to narrow discriminated unions:

```typescript
function isParkVersion(version: EntityVersion): version is ParkVersion {
  return 'park_id' in version;
}

function isRideVersion(version: EntityVersion): version is RideVersion {
  return 'ride_id' in version;
}

// Usage
if (isParkVersion(version)) {
  console.log(version.park_type); // TypeScript knows this is a park
}
```

## Error Handling

All hooks handle errors gracefully:

```typescript
const { versions, loading } = useEntityVersions('park', parkId);

// Loading state
if (loading) return <Skeleton />;

// Error state (versions will be empty array)
if (versions.length === 0) {
  return <EmptyState message="No version history available" />;
}

// Success state
return <VersionList versions={versions} />;
```

## Best Practices

### DO

✅ Use `VersionIndicator` on all entity detail pages  
✅ Add history tab to entity pages  
✅ Let hooks manage state and subscriptions  
✅ Use type guards for discriminated unions  
✅ Handle loading and error states  

### DON'T

❌ Manually query version tables (use hooks)  
❌ Bypass type system with `any`  
❌ Forget to cleanup subscriptions  
❌ Display sensitive data (created_by IDs) to public  

## Performance Optimization

### Pagination

For entities with many versions, implement pagination:

```typescript
const { versions } = useEntityVersions('park', parkId);
const [page, setPage] = useState(1);
const pageSize = 10;

const paginatedVersions = versions.slice(
  (page - 1) * pageSize,
  page * pageSize
);
```

### Lazy Loading

Only load version details when needed:

```typescript
const [selectedVersion, setSelectedVersion] = useState<string | null>(null);
const { fieldHistory, fetchFieldHistory } = useEntityVersions('park', parkId);

const handleVersionClick = (versionId: string) => {
  setSelectedVersion(versionId);
  fetchFieldHistory(versionId); // Load on demand
};
```

## Testing

### Example Unit Test

```typescript
import { renderHook, waitFor } from '@testing-library/react';
import { useEntityVersions } from '@/hooks/useEntityVersions';

describe('useEntityVersions', () => {
  it('fetches versions on mount', async () => {
    const { result } = renderHook(() => 
      useEntityVersions('park', 'test-park-id')
    );

    expect(result.current.loading).toBe(true);

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
      expect(result.current.versions.length).toBeGreaterThan(0);
    });
  });
});
```

## Troubleshooting

See [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) for common issues and solutions.
