npm package: @pressbuddy/site-control
Description: Drop-in React/Next.js package for site status control based on active plan status
Author: Pressbuddy
Latest version: 1.0.1

# @pressbuddy/site-control

A drop-in React/Next.js package for controlling site access based on active plan status. Provides seamless integration for checking site subscription status and conditionally rendering content.

## Features

- 🚀 **Drop-in integration** for React and Next.js applications
- 🔒 **Zero secrets** - uses only public API endpoints
- ⚡ **Smart caching** with configurable TTL and background refresh
- 🎨 **Customizable UI** with built-in Material Design components
- 🛡️ **Fail-safe** - configurable behavior when API is unavailable
- 📱 **SSR/SSG support** - works with all Next.js rendering methods
- 🎯 **TypeScript** - fully typed for better developer experience
- 🪶 **Lightweight** - tree-shakeable with minimal dependencies

## Installation

```bash
npm install @pressbuddy/site-control
```

### Peer Dependencies

For React projects:
```bash
npm install react react-dom
```

For Material-UI styling (optional):
```bash
npm install @mui/material @emotion/react @emotion/styled
```

For Next.js projects:
```bash
npm install next
```

## Quick Start

### 1. Initialize Site Control

```typescript
import { initSiteControl } from '@pressbuddy/site-control';

// Initialize once in your app (e.g., in _app.tsx or main.tsx)
initSiteControl({
  baseUrl: 'https://api.yourserver.com',
  siteId: 'your-site-id',
  domain: 'example.com', // optional, defaults to window.location.hostname
  mode: 'flag', // 'flag' for basic check, 'check' for detailed info
  ttlMs: 60000, // Cache for 1 minute
  failBehavior: 'fail-open', // Allow access if API fails
});
```

### 2. Use in React Components

```tsx
import { SiteGuard } from '@pressbuddy/site-control';

function App() {
  return (
    <SiteGuard>
      <YourAppContent />
    </SiteGuard>
  );
}
```

### 3. Custom Hook Usage

```tsx
import { useSiteStatus } from '@pressbuddy/site-control';

function MyComponent() {
  const { loading, active, details, refresh } = useSiteStatus();

  if (loading) return <div>Checking site status...</div>;
  if (!active) return <div>Site is inactive</div>;
  
  return <div>Site is active! Plan: {details?.planName}</div>;
}
```

## API Reference

### Core Functions

#### `initSiteControl(options)`

Initialize the global site control configuration.

```typescript
interface InitOptions {
  baseUrl: string;                    // API server URL
  siteId: string;                     // Your site identifier
  domain?: string;                    // Domain (auto-detected if not provided)
  mode?: 'flag' | 'check';           // API mode (default: 'flag')
  checkPath?: string;                 // Custom API endpoint path
  ttlMs?: number;                     // Cache TTL (default: 60000)
  failBehavior?: 'fail-open' | 'fail-closed'; // Error handling (default: 'fail-open')
  renderStrategy?: 'package-view' | 'server-html'; // Render strategy (default: 'package-view')
  inactiveHtmlEndpoint?: string;      // Server HTML endpoint (for server-html strategy)
  onStatus?: (status: SiteStatus) => void; // Status change callback
}
```

#### `fetchSiteStatus(options)`

Manually fetch site status.

```typescript
const status = await fetchSiteStatus({
  siteId: 'your-site-id',
  domain: 'example.com', // optional
});

console.log(status.active); // boolean
console.log(status.details); // additional info if mode: 'check'
```

### React Components

#### `<SiteGuard>`

Wrapper component that conditionally renders content based on site status.

```tsx
<SiteGuard
  loadingFallback={<CustomLoader />}
  activeFallback={<CustomInactiveView />}
>
  <YourApp />
</SiteGuard>
```

Props:
- `children`: Content to render when site is active
- `loadingFallback?`: Custom loading component
- `activeFallback?`: Custom inactive state component

#### `<InactiveView>`

Default inactive state component with Material Design styling.

```tsx
<InactiveView
  title="Custom Title"
  subtitle="Custom message"
  ctaLabel="Contact Us"
  ctaHref="/contact"
  details={siteDetails}
/>
```

### React Hooks

#### `useSiteStatus()`

Hook for accessing site status in components.

```typescript
const { loading, active, details, refresh } = useSiteStatus();
```

Returns:
- `loading`: boolean - Whether status check is in progress
- `active`: boolean | null - Site active status
- `details`: object - Additional site information (if available)
- `refresh`: function - Manually refresh status

