diff --git a/lib/api_form.dart b/lib/api_form.dart index b2626729..186dff56 100644 --- a/lib/api_form.dart +++ b/lib/api_form.dart @@ -1,7 +1,10 @@ import 'dart:convert'; import 'package:InvenTree/api.dart'; - +import 'package:InvenTree/widget/dialogs.dart'; +import 'package:InvenTree/widget/fields.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; /* * Class that represents a single "form field", @@ -24,13 +27,71 @@ class APIFormField { // Is this field read only? bool get readOnly => (data['read_only'] ?? false) as bool; + // Get the "value" as a string (look for "default" if not available) + dynamic get value => (data['value'] ?? data['default']); + + // Get the "default" as a string + dynamic get defaultValue => data['default']; + + // Return the error message associated with this field + String get errorMessage => data['error']; + // Is this field required? bool get required => (data['required'] ?? false) as bool; - String get label => (data['label'] ?? '') as String; + String get type => (data['type'] ?? '').toString(); - String get helpText => (data['help_text'] ?? '') as String; + String get label => (data['label'] ?? '').toString(); + String get helpText => (data['help_text'] ?? '').toString(); + + // Construct a widget for this input + Widget constructField() { + switch (type) { + case "string": + return _constructString(); + case "boolean": + return _constructBoolean(); + default: + return ListTile( + title: Text("Unsupported field type: '${type}'") + ); + } + } + + // Consturct a string input element + Widget _constructString() { + + return TextFormField( + decoration: InputDecoration( + labelText: required ? label + "*" : label, + hintText: helpText, + ), + initialValue: value ?? '', + onSaved: (val) { + data["value"] = val; + print("${name} -> ${val}"); + }, + validator: (value) { + + // TODO - Custom field validation + }, + ); + } + + // Construct a boolean input element + Widget _constructBoolean() { + + return CheckBoxField( + label: label, + hint: helpText, + initial: value, + onSaved: (val) { + data['value'] = val; + print("${name} -> ${val}"); + }, + ); + } } @@ -56,9 +117,15 @@ Map extractFields(dynamic options) { * Launch an API-driven form, * which uses the OPTIONS metadata (at the provided URL) * to determine how the form elements should be rendered! + * + * @param title is the title text to display on the form + * @param url is the API URl to make the OPTIONS request to + * @param fields is a map of fields to display (with optional overrides) + * @param modelData is the (optional) existing modelData + * @param method is the HTTP method to use to send the form data to the server (e.g. POST / PATCH) */ -Future launchApiForm(String url, Map fields, {String method = "PATCH"}) async { +Future launchApiForm(String title, String url, Map fields, {Map modelData = const {}, String method = "PATCH"}) async { dynamic options = await InvenTreeAPI().options(url); @@ -103,12 +170,30 @@ Future launchApiForm(String url, Map fields, {String meth } } + // Update fields with existing model data + for (String key in modelData.keys) { + + dynamic value = modelData[key]; + + if (availableFields.containsKey(key)) { + availableFields[key]['value'] = value; + } + } + formFields.add(APIFormField(fieldName, remoteField)); } + List widgets = []; + for (var ff in formFields) { - print("${ff.name} -> ${ff.label} (${ff.helpText})"); + widgets.add(ff.constructField()); } + final formKey = new GlobalKey(); + + showFormDialog(title, fields: widgets, key: formKey, callback: () { + print("submitted, I guess?"); + }); + return true; } \ No newline at end of file