import 'package:pve_flutter_frontend/widgets/pve_task_log_expansiontile_widget.dart';
class PveTaskLog extends StatefulWidget {
- PveTaskLog({Key key}) : super(key: key);
+ const PveTaskLog({super.key});
@override
_PveTaskLogState createState() => _PveTaskLogState();
class _PveTaskLogState extends State<PveTaskLog> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
- TextEditingController _userFilterController;
- TextEditingController _typeFilterController;
+ late TextEditingController _userFilterController;
+ late TextEditingController _typeFilterController;
@override
void initState() {
super.initState();
final bloc = Provider.of<PveTaskLogBloc>(context, listen: false);
- final state = bloc.latestState;
+ final PveTaskLogState state = bloc.latestState;
_userFilterController = TextEditingController.fromValue(
TextEditingValue(text: state.userFilter ?? ''));
_typeFilterController = TextEditingController.fromValue(
return ProxmoxStreamBuilder<PveTaskLogBloc, PveTaskLogState>(
bloc: bloc,
builder: (context, state) {
- if (state.tasks != null) {
- return SafeArea(
- child: Scaffold(
- key: _scaffoldKey,
- appBar: AppBar(
- leading: IconButton(
- icon: Icon(Icons.close),
- onPressed: () => Navigator.of(context).pop(),
- ),
- actions: <Widget>[
- IconButton(
- icon: Icon(Icons.more_vert),
- onPressed: () =>
- _scaffoldKey.currentState.openEndDrawer(),
- )
- ],
+ return SafeArea(
+ child: Scaffold(
+ key: _scaffoldKey,
+ appBar: AppBar(
+ leading: IconButton(
+ icon: const Icon(Icons.close),
+ onPressed: () => Navigator.of(context).pop(),
),
- endDrawer: Drawer(
- child: Padding(
- padding: const EdgeInsets.fromLTRB(16.0, 20.0, 16.0, 0),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- Text(
- 'Filters',
- style: Theme.of(context).textTheme.headline5,
- ),
- SizedBox(
- height: 20,
- ),
- TextFormField(
- decoration: InputDecoration(
- labelText: 'by user',
- filled: true,
- prefixIcon: Icon(Icons.person)),
- onChanged: (newValue) {
- bloc.events.add(FilterTasksByUser(newValue));
- bloc.events.add(LoadTasks());
- },
- controller: _userFilterController,
- ),
- SizedBox(
- height: 20,
- ),
- TextFormField(
- decoration: InputDecoration(
- labelText: 'by type',
- filled: true,
- prefixIcon: Icon(Icons.description)),
- onChanged: (newValue) {
- bloc.events.add(FilterTasksByType(newValue));
- bloc.events.add(LoadTasks());
- },
- controller: _typeFilterController,
- ),
- SizedBox(
- height: 20,
- ),
- DropdownButtonFormField<String>(
- decoration: InputDecoration(labelText: 'Source'),
- value: state.source,
- icon: Icon(Icons.arrow_downward),
- iconSize: 24,
- elevation: 16,
- onChanged: (String newValue) {
- bloc.events.add(FilterTasksBySource(newValue));
- bloc.events.add(LoadTasks());
- },
- items: <String>[
- 'all',
- 'active',
- 'archive',
- ].map<DropdownMenuItem<String>>((String value) {
- return DropdownMenuItem<String>(
- value: value,
- child: Container(child: Text(value)),
- );
- }).toList(),
- ),
- SizedBox(
- height: 20,
+ actions: <Widget>[
+ IconButton(
+ icon: const Icon(Icons.more_vert),
+ onPressed: () => _scaffoldKey.currentState?.openEndDrawer(),
+ )
+ ],
+ ),
+ endDrawer: Drawer(
+ child: Padding(
+ padding: const EdgeInsets.fromLTRB(16.0, 20.0, 16.0, 0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: <Widget>[
+ Text(
+ 'Filters',
+ style: Theme.of(context).textTheme.headlineSmall,
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+ TextFormField(
+ decoration: const InputDecoration(
+ labelText: 'by user',
+ filled: true,
+ prefixIcon: Icon(Icons.person)),
+ onChanged: (newValue) {
+ bloc.events.add(FilterTasksByUser(newValue));
+ bloc.events.add(LoadTasks());
+ },
+ controller: _userFilterController,
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+ TextFormField(
+ decoration: const InputDecoration(
+ labelText: 'by type',
+ filled: true,
+ prefixIcon: Icon(Icons.description)),
+ onChanged: (newValue) {
+ bloc.events.add(FilterTasksByType(newValue));
+ bloc.events.add(LoadTasks());
+ },
+ controller: _typeFilterController,
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+ DropdownButtonFormField<String>(
+ decoration: const InputDecoration(labelText: 'Source'),
+ value: state.source,
+ icon: const Icon(Icons.arrow_downward),
+ iconSize: 24,
+ elevation: 16,
+ onChanged: (String? newValue) {
+ bloc.events.add(FilterTasksBySource(newValue));
+ bloc.events.add(LoadTasks());
+ },
+ items: <String>[
+ 'all',
+ 'active',
+ 'archive',
+ ].map<DropdownMenuItem<String>>((String value) {
+ return DropdownMenuItem<String>(
+ value: value,
+ child: Container(child: Text(value)),
+ );
+ }).toList(),
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+ FormField(
+ builder: (FormFieldState<bool> formFieldState) => Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: <Widget>[
+ const Text("Only errors"),
+ Checkbox(
+ value: state.onlyErrors,
+ onChanged: (value) {
+ formFieldState.didChange(value);
+ bloc.events.add(FilterTasksByError());
+ bloc.events.add(LoadTasks());
+ },
+ ),
+ ],
),
- FormField(
- builder: (FormFieldState<bool> formFieldState) => Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: <Widget>[
- Text("Only errors"),
- Checkbox(
- value: state.onlyErrors,
- onChanged: (value) {
- formFieldState.didChange(value);
- bloc.events.add(FilterTasksByError());
- bloc.events.add(LoadTasks());
- },
- ),
- ],
- ),
- )
- ],
- ),
+ )
+ ],
),
),
- body: NotificationListener<ScrollNotification>(
- onNotification: (ScrollNotification scrollInfo) {
- if (scrollInfo.metrics.pixels >=
- (0.8 * scrollInfo.metrics.maxScrollExtent)) {
- if (!state.isLoading) {
- bloc.events.add(LoadMoreTasks());
- }
+ ),
+ body: NotificationListener<ScrollNotification>(
+ onNotification: (ScrollNotification scrollInfo) {
+ if (scrollInfo.metrics.pixels >=
+ (0.8 * scrollInfo.metrics.maxScrollExtent)) {
+ if (!state.isLoading) {
+ bloc.events.add(LoadMoreTasks());
}
- return false;
- },
- child: state.tasks.isNotEmpty
- ? ListView.builder(
- itemCount: state.tasks.length,
- itemBuilder: (context, index) => PveTaskExpansionTile(
- task: state.tasks[index],
- ),
- )
- : Center(
- child: Text("No tasks found"),
+ }
+ return false;
+ },
+ child: state.tasks.isNotEmpty
+ ? ListView.builder(
+ itemCount: state.tasks.length,
+ itemBuilder: (context, index) => PveTaskExpansionTile(
+ task: state.tasks[index],
),
- ),
+ )
+ : const Center(
+ child: Text("No tasks found"),
+ ),
),
- );
- }
+ ),
+ );
return Container();
});
final Widget jobTitle;
const PveTaskLogScrollView({
- Key key,
- this.icon,
- this.jobTitle,
- }) : super(key: key);
+ super.key,
+ required this.icon,
+ required this.jobTitle,
+ });
@override
_PveTaskLogScrollViewState createState() => _PveTaskLogScrollViewState();
}
class _PveTaskLogScrollViewState extends State<PveTaskLogScrollView> {
- ScrollController _scrollController = new ScrollController();
+ ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
indicatorColor = Colors.red;
statusChipColor = Colors.red.shade100;
}
- return Container(
+ return SizedBox(
height: MediaQuery.of(context).size.height * 0.5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
if (state.isBlank)
- Align(
+ const Align(
alignment: Alignment.center,
child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 8.0),
+ padding: EdgeInsets.symmetric(vertical: 8.0),
child: Text("Loading log data.."),
),
),
if (!state.isBlank) ...[
Padding(
- padding: EdgeInsets.fromLTRB(0, 5, 0, 5),
+ padding: const EdgeInsets.fromLTRB(0, 5, 0, 5),
child: Align(
alignment: Alignment.topCenter,
child: Container(
title: AnimatedDefaultTextStyle(
style: Theme.of(context)
.textTheme
- .subtitle1
+ .titleMedium!
.copyWith(fontWeight: FontWeight.bold),
duration: kThemeChangeDuration,
- child: widget.jobTitle ?? const SizedBox(),
+ child: widget.jobTitle,
),
trailing: Chip(
label: Text(
- state.status.status.name,
+ state.status!.status.name,
style: TextStyle(color: indicatorColor),
),
backgroundColor: statusChipColor,
),
),
- Divider(),
+ const Divider(),
if (state.log != null)
Expanded(
child: Padding(
padding: const EdgeInsets.all(14.0),
child: ListView.builder(
controller: _scrollController,
- itemCount: state.log.lines.length,
+ itemCount: state.log!.lines!.length,
itemBuilder: (context, index) {
- final isLast =
- index == state.log.lines.length - 1;
- final errorLine = state.log.lines[index].lineText
- .contains('ERROR');
+ final log = state.log!.lines!;
+ final isLast = index == log.length - 1;
+ final errorLine =
+ log[index].lineText?.contains('ERROR') ??
+ false;
+ final warningLine =
+ log[index].lineText?.contains('WARNING') ??
+ false;
return Card(
color: isLast || errorLine
? indicatorColor
: Colors.white,
child: Padding(
- padding: const EdgeInsets.all(8.0),
+ padding: const EdgeInsets.all(5.0),
child: Text(
- state.log.lines[index].lineText,
+ log[index].lineText ?? '<unknown>',
style: TextStyle(
color: isLast || errorLine
? Colors.white
void _scrollToBottom() {
if (_scrollController.hasClients) {
_scrollController.animateTo(_scrollController.position.maxScrollExtent,
- duration: Duration(milliseconds: 50), curve: Curves.ease);
+ duration: const Duration(milliseconds: 500), curve: Curves.easeOut);
}
}
}