Compare commits

..

3 Commits

Author SHA1 Message Date
0dd56cfaa6 Test 2023-05-23 17:59:16 -07:00
162f2469f8 Incremental changes to TodayPage... 2023-05-23 17:58:55 -07:00
77e4adc2ad Begin making CategoriesPage and TodayPage 2023-05-22 10:34:25 -07:00
9 changed files with 275 additions and 33 deletions

View File

@ -0,0 +1,71 @@
import 'package:appwrite/appwrite.dart';
import 'package:appwrite/models.dart';
import 'package:ltx_flutter/constants/constants.dart';
import 'package:flutter/widgets.dart';
import 'package:ltx_flutter/appwrite/appwrite.dart';
class CategoriesAPI extends ChangeNotifier {
Client client = Client();
late final Account account;
late final Databases databases;
late final String userId;
final AuthAPI auth = AuthAPI();
// Constructor
CategoriesAPI() {
init();
}
// loadUser() async {
// try {
// user = await account.get();
// notifyListeners();
// } catch (e) {
// print(e);
// notifyListeners();
// }
// }
init() {
client.setEndpoint(APPWRITE_URL).setProject(APPWRITE_PROJECT_ID);
account = Account(client);
databases = Databases(client);
}
Future<DocumentList> getCategories() {
print("Getting categories");
return databases.listDocuments(
databaseId: CATEGORIES_DB,
collectionId: COLLECTION,
queries: [
Query.orderAsc("number"),
]);
}
Future<Document> addCategory({
required String name,
required double number,
required String color,
String? description,
int? parentId,
}) {
return databases.createDocument(
databaseId: CATEGORIES_DB,
collectionId: COLLECTION,
documentId: "category-${number.toString()}",
data: {
'name': name,
'number': number,
'color': color,
'parent': parentId,
'description': description,
});
}
Future<dynamic> deleteCategory({required double number}) {
return databases.deleteDocument(
databaseId: CATEGORIES_DB,
collectionId: COLLECTION,
documentId: "category-${number.toString()}");
}
}

View File

