This commit is contained in:
JMARyA 2024-10-07 21:48:48 +02:00
parent d21554cddc
commit b13b385cb7
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
10 changed files with 84 additions and 70 deletions

View file

@ -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

View file

@ -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

View file

@ -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"];
} }
} }

View file

@ -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());
} }
} }

View file

@ -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';

View file

@ -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) {

View file

@ -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;

View file

@ -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,
), ),
], ],
); );

View file

@ -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:

View file

@ -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