Browse Source

refactored controllers && added update / create / delete

master
TheNils 3 months ago
parent
commit
47cdb10cf4
  1. 37
      lib/api/api.dart
  2. 48
      lib/api/schema.dart
  3. 53
      lib/components/editFieldModal.dart
  4. 6
      lib/lang/en.dart
  5. 6
      lib/lang/fr.dart
  6. 15
      lib/main.dart
  7. 7
      lib/store/agencyController.dart
  8. 129
      lib/store/itemController.dart
  9. 15
      lib/store/projectController.dart
  10. 7
      lib/store/repportController.dart
  11. 7
      lib/store/stepsController.dart
  12. 157
      lib/store/user.dart
  13. 55
      lib/views/account.dart
  14. 68
      lib/views/accountForm.dart
  15. 25
      lib/views/agency.dart
  16. 101
      lib/views/project.dart

37
lib/api/api.dart

@ -33,7 +33,7 @@ class Api {
};
}
Future<Map<dynamic, dynamic>> handleResponse(
Future<dynamic> handleResponse(
Future<dynamic> response,
String path,
Map<dynamic, dynamic> body,
@ -41,9 +41,15 @@ class Api {
Function(String path, Map<dynamic, dynamic> body, int tryCount)
callback) async {
return await response.then((res) {
var json = jsonDecode(res.body);
var json =
res.body != null && res.body != "" ? jsonDecode(res.body) : null;
if (res.statusCode == 200) {
return json['data'];
if (json != null && json.containsKey("data")) {
return json['data'];
} else {
return {"success": path};
}
} else {
if (tryCount < maxRetryCount) {
return callback(path, body, tryCount + 1);
@ -56,12 +62,12 @@ class Api {
return callback(path, body, tryCount + 1);
} else {
globals.isApiReachabe = false;
return jsonDecode('{"code": 0, "message": "$err"}');
return jsonDecode('{"errors":[{"code": 0, "message": "$err"}]}');
}
});
}
Future<Map<dynamic, dynamic>> post(String path, Map<dynamic, dynamic> body,
Future<dynamic> post(String path, Map<dynamic, dynamic> body,
[int tryCount = 1]) async {
await refreshJwt();
@ -70,7 +76,7 @@ class Api {
return await handleResponse(res, path, body, tryCount, post);
}
Future<Map<dynamic, dynamic>> patch(String path, Map<dynamic, dynamic> body,
Future<dynamic> patch(String path, Map<dynamic, dynamic> body,
[int tryCount = 1]) async {
await refreshJwt();
@ -79,7 +85,7 @@ class Api {
return await handleResponse(res, path, body, tryCount, patch);
}
Future<Map<dynamic, dynamic>> get(String path,
Future<dynamic> get(String path,
[Map<dynamic, dynamic> body = const {}, int tryCount = 1]) async {
await refreshJwt();
@ -90,16 +96,7 @@ class Api {
return await handleResponse(res, path, body, tryCount, get);
}
// Future<Map<dynamic, dynamic>> put(String path, Map<dynamic, dynamic> body,
// [int tryCount = 1]) async {
// await refreshJwt();
// var res = client.put('${globals.apiUrl}$path',
// headers: this.apiHeaders, body: body);
// return await handleResponse(res, path, body, tryCount, put);
// }
Future<Map<dynamic, dynamic>> delete(String path,
Future<dynamic> delete(String path,
[Map<dynamic, dynamic> body = const {}, int tryCount = 1]) async {
await refreshJwt();
@ -132,8 +129,6 @@ class Api {
return authenticate(body, tryCount + 1);
} else {
return {"error": "http", "code": "${res.statusCode}"};
// return json;
}
}
}).catchError((err) {
@ -148,6 +143,10 @@ class Api {
return res;
}
// todo
// Future<dynamic> upload(String fileName) async {
// }
Future<bool> refreshJwt() async {
if (!this.credentials.containsKey('expiresAt')) {
return false;

48
lib/api/schema.dart

@ -1,13 +1,25 @@
Map<dynamic, dynamic> userSchema = {
"projects": {
"project_id": {
"collection": "project",
"kind": "M2M",
"customers": {"collection": "directus_users", "kind": "M2M"},
"agencies": {"collection": "agency", "kind": "M2M"},
"steps": {
"collection": "steps",
"kind": "O2M",
"repports": {"collection": "repport", "kind": "O2M"}
}
},
"agencies": {
"collection": "agency",
"employees": {"collection": "directus_users"},
"projects": {
"collection": "project",
"kind": "M2M",
"customers": {
"directu_users_id": {"collection": "directus_users", "kind": "M2M"}
},
"customers": {"collection": "directus_users", "kind": "M2M"},
"agencies": {
"agency_id": {"collection": "agency", "kind": "M2M"}
"collection": "agency",
"kind": "M2M",
},
"steps": {
"collection": "steps",
@ -15,31 +27,5 @@ Map<dynamic, dynamic> userSchema = {
"repports": {"collection": "repport", "kind": "O2M"}
}
}
},
"agencies": {
"agency_id": {
"collection": "agency",
"employees": {"collection": "directus_users"},
"projects": {
"project_id": {
"collection": "project",
"kind": "M2M",
"customers": {
"directus_users_id": {"collection": "directus_users", "kind": "M2M"}
},
"agencies": {
"agency_id": {
"collection": "agency",
"kind": "M2M",
},
},
"steps": {
"collection": "steps",
"kind": "O2M",
"repports": {"collection": "repport", "kind": "O2M"}
}
}
}
}
}
};

53
lib/components/editFieldModal.dart

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class EditFieldModal extends StatelessWidget {
const EditFieldModal({
Key key,
@required this.field,
@required this.controller,
}) : super(key: key);
final String field;
final controller;
@override
Widget build(BuildContext context) {
TextEditingController fieldController = TextEditingController();
return AlertDialog(
title: Text('edit-field'.trParams({'field': field.tr})),
content: Obx(() {
var project = controller.getItemById(Get.parameters['id']);
fieldController.text = project[field] != null ? project[field] : "";
return SizedBox(
width: Get.width * 0.8,
height: Get.height * 0.3,
child: Center(
child: Column(
children: [
TextFormField(
autofocus: true,
controller: fieldController,
),
SizedBox(height: 50),
OutlinedButton(
onPressed: () async {
if (project[field] != fieldController.text) {
await controller.patch({
'id': Get.parameters['id'],
field: fieldController.text,
});
}
Get.back();
},
child: Text('save'.tr),
)
],
),
),
);
}),
);
}
}

6
lib/lang/en.dart

@ -4,8 +4,14 @@ Map<String, String> en = {
'signup': 'Sign up',
'my-account': 'My Account',
'my-projects': 'My Projects',
'my-agencies': 'My Agencies',
'see': 'See',
'edit': 'Edit',
'edit-field': 'Edit @field',
'save': 'Save',
'delete': 'Delete',
'first-name': 'First name',
'test-create': 'create a report in a step',
'title': 'title',
'steps': 'Steps',
};

6
lib/lang/fr.dart

@ -4,8 +4,14 @@ Map<String, String> fr = {
'signup': 'Créer un compte',
'my-account': 'Mon compte',
'my-projects': 'Mes Projets',
'my-agencies': 'Mes Agences',
'see': 'Voir',
'edit': 'Modifier',
'edit-field': 'Modifier @field',
'save': 'Sauvegarder',
'delete': 'Supprimer',
'first-name': 'Prénom',
'test-create': 'creer un rapport dans un compte rendu',
'title': 'titre',
'steps': 'Etapes',
};

15
lib/main.dart

@ -10,6 +10,8 @@ import 'package:youtribe_lib/lang/fr.dart';
import 'package:youtribe_lib/views/login/login.dart';
import 'package:youtribe_lib/views/account.dart';
import 'package:youtribe_lib/views/accountForm.dart';
import 'package:youtribe_lib/views/agency.dart';
import 'package:youtribe_lib/views/project.dart';
// translation
class Messages extends Translations {
@ -33,11 +35,14 @@ class NavMap extends StatelessWidget {
locale: globals.locale,
fallbackLocale: globals.fallbackLocale,
translations: Messages(),
routes: {
'/': (context) => Login(),
'/account': (context) => Account(),
'/account_form': (context) => AccountForm(),
},
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => Login()),
GetPage(name: '/account', page: () => Account()),
GetPage(name: '/account_form', page: () => AccountForm()),
GetPage(name: '/agency', page: () => Agency()),
GetPage(name: '/project', page: () => Project()),
],
);
}
}

7
lib/store/agencyController.dart

@ -0,0 +1,7 @@
import 'package:youtribe_lib/store/itemController.dart';
class AgencyController extends ItemsController {
AgencyController() : super("agency", fields: ["*.*"]) {
print("projectController init...");
}
}

129
lib/store/itemController.dart

@ -0,0 +1,129 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:get/get.dart';
import 'package:get/state_manager.dart';
import 'package:youtribe_lib/api/api.dart';
import 'package:localstorage/localstorage.dart';
// import 'package:youtribe_lib/globals.dart';
class ItemsController extends GetxController {
final _api = Api();
var _localStorage;
String _collection;
List<String> _fields;
String order;
bool _storeLocally;
final _items = Rx<List<dynamic>>([]);
final isFeeded = RxBool(false);
ItemsController(
String collection, {
bool storeLocally = true,
List<String> fields = const ["*"],
String orderBy = "id",
}) {
_storeLocally = storeLocally;
_collection = collection;
_fields = fields;
}
get items => _items();
set items(payload) {
_items(payload);
_localStorage.setItem(_collection, jsonEncode(items));
}
get itemsMappedOnId =>
Map.fromIterable(items, key: (v) => v["id"], value: (v) => v);
getItemById(id) => _items().firstWhere((item) => item['id'] == id);
getItemsByIds(List<dynamic> ids) =>
_items().where((item) => ids.contains(item['id'])).toList();
@override
@mustCallSuper
void onInit() async {
if (_storeLocally) {
_localStorage = new LocalStorage("$_collection-store");
var localItems = await _localStorage.getItem(_collection);
if (localItems != null) {
items = jsonDecode(localItems);
}
}
if (_items().length > 0) {
isFeeded(true);
}
//todo
//check if api is resumed, if so fetch data
fetch();
//todo
// else display snackbar
super.onInit();
}
Future<bool> fetch() async {
var res = await _api.get("/items/$_collection?fields=${_fields.join(",")}");
var isOk = handleResponse(res);
if (isOk) {
items = res;
_items.refresh();
}
isFeeded(true);
return isOk;
}
Future<bool> create(Map<dynamic, dynamic> item) async {
var res = await _api.post("/items/$_collection", item);
var isOk = handleResponse(res);
if (isOk) {
_items().add(res);
_items.refresh();
}
return isOk;
}
Future<bool> delete(dynamic id) async {
var res = await _api.delete("/items/$_collection/$id");
var isOk = handleResponse(res);
if (isOk) {
_items().removeWhere((element) => element["id"] == id);
_items.refresh();
}
return isOk;
}
Future<bool> patch(Map<dynamic, dynamic> item) async {
var sanitized = {...item};
sanitized.remove("id");
var res = await _api.patch("/items/$_collection/${item["id"]}", sanitized);
var isOk = handleResponse(res);
if (isOk) {
var itemIndex = _items().indexWhere((value) => value["id"] == item["id"]);
_items()[itemIndex] = {..._items()[itemIndex], ...item};
_items.refresh();
}
return isOk;
}
bool handleResponse(res) {
if (res is List) {
return true;
} else if (res is Map && res.containsKey("errors")) {
Get.snackbar("Error", res["errors"][0]["message"],
snackPosition: SnackPosition.BOTTOM, colorText: Colors.red);
return false;
} else if (res is Map) {
return true;
} else {
return false;
}
}
}

15
lib/store/projectController.dart

@ -0,0 +1,15 @@
import 'package:youtribe_lib/store/itemController.dart';
class ProjectController extends ItemsController {
ProjectController()
: super("project", fields: [
'*',
'agencies.agency_id',
'customer.directus_users_id',
'documents.directus_files_id',
'initial_shots.directus_files_id',
'steps.id',
]) {
print("projectController init...");
}
}

7
lib/store/repportController.dart

@ -0,0 +1,7 @@
import 'package:youtribe_lib/store/itemController.dart';
class RepportController extends ItemsController {
RepportController() : super("repport", fields: ["*.*"]) {
print("projectController init...");
}
}

7
lib/store/stepsController.dart

@ -0,0 +1,7 @@
import 'package:youtribe_lib/store/itemController.dart';
class StepsController extends ItemsController {
StepsController() : super("steps", fields: ["*.*"]) {
print("projectController init...");
}
}

157
lib/store/user.dart

@ -1,27 +1,21 @@
import 'dart:convert';
import 'package:get/get.dart';
import 'package:deeply/deeply.dart';
import 'package:get/state_manager.dart';
import 'package:youtribe_lib/api/api.dart';
import 'package:localstorage/localstorage.dart';
import 'package:youtribe_lib/api/schema.dart';
class UserController extends GetxController {
final _localStorage = new LocalStorage('user-store');
final _api = Api();
final _user = Rx<Map<dynamic, dynamic>>({}).obs;
Map<dynamic, dynamic> patchStatuses;
get user => _user().value;
set user(payload) {
// patch(payload);
_user(Rx({..._user().value, ...payload}));
_localStorage.setItem('user', jsonEncode(user));
}
get projects => _user().value['projects'].map((p) => p['project_id']);
Future<Map<dynamic, dynamic>> authenticate(
Map<String, String> payload) async {
var res = await _api.authenticate(payload);
@ -56,115 +50,19 @@ class UserController extends GetxController {
Future<Map<dynamic, dynamic>> fetch() async {
var res = await _api.get('''
/users/me?fields=*,
agencies.agency_id.*,
agencies.agency_id.projects.project_id.*,
agencies.agency_id.projects.project_id.customers.*,
agencies.agency_id.projects.project_id.steps.*,
agencies.agency_id.projects.project_id.steps.repports.*,
projects.project_id.*,
projects.project_id.agencies.agency_id.*,
projects.project_id.initial_shots.*,
projects.project_id.steps.*,
projects.project_id.steps.repports.*
agencies.agency_id,
projects.project_id
'''
.trim()
.replaceAll("\n", "")
.replaceAll(" ", ""));
if (!res.containsKey('error')) {
if (!res.containsKey('errors')) {
user = res;
}
return user;
}
Future<bool> delete(String id, String collection) async {
var res = await _api.delete("/items/$collection/$id");
if (!res.containsKey('error')) {
_deleteInCollection(collection, id);
return true;
} else {
return false;
}
}
void _deleteInCollection(String collection, String id,
[List<String> startPath]) {
var currentPath = startPath != null ? startPath : [];
var subSchema = userSchema;
currentPath.forEach((value) {
subSchema = subSchema[value];
});
List<String> reserved = ["collection", "kind"];
subSchema.forEach((key, value) {
if (!reserved.contains(key) && value is Map) {
_deleteInCollection(collection, id, [
...currentPath,
...[key]
]);
if (value.containsKey("collection")) {
if (value["collection"] == collection) {
updateDeeply(currentPath, _user().value, (value) {
if (value is List) {
var index =
value.indexWhere((element) => element[key]["id"] == id);
value.removeAt(index);
}
});
}
}
}
});
}
Future<bool> create(Map<dynamic, dynamic> item, String collection) async {
var res = await _api.post("/items/$collection", item);
if (!res.containsKey('error')) {
print(res);
_createInCollection(collection, res);
print(_user().value);
return true;
} else {
return false;
}
}
void _createInCollection(String collection, Map<dynamic, dynamic> item,
[List<String> startPath]) {
var currentPath = startPath != null ? startPath : [];
var subSchema = userSchema;
currentPath.forEach((value) {
subSchema = subSchema[value];
});
List<String> reserved = ["collection", "kind"];
subSchema.forEach((key, value) {
if (!reserved.contains(key) && value is Map) {
_updateInCollection(collection, item, [
...currentPath,
...[key]
]);
if (value.containsKey("collection")) {
if (value["collection"] == collection) {
updateDeeply(currentPath, _user().value, (value) {
if (value is List) {
value.add({key: item});
} else {
value = [item];
}
});
}
}
}
});
}
Future<bool> patch(Map<dynamic, dynamic> item, {String collection}) async {
var id = item.containsKey("id") ? item["id"] : null;
item.remove("id");
@ -172,49 +70,16 @@ class UserController extends GetxController {
collection != null ? "/items/$collection/$id" : "/users/me";
var res = await _api.patch(computedPath, item);
if (!res.containsKey('error')) {
_updateInCollection(collection, {
...item,
...{"id": id}
});
return handleResponse(res);
}
bool handleResponse(res) {
if (!res.containsKey('errors')) {
return true;
} else {
Get.snackbar("Error", res["errors"][0]["message"],
snackPosition: SnackPosition.BOTTOM);
return false;
}
}
void _updateInCollection(String collection, Map<dynamic, dynamic> item,
[List<String> startPath]) {
var currentPath = startPath != null ? startPath : [];
var subSchema = userSchema;
currentPath.forEach((value) {
subSchema = subSchema[value];
});
List<String> reserved = ["collection", "kind"];
subSchema.forEach((key, value) {
if (!reserved.contains(key) && value is Map) {
_updateInCollection(collection, item, [
...currentPath,
...[key]
]);
if (value.containsKey("collection")) {
if (value["collection"] == collection) {
updateDeeply(currentPath, _user().value, (value) {
if (value is List) {
var index = value
.indexWhere((element) => element[key]["id"] == item["id"]);
value[index][key] = {...value[index][key], ...item};
} else if (value != null) {
value = {...value, ...item};
}
});
}
}
}
});
}
}

55
lib/views/account.dart

@ -1,9 +1,13 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:youtribe_lib/store/agencyController.dart';
import 'package:youtribe_lib/store/projectController.dart';
import 'package:youtribe_lib/store/user.dart';
class Account extends StatelessWidget {
final UserController userController = Get.put(UserController());
final AgencyController agencyController = Get.put(AgencyController());
final ProjectController projectController = Get.put(ProjectController());
@override
Widget build(BuildContext context) {
@ -15,6 +19,7 @@ class Account extends StatelessWidget {
child: Obx(
() => (Column(
children: [
Title(color: Colors.blue, child: Text("my-account".tr)),
Text('id: ${userController.user['id']}'),
Text('first name: ${userController.user['first_name']}'),
Text('last name: ${userController.user['last_name']}'),
@ -23,10 +28,60 @@ class Account extends StatelessWidget {
onPressed: () => Get.toNamed('/account_form'),
child: Text('edit'.tr),
),
SizedBox(height: 50),
if (agencyController.items.length > 0) ...myAgencies(),
if (projectController.items.length > 0) ...myProjects(),
],
)),
),
),
);
}
List<Widget> myAgencies() {
return [
Title(color: Colors.blue, child: Text('my-agencies'.tr)),
SizedBox(
height: 100,
child: ListView.builder(
itemCount: agencyController.items.length,
itemBuilder: (ctx, index) {
return ListTile(
title: Text(agencyController.items[index]["title"]),
subtitle: Text(agencyController.items[index]["addresse"]),
);
}),
),
];
}
List<Widget> myProjects() {
return [
Title(color: Colors.blue, child: Text('my-projects'.tr)),
SizedBox(
height: 200,
child: ListView.builder(
itemCount: projectController.items.length,
itemBuilder: (ctx, index) {
return Card(
child: Column(children: [
ListTile(
title: Text(projectController.items[index]["title"]),
subtitle: Text(projectController.items[index]["adresse"]),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Get.offAllNamed(
'/project?id=${projectController.items[index]["id"]}'),
child: Text('see'.tr))
],
)
]),
);
}),
),
];
}
}

68
lib/views/accountForm.dart

@ -1,5 +1,9 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:youtribe_lib/store/agencyController.dart';
import 'package:youtribe_lib/store/projectController.dart';
import 'package:youtribe_lib/store/repportController.dart';
import 'package:youtribe_lib/store/stepsController.dart';
import 'package:youtribe_lib/store/user.dart';
class AccountForm extends StatefulWidget {
@ -9,6 +13,11 @@ class AccountForm extends StatefulWidget {
class AccountFormState extends State<AccountForm> {
final userController = Get.put(UserController());
final projectController = Get.put(ProjectController());
final stepsController = Get.put(StepsController());
final agencyController = Get.put(AgencyController());
final repportController = Get.put(RepportController());
final _formKey = GlobalKey<FormState>();
String email;
@ -17,49 +26,32 @@ class AccountFormState extends State<AccountForm> {
@override
Widget build(BuildContext context) {
// var repportList;
return Scaffold(
appBar: AppBar(
title: Text('my-account'.tr),
),
body: Center(
child: FractionallySizedBox(
widthFactor: 0.5,
child: Obx(
() => (Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('my-account'.tr),
TextFormField(
decoration: InputDecoration(labelText: 'first-name'.tr),
initialValue: userController.user['first_name'],
onChanged: (value) => firstName = value,
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () => userController.patch(
{"id": 2, "title": "bbb"},
collection: "agency"),
// userController.patch({'first_name': firstName}),
child: Text('save'.tr),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () => userController.create({
"picture": "acef13dc-5e11-48f8-9273-f0471a83a16d",
"step": "383dd1c2-3fd1-40d1-9b45-5f7639075c16",
"title": "test api create",
"client_thoughts": "client thoughts",
"provider_thoughts": "provider thoughts"
}, "repport"),
child: Text('test-create'.tr),
),
],
),
)),
),
child: Obx(
() => (Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('my-account'.tr),
TextFormField(
decoration: InputDecoration(labelText: 'first-name'.tr),
initialValue: userController.user['first_name'],
onChanged: (value) => firstName = value,
),
SizedBox(height: 16),
SizedBox(height: 16),
SizedBox(height: 16),
],
),
)),
),
));
}

25
lib/views/agency.dart

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:youtribe_lib/store/agencyController.dart';
import 'package:get/get.dart';
import 'package:youtribe_lib/store/stepsController.dart';
class Agency extends StatelessWidget {
final AgencyController agencyController = Get.put(AgencyController());
final StepsController stepsController = Get.put(StepsController());
@override
Widget build(BuildContext ctx) {
return Scaffold(
appBar: AppBar(
title: Text('my-account'.tr),
),
body: Center(
child: Obx(() {
var agency = agencyController.itemsMappedOnId()[Get.parameters['id']];
return Column(children: [Text(agency.title)]);
}),
),
);
}
}

101
lib/views/project.dart

@ -0,0 +1,101 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:youtribe_lib/components/editFieldModal.dart';
// import 'package:youtribe_lib/store/itemController.dart';
import 'package:youtribe_lib/store/projectController.dart';
import 'package:youtribe_lib/store/stepsController.dart';
class Project extends StatelessWidget {
final ProjectController projectController = Get.put(ProjectController());
final StepsController stepsController = Get.put(StepsController());
@override
Widget build(BuildContext ctx) {
return Scaffold(
appBar: AppBar(
title: Text('my-projects'.tr),
),
body: Center(
child: Obx(() {
if (!projectController.isFeeded()) {
return Text('loading ...');
} else {
var project = projectController.getItemById(Get.parameters['id']);
var steps = stepsController
.getItemsByIds(project['steps'].map((_) => _['id']).toList());
return ListView(
children: [
Center(
heightFactor: 3,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(project['title']),
TextButton(
onPressed: () => Get.dialog(EditFieldModal(
field: "title", controller: projectController)),
child: Icon(Icons.edit)),
],
)),
Center(
heightFactor: 3,
child: Text(project["adresse"]),
),
Divider(
color: Colors.blue,
height: 2,
),
if (steps.length > 0) ...projectSteps(),
OutlinedButton(
onPressed: () async {
await stepsController.create({
'description': '<p>description created in app</p>',
'title': 'title created in app',
'project': project['id']
});
projectController.fetch();
},
child: Text('Create new step'),
)
],
);
}
}),
),
);
}
List<Widget> projectSteps() {
return [
Title(
color: Colors.blue,
child: Text('steps'.tr),
),
SizedBox(
height: 400,
child: Obx(() {
var project = projectController.getItemById(Get.parameters['id']);
var steps = stepsController
.getItemsByIds(project['steps'].map((_) => _['id']).toList());
return ListView.builder(
itemCount: steps.length,
itemBuilder: (ctx, index) {
return Card(
child: Column(
children: [
ListTile(title: Text(steps[index]["title"])),
TextButton(
onPressed: () =>
stepsController.delete(steps[index]['id']),
child: Text('delete'.tr))
],
),
);
},
);
}),
)
];
}
}
Loading…
Cancel
Save