Files
picpeak/frontend/src/components/MaintenanceMode.tsx
T
paul a3638fe954
Test and Lint / backend-test (push) Successful in 1m11s
continuous-integration/drone/push Build is passing
Test and Lint / frontend-test (push) Successful in 2m17s
Version and Release / version-bump (push) Successful in 43s
Version and Release / trigger-drone (push) Successful in 3s
fix: remove hardcoded localhost URLs for production deployment
- Add URL utility functions for building resource URLs
- Update all components to use relative URLs in production
- Add production deployment documentation
- Update nginx config to proxy all required endpoints
- Add .env.production.example with proper configuration
2025-07-13 21:14:26 +02:00

113 lines
4.0 KiB
TypeScript

import React, { useEffect } from 'react';
import { AlertTriangle } from 'lucide-react';
import { useQuery } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { api } from '../config/api';
import { buildResourceUrl } from '../utils/url';
interface BrandingSettings {
branding_company_name?: string;
branding_company_tagline?: string;
branding_support_email?: string;
branding_footer_text?: string;
branding_favicon_url?: string;
branding_logo_url?: string;
default_language?: string;
}
export const MaintenanceMode: React.FC = () => {
const { t, i18n } = useTranslation();
// Fetch branding settings
const { data: settings } = useQuery<BrandingSettings>({
queryKey: ['public-settings-maintenance'],
queryFn: async () => {
try {
const response = await api.get('/api/public/settings');
return response.data;
} catch (error) {
// Return empty object if settings can't be fetched
return {};
}
},
staleTime: 5 * 60 * 1000, // Cache for 5 minutes
retry: false, // Don't retry on failure
});
// Set language based on system settings
useEffect(() => {
if (settings?.default_language && settings.default_language !== i18n.language) {
i18n.changeLanguage(settings.default_language);
}
}, [settings?.default_language, i18n]);
return (
<div className="min-h-screen bg-neutral-50 flex flex-col">
{/* Header with branding - Always show, with PicPeak logo as fallback */}
<div className="bg-white border-b border-neutral-200 py-4">
<div className="container">
<div className="flex items-center justify-center">
<img
src={settings?.branding_logo_url ?
(settings.branding_logo_url.startsWith('http')
? settings.branding_logo_url
: buildResourceUrl(settings.branding_logo_url))
: '/picpeak-logo-transparent.png'
}
alt={settings?.branding_company_name || 'PicPeak'}
className="h-12 w-auto object-contain"
/>
{settings?.branding_company_name && settings.branding_company_name !== 'PicPeak' && (
<div className="ml-4 text-center">
<h2 className="text-xl font-semibold text-neutral-800">{settings.branding_company_name}</h2>
{settings.branding_company_tagline && (
<p className="text-sm text-neutral-600">{settings.branding_company_tagline}</p>
)}
</div>
)}
</div>
</div>
</div>
{/* Main content */}
<div className="flex-1 flex items-center justify-center p-4">
<div className="max-w-md w-full text-center">
<div className="inline-flex items-center justify-center w-20 h-20 bg-amber-100 rounded-full mb-6">
<AlertTriangle className="w-10 h-10 text-amber-600" />
</div>
<h1 className="text-3xl font-bold text-neutral-900 mb-4">
{t('maintenance.title')}
</h1>
<p className="text-lg text-neutral-600 mb-8">
{t('maintenance.message')}
</p>
{settings?.branding_support_email && (
<p className="text-sm text-neutral-500 mt-8">
{t('maintenance.urgentMatters')}{' '}
<a
href={`mailto:${settings.branding_support_email}`}
className="text-primary-600 hover:text-primary-700"
>
{settings.branding_support_email}
</a>
</p>
)}
</div>
</div>
{/* Footer */}
{settings?.branding_footer_text && (
<footer className="py-4 border-t border-neutral-200">
<div className="container text-center">
<p className="text-sm text-neutral-500">
{settings.branding_footer_text}
</p>
</div>
</footer>
)}
</div>
);
};