From 20bc5a9ae675611ebbe24f25256de65a31483d9f Mon Sep 17 00:00:00 2001 From: JMARyA Date: Wed, 25 Sep 2024 09:02:50 +0200 Subject: [PATCH] add flow notes --- lib/api.dart | 26 ++++ lib/pages/flow/active_flow_page.dart | 177 ++++++++++++++++----------- lib/pages/flow/flow_note.dart | 57 +++++++++ lib/pages/supply.dart | 2 +- 4 files changed, 190 insertions(+), 72 deletions(-) create mode 100644 lib/pages/flow/flow_note.dart diff --git a/lib/api.dart b/lib/api.dart index 6a0f274..0d3f926 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -289,6 +289,18 @@ class API { var resp = jsonDecode(await getRequest("$instance/location/$id")); return Location(resp); } + + Future addNoteToFlow(String flowID, String content) async { + var res = jsonDecode( + await postRequest("$instance/flow/$flowID/note", {"content": content})); + return res["uuid"]; + } + + Future> getNotesOfFlow(String flowID) async { + var resp = jsonDecode(await getRequest("$instance/flow/$flowID/notes")) + as List; + return resp.map((x) => FlowNote(x)).toList(); + } } class FlowInfo { @@ -483,6 +495,20 @@ class FlowDone { } } +class FlowNote { + late String uuid; + late int timestamp; + late String content; + late String onFlow; + + FlowNote(Map json) { + uuid = json["uuid"]; + timestamp = json["timestamp"]; + content = json["content"]; + onFlow = json["on_flow"]; + } +} + class GlobalItemStat { late int item_count; late int total_transactions; diff --git a/lib/pages/flow/active_flow_page.dart b/lib/pages/flow/active_flow_page.dart index 0fef0cb..240cb03 100644 --- a/lib/pages/flow/active_flow_page.dart +++ b/lib/pages/flow/active_flow_page.dart @@ -1,92 +1,127 @@ import 'package:cdb_ui/api.dart' as API; import 'package:cdb_ui/pages/flow/create_flow_page.dart'; import 'package:cdb_ui/pages/flow/end_flow_page.dart'; +import 'package:cdb_ui/pages/flow/flow_note.dart'; import 'package:cdb_ui/pages/transaction.dart'; import 'package:flutter/material.dart'; -class ActiveFlowPage extends StatelessWidget { +class ActiveFlowPage extends StatefulWidget { final API.Flow flow; final API.FlowInfo info; const ActiveFlowPage(this.flow, this.info, {super.key}); + @override + State createState() => _ActiveFlowPageState(); +} + +class _ActiveFlowPageState extends State { + _refresh() { + setState(() {}); + } + @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text(info.name), - actions: [ + appBar: AppBar( + title: Text(widget.info.name), + actions: [ + IconButton( + onPressed: () async { + if (widget.info.produces?.isNotEmpty ?? false) { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => + EndFlowWithProduce(widget.flow, widget.info))); + return; + } + + // simple dialog + var confirm = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Are you sure?'), + content: const Text('This will end the flow.'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text('Cancel'), + ), + ElevatedButton( + onPressed: () => Navigator.of(context).pop(true), + child: const Text('End'), + ), + ], + ), + ); + + if (confirm) { + await API + .API() + .endFlow(widget.flow.id) + .then((x) => Navigator.of(context).pop()); + } + }, + icon: const Icon(Icons.stop)), + // todo : continue next flow + if (widget.info.next != null) IconButton( - onPressed: () async { - if (info.produces?.isNotEmpty ?? false) { + onPressed: () { + API.API().getFlowInfo(widget.info.next!).then((newInfo) { Navigator.of(context).push(MaterialPageRoute( - builder: (context) => EndFlowWithProduce(flow, info))); - return; - } - - // simple dialog - var confirm = await showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('Are you sure?'), - content: const Text('This will end the flow.'), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(false), - child: const Text('Cancel'), - ), - ElevatedButton( - onPressed: () => Navigator.of(context).pop(true), - child: const Text('End'), - ), - ], - ), - ); - - if (confirm) { - await API - .API() - .endFlow(flow.id) - .then((x) => Navigator.of(context).pop()); - } + builder: (context) { + return CreateFlowPage( + newInfo, + () {}, + previousFlow: widget.flow, + ); + }, + )); + }); }, - icon: const Icon(Icons.stop)), - // todo : continue next flow - if (info.next != null) - IconButton( - onPressed: () { - API.API().getFlowInfo(info.next!).then((newInfo) { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) { - return CreateFlowPage( - newInfo, - () {}, - previousFlow: flow, - ); - }, - )); - }); - }, - icon: const Icon(Icons.arrow_forward)), - - // todo : add notes to flow - IconButton(onPressed: () {}, icon: const Icon(Icons.note)) + icon: const Icon(Icons.arrow_forward)), + ], + ), + body: Column( + children: [ + Text("ID: ${widget.flow.id}"), + Text("Started since: ${tsFormat(widget.flow.started)}"), + ...widget.flow.input!.map((x) => Text("Input: $x")).toList(), + if (widget.flow.done != null) ...[ + Text("Ended: ${tsFormat(widget.flow.done!.ended)}"), + if (widget.flow.done!.next != null) + Text("Next: ${widget.flow.done!.next!}"), + ...widget.flow.done!.produced! + .map((x) => Text("Produced: $x")) + .toList(), ], - ), - body: Column( - children: [ - Text("ID: ${flow.id}"), - Text("Started since: ${tsFormat(flow.started)}"), - ...flow.input!.map((x) => Text("Input: $x")).toList(), - if (flow.done != null) ...[ - Text("Ended: ${tsFormat(flow.done!.ended)}"), - if (flow.done!.next != null) Text("Next: ${flow.done!.next!}"), - ...flow.done!.produced!.map((x) => Text("Produced: $x")).toList(), - ], + const Divider(), + FutureBuilder( + future: API.API().getNotesOfFlow(widget.flow.id), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const CircularProgressIndicator(); + } - const Divider(), - // todo : show notes - ], - )); + var data = snapshot.data!; + + return Expanded( + child: ListView( + children: data + .map( + (x) => FlowNoteCard(x), + ) + .toList())); + }, + ) + ], + ), + floatingActionButton: FloatingActionButton( + onPressed: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => AddNotePage(widget.flow, _refresh))); + }, + child: const Icon(Icons.note_add), + ), + ); } } diff --git a/lib/pages/flow/flow_note.dart b/lib/pages/flow/flow_note.dart new file mode 100644 index 0000000..5e8b2eb --- /dev/null +++ b/lib/pages/flow/flow_note.dart @@ -0,0 +1,57 @@ +import 'package:cdb_ui/pages/transaction.dart'; +import 'package:flutter/material.dart'; +import 'package:cdb_ui/api.dart' as API; + +class AddNotePage extends StatelessWidget { + late final TextEditingController _noteController = TextEditingController(); + final API.Flow flow; + final Function refresh; + + AddNotePage(this.flow, this.refresh, {super.key}); + + _submit(BuildContext context) { + API.API().addNoteToFlow(flow.id, _noteController.text).then((x) { + refresh(); + Navigator.of(context).pop(); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Add Note"), + ), + body: Column( + children: [ + TextFormField( + decoration: const InputDecoration(labelText: 'Note'), + controller: _noteController, + maxLines: 10), + const SizedBox( + height: 14, + ), + ElevatedButton( + onPressed: _submit(context), child: const Text("Add Note")) + ], + ), + ); + } +} + +class FlowNoteCard extends StatelessWidget { + final API.FlowNote note; + + const FlowNoteCard(this.note, {super.key}); + + @override + Widget build(BuildContext context) { + return Card( + child: ListTile( + title: Text(tsFormat(note.timestamp), + style: const TextStyle(fontSize: 12)), + subtitle: Text(note.content, overflow: TextOverflow.ellipsis), + ), + ); + } +} diff --git a/lib/pages/supply.dart b/lib/pages/supply.dart index 2597833..20c3d14 100644 --- a/lib/pages/supply.dart +++ b/lib/pages/supply.dart @@ -179,8 +179,8 @@ class _SupplyPageState extends State { // Note TextFormField( decoration: const InputDecoration(labelText: 'Note'), - keyboardType: TextInputType.number, controller: _noteController, + maxLines: 5 ), const SizedBox(height: 20),