import 'package:flutter/material.dart'; import '../api/client.dart'; import '../api/models.dart'; import 'run_screen.dart'; import 'pipeline_editor.dart'; class PipelineDetailScreen extends StatefulWidget { final Pipeline pipeline; const PipelineDetailScreen({super.key, required this.pipeline}); @override State createState() => _PipelineDetailScreenState(); } class _PipelineDetailScreenState extends State { late APIClient _api; late Pipeline _pipeline; bool _loading = true; @override void initState() { super.initState(); _pipeline = widget.pipeline; _api = APIClient(baseUrl: _parseBaseUrl()); _load(); } static String _parseBaseUrl() { final uri = Uri.base; return '${uri.scheme}://${uri.host}${uri.port != 80 && uri.port != 443 ? ':${uri.port}' : ''}'; } Future _load() async { try { final p = await _api.getPipeline(_pipeline.id); setState(() { _pipeline = p; _loading = false; }); } catch (e) { setState(() => _loading = false); if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(e.toString()))); } } } Future _runPipeline() async { await Navigator.push( context, MaterialPageRoute( builder: (_) => RunScreen(pipeline: _pipeline, api: _api), ), ); _load(); } Future _editPipeline() async { final result = await Navigator.push( context, MaterialPageRoute( builder: (_) => PipelineEditorScreen(pipeline: _pipeline), ), ); if (result != null) { setState(() => _pipeline = result); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(_pipeline.name), actions: [ IconButton( icon: const Icon(Icons.edit), onPressed: _editPipeline, tooltip: 'Edit', ), IconButton( icon: const Icon(Icons.refresh), onPressed: _load, tooltip: 'Refresh', ), ], ), body: _loading ? const Center(child: CircularProgressIndicator()) : SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: double.infinity, height: 48, child: FilledButton.icon( onPressed: _runPipeline, icon: const Icon(Icons.play_arrow), label: const Text('Run Pipeline'), ), ), const SizedBox(height: 24), _infoRow('ID', _pipeline.id), _infoRow( 'Working Dir', _pipeline.workingDir.isNotEmpty ? _pipeline.workingDir : '(not set)', ), _infoRow('Created', _pipeline.createdAt), _infoRow('Updated', _pipeline.updatedAt), const SizedBox(height: 24), const Text( 'Prompts', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), const SizedBox(height: 12), ..._pipeline.prompts.asMap().entries.map((entry) { final i = entry.key; final p = entry.value; return Card( margin: const EdgeInsets.only(bottom: 12), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Chip(label: Text('Step ${i + 1}')), const SizedBox(width: 8), Text( p.title, style: const TextStyle( fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 8), Text(p.text), ], ), ), ); }), ], ), ), ); } Widget _infoRow(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 100, child: Text( label, style: const TextStyle( color: Colors.grey, fontWeight: FontWeight.bold, ), ), ), Expanded(child: Text(value)), ], ), ); } }