@ -27,13 +27,15 @@ class DatabaseAPI {
Future<DocumentList> getEntries({int limit = 100, String dateISO = ""}) { Future<DocumentList> getEntries({int limit = 100, String dateISO = ""}) {
if (dateISO == "") { if (dateISO == "") {
dateISO = formatter.format(DateTime.now()); dateISO = DateTime.now().toIso8601String();
} }
final date = DateTime.parse(dateISO); var referenceDate = DateTime.parse("2023-01-01");
final offset = date.difference(DateTime.now()).inDays;
print("Getting ${limit} entries starting from ${offset}"); final date = DateTime.parse(dateISO);
final offset = date.difference(referenceDate).inDays;
print("Getting $limit entries starting from $offset");
return databases.listDocuments( return databases.listDocuments(
databaseId: APPWRITE_DATABASE_ID, databaseId: APPWRITE_DATABASE_ID,

View File

@ -1,4 +1,5 @@
const String APPWRITE_PROJECT_ID = "lifetracker"; const String APPWRITE_PROJECT_ID = "lifetracker";
const String APPWRITE_DATABASE_ID = "lifetracker-db"; const String APPWRITE_DATABASE_ID = "lifetracker-db";
const String CATEGORIES_DB = "categories";
const String APPWRITE_URL = "https://db.ryanpandya.com/v1"; const String APPWRITE_URL = "https://db.ryanpandya.com/v1";
const String COLLECTION = "ryan"; const String COLLECTION = "ryan";

View File

@ -0,0 +1,64 @@
import 'package:ltx_flutter/appwrite/categories_api.dart';
import 'package:flutter/material.dart';
import 'package:appwrite/models.dart';
class CategoriesPage extends StatefulWidget {
const CategoriesPage({Key? key}) : super(key: key);
@override
_CategoriesPageState createState() => _CategoriesPageState();
}
class _CategoriesPageState extends State<CategoriesPage> {
final api = CategoriesAPI();
late List<Document>? categories = [];
@override
void initState() {
super.initState();
loadCategories();
}
Future loadCategories() async {
try {
final value = await api.getCategories();
setState(() {
categories = value.documents;
});
} catch (e) {
print(e);
}
}
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: loadCategories,
child: Center(
child: ListView.builder(
itemCount: categories!.length + 1,
itemBuilder: (context, index) {
if (index >= categories!.length || categories!.isEmpty) {
return ListTile(
leading: Text("New Category"),
);
} else {
Document? category = categories?[index];
Color backgroundColor =
Color(int.parse("0x${category!.data['color']}"));
Color textColor = backgroundColor.computeLuminance() > 0.2
? Colors.black
: Colors.white;
return ListTile(
tileColor: backgroundColor,
textColor: textColor,
leading: Text(category.data['number'].toString()),
title: Text(category.data['name']),
);
}
},
),
),
);
}
}

View File

@ -1,7 +1,6 @@
import 'package:appwrite/appwrite.dart'; import 'package:appwrite/appwrite.dart';
import 'package:ltx_flutter/appwrite/appwrite.dart'; import 'package:ltx_flutter/appwrite/appwrite.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class LoginPage extends StatefulWidget { class LoginPage extends StatefulWidget {

View File

@ -61,7 +61,7 @@ class _TablePageState extends State<TablePage> {
title: hourTitle, title: hourTitle,
field: "hours[$e]", field: "hours[$e]",
type: PlutoColumnType.number(), type: PlutoColumnType.number(),
width: 65, width: 70,
enableContextMenu: false, enableContextMenu: false,
enableDropToResize: false, enableDropToResize: false,
textAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center,
@ -75,23 +75,22 @@ class _TablePageState extends State<TablePage> {
field: 'date', field: 'date',
type: PlutoColumnType.text(), type: PlutoColumnType.text(),
readOnly: true, readOnly: true,
width: 110, width: 70,
frozen: PlutoColumnFrozen.start,
enableContextMenu: false, enableContextMenu: false,
enableDropToResize: false, enableDropToResize: false,
textAlign: PlutoColumnTextAlign.center, textAlign: PlutoColumnTextAlign.center,
titleTextAlign: PlutoColumnTextAlign.center, titleTextAlign: PlutoColumnTextAlign.center,
frozen: PlutoColumnFrozen.start,
),
PlutoColumn(
title: 'DAY',
field: 'day',
type: PlutoColumnType.text(),
readOnly: true,
width: 53,
enableContextMenu: false,
enableDropToResize: false,
), ),
// PlutoColumn(
// title: 'DAY',
// field: 'day',
// type: PlutoColumnType.text(),
// readOnly: true,
// width: 50,
// frozen: PlutoColumnFrozen.start,
// enableContextMenu: false,
// enableDropToResize: false,
// ),
] + ] +
hourCols + hourCols +
[ [
@ -117,23 +116,27 @@ class _TablePageState extends State<TablePage> {
var cells = { var cells = {
'date': PlutoCell( 'date': PlutoCell(
value: formatDate( value: formatDate(
format: "MM/DD E", format: "MM/DD",
dateISO: e.data['date'],
),
),
'day': PlutoCell(
value: formatDate(
format: "E",
dateISO: e.data['date'], dateISO: e.data['date'],
), ),
), ),
// 'day': PlutoCell(
// value: formatDate(
// format: "E",
// dateISO: e.data['date'],
// ),
// ),
'mood': PlutoCell(value: e.data['mood']), 'mood': PlutoCell(value: e.data['mood']),
'comments': PlutoCell(value: e.data['comments']), 'comments': PlutoCell(value: e.data['comments']),
}; };
var hours = <String, PlutoCell>{}; var hours = <String, PlutoCell>{};
for (int i = 0; i < 24; i++) { for (int i = 0; i < 24; i++) {
hours.putIfAbsent( hours.putIfAbsent(
"hours[$i]", () => PlutoCell(value: e.data['hours'][i])); "hours[$i]",
() => PlutoCell(
value: e.data['hours'][i],
),
);
} }
cells.addEntries(hours.entries); cells.addEntries(hours.entries);
@ -159,8 +162,7 @@ class _TablePageState extends State<TablePage> {
child: entries!.isEmpty child: entries!.isEmpty
? Center(child: const CircularProgressIndicator()) ? Center(child: const CircularProgressIndicator())
: Scaffold( : Scaffold(
body: Container( body: InteractiveViewer(
padding: const EdgeInsets.all(0),
child: PlutoGrid( child: PlutoGrid(
columns: columns, columns: columns,
rows: rows, rows: rows,
@ -252,7 +254,7 @@ class SpreadsheetWidget extends StatelessWidget {
List hours = entry?.data['hours']; List hours = entry?.data['hours'];
if (col == 0) { if (col == 0) {
return Center(child: Text('$day')); return Center(child: Text(day));
} else if (col > 0 && col < 25) { } else if (col > 0 && col < 25) {
return Center(child: Text(hours[col - 1].toString())); return Center(child: Text(hours[col - 1].toString()));
} else { } else {

View File

@ -1,5 +1,6 @@
import 'package:ltx_flutter/pages/table_page.dart';
import 'package:ltx_flutter/pages/account_page.dart'; import 'package:ltx_flutter/pages/account_page.dart';
import 'package:ltx_flutter/pages/categories_page.dart';
import 'package:ltx_flutter/pages/today_page.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class TabsPage extends StatefulWidget { class TabsPage extends StatefulWidget {
@ -12,7 +13,7 @@ class TabsPage extends StatefulWidget {
class _TabsPageState extends State<TabsPage> { class _TabsPageState extends State<TabsPage> {
int _selectedIndex = 0; int _selectedIndex = 0;
static const _widgets = [TablePage(), AccountPage()]; static const _widgets = [TodayPage(), CategoriesPage(), AccountPage()];
void _onItemTapped(int index) { void _onItemTapped(int index) {
setState(() { setState(() {
@ -24,13 +25,15 @@ class _TabsPageState extends State<TabsPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text("Lifetracker"), title: Text("LTX Android"),
), ),
body: _widgets.elementAt(_selectedIndex), body: _widgets.elementAt(_selectedIndex),
bottomNavigationBar: BottomNavigationBar( bottomNavigationBar: BottomNavigationBar(
items: const [ items: const [
BottomNavigationBarItem( BottomNavigationBarItem(
icon: Icon(Icons.table_chart_outlined), label: "Tracker"), icon: Icon(Icons.today_outlined), label: "Today"),
BottomNavigationBarItem(
icon: Icon(Icons.category_outlined), label: "Categories"),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: Icon(Icons.account_circle_outlined), label: "Account") icon: Icon(Icons.account_circle_outlined), label: "Account")
], ],

View File

@ -0,0 +1,99 @@
import 'package:ltx_flutter/appwrite/appwrite.dart';
import 'package:ltx_flutter/appwrite/database_api.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:appwrite/models.dart';
import 'package:intl/intl.dart';
class TodayPage extends StatefulWidget {
const TodayPage({Key? key}) : super(key: key);
@override
_TodayPageState createState() => _TodayPageState();
}
class _TodayPageState extends State<TodayPage> {
final database = DatabaseAPI();
AuthStatus authStatus = AuthStatus.uninitialized;
DateTime? date;
Document? entry;
var hours = List<int>.generate(24, (i) => i).map((e) {
var meridien = "AM";
var hour = 12;
if (e > 12) {
hour = e - 12;
} else if (e > 0) {
hour = e;
}
if (e > 11) {
meridien = "PM";
}
return {
"string": "${hour.toString()} $meridien",
"index": e.toInt(),
};
}).toList();
@override
void initState() {
super.initState();
final AuthAPI appwrite = context.read<AuthAPI>();
authStatus = appwrite.status;
loadEntry();
}
loadEntry() async {
try {
final value = await database.getEntries(limit: 1, dateISO: "");
setState(() {
entry = value.documents[0];
});
} catch (e) {
print(e);
}
}
String formatDate({String format = "", String? dateISO}) {
final DateFormat dateFormatter = DateFormat(format);
final date = dateISO!.isEmpty ? DateTime.now() : DateTime.parse(dateISO);
return dateFormatter.format(date);
}
@override
Widget build(BuildContext context) {
return Center(
child: entry == null
? CircularProgressIndicator()
: Column(
children: [
Text(
style: TextStyle(
fontSize: 40,
),
formatDate(
format: "E, MMM dd, yyyy",
dateISO: entry?.data['date'],
),
),
Expanded(
child: ListView(
children: hours.map(
(e) {
int h = e["index"] as int;
List<dynamic> hoursData = entry!.data['hours'];
String hourData = (h >= hoursData.length)
? ""
: hoursData[h].toString();
return ListTile(
leading: Text(e["string"] as String),
title: Text(hourData),
);
},
).toList()),
),
],
),
);
}
}

View File

@ -17,6 +17,7 @@ dependencies:
flutter_svg: ^2.0.6 flutter_svg: ^2.0.6
intl: ^0.18.1 intl: ^0.18.1
pluto_grid: ^7.0.2 pluto_grid: ^7.0.2
string_to_hex: ^0.2.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: