initial commit
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
interface User {
|
||||
id: number;
|
||||
email: string;
|
||||
displayName: string;
|
||||
role: string;
|
||||
status: string;
|
||||
balance: number;
|
||||
createdAt: string;
|
||||
sessionId: string;
|
||||
}
|
||||
|
||||
export default function DashboardPage() {
|
||||
const router = useRouter();
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [uniqueizerLoading, setUniqueizerLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
async function loadUser() {
|
||||
try {
|
||||
const response = await fetch('/api/auth/me', { credentials: 'same-origin' });
|
||||
if (!response.ok) {
|
||||
router.push('/');
|
||||
return;
|
||||
}
|
||||
const data = await response.json();
|
||||
setUser(data.user);
|
||||
} catch (err) {
|
||||
router.push('/');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
loadUser();
|
||||
}, [router]);
|
||||
|
||||
async function handleLogout() {
|
||||
try {
|
||||
await fetch('/api/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
router.push('/');
|
||||
} catch (err) {
|
||||
console.error('Logout error:', err);
|
||||
router.push('/');
|
||||
}
|
||||
}
|
||||
|
||||
async function getCsrfToken(): Promise<string> {
|
||||
const response = await fetch('/api/auth/csrf', { credentials: 'same-origin' });
|
||||
const data = await response.json();
|
||||
return data.csrfToken || '';
|
||||
}
|
||||
|
||||
async function startUniqueizer() {
|
||||
setUniqueizerLoading(true);
|
||||
try {
|
||||
const csrfToken = await getCsrfToken();
|
||||
const response = await fetch('/api/scenario/uniqueizer/start', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-csrf-token': csrfToken,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
// Передаём generationUuid в URL
|
||||
router.push(`/uniqueizer?generationUuid=${data.generationUuid}`);
|
||||
} else {
|
||||
const data = await response.json();
|
||||
alert('Ошибка запуска сценария: ' + (data.message || response.statusText));
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
alert('Ошибка: ' + (err instanceof Error ? err.message : String(err)));
|
||||
} finally {
|
||||
setUniqueizerLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <div style={styles.loading}>Загрузка...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={styles.page}>
|
||||
<div style={styles.container}>
|
||||
<div style={styles.header}>
|
||||
<h1 style={styles.headerTitle}>Uno Click</h1>
|
||||
<button onClick={handleLogout} style={styles.logoutBtn}>
|
||||
Выйти
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style={styles.welcome}>
|
||||
<h2 style={styles.welcomeTitle}>
|
||||
Добро пожаловать, {user?.displayName || user?.email}!
|
||||
</h2>
|
||||
<p style={styles.welcomeText}>Выберите сценарий для запуска</p>
|
||||
</div>
|
||||
|
||||
<div style={styles.scenariosGrid}>
|
||||
{/* Nano Banana */}
|
||||
<div style={styles.scenarioCard}>
|
||||
<div style={{ ...styles.scenarioIcon, ...styles.scenarioIconBlue }}>
|
||||
🎨
|
||||
</div>
|
||||
<div style={styles.scenarioName}>Nano Banana</div>
|
||||
<div style={styles.scenarioDesc}>
|
||||
Генерация изображений по текстовому описанию. Введите промпт и получите уникальную иллюстрацию.
|
||||
</div>
|
||||
<a href="/prompt?scenario=nano-banana" style={styles.btnPrimary}>
|
||||
Запустить
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Demo Scenario */}
|
||||
<div style={styles.scenarioCard}>
|
||||
<div style={{ ...styles.scenarioIcon, ...styles.scenarioIconGreen }}>
|
||||
🧪
|
||||
</div>
|
||||
<div style={styles.scenarioName}>Demo Scenario</div>
|
||||
<div style={styles.scenarioDesc}>
|
||||
Тестовый сценарий для проверки функциональности. Многоступенчатая генерация с подтверждением.
|
||||
</div>
|
||||
<a href="/prompt?scenario=demo-scenario" style={styles.btnSecondary}>
|
||||
Запустить
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Уникализация */}
|
||||
<div style={styles.scenarioCard}>
|
||||
<div style={{ ...styles.scenarioIcon, ...styles.scenarioIconBlue }}>
|
||||
✨
|
||||
</div>
|
||||
<div style={styles.scenarioName}>Уникализация</div>
|
||||
<div style={styles.scenarioDesc}>
|
||||
Запуск сценария уникализации контента.
|
||||
</div>
|
||||
<button
|
||||
onClick={startUniqueizer}
|
||||
disabled={uniqueizerLoading}
|
||||
style={{
|
||||
...styles.btnPrimary,
|
||||
...(uniqueizerLoading ? styles.btnDisabled : {}),
|
||||
}}
|
||||
>
|
||||
{uniqueizerLoading ? 'Запуск...' : 'Запустить'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const styles: Record<string, React.CSSProperties> = {
|
||||
page: {
|
||||
minHeight: '100vh',
|
||||
padding: '40px 20px',
|
||||
background: '#f5f5f5',
|
||||
},
|
||||
container: {
|
||||
maxWidth: '800px',
|
||||
margin: '0 auto',
|
||||
},
|
||||
header: {
|
||||
background: 'white',
|
||||
padding: '20px 30px',
|
||||
borderRadius: '8px',
|
||||
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
|
||||
marginBottom: '20px',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: '20px',
|
||||
fontWeight: 600,
|
||||
color: '#333',
|
||||
},
|
||||
logoutBtn: {
|
||||
background: 'none',
|
||||
border: '1px solid #ddd',
|
||||
padding: '8px 16px',
|
||||
borderRadius: '6px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px',
|
||||
color: '#666',
|
||||
},
|
||||
welcome: {
|
||||
background: 'white',
|
||||
padding: '20px 30px',
|
||||
borderRadius: '8px',
|
||||
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
|
||||
marginBottom: '20px',
|
||||
},
|
||||
welcomeTitle: {
|
||||
fontSize: '18px',
|
||||
fontWeight: 600,
|
||||
color: '#333',
|
||||
marginBottom: '8px',
|
||||
},
|
||||
welcomeText: {
|
||||
color: '#666',
|
||||
fontSize: '14px',
|
||||
},
|
||||
scenariosGrid: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
|
||||
gap: '20px',
|
||||
},
|
||||
scenarioCard: {
|
||||
background: 'white',
|
||||
borderRadius: '8px',
|
||||
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
|
||||
padding: '24px',
|
||||
transition: 'box-shadow 0.2s',
|
||||
},
|
||||
scenarioIcon: {
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
borderRadius: '10px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: '24px',
|
||||
marginBottom: '16px',
|
||||
},
|
||||
scenarioIconBlue: {
|
||||
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
||||
},
|
||||
scenarioIconGreen: {
|
||||
background: 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)',
|
||||
},
|
||||
scenarioName: {
|
||||
fontSize: '16px',
|
||||
fontWeight: 600,
|
||||
color: '#333',
|
||||
marginBottom: '8px',
|
||||
},
|
||||
scenarioDesc: {
|
||||
fontSize: '14px',
|
||||
color: '#666',
|
||||
marginBottom: '20px',
|
||||
lineHeight: 1.5,
|
||||
},
|
||||
btnPrimary: {
|
||||
width: '100%',
|
||||
padding: '12px',
|
||||
background: '#007bff',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '6px',
|
||||
fontSize: '14px',
|
||||
fontWeight: 500,
|
||||
cursor: 'pointer',
|
||||
textAlign: 'center',
|
||||
textDecoration: 'none',
|
||||
display: 'inline-block',
|
||||
},
|
||||
btnSecondary: {
|
||||
width: '100%',
|
||||
padding: '12px',
|
||||
background: '#f5f5f5',
|
||||
color: '#333',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '6px',
|
||||
fontSize: '14px',
|
||||
fontWeight: 500,
|
||||
cursor: 'pointer',
|
||||
textAlign: 'center',
|
||||
textDecoration: 'none',
|
||||
display: 'inline-block',
|
||||
},
|
||||
btnDisabled: {
|
||||
background: '#ccc',
|
||||
cursor: 'not-allowed',
|
||||
},
|
||||
loading: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
minHeight: '100vh',
|
||||
fontSize: '18px',
|
||||
color: '#666',
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user