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 {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.cdb_ui"
applicationId "de.hydrar.red.cdb"
// 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.
minSdkVersion flutter.minSdkVersion

View file

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="cdb_ui"
android:label="CDB"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity

View file

@ -6,7 +6,7 @@ import 'package:shared_preferences/shared_preferences.dart';
// todo : api errors
class API {
late SharedPreferences pref;
SharedPreferences? pref;
static final API _instance = API._internal();
// cache
@ -41,16 +41,24 @@ class API {
Future<void> init() async {
pref = await SharedPreferences.getInstance();
instance = pref.getString("instance") ?? "";
instance = pref!.getString("instance") ?? "";
}
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) {
pref.setString("instance", instance);
pref.setString("token", token);
pref!.setString("instance", instance);
pref!.setString("token", token);
this.instance = instance;
}
@ -60,7 +68,7 @@ class API {
var resp = await http.get(Uri.parse(url), headers: <String, String>{
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json; charset=UTF-8',
'Token': pref.getString("token")!
'Token': pref!.getString("token")!
});
return utf8.decode(resp.bodyBytes);
@ -71,7 +79,7 @@ class API {
headers: <String, String>{
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json; charset=UTF-8',
'Token': pref.getString("token")!
'Token': pref!.getString("token")!
},
body: jsonEncode(data));
@ -163,7 +171,7 @@ class API {
}
// /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 {
if (origin!.isEmpty) {
origin = null;
@ -214,7 +222,7 @@ class API {
}
// /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 {
var url = "$instance/item/$item/$variant/price_history";
@ -224,10 +232,10 @@ class API {
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 {
var url = "$instance/item/$item/$variant/price_latest";
@ -237,7 +245,7 @@ class API {
var resp = jsonDecode(await getRequest(url)) as Map<String, dynamic>;
return Price(resp);
return resp as double;
}
// 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 {
late String uuid;
late String item;
late String variant;
late Price price;
late double price;
String? origin;
late int timestamp;
ConsumeInfo? consumed;
@ -422,7 +410,7 @@ class Transaction {
uuid = json["uuid"];
item = json["item"];
variant = json["variant"];
price = Price(json["price"]);
price = json["price"];
origin = json["origin"];
timestamp = json["timestamp"];
expired = json["expired"];
@ -431,12 +419,11 @@ class Transaction {
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) {
uuid = "";
item = itemID;
variant = variantID;
this.price = Price.fromString(price);
origin = origin;
timestamp = 0;
consumed = null;
@ -448,12 +435,12 @@ class Transaction {
class ConsumeInfo {
late String destination;
late Price price;
late double price;
late int timestamp;
ConsumeInfo(Map<String, dynamic> json) {
destination = json["destination"];
price = Price(json["price"]);
price = json["price"];
timestamp = json["timestamp"];
}
}

View file

@ -7,27 +7,49 @@ import 'package:cdb_ui/pages/stats.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
await API().init();
if (API().isInit()) {
await API().prefetch();
}
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
class MyApp extends StatefulWidget {
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
Widget build(BuildContext context) {
return MaterialApp(
title: 'CDB',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple, brightness: Brightness.dark),
useMaterial3: true,
),
home: API().isInit() ? const MyHomePage() : const SetupPage(),
);
title: 'CDB',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple, brightness: Brightness.dark),
useMaterial3: true,
),
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';
import 'package:cdb_ui/pages/supply.dart';

View file

@ -24,6 +24,7 @@ class StatsPage extends StatelessWidget {
// global origin / destinations
return Scaffold(
appBar: AppBar(title: Text("Home"),),
body: FutureBuilder(
future: _fetchData(),
builder: (context, snapshot) {

View file

@ -53,7 +53,7 @@ class _SupplyPageState extends State<SupplyPage> {
var t = SupplyForm(
itemID: widget.item.id,
variant: variant,
price: "${_priceController.text}",
price: double.parse(_priceController.text),
origin: _selectedOrigin,
location: _selectedLocation,
note: _noteController.text);
@ -66,8 +66,13 @@ class _SupplyPageState extends State<SupplyPage> {
}
API()
.supplyItem(widget.item.id, variant, "${_priceController.text}",
_selectedOrigin, _selectedLocation, _noteController.text)
.supplyItem(
widget.item.id,
variant,
double.parse(_priceController.text),
_selectedOrigin,
_selectedLocation,
_noteController.text)
.then((_) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Item added successfully!')),
@ -138,7 +143,7 @@ class _SupplyPageState extends State<SupplyPage> {
? await API()
.getLatestPrice(widget.item.id, variant,
origin: selection)
.then((x) => x.value.toStringAsFixed(2))
.then((x) => x.toStringAsFixed(2))
: _priceController.text;
setState(() {
_priceController.text = price;
@ -293,7 +298,7 @@ class AutocompletedTextField extends StatelessWidget {
class SupplyForm {
final String itemID;
final String variant;
final String price;
final double price;
final String? origin;
final String? location;
final String note;

View file

@ -165,7 +165,7 @@ class _TransactionPageState extends State<TransactionPage> {
if (transaction.expired) const Text("Transaction is Expired!"),
IconText(Icons.money, transaction.price.format(),
IconText(Icons.money, transaction.price.toStringAsFixed(2),
color: Colors.green),
if (transaction.origin != null)
@ -181,7 +181,8 @@ class _TransactionPageState extends State<TransactionPage> {
Text("Consumed on: ${tsFormat(transaction.consumed!.timestamp)}"),
IconText(Icons.store, transaction.consumed!.destination,
color: Colors.blue),
IconText(Icons.money, transaction.consumed!.price.format(),
IconText(
Icons.money, transaction.consumed!.price.toStringAsFixed(2),
color: Colors.green),
]
@ -286,8 +287,7 @@ class TransactionCard extends StatelessWidget {
const SizedBox(
height: 10,
),
IconText(Icons.money,
"${t.price.value.toStringAsFixed(2)} ${t.price.currency}",
IconText(Icons.money, "${t.price.toStringAsFixed(2)}",
color: Colors.green),
if (t.origin != null) ...[
const SizedBox(height: 8),
@ -328,6 +328,7 @@ class IconText extends StatelessWidget {
Text(
text,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
overflow: TextOverflow.fade,
),
],
);

View file

@ -124,10 +124,10 @@ packages:
dependency: "direct main"
description:
name: http
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
url: "https://pub.dev"
source: hosted
version: "1.2.1"
version: "1.2.2"
http_parser:
dependency: transitive
description:
@ -417,10 +417,10 @@ packages:
dependency: transitive
description:
name: web
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.dev"
source: hosted
version: "0.5.1"
version: "1.1.0"
xdg_directories:
dependency: transitive
description:

View file

@ -1,5 +1,5 @@
name: cdb_ui
description: A new Flutter project.
description: Economic Database.
# The following line prevents the package from being accidentally published to
# 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