Skip to main content

Policies

The Policies app is the central hub for all legal, privacy, and compliance content, built with Next.js 16 and featuring full internationalization support.

Features

  • Privacy Policy - Data collection, usage, and user rights
  • Terms of Service - Legal terms and conditions
  • Cookie Policy - Cookie usage and consent information
  • Security - Security practices and policies
  • FAQ - Frequently asked questions

Competition Rules

Dedicated section for competition-specific terms and rules.

Internationalization

Full multi-language support:

  • English (default)
  • German

Language detection based on browser preferences with manual override.

Pages & Routes

RouteDescription
/Overview/index page
/privacyPrivacy policy
/termsTerms of service
/terms/competitionCompetition rules
/cookiesCookie policy
/securitySecurity practices
/faqFrequently asked questions

All routes are localized under /{locale}/ (e.g., /en/privacy, /de/privacy).

Technology Stack

TechnologyPurpose
Next.js 16React framework with App Router
next-intlInternationalization
TailwindCSS 4Styling
@elcto/uiShared UI components
@elcto/cookies-consentCookie consent management

Project Structure

platform/policies/
├── src/
│ ├── app/
│ │ ├── [locale]/ # Localized routes
│ │ │ ├── cookies/ # Cookie policy
│ │ │ ├── faq/ # FAQ
│ │ │ ├── privacy/ # Privacy policy
│ │ │ ├── security/ # Security practices
│ │ │ ├── terms/ # Terms of service
│ │ │ │ └── competition/
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ │ ├── api/
│ │ │ ├── auth/ # NextAuth handlers
│ │ │ ├── health/ # Health check endpoint
│ │ │ └── user/ # User endpoints
│ │ ├── layout.tsx
│ │ └── globals.css
│ ├── components/
│ ├── lib/
│ └── types/
├── messages/ # i18n translation files
│ ├── en.json
│ └── de.json
├── public/
├── src/proxy.ts # Next.js proxy (renamed from middleware in v16)
├── next.config.ts
├── package.json
└── tsconfig.json

Environment Variables

VariableDescriptionRequired
NEXTAUTH_URLThe base URL of the appYes
NEXTAUTH_SECRETSecret for signing tokensYes
API_URLRust API URLYes
NEXT_PUBLIC_API_URLPublic API URLYes

Development

cd platform/policies

# Install dependencies
pnpm install

# Run development server
pnpm dev

# Build for production
pnpm build

# Start production server
pnpm start

# Run linting
pnpm lint

URLs:

  • Production: https://policies.elcto.com
  • Development: http://localhost:3004

The Policies app integrates with the @elcto/cookies-consent library to provide GDPR-compliant cookie consent:

import { CookieBanner, useCookieConsent } from '@elcto/cookies-consent';

function App() {
const { consent, updateConsent } = useCookieConsent();

return (
<>
<CookieBanner
onAcceptAll={() => updateConsent({ analytics: true, marketing: true })}
onRejectAll={() => updateConsent({ analytics: false, marketing: false })}
/>
{/* App content */}
</>
);
}

API Health Monitoring

The Policies app includes real-time API health monitoring using the shared ApiHealthBanner component from @elcto/ui:

Integration

// src/components/ApiHealthBanner.tsx
import {
ApiHealthBanner as BaseApiHealthBanner,
useApiHealth as baseUseApiHealth,
useShowApiHealthBanner,
type ApiHealthBannerLabels,
} from "@elcto/ui/components";
import { useTranslations } from "next-intl";

export function useApiHealth(baseInterval = 30000, maxInterval = 600000) {
return baseUseApiHealth({
baseInterval,
maxInterval,
apiUrl: process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000",
});
}

export function ApiHealthBanner({ apiHealth, show, fixedTop, onRefresh }) {
const t = useTranslations("apiHealth");

const labels: ApiHealthBannerLabels = {
unreachable: t("unreachable"),
serviceIssues: t("serviceIssues"),
database: t("database"),
redis: t("redis"),
websocket: t("websocket"),
checking: t("checking"),
refreshNow: t("refreshNow"),
};

return (
<BaseApiHealthBanner
apiHealth={apiHealth}
show={show}
labels={labels}
fixedTop={fixedTop}
onRefresh={onRefresh}
/>
);
}

Usage in PoliciesLayout

The ApiHealthBanner is integrated into the main PoliciesLayout component:

// src/components/PoliciesLayout.tsx
import { ApiHealthBanner, useApiHealth, useShowApiHealthBanner } from "@/components/ApiHealthBanner";

export function PoliciesLayout({ children }) {
const { state: apiHealth, refresh: refreshApiHealth } = useApiHealth();
const showApiHealthBanner = useShowApiHealthBanner(apiHealth);

return (
<>
<Header />
<Navigation />
<ApiHealthBanner
apiHealth={apiHealth}
show={showApiHealthBanner}
fixedTop="7.5rem"
onRefresh={refreshApiHealth}
/>
<main className={showApiHealthBanner ? "pt-[10rem]" : "pt-[7.5rem]"}>
{children}
</main>
</>
);
}

Features

  • Exponential Backoff: Health checks use exponential backoff when failures occur to avoid overwhelming the server
  • Real-time Status: Shows API connectivity, database, Redis, and WebSocket status
  • Manual Refresh: Users can manually trigger a health check
  • i18n Support: All messages are translated via next-intl
  • Fixed Positioning: Banner appears below the header and navigation with proper offset

Required Translations

Add the following to your messages/{locale}.json files:

{
"apiHealth": {
"unreachable": "Unable to connect to the API server. Some features may be unavailable.",
"serviceIssues": "Service issues:",
"database": "Database",
"redis": "Redis",
"websocket": "WebSocket",
"checking": "Checking...",
"refreshNow": "Click to refresh now"
}
}

Next Steps