## Next.js Integration

### Middleware

Protect routes with middleware:

```typescript
// middleware.ts
import { nextMiddlewareGuard } from '@pressbuddy/site-control/next';

export default nextMiddlewareGuard({
  siteId: 'your-site-id',
  baseUrl: 'https://api.yourserver.com',
  redirectUrl: '/inactive',
});

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico|inactive).*)'],
};
```

### Pages Router (getServerSideProps)

```typescript
import { withSiteGuardGSSP } from '@pressbuddy/site-control/next';

export const getServerSideProps = withSiteGuardGSSP(
  async (context) => {
    // Your original getServerSideProps logic
    return { props: { data: 'your-data' } };
  },
  {
    siteId: 'your-site-id',
    baseUrl: 'https://api.yourserver.com',
    inactiveReturn: 'notFound', // or 'redirect' or 'props'
  }
);
```

### App Router

```typescript
// app/page.tsx
import { appRouterServerCheck } from '@pressbuddy/site-control/next';
import { redirect, notFound } from 'next/navigation';

export default async function Page() {
  const { active } = await appRouterServerCheck({
    siteId: 'your-site-id',
    baseUrl: 'https://api.yourserver.com',
  });

  if (!active) {
    notFound(); // or redirect('/inactive')
  }

  return <YourPageContent />;
}
```

## Advanced Usage

### Custom Inactive View

```tsx
import { SiteGuard, useSiteStatus } from '@pressbuddy/site-control';

function CustomInactiveView() {
  const { details } = useSiteStatus();
  
  return (
    <div className="custom-inactive">
      <h1>Subscription Required</h1>
      <p>Plan: {details?.planName}</p>
      <p>Expired: {details?.planExpiry}</p>
      <button onClick={() => window.location.href = '/billing'}>
        Renew Subscription
      </button>
    </div>
  );
}

function App() {
  return (
    <SiteGuard activeFallback={<CustomInactiveView />}>
      <YourApp />
    </SiteGuard>
  );
}
```

### Server HTML Strategy

```typescript
initSiteControl({
  baseUrl: 'https://api.yourserver.com',
  siteId: 'your-site-id',
  renderStrategy: 'server-html',
  inactiveHtmlEndpoint: '/api/site/inactive-html',
});
```

This will fetch and render HTML from your server when the site is inactive.

### Error Handling

```typescript
import { fetchSiteStatus } from '@pressbuddy/site-control';

try {
  const status = await fetchSiteStatus({ siteId: 'your-site-id' });
  console.log('Site is active:', status.active);
} catch (error) {
  if (error.type === 'NETWORK_ERROR') {
    console.log('Network issue:', error.message);
  } else if (error.type === 'TIMEOUT_ERROR') {
    console.log('Request timed out');
  }
}
```

### Cache Management

```typescript
import { clearCachedStatus } from '@pressbuddy/site-control';

// Clear cache for a specific site
clearCachedStatus('your-site-id', 'example.com');

// Force refresh
const { refresh } = useSiteStatus();
await refresh();
```

## Configuration Options

### API Modes

- **`flag`** (default): Minimal endpoint that returns only active status
- **`check`**: Detailed endpoint that returns additional site information

### Fail Behaviors

- **`fail-open`** (default): Allow access when API is unavailable
- **`fail-closed`**: Block access when API is unavailable

### Render Strategies

- **`package-view`** (default): Use built-in inactive components
- **`server-html`**: Fetch and render HTML from server endpoint

## TypeScript Support

The package is fully typed with TypeScript. All interfaces are exported:

```typescript
import type { 
  SiteStatus, 
  InitOptions, 
  SiteGuardProps,
  UseSiteStatusResult 
} from '@pressbuddy/site-control';
```

## Browser Support

- Modern browsers (ES2020+)
- React 16.8+ (hooks support)
- Next.js 12+ (for Next.js features)

## Security Considerations

- ✅ No secrets in client code
- ✅ Public API endpoints only
- ✅ HTML sanitization for server-html strategy
- ✅ Fail-safe defaults
- ⚠️  Consider using HTTPS for API endpoints
- ⚠️  Validate domain in your API endpoints

## Contributing

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## License

MIT License - see [LICENSE](LICENSE) file for details.

## Support

- 📖 [Documentation](https://github.com/pressbuddy/site-control#readme)
- 🐛 [Issues](https://github.com/pressbuddy/site-control/issues)
- 💬 [Discussions](https://github.com/pressbuddy/site-control/discussions)