Skip to content

Commit

Permalink
Merge pull request #47 from jogboms/develop
Browse files Browse the repository at this point in the history
feat: 0.2.0 (#46)
  • Loading branch information
jogboms authored Jul 15, 2018
2 parents 507f92f + 5ee44d8 commit 0724a8f
Show file tree
Hide file tree
Showing 64 changed files with 1,015 additions and 468 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"type": "dart",
"request": "launch",
"args": [
"--flavor=development"
"--flavor=dev"
],
"program": "lib/main.dart"
}]
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ For a full description of OSS used, see pubspec.yaml
<img src="./screenshots/ss20.png" width="200" />
</td>
</tr>
<tr>
<td style="text-align: center">
<img src="./screenshots/ss21.png" width="200" />
</td>
<td style="text-align: center">
<img src="./screenshots/ss22.png" width="200" />
</td>
</tr>
</table>
</div>

Expand Down
9 changes: 7 additions & 2 deletions TASKS.todo
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
PENDING:
☐ remodel w/ authID
☐ delete-able payment
☐ delete-able payment
☐ delete-able images
☐ implement share payment + image
☐ filter + search jobs & contacts
☐ create a new contact from phone contact
☐ Collect account details on account creation
☐ Generate invoice & share as SMS containing link to pay on paystack
☐ Firebase function on successful payments w/ push notification

