update
This commit is contained in:
parent
d21554cddc
commit
b13b385cb7
10 changed files with 84 additions and 70 deletions
|
@ -41,8 +41,7 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
applicationId "de.hydrar.red.cdb"
|
||||||
applicationId "com.example.cdb_ui"
|
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdkVersion flutter.minSdkVersion
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<application
|
<application
|
||||||
android:label="cdb_ui"
|
android:label="CDB"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<activity
|
<activity
|
||||||
|
|
63
lib/api.dart
63
lib/api.dart
|
@ -6,7 +6,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||||
// todo : api errors
|
// todo : api errors
|
||||||
|
|
||||||
class API {
|
class API {
|
||||||
late SharedPreferences pref;
|
SharedPreferences? pref;
|
||||||
static final API _instance = API._internal();
|
static final API _instance = API._internal();
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
|
@ -41,16 +41,24 @@ class API {
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
pref = await SharedPreferences.getInstance();
|
pref = await SharedPreferences.getInstance();
|
||||||
instance = pref.getString("instance") ?? "";
|
instance = pref!.getString("instance") ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isInit() {
|
bool isInit() {
|
||||||
return pref.containsKey("token") && pref.containsKey("instance");
|
if (pref == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pref!.containsKey("token") && pref!.containsKey("instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isPrefetched() {
|
||||||
|
return items != null && locations != null && flowInfos != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void save(String instance, String token) {
|
void save(String instance, String token) {
|
||||||
pref.setString("instance", instance);
|
pref!.setString("instance", instance);
|
||||||
pref.setString("token", token);
|
pref!.setString("token", token);
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +68,7 @@ class API {
|
||||||
var resp = await http.get(Uri.parse(url), headers: <String, String>{
|
var resp = await http.get(Uri.parse(url), headers: <String, String>{
|
||||||
'Access-Control-Allow-Origin': '*',
|
'Access-Control-Allow-Origin': '*',
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
'Token': pref.getString("token")!
|
'Token': pref!.getString("token")!
|
||||||
});
|
});
|
||||||
|
|
||||||
return utf8.decode(resp.bodyBytes);
|
return utf8.decode(resp.bodyBytes);
|
||||||
|
@ -71,7 +79,7 @@ class API {
|
||||||
headers: <String, String>{
|
headers: <String, String>{
|
||||||
'Access-Control-Allow-Origin': '*',
|
'Access-Control-Allow-Origin': '*',
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
'Token': pref.getString("token")!
|
'Token': pref!.getString("token")!
|
||||||
},
|
},
|
||||||
body: jsonEncode(data));
|
body: jsonEncode(data));
|
||||||
|
|
||||||
|
@ -163,7 +171,7 @@ class API {
|
||||||
}
|
}
|
||||||
|
|
||||||
// /supply
|
// /supply
|
||||||
Future<String> supplyItem(String item, String variant, String price,
|
Future<String> supplyItem(String item, String variant, double price,
|
||||||
String? origin, String? location, String? note) async {
|
String? origin, String? location, String? note) async {
|
||||||
if (origin!.isEmpty) {
|
if (origin!.isEmpty) {
|
||||||
origin = null;
|
origin = null;
|
||||||
|
@ -214,7 +222,7 @@ class API {
|
||||||
}
|
}
|
||||||
|
|
||||||
// /item/<item_id>/<variant_id>/price_history?<origin>
|
// /item/<item_id>/<variant_id>/price_history?<origin>
|
||||||
Future<List<Price>> getPriceHistory(String item, String variant,
|
Future<List<double>> getPriceHistory(String item, String variant,
|
||||||
{String? origin}) async {
|
{String? origin}) async {
|
||||||
var url = "$instance/item/$item/$variant/price_history";
|
var url = "$instance/item/$item/$variant/price_history";
|
||||||
|
|
||||||
|
@ -224,10 +232,10 @@ class API {
|
||||||
|
|
||||||
var resp = jsonDecode(await getRequest(url)) as List<dynamic>;
|
var resp = jsonDecode(await getRequest(url)) as List<dynamic>;
|
||||||
|
|
||||||
return resp.map((x) => Price(x)).toList();
|
return resp.map((x) => x as double).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Price> getLatestPrice(String item, String variant,
|
Future<double> getLatestPrice(String item, String variant,
|
||||||
{String? origin}) async {
|
{String? origin}) async {
|
||||||
var url = "$instance/item/$item/$variant/price_latest";
|
var url = "$instance/item/$item/$variant/price_latest";
|
||||||
|
|
||||||
|
@ -237,7 +245,7 @@ class API {
|
||||||
|
|
||||||
var resp = jsonDecode(await getRequest(url)) as Map<String, dynamic>;
|
var resp = jsonDecode(await getRequest(url)) as Map<String, dynamic>;
|
||||||
|
|
||||||
return Price(resp);
|
return resp as double;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flows
|
// Flows
|
||||||
|
@ -386,31 +394,11 @@ class ItemVariant {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Price {
|
|
||||||
late double value;
|
|
||||||
late String currency;
|
|
||||||
|
|
||||||
Price(Map<String, dynamic> json) {
|
|
||||||
value = json["value"];
|
|
||||||
currency = json["currency"];
|
|
||||||
}
|
|
||||||
|
|
||||||
Price.fromString(String value) {
|
|
||||||
var priceSplit = value.split(" ");
|
|
||||||
this.value = double.parse(priceSplit[0]);
|
|
||||||
currency = priceSplit[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
String format() {
|
|
||||||
return "${value.toStringAsFixed(2)} $currency";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Transaction {
|
class Transaction {
|
||||||
late String uuid;
|
late String uuid;
|
||||||
late String item;
|
late String item;
|
||||||
late String variant;
|
late String variant;
|
||||||
late Price price;
|
late double price;
|
||||||
String? origin;
|
String? origin;
|
||||||
late int timestamp;
|
late int timestamp;
|
||||||
ConsumeInfo? consumed;
|
ConsumeInfo? consumed;
|
||||||
|
@ -422,7 +410,7 @@ class Transaction {
|
||||||
uuid = json["uuid"];
|
uuid = json["uuid"];
|
||||||
item = json["item"];
|
item = json["item"];
|
||||||
variant = json["variant"];
|
variant = json["variant"];
|
||||||
price = Price(json["price"]);
|
price = json["price"];
|
||||||
origin = json["origin"];
|
origin = json["origin"];
|
||||||
timestamp = json["timestamp"];
|
timestamp = json["timestamp"];
|
||||||
expired = json["expired"];
|
expired = json["expired"];
|
||||||
|
@ -431,12 +419,11 @@ class Transaction {
|
||||||
location = json["location"] != null ? Location(json["location"]) : null;
|
location = json["location"] != null ? Location(json["location"]) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Transaction.inMemory(String itemID, String variantID, String price,
|
Transaction.inMemory(String itemID, String variantID, this.price,
|
||||||
String? origin, Location? location, String? note) {
|
String? origin, Location? location, String? note) {
|
||||||
uuid = "";
|
uuid = "";
|
||||||
item = itemID;
|
item = itemID;
|
||||||
variant = variantID;
|
variant = variantID;
|
||||||
this.price = Price.fromString(price);
|
|
||||||
origin = origin;
|
origin = origin;
|
||||||
timestamp = 0;
|
timestamp = 0;
|
||||||
consumed = null;
|
consumed = null;
|
||||||
|
@ -448,12 +435,12 @@ class Transaction {
|
||||||
|
|
||||||
class ConsumeInfo {
|
class ConsumeInfo {
|
||||||
late String destination;
|
late String destination;
|
||||||
late Price price;
|
late double price;
|
||||||
late int timestamp;
|
late int timestamp;
|
||||||
|
|
||||||
ConsumeInfo(Map<String, dynamic> json) {
|
ConsumeInfo(Map<String, dynamic> json) {
|
||||||
destination = json["destination"];
|
destination = json["destination"];
|
||||||
price = Price(json["price"]);
|
price = json["price"];
|
||||||
timestamp = json["timestamp"];
|
timestamp = json["timestamp"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,27 +7,49 @@ import 'package:cdb_ui/pages/stats.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
await API().init();
|
|
||||||
if (API().isInit()) {
|
|
||||||
await API().prefetch();
|
|
||||||
}
|
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatefulWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MyApp> createState() => _MyAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MyAppState extends State<MyApp> {
|
||||||
|
bool init = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
() async {
|
||||||
|
await API().init();
|
||||||
|
if (API().isInit()) {
|
||||||
|
await API().prefetch();
|
||||||
|
setState(() {
|
||||||
|
init = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'CDB',
|
title: 'CDB',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
colorScheme: ColorScheme.fromSeed(
|
colorScheme: ColorScheme.fromSeed(
|
||||||
seedColor: Colors.deepPurple, brightness: Brightness.dark),
|
seedColor: Colors.deepPurple, brightness: Brightness.dark),
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
home: API().isInit() ? const MyHomePage() : const SetupPage(),
|
home: API().isInit()
|
||||||
);
|
? (API().isPrefetched()
|
||||||
|
? const MyHomePage()
|
||||||
|
: const Scaffold(
|
||||||
|
body: CircularProgressIndicator(),
|
||||||
|
))
|
||||||
|
: const SetupPage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import 'dart:js_interop_unsafe';
|
|
||||||
|
|
||||||
import 'package:cdb_ui/api.dart' as API;
|
import 'package:cdb_ui/api.dart' as API;
|
||||||
import 'package:cdb_ui/api.dart';
|
import 'package:cdb_ui/api.dart';
|
||||||
import 'package:cdb_ui/pages/supply.dart';
|
import 'package:cdb_ui/pages/supply.dart';
|
||||||
|
|
|
@ -24,6 +24,7 @@ class StatsPage extends StatelessWidget {
|
||||||
// global origin / destinations
|
// global origin / destinations
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: Text("Home"),),
|
||||||
body: FutureBuilder(
|
body: FutureBuilder(
|
||||||
future: _fetchData(),
|
future: _fetchData(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
|
|
@ -53,7 +53,7 @@ class _SupplyPageState extends State<SupplyPage> {
|
||||||
var t = SupplyForm(
|
var t = SupplyForm(
|
||||||
itemID: widget.item.id,
|
itemID: widget.item.id,
|
||||||
variant: variant,
|
variant: variant,
|
||||||
price: "${_priceController.text} €",
|
price: double.parse(_priceController.text),
|
||||||
origin: _selectedOrigin,
|
origin: _selectedOrigin,
|
||||||
location: _selectedLocation,
|
location: _selectedLocation,
|
||||||
note: _noteController.text);
|
note: _noteController.text);
|
||||||
|
@ -66,8 +66,13 @@ class _SupplyPageState extends State<SupplyPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
API()
|
API()
|
||||||
.supplyItem(widget.item.id, variant, "${_priceController.text} €",
|
.supplyItem(
|
||||||
_selectedOrigin, _selectedLocation, _noteController.text)
|
widget.item.id,
|
||||||
|
variant,
|
||||||
|
double.parse(_priceController.text),
|
||||||
|
_selectedOrigin,
|
||||||
|
_selectedLocation,
|
||||||
|
_noteController.text)
|
||||||
.then((_) {
|
.then((_) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text('Item added successfully!')),
|
const SnackBar(content: Text('Item added successfully!')),
|
||||||
|
@ -138,7 +143,7 @@ class _SupplyPageState extends State<SupplyPage> {
|
||||||
? await API()
|
? await API()
|
||||||
.getLatestPrice(widget.item.id, variant,
|
.getLatestPrice(widget.item.id, variant,
|
||||||
origin: selection)
|
origin: selection)
|
||||||
.then((x) => x.value.toStringAsFixed(2))
|
.then((x) => x.toStringAsFixed(2))
|
||||||
: _priceController.text;
|
: _priceController.text;
|
||||||
setState(() {
|
setState(() {
|
||||||
_priceController.text = price;
|
_priceController.text = price;
|
||||||
|
@ -293,7 +298,7 @@ class AutocompletedTextField extends StatelessWidget {
|
||||||
class SupplyForm {
|
class SupplyForm {
|
||||||
final String itemID;
|
final String itemID;
|
||||||
final String variant;
|
final String variant;
|
||||||
final String price;
|
final double price;
|
||||||
final String? origin;
|
final String? origin;
|
||||||
final String? location;
|
final String? location;
|
||||||
final String note;
|
final String note;
|
||||||
|
|
|
@ -165,7 +165,7 @@ class _TransactionPageState extends State<TransactionPage> {
|
||||||
|
|
||||||
if (transaction.expired) const Text("Transaction is Expired!"),
|
if (transaction.expired) const Text("Transaction is Expired!"),
|
||||||
|
|
||||||
IconText(Icons.money, transaction.price.format(),
|
IconText(Icons.money, transaction.price.toStringAsFixed(2),
|
||||||
color: Colors.green),
|
color: Colors.green),
|
||||||
|
|
||||||
if (transaction.origin != null)
|
if (transaction.origin != null)
|
||||||
|
@ -181,7 +181,8 @@ class _TransactionPageState extends State<TransactionPage> {
|
||||||
Text("Consumed on: ${tsFormat(transaction.consumed!.timestamp)}"),
|
Text("Consumed on: ${tsFormat(transaction.consumed!.timestamp)}"),
|
||||||
IconText(Icons.store, transaction.consumed!.destination,
|
IconText(Icons.store, transaction.consumed!.destination,
|
||||||
color: Colors.blue),
|
color: Colors.blue),
|
||||||
IconText(Icons.money, transaction.consumed!.price.format(),
|
IconText(
|
||||||
|
Icons.money, transaction.consumed!.price.toStringAsFixed(2),
|
||||||
color: Colors.green),
|
color: Colors.green),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -286,8 +287,7 @@ class TransactionCard extends StatelessWidget {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
IconText(Icons.money,
|
IconText(Icons.money, "${t.price.toStringAsFixed(2)} €",
|
||||||
"${t.price.value.toStringAsFixed(2)} ${t.price.currency}",
|
|
||||||
color: Colors.green),
|
color: Colors.green),
|
||||||
if (t.origin != null) ...[
|
if (t.origin != null) ...[
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
@ -328,6 +328,7 @@ class IconText extends StatelessWidget {
|
||||||
Text(
|
Text(
|
||||||
text,
|
text,
|
||||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -124,10 +124,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
|
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.2"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -417,10 +417,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.1"
|
version: "1.1.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: cdb_ui
|
name: cdb_ui
|
||||||
description: A new Flutter project.
|
description: Economic Database.
|
||||||
# The following line prevents the package from being accidentally published to
|
# The following line prevents the package from being accidentally published to
|
||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
|
|
Loading…
Reference in a new issue