- Updated all API endpoint strings in 'frontend/src' (via sed and manual fixes) to remove the '/api/' prefix. - Manually fixed 'Timeline.tsx' absolute URLs to use the 'api' subdomain and correct path. - Manually fixed 'useAuth.ts' logout fetch URLs. - Updated 'HelpApiDocs.tsx' sandbox URL. - This change, combined with the backend URL update, fully transitions the application to use subdomain-based routing (e.g., 'http://api.lvh.me:8000/resource/') instead of path-prefix routing (e.g., 'http://api.lvh.me:8000/api/resource/').
113 lines
4.0 KiB
TypeScript
113 lines
4.0 KiB
TypeScript
import React from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { Mail, ExternalLink } from 'lucide-react';
|
|
import api from '../api/client';
|
|
import { EmailTemplate } from '../types';
|
|
|
|
interface EmailTemplateSelectorProps {
|
|
value: string | number | undefined;
|
|
onChange: (templateId: string | number | undefined) => void;
|
|
category?: string;
|
|
placeholder?: string;
|
|
required?: boolean;
|
|
disabled?: boolean;
|
|
className?: string;
|
|
}
|
|
|
|
const EmailTemplateSelector: React.FC<EmailTemplateSelectorProps> = ({
|
|
value,
|
|
onChange,
|
|
category,
|
|
placeholder,
|
|
required = false,
|
|
disabled = false,
|
|
className = '',
|
|
}) => {
|
|
const { t } = useTranslation();
|
|
|
|
// Fetch email templates
|
|
const { data: templates = [], isLoading } = useQuery<EmailTemplate[]>({
|
|
queryKey: ['email-templates-list', category],
|
|
queryFn: async () => {
|
|
const params = new URLSearchParams();
|
|
if (category) params.append('category', category);
|
|
const { data } = await api.get(`/email-templates/?${params.toString()}`);
|
|
return data.map((t: any) => ({
|
|
id: String(t.id),
|
|
name: t.name,
|
|
description: t.description,
|
|
category: t.category,
|
|
scope: t.scope,
|
|
updatedAt: t.updated_at,
|
|
}));
|
|
},
|
|
});
|
|
|
|
const selectedTemplate = templates.find(t => String(t.id) === String(value));
|
|
|
|
return (
|
|
<div className={`space-y-2 ${className}`}>
|
|
<div className="relative">
|
|
<select
|
|
value={value || ''}
|
|
onChange={(e) => onChange(e.target.value || undefined)}
|
|
disabled={disabled || isLoading}
|
|
className="w-full pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-brand-500 focus:border-brand-500 disabled:opacity-50 disabled:cursor-not-allowed appearance-none"
|
|
>
|
|
<option value="">
|
|
{isLoading
|
|
? t('common.loading', 'Loading...')
|
|
: placeholder || t('emailTemplates.selectTemplate', 'Select a template...')}
|
|
</option>
|
|
{templates.map((template) => (
|
|
<option key={template.id} value={template.id}>
|
|
{template.name}
|
|
{template.category !== 'OTHER' && ` (${template.category})`}
|
|
</option>
|
|
))}
|
|
</select>
|
|
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400 pointer-events-none" />
|
|
<div className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none">
|
|
<svg className="h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Selected template info */}
|
|
{selectedTemplate && (
|
|
<div className="flex items-center justify-between p-2 bg-gray-50 dark:bg-gray-800 rounded text-sm">
|
|
<span className="text-gray-600 dark:text-gray-400 truncate">
|
|
{selectedTemplate.description || selectedTemplate.name}
|
|
</span>
|
|
<a
|
|
href={`#/email-templates`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-center gap-1 text-brand-600 dark:text-brand-400 hover:underline ml-2 flex-shrink-0"
|
|
>
|
|
<ExternalLink className="h-3 w-3" />
|
|
{t('common.edit', 'Edit')}
|
|
</a>
|
|
</div>
|
|
)}
|
|
|
|
{/* Empty state with link to create */}
|
|
{!isLoading && templates.length === 0 && (
|
|
<div className="text-sm text-gray-500 dark:text-gray-400">
|
|
{t('emailTemplates.noTemplatesYet', 'No email templates yet.')}{' '}
|
|
<a
|
|
href="#/email-templates"
|
|
className="text-brand-600 dark:text-brand-400 hover:underline"
|
|
>
|
|
{t('emailTemplates.createFirst', 'Create your first template')}
|
|
</a>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default EmailTemplateSelector;
|