voxblog/apps/admin/src/components/Settings.tsx
Ender 6b2f80cda4
Some checks failed
Deploy to Production / deploy (push) Failing after 1m30s
feat: add mobile responsive layouts and touch-friendly controls
- Updated all components with responsive breakpoints for mobile-first design
- Added touch-friendly controls with proper spacing and small button sizes
- Implemented responsive layouts that stack/wrap on mobile screens
- Created detailed mobile compatibility documentation
- Enhanced horizontal scrolling for data grids and steppers on mobile
- Optimized media library grid for smaller screens
- Added iOS safe area inset padding for bottom
2025-10-26 21:57:54 +01:00

146 lines
4.8 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Box, TextField, Button, Typography, Alert, Paper, Stack } from '@mui/material';
import { getSetting, updateSetting, resetSetting } from '../services/settings';
export default function Settings({ onBack }: { onBack?: () => void }) {
const [systemPrompt, setSystemPrompt] = useState('');
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [isDefault, setIsDefault] = useState(true);
const [message, setMessage] = useState<{ text: string; type: 'success' | 'error' } | null>(null);
useEffect(() => {
loadSystemPrompt();
}, []);
const loadSystemPrompt = async () => {
try {
setLoading(true);
const data = await getSetting('system_prompt');
setSystemPrompt(data.value);
setIsDefault(data.isDefault);
} catch (err: any) {
setMessage({ text: err?.message || 'Failed to load settings', type: 'error' });
} finally {
setLoading(false);
}
};
const handleSave = async () => {
try {
setSaving(true);
setMessage(null);
await updateSetting('system_prompt', systemPrompt);
setIsDefault(false);
setMessage({ text: 'System prompt saved successfully!', type: 'success' });
} catch (err: any) {
setMessage({ text: err?.message || 'Failed to save settings', type: 'error' });
} finally {
setSaving(false);
}
};
const handleReset = async () => {
if (!confirm('Reset to default system prompt? This will delete your custom prompt.')) {
return;
}
try {
setSaving(true);
setMessage(null);
await resetSetting('system_prompt');
await loadSystemPrompt();
setMessage({ text: 'Reset to default system prompt', type: 'success' });
} catch (err: any) {
setMessage({ text: err?.message || 'Failed to reset settings', type: 'error' });
} finally {
setSaving(false);
}
};
if (loading) {
return (
<Box sx={{ p: 3 }}>
<Typography>Loading settings...</Typography>
</Box>
);
}
return (
<Box sx={{ p: { xs: 2, md: 3 }, maxWidth: '1200px', margin: '0 auto' }}>
<Stack direction={{ xs: 'column', sm: 'row' }} justifyContent="space-between" alignItems={{ xs: 'stretch', sm: 'center' }} sx={{ mb: 3, gap: 1 }}>
<Typography variant="h4">Settings</Typography>
{onBack && (
<Button size="small" variant="outlined" onClick={onBack}>
Back to Posts
</Button>
)}
</Stack>
{message && (
<Alert severity={message.type} sx={{ mb: 2 }} onClose={() => setMessage(null)}>
{message.text}
</Alert>
)}
<Paper sx={{ p: 3 }}>
<Typography variant="h6" sx={{ mb: 1 }}>
AI System Prompt
</Typography>
<Typography variant="body2" sx={{ mb: 2, color: 'text.secondary' }}>
This prompt defines how the AI generates article content. It controls the output format,
HTML structure, image placeholder format, and writing style.
{isDefault && (
<Typography component="span" sx={{ display: 'block', mt: 1, fontWeight: 'bold', color: 'info.main' }}>
Currently using default system prompt
</Typography>
)}
</Typography>
<TextField
label="System Prompt"
value={systemPrompt}
onChange={(e) => setSystemPrompt(e.target.value)}
fullWidth
multiline
minRows={15}
maxRows={30}
sx={{ mb: 2, fontFamily: 'monospace' }}
placeholder="Enter custom system prompt..."
/>
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
<Button
size="small"
variant="contained"
onClick={handleSave}
disabled={saving || !systemPrompt.trim()}
>
{saving ? 'Saving...' : 'Save Custom Prompt'}
</Button>
<Button
size="small"
variant="outlined"
onClick={handleReset}
disabled={saving || isDefault}
>
Reset to Default
</Button>
</Stack>
<Box sx={{ mt: 3, p: 2, bgcolor: 'grey.100', borderRadius: 1 }}>
<Typography variant="subtitle2" sx={{ mb: 1 }}>
💡 Tips for customizing the system prompt:
</Typography>
<Typography variant="body2" component="ul" sx={{ pl: 2 }}>
<li>Keep the image placeholder format: <code>{`{{IMAGE:description}}`}</code></li>
<li>Specify HTML tags to use (h2, h3, p, ul, ol, etc.)</li>
<li>Define the writing style and tone</li>
<li>Set content structure requirements</li>
<li>Add SEO or formatting guidelines</li>
</Typography>
</Box>
</Paper>
</Box>
);
}