feat: deploy infrastructure + disk/drive, calendar, presentation, workspaces, onboarding, demo user
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../../core/api/client.dart';
|
||||
import '../../../core/api/endpoints.dart';
|
||||
import '../../../models/voice.dart';
|
||||
import '../../../widgets/loading_indicator.dart';
|
||||
import '../../../widgets/empty_state.dart';
|
||||
import '../../../core/theme/app_theme.dart';
|
||||
|
||||
class VoiceSessionsScreen extends ConsumerStatefulWidget {
|
||||
const VoiceSessionsScreen({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<VoiceSessionsScreen> createState() =>
|
||||
_VoiceSessionsScreenState();
|
||||
}
|
||||
|
||||
class _VoiceSessionsScreenState extends ConsumerState<VoiceSessionsScreen> {
|
||||
List<VoiceSession> _sessions = [];
|
||||
bool _isLoading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadSessions();
|
||||
}
|
||||
|
||||
Future<void> _loadSessions() async {
|
||||
setState(() => _isLoading = true);
|
||||
try {
|
||||
final api = ref.read(apiClientProvider);
|
||||
final response = await api.get(Endpoints.voiceSessions);
|
||||
final list = (response.data as List<dynamic>)
|
||||
.map((e) => VoiceSession.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
if (mounted) setState(() => _sessions = list);
|
||||
} catch (_) {}
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('История сессий')),
|
||||
body: _isLoading
|
||||
? const LoadingIndicator()
|
||||
: _sessions.isEmpty
|
||||
? const EmptyState(
|
||||
icon: Icons.history,
|
||||
title: 'Нет сессий',
|
||||
subtitle: 'Начните голосовой ввод',
|
||||
)
|
||||
: RefreshIndicator(
|
||||
onRefresh: _loadSessions,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: _sessions.length,
|
||||
itemBuilder: (_, i) {
|
||||
final session = _sessions[i];
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
VoIdeaColors.primary.withOpacity(0.1),
|
||||
child: const Icon(Icons.mic,
|
||||
color: VoIdeaColors.primary),
|
||||
),
|
||||
title: Text(
|
||||
session.title ?? 'Сессия ${i + 1}'),
|
||||
subtitle: Text(
|
||||
'${session.messageCount} сообщений · ${DateFormat('d MMM').format(session.createdAt)}',
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
trailing: Chip(
|
||||
label: Text(
|
||||
session.status,
|
||||
style: const TextStyle(fontSize: 11),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user