COMPLETED:
✔ remodel w/ authID @done(18-07-15 14:43)
✔ scope storage path to accounts @high @done(18-07-13 16:19)
✔ update stats db on creation @high @done(18-07-10 16:39)
✔ add more flesh to payment view @done(18-07-10 15:36)
✔ navigate to job after creation @high @done(18-07-10 15:23)
Expand Down
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ android {
flavorDimensions "flavor-type"

productFlavors {
development {
dev {
dimension "flavor-type"
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
}
production {
prod {
dimension "flavor-type"
}
}
Expand Down
62 changes: 46 additions & 16 deletions lib/models/stats.dart
Original file line number Diff line number Diff line change
@@ -1,27 +1,47 @@
class DetailStatsModel {
import 'package:tailor_made/models/main.dart';

class IntStatsModel extends Model {
int total;
int pending;
int completed;

DetailStatsModel.fromJson(Map<String, dynamic> json) {
IntStatsModel.fromJson(Map<String, dynamic> json) {
assert(json != null);
total = json["total"];
pending = json["pending"];
completed = json["completed"];
total = int.tryParse(json["total"].toString());
pending = int.tryParse(json["pending"].toString());
completed = int.tryParse(json["completed"].toString());
}

@override
toMap() {
return {
"total": total,
"pending": pending,
"completed": completed,
};
}
}

class PaymentStatsModel {
class DoubleStatsModel extends Model {
double total;
double pending;
double completed;

PaymentStatsModel.fromJson(Map<String, dynamic> json) {
DoubleStatsModel.fromJson(Map<String, dynamic> json) {
assert(json != null);
total = double.tryParse(json["total"].toString());
pending = double.tryParse(json["pending"].toString());
completed = double.tryParse(json["completed"].toString());
}

@override
toMap() {
return {
"total": total,
"pending": pending,
"completed": completed,
};
}
}

// KEEP
Expand All @@ -44,17 +64,27 @@ class PaymentStatsModel {
// },
// }

class StatsModel {
DetailStatsModel jobs;
DetailStatsModel contacts;
DetailStatsModel gallery;
PaymentStatsModel payments;
class StatsModel extends Model {
IntStatsModel jobs;
IntStatsModel contacts;
IntStatsModel gallery;
DoubleStatsModel payments;

StatsModel.fromJson(Map<String, dynamic> json) {
assert(json != null);
jobs = DetailStatsModel.fromJson(json['jobs'].cast<String, dynamic>());
contacts = DetailStatsModel.fromJson(json['contacts'].cast<String, dynamic>());
gallery = DetailStatsModel.fromJson(json['gallery'].cast<String, dynamic>());
payments = PaymentStatsModel.fromJson(json['payments'].cast<String, dynamic>());
jobs = IntStatsModel.fromJson(json['jobs'].cast<String, dynamic>());
contacts = IntStatsModel.fromJson(json['contacts'].cast<String, dynamic>());
gallery = IntStatsModel.fromJson(json['gallery'].cast<String, dynamic>());
payments = DoubleStatsModel.fromJson(json['payments'].cast<String, dynamic>());
}

@override
toMap() {
return {
"jobs": jobs.toMap(),
"contacts": contacts.toMap(),
"gallery": gallery.toMap(),
"payments": payments.toMap(),
};
}
}
174 changes: 85 additions & 89 deletions lib/pages/contacts/contact.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:tailor_made/models/contact.dart';
import 'package:tailor_made/models/job.dart';
import 'package:tailor_made/pages/contacts/ui/contact_appbar.dart';
import 'package:tailor_made/pages/contacts/ui/contact_gallery_grid.dart';
import 'package:tailor_made/pages/contacts/ui/contact_jobs_list.dart';
import 'package:tailor_made/pages/contacts/ui/contact_payments_list.dart';
import 'package:tailor_made/services/cloudstore.dart';
import 'package:tailor_made/redux/states/main.dart';
import 'package:tailor_made/redux/view_models/contacts.dart';
import 'package:tailor_made/services/cloud_db.dart';
import 'package:tailor_made/ui/tm_loading_spinner.dart';
import 'package:tailor_made/utils/tm_theme.dart';

Expand All @@ -29,102 +32,95 @@ class _ContactState extends State<ContactPage> {
Widget build(BuildContext context) {
final TMTheme theme = TMTheme.of(context);

return new Scaffold(
backgroundColor: theme.scaffoldColor,
body: new DefaultTabController(
length: 3,
child: new NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
new SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
child: new SliverAppBar(
backgroundColor: kAccentColor,
automaticallyImplyLeading: false,
title: new ContactAppBar(contact: widget.contact),
pinned: true,
titleSpacing: 0.0,
centerTitle: false,
brightness: Brightness.dark,
expandedHeight: 0.0,
forceElevated: true,
bottom: tabTitles(),
),
),
];
},
body: StreamBuilder(
stream: Cloudstore.jobs.where("contactID", isEqualTo: widget.contact.id).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: loadingSpinner(),
);
}
// TODO Maybe a clean up here
return StoreConnector<ReduxState, ContactsViewModel>(
converter: (store) => ContactsViewModel(store)..contactID = widget.contact.id,
builder: (BuildContext context, ContactsViewModel vm) {
final contact = vm.selected;
return new DefaultTabController(
length: 3,
child: new Scaffold(
backgroundColor: theme.scaffoldColor,
appBar: new AppBar(
backgroundColor: kAccentColor,
automaticallyImplyLeading: false,
title: new ContactAppBar(contact: contact),
titleSpacing: 0.0,
centerTitle: false,
brightness: Brightness.dark,
bottom: tabTitles(),
),
// TODO could maybe refactor this as well
body: new StreamBuilder(
stream: CloudDb.jobs.where("contactID", isEqualTo: contact.id).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: loadingSpinner(),
);
}

List<DocumentSnapshot> list = snapshot.data.documents;
List<DocumentSnapshot> list = snapshot.data.documents;

final jobs = list.map((item) => JobModel.fromDoc(item)).toList();
final jobs = list.map((item) => JobModel.fromDoc(item)).toList();

return new TabBarView(
children: [
tabView(
name: TABS[0].toLowerCase(),
child: JobsListWidget(contact: widget.contact),
),
tabView(
name: TABS[1].toLowerCase(),
child: GalleryGridWidget(contact: widget.contact, jobs: jobs),
),
tabView(
name: TABS[2].toLowerCase(),
child: PaymentsListWidget(contact: widget.contact, jobs: jobs),
),
],
);
},
return new TabBarView(
children: [
tabView(
name: TABS[0].toLowerCase(),
child: JobsListWidget(contact: contact),
),
tabView(
name: TABS[1].toLowerCase(),
child: GalleryGridWidget(contact: contact, jobs: jobs),
),
tabView(
name: TABS[2].toLowerCase(),
child: PaymentsListWidget(contact: contact, jobs: jobs),
),
],
);
},
),
),
);
},
);
}

Widget tabTitles() {
return PreferredSize(
child: Container(
child: TabBar(
labelStyle: ralewayMedium(14.0),
tabs: [
Tab(child: Text(TABS[0])),
Tab(child: Text(TABS[1])),
Tab(child: Text(TABS[2])),
],
),
),
preferredSize: Size.fromHeight(kTextTabBarHeight),
);
}
}

Widget tabTitles() {
return PreferredSize(
child: Container(
child: TabBar(
labelStyle: ralewayMedium(14.0),
tabs: [
Tab(child: Text(TABS[0])),
Tab(child: Text(TABS[1])),
Tab(child: Text(TABS[2])),
],
Widget tabView({name: String, child: Widget}) {
return new SafeArea(
top: false,
bottom: true,
child: new Builder(
builder: (BuildContext context) {
return new CustomScrollView(
key: new PageStorageKey<String>(name),
slivers: <Widget>[
new SliverPadding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
sliver: child,
),
],
);
},
),
),
preferredSize: Size.fromHeight(kTextTabBarHeight),
);
}

Widget tabView({name: String, child: Widget}) {
return new SafeArea(
top: false,
bottom: true,
child: new Builder(
builder: (BuildContext context) {
return new CustomScrollView(
key: new PageStorageKey<String>(name),
slivers: <Widget>[
new SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
new SliverPadding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
sliver: child,
),
],
);
},
),
);
);
}
}
10 changes: 7 additions & 3 deletions lib/pages/contacts/contacts_create.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:tailor_made/models/contact.dart';
import 'package:tailor_made/pages/contacts/contact.dart';
import 'package:tailor_made/pages/contacts/ui/contact_form.dart';
import 'package:tailor_made/pages/contacts/ui/contact_measure.dart';
import 'package:tailor_made/services/cloudstore.dart';
import 'package:tailor_made/services/cloud_db.dart';
import 'package:tailor_made/ui/app_bar.dart';
import 'package:tailor_made/utils/tm_confirm_dialog.dart';
import 'package:tailor_made/utils/tm_navigate.dart';
Expand Down Expand Up @@ -57,16 +57,20 @@ class _ContactsCreatePageState extends State<ContactsCreatePage> with SnackBarPr
key: _formKey,
contact: contact,
onHandleSubmit: _handleSubmit,
scaffoldKey: scaffoldKey,
onHandleValidate: _handleValidate,
),
);
}

void _handleValidate() async {
showInSnackBar('Please fix the errors in red before submitting.');
}

void _handleSubmit(ContactModel contact) async {
showLoadingSnackBar();

try {
final ref = Cloudstore.contacts.document(contact.id);
final ref = CloudDb.contactsRef.document(contact.id);
await ref.setData(contact.toMap());

ref.snapshots().listen((snap) async {
Expand Down
Loading

0 comments on commit 0724a8f

Please sign in to comment.