diff --git a/one_trip/lib/api/models/list.dart b/one_trip/lib/api/models/list.dart index 5b60843..0d58b8c 100644 --- a/one_trip/lib/api/models/list.dart +++ b/one_trip/lib/api/models/list.dart @@ -9,12 +9,10 @@ import 'package:one_trip/api/models/recipeingredient.dart'; class ShoppingList { List ingredients; - int updates; int homegroup; ShoppingList({ required this.ingredients, - required this.updates, required this.homegroup, }); @@ -25,9 +23,7 @@ class ShoppingList { } return ShoppingList( - ingredients: ingredients, - updates: json["updates"] as int, - homegroup: json["homegroup"] as int); + ingredients: ingredients, homegroup: json["homegroup"] as int); } static Future get(int id) async { @@ -72,8 +68,8 @@ class ShoppingList { bool anySuccesses = false; for (RecipeIngredient ingredient in recipe.ingredients) { - ListIngredient? newIngredient = - await ListIngredient.create(ingredient.name, homegroup); + ListIngredient? newIngredient = await ListIngredient.create( + homegroup, ingredient.name, ingredient.quantity); if (newIngredient != null) { anySuccesses = true; @@ -81,7 +77,7 @@ class ShoppingList { } if (anySuccesses) { - return patch(updates: updates + 1); + return get(homegroup); } return null; @@ -98,7 +94,7 @@ class ShoppingList { } if (anySuccess) { - return patch(updates: updates + 1); + return get(homegroup); } return null; diff --git a/one_trip/lib/api/models/listingredient.dart b/one_trip/lib/api/models/listingredient.dart index 8ad23a3..4e1dadf 100644 --- a/one_trip/lib/api/models/listingredient.dart +++ b/one_trip/lib/api/models/listingredient.dart @@ -7,12 +7,14 @@ import 'package:http/http.dart' as http; class ListIngredient { int id; String name; + String? quantity; int list; bool inCart; ListIngredient({ required this.id, required this.name, + required this.quantity, required this.list, required this.inCart, }); @@ -21,21 +23,33 @@ class ListIngredient { return ListIngredient( id: json["id"] as int, name: json["name"] as String, + quantity: json["quantity"] as String?, list: json["list"] as int, inCart: json["in_cart"] as bool, ); } - static Future create(String name, int list) async { + static Future create( + int list, String name, String? quantity) async { const String requestURL = "$baseURL/api/listingredients/"; String token = TokenSingleton().getToken(); + + Map body = { + "name": name, + "list": list, + }; + + if (quantity != null) { + body["quantity"] = quantity; + } + http.Response response = await http.post( Uri.parse(requestURL), - headers: {"Authorization": "Token $token"}, - body: { - "name": name, - "list": "$list", + headers: { + "Authorization": "Token $token", + "Content-Type": "application/json", }, + body: jsonEncode(body), ); if (response.statusCode == 201) { @@ -45,21 +59,27 @@ class ListIngredient { } } - Future patch({String? name, bool? inCart}) async { + Future patch( + {String? name, String? quantity, bool? inCart}) async { String requestURL = "$baseURL/api/listingredients/$id/"; String token = TokenSingleton().getToken(); - Map body = {}; + Map body = {"quantity": quantity ?? this.quantity}; + if (name != null) { body["name"] = name; } if (inCart != null) { - body["in_cart"] = "$inCart"; + body["in_cart"] = inCart; } http.Response response = await http.patch(Uri.parse(requestURL), - headers: {"Authorization": "Token $token"}, body: body); + headers: { + "Authorization": "Token $token", + "Content-Type": "application/json", + }, + body: jsonEncode(body)); if (response.statusCode == 200) { return ListIngredient.fromJson(jsonDecode(response.body)); @@ -86,8 +106,9 @@ class ListIngredient { other is ListIngredient && other.id == id && other.name == name && + other.quantity == quantity && other.inCart == inCart; @override - int get hashCode => Object.hash(id, name, inCart); + int get hashCode => Object.hash(id, name, quantity, inCart); } diff --git a/one_trip/lib/api/models/recipeingredient.dart b/one_trip/lib/api/models/recipeingredient.dart index 8fe2258..f85e9df 100644 --- a/one_trip/lib/api/models/recipeingredient.dart +++ b/one_trip/lib/api/models/recipeingredient.dart @@ -7,11 +7,13 @@ import 'package:http/http.dart' as http; class RecipeIngredient { int id; String name; + String? quantity; int recipe; RecipeIngredient({ required this.id, required this.name, + required this.quantity, required this.recipe, }); @@ -19,20 +21,32 @@ class RecipeIngredient { return RecipeIngredient( id: json["id"] as int, name: json["name"] as String, + quantity: json["quantity"] as String?, recipe: json["recipe"] as int, ); } - static Future create(String name, int recipeID) async { + static Future create( + int recipeID, String name, String? quantity) async { const String requestURL = "$baseURL/api/recipeingredients/"; String token = TokenSingleton().getToken(); + + Map body = { + "name": name, + "recipe": recipeID, + }; + + if (quantity != null) { + body["quantity"] = quantity; + } + http.Response response = await http.post( Uri.parse(requestURL), - headers: {"Authorization": "Token $token"}, - body: { - "name": name, - "recipe": "$recipeID", + headers: { + "Authorization": "Token $token", + "Content-Type": "application/json", }, + body: jsonEncode(body), ); if (response.statusCode == 201) { @@ -42,12 +56,22 @@ class RecipeIngredient { } } - Future patch(String name) async { + Future patch({String? name, String? quantity}) async { + Map body = {"quantity": quantity}; + + if (name != null) { + body["name"] = name; + } + String requestURL = "$baseURL/api/recipeingredients/$id/"; String token = TokenSingleton().getToken(); http.Response response = await http.patch(Uri.parse(requestURL), - headers: {"Authorization": "Token $token"}, body: {"name": name}); + headers: { + "Authorization": "Token $token", + "Content-Type": "application/json", + }, + body: jsonEncode(body)); if (response.statusCode == 200) { return RecipeIngredient.fromJson(jsonDecode(response.body)); diff --git a/one_trip/lib/pages/list_page/list_page.dart b/one_trip/lib/pages/list_page/list_page.dart index 3377412..41d2aef 100644 --- a/one_trip/lib/pages/list_page/list_page.dart +++ b/one_trip/lib/pages/list_page/list_page.dart @@ -6,8 +6,9 @@ import 'package:one_trip/api/models/list.dart'; import 'package:one_trip/api/models/listingredient.dart'; import 'package:one_trip/api/models/user.dart'; import 'package:one_trip/pages/list_page/widgets/listrow.dart'; -import 'package:one_trip/pages/list_page/widgets/search_recipes.dart'; -import 'package:one_trip/widgets/text_entry_dialog.dart'; +import 'package:one_trip/pages/list_page/widgets/search_recipes_dialog.dart'; +import 'package:one_trip/widgets/confirm_dialog.dart'; +import 'package:one_trip/widgets/ingredient_dialog.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; class ListPage extends StatefulWidget { @@ -56,8 +57,8 @@ class _ListPageState extends State { } } }, + // ignore: avoid_print onError: (error) => print("Websocket error: $error"), - onDone: () => print("Websocket Done"), ); } @@ -108,15 +109,18 @@ class _ListPageState extends State { return ListArea( list: _list!, onAddOne: () async { - String? itemName = - await textEntryDialog(context, "Item Name", "Item"); + IngredientDetails? details = + await ingredientDialog(context, "", ""); - if (itemName == null || itemName == "") { + if (details == null || details.name == "") { return; } - ListIngredient? newIngredient = - await ListIngredient.create(itemName, _list!.homegroup); + ListIngredient? newIngredient = await ListIngredient.create( + _list!.homegroup, + details.name, + details.quantity != "" ? details.quantity : null); + if (newIngredient == null) { return; } @@ -288,7 +292,12 @@ class ListArea extends StatelessWidget { foregroundColor: MaterialStatePropertyAll( Theme.of(context).colorScheme.onError), ), - onPressed: () => onClear(), + onPressed: () async { + bool doDelete = await confirmDialog(context, "Clear List"); + if (doDelete) { + onClear(); + } + }, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: const [Icon(Icons.delete), Text("Clear List")], diff --git a/one_trip/lib/pages/list_page/widgets/listrow.dart b/one_trip/lib/pages/list_page/widgets/listrow.dart index f00c536..2e242d8 100644 --- a/one_trip/lib/pages/list_page/widgets/listrow.dart +++ b/one_trip/lib/pages/list_page/widgets/listrow.dart @@ -67,7 +67,9 @@ class _ListRowState extends State { Expanded( child: Text( // _ingredient.name, - widget.ingredient.name, + widget.ingredient.quantity == null + ? widget.ingredient.name + : "${widget.ingredient.name} - ${widget.ingredient.quantity}", style: Theme.of(context).textTheme.titleMedium!.copyWith( decoration: widget.ingredient.inCart ? TextDecoration.lineThrough diff --git a/one_trip/lib/pages/list_page/widgets/search_recipes.dart b/one_trip/lib/pages/list_page/widgets/search_recipes_dialog.dart similarity index 89% rename from one_trip/lib/pages/list_page/widgets/search_recipes.dart rename to one_trip/lib/pages/list_page/widgets/search_recipes_dialog.dart index 4d8bd08..14a1daa 100644 --- a/one_trip/lib/pages/list_page/widgets/search_recipes.dart +++ b/one_trip/lib/pages/list_page/widgets/search_recipes_dialog.dart @@ -80,6 +80,7 @@ class _SearchRecipesDialogState extends State { child: PaginationListView( state: _listState, shrinkWrap: true, + prefetchOne: true, itemBuilder: (context, data) { return GestureDetector( behavior: HitTestBehavior.translucent, @@ -98,7 +99,13 @@ class _SearchRecipesDialogState extends State { color: selectedIDs.contains(data.id) ? Theme.of(context).colorScheme.secondary : null, - child: Text(data.name), + child: Text( + data.name, + style: TextStyle( + color: selectedIDs.contains(data.id) + ? Theme.of(context).colorScheme.onSecondary + : null), + ), ), ); }, @@ -106,16 +113,6 @@ class _SearchRecipesDialogState extends State { return const Divider(); }, dataProvider: (int page) async { - // SearchResult result = - // await SimpleUser.search(_searchController.text, page); - // List users = List.from(result.results); - - // if (result.next == null) { - // users.add(null); - // } - - // return users; - SearchResult result = await Recipe.search(_searchController.text, page); List recipes = @@ -137,12 +134,15 @@ class _SearchRecipesDialogState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( + style: negativeButtonStyle(context), onPressed: () => Navigator.pop(context), child: const Text("Cancel"), ), ElevatedButton( - onPressed: () => Navigator.pop(context, selectedIDs), - child: const Text("Done")), + style: positiveButtonStyle(context), + onPressed: () => Navigator.pop(context, selectedIDs), + child: const Text("Done"), + ), ], ), ) diff --git a/one_trip/lib/pages/profile_page/widgets/invite_homegroup_dialog.dart b/one_trip/lib/pages/profile_page/widgets/invite_homegroup_dialog.dart index 76cd102..a14d012 100644 --- a/one_trip/lib/pages/profile_page/widgets/invite_homegroup_dialog.dart +++ b/one_trip/lib/pages/profile_page/widgets/invite_homegroup_dialog.dart @@ -139,12 +139,15 @@ class _InviteHomegroupDialogState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( + style: negativeButtonStyle(context), onPressed: () => Navigator.pop(context), child: const Text("Cancel"), ), ElevatedButton( - onPressed: () => Navigator.pop(context, selectedIDs), - child: const Text("Done")), + style: positiveButtonStyle(context), + onPressed: () => Navigator.pop(context, selectedIDs), + child: const Text("Done"), + ), ], ), ) diff --git a/one_trip/lib/pages/recipes_page/widgets/recipe_card_widget.dart b/one_trip/lib/pages/recipes_page/widgets/recipe_card_widget.dart index 45aeb25..5e36a10 100644 --- a/one_trip/lib/pages/recipes_page/widgets/recipe_card_widget.dart +++ b/one_trip/lib/pages/recipes_page/widgets/recipe_card_widget.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:one_trip/api/models/recipeingredient.dart'; import 'package:one_trip/api/models/recipe.dart'; import 'package:one_trip/theme.dart'; -import 'package:one_trip/widgets/text_entry_dialog.dart'; +import 'package:one_trip/widgets/ingredient_dialog.dart'; class RecipeCard extends StatefulWidget { final Recipe recipe; @@ -145,15 +145,18 @@ class _RecipeCardState extends State with TickerProviderStateMixin { shape: const MaterialStatePropertyAll( RoundedRectangleBorder())), onPressed: () async { - String? name = await textEntryDialog( - context, "Ingredient Name", "Ingredient"); + IngredientDetails? details = + await ingredientDialog(context, "", ""); - if (name == null || name == "") { + if (details == null || details.name == "") { return; } RecipeIngredient? ingredient = - await RecipeIngredient.create(name, widget.recipe.id); + await RecipeIngredient.create( + widget.recipe.id, + details.name, + details.quantity != "" ? details.quantity : null); if (ingredient != null) { widget.onChanged(); } @@ -233,21 +236,29 @@ class _IngredientSectionState extends State { children: [ Expanded( child: Text( - widget.ingredients[index].name, + widget.ingredients[index].quantity == null + ? widget.ingredients[index].name + : "${widget.ingredients[index].name} - ${widget.ingredients[index].quantity}", style: Theme.of(context).textTheme.titleMedium, )), IconButton( onPressed: () async { - String? name = await textEntryDialog( - context, "Change Ingredient Name", "Ingredient", - defaultValue: widget.ingredients[index].name); + IngredientDetails? details = await ingredientDialog( + context, + widget.ingredients[index].name, + widget.ingredients[index].quantity ?? ""); - if (name == null || name == "") { + if (details == null || details.name == "") { return; } RecipeIngredient? changed = - await widget.ingredients[index].patch(name); + await widget.ingredients[index].patch( + name: details.name, + quantity: details.quantity != "" + ? details.quantity + : null); + if (changed != null) { widget.onChanged(); } diff --git a/one_trip/lib/screens/home_screen.dart b/one_trip/lib/screens/home_screen.dart index aa51075..10c0a37 100644 --- a/one_trip/lib/screens/home_screen.dart +++ b/one_trip/lib/screens/home_screen.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:one_trip/pages/list_page/list_page.dart'; import 'package:one_trip/pages/profile_page/profile_page.dart'; import 'package:one_trip/pages/recipes_page/recipes_page.dart'; -import 'package:one_trip/pages/themetest.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); diff --git a/one_trip/lib/theme.dart b/one_trip/lib/theme.dart index 1882547..7dafeca 100644 --- a/one_trip/lib/theme.dart +++ b/one_trip/lib/theme.dart @@ -38,3 +38,28 @@ class MyBehavior extends ScrollBehavior { return child; } } + +ButtonStyle positiveButtonStyle(BuildContext context) { + Brightness brightness = Theme.of(context).colorScheme.brightness; + + if (brightness == Brightness.dark) { + return ButtonStyle( + backgroundColor: MaterialStatePropertyAll(Colors.green[200]), + foregroundColor: MaterialStatePropertyAll(Colors.green[900]), + ); + } else { + return ButtonStyle( + backgroundColor: MaterialStatePropertyAll(Colors.green[900]), + foregroundColor: const MaterialStatePropertyAll(Colors.white), + ); + } +} + +ButtonStyle negativeButtonStyle(BuildContext context) { + return ButtonStyle( + backgroundColor: + MaterialStatePropertyAll(Theme.of(context).colorScheme.error), + foregroundColor: + MaterialStatePropertyAll(Theme.of(context).colorScheme.onError), + ); +} diff --git a/one_trip/lib/widgets/confirm_dialog.dart b/one_trip/lib/widgets/confirm_dialog.dart new file mode 100644 index 0000000..29fad06 --- /dev/null +++ b/one_trip/lib/widgets/confirm_dialog.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:one_trip/theme.dart'; + +class ConfirmForm extends StatefulWidget { + final String title; + const ConfirmForm({super.key, required this.title}); + + @override + State createState() => _ConfirmFormState(); +} + +class _ConfirmFormState extends State { + @override + Widget build(BuildContext context) { + return Card( + margin: EdgeInsets.zero, + child: Padding( + padding: const EdgeInsets.all(8), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + widget.title, + style: Theme.of(context).textTheme.titleMedium, + ), + const Divider(), + const Text("This action is permanent. Do you want to continue?"), + Padding( + padding: const EdgeInsets.only(top: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + style: positiveButtonStyle(context), + onPressed: () => Navigator.pop(context, false), + child: const Text("Go Back"), + ), + ElevatedButton( + style: negativeButtonStyle(context), + onPressed: () => Navigator.pop(context, true), + child: const Text("Continue"), + ), + ], + ), + ), + ], + ), + ), + ); + } +} + +Future confirmDialog(BuildContext context, String title) async { + bool? value = await showDialog( + context: context, + builder: (context) { + return Dialog( + child: ConfirmForm( + title: title, + ), + ); + }, + ); + + return value ?? false; +} diff --git a/one_trip/lib/widgets/ingredient_dialog.dart b/one_trip/lib/widgets/ingredient_dialog.dart new file mode 100644 index 0000000..9eaa342 --- /dev/null +++ b/one_trip/lib/widgets/ingredient_dialog.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'package:one_trip/theme.dart'; + +class IngredientDetails { + String name; + String quantity; + + IngredientDetails({required this.name, required this.quantity}); +} + +class IngredientForm extends StatefulWidget { + final String nameStartingValue; + final String quantityStartingValue; + const IngredientForm( + {super.key, + required this.nameStartingValue, + required this.quantityStartingValue}); + + @override + State createState() => _IngredientFormState(); +} + +class _IngredientFormState extends State { + late TextEditingController _nameController; + late TextEditingController _quantityController; + + @override + void dispose() { + _nameController.dispose(); + _quantityController.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + _nameController = TextEditingController(text: widget.nameStartingValue); + _quantityController = + TextEditingController(text: widget.quantityStartingValue); + } + + @override + Widget build(BuildContext context) { + return Card( + margin: EdgeInsets.zero, + child: Padding( + padding: const EdgeInsets.all(8), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Add / Edit Ingredient", + style: Theme.of(context).textTheme.titleMedium, + ), + const Divider(), + TextFormField( + autofocus: true, + controller: _nameController, + textInputAction: TextInputAction.next, + // onFieldSubmitted: (value) { + // Navigator.pop(context, value); + // }, + decoration: const InputDecoration(hintText: "Name"), + ), + TextFormField( + controller: _quantityController, + textInputAction: TextInputAction.done, + onFieldSubmitted: (value) => Navigator.pop( + context, + IngredientDetails( + name: _nameController.text, + quantity: _quantityController.text), + ), + decoration: const InputDecoration(hintText: "Quantity"), + ), + Padding( + padding: const EdgeInsets.only(top: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + style: negativeButtonStyle(context), + onPressed: () => Navigator.pop(context), + child: const Text("Cancel"), + ), + ElevatedButton( + style: positiveButtonStyle(context), + onPressed: () => Navigator.pop( + context, + IngredientDetails( + name: _nameController.text, + quantity: _quantityController.text), + ), + child: const Text("Done"), + ), + ], + ), + ), + ], + ), + ), + ); + } +} + +Future ingredientDialog( + BuildContext context, String currentName, String currentQuantity) async { + IngredientDetails? details = await showDialog( + context: context, + builder: (context) { + return Dialog( + child: IngredientForm( + nameStartingValue: currentName, + quantityStartingValue: currentQuantity, + ), + ); + }, + ); + + return details; +} diff --git a/one_trip/lib/widgets/text_entry_dialog.dart b/one_trip/lib/widgets/text_entry_dialog.dart index ee8a5a6..0472ef9 100644 --- a/one_trip/lib/widgets/text_entry_dialog.dart +++ b/one_trip/lib/widgets/text_entry_dialog.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:one_trip/theme.dart'; class TextEntryForm extends StatefulWidget { final String title; @@ -55,13 +56,16 @@ class _TextEntryFormState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( + style: negativeButtonStyle(context), onPressed: () => Navigator.pop(context), child: const Text("Cancel"), ), ElevatedButton( - onPressed: () => - Navigator.pop(context, _textController.text), - child: const Text("Done")), + style: positiveButtonStyle(context), + onPressed: () => + Navigator.pop(context, _textController.text), + child: const Text("Done"), + ), ], ), ), diff --git a/one_trip_api/api/migrations/0006_remove_list_updates_listingredient_quantity_and_more.py b/one_trip_api/api/migrations/0006_remove_list_updates_listingredient_quantity_and_more.py new file mode 100644 index 0000000..5cd99a3 --- /dev/null +++ b/one_trip_api/api/migrations/0006_remove_list_updates_listingredient_quantity_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 4.1.3 on 2022-12-06 22:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0005_list_updates'), + ] + + operations = [ + migrations.RemoveField( + model_name='list', + name='updates', + ), + migrations.AddField( + model_name='listingredient', + name='quantity', + field=models.CharField(blank=True, max_length=50, null=True), + ), + migrations.AddField( + model_name='recipeingredient', + name='quantity', + field=models.CharField(blank=True, max_length=50, null=True), + ), + ] diff --git a/one_trip_api/api/models.py b/one_trip_api/api/models.py index 412d466..169c4f0 100644 --- a/one_trip_api/api/models.py +++ b/one_trip_api/api/models.py @@ -26,7 +26,6 @@ class Homegroup(models.Model): class List(models.Model): # Foreign Key ListIngredient -> List [as ingredients] homegroup = models.OneToOneField(Homegroup, on_delete=models.CASCADE, primary_key=True) - updates = models.BigIntegerField(default=0); class Recipe(models.Model): @@ -37,9 +36,11 @@ class Recipe(models.Model): class RecipeIngredient(models.Model): name = models.CharField(max_length=50) + quantity = models.CharField(max_length=50, null=True, blank=True) recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name="ingredients") class ListIngredient(models.Model): name = models.CharField(max_length=50) + quantity = models.CharField(max_length=50, null=True, blank=True) list = models.ForeignKey(List, on_delete=models.CASCADE, related_name="ingredients") in_cart = models.BooleanField(default=False) \ No newline at end of file diff --git a/one_trip_api/api/serializers.py b/one_trip_api/api/serializers.py index 811dfd3..c4a6225 100644 --- a/one_trip_api/api/serializers.py +++ b/one_trip_api/api/serializers.py @@ -9,12 +9,12 @@ channel_layer = get_channel_layer() class RecipeIngredientSerializer(serializers.ModelSerializer): class Meta: model = RecipeIngredient - fields = ["id", "name", "recipe"] + fields = ["id", "name", "quantity", "recipe"] class ListIngredientSerializer(serializers.ModelSerializer): class Meta: model = ListIngredient - fields = ["id", "name", "list", "in_cart"] + fields = ["id", "name", "quantity", "list", "in_cart"] class RecipeSerializer(serializers.ModelSerializer): ingredients = serializers.SerializerMethodField() @@ -33,13 +33,9 @@ class ListSerializer(serializers.ModelSerializer): class Meta: model = List - fields = ["homegroup", "updates", "ingredients"] + fields = ["homegroup", "ingredients"] read_only_fields = ["homegroup"] - def update(self, instance, validated_data): - # async_to_sync(channel_layer.group_send)(f"group_{instance.homegroup.id}", {"type": "model_update"}) - return super().update(instance, validated_data) - def get_ingredients(self, instance): ingredients = instance.ingredients.all().order_by("name") return ListIngredientSerializer(ingredients, many=True).data diff --git a/one_trip_api/api/views.py b/one_trip_api/api/views.py index 368b30e..c142b77 100644 --- a/one_trip_api/api/views.py +++ b/one_trip_api/api/views.py @@ -12,7 +12,7 @@ class HasHomegroup(permissions.BasePermission): return super().has_permission(request, view) class Pagination(pagination.PageNumberPagination): - page_size = 4 + page_size = 10 class NoListModelViewset(mixins.CreateModelMixin, mixins.DestroyModelMixin, mixins.UpdateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): pass diff --git a/planning/backend-structure.dia b/planning/backend-structure.dia index 912d771..ecab62f 100644 Binary files a/planning/backend-structure.dia and b/planning/backend-structure.dia differ diff --git a/planning/backend-structure.png b/planning/backend-structure.png index d4d9e6b..e81c0e4 100644 Binary files a/planning/backend-structure.png and b/planning/backend-structure.png differ diff --git a/readme.md b/readme.md index 7d18122..d2e1b95 100644 --- a/readme.md +++ b/readme.md @@ -20,30 +20,36 @@ ## Pictures ### Main Menu -![Login Screen](https://user-images.githubusercontent.com/44736322/204997033-fa10ceae-ae54-4816-b043-a1663532b5b3.png) +![Login Screen](https://user-images.githubusercontent.com/44736322/206134503-490b7b5c-9ef1-4a32-af2f-a664b587c4d6.png) ### Profile Page -![Profile Page](https://user-images.githubusercontent.com/44736322/204997046-d3b09233-266f-4082-8f05-4b97d5369a18.png) +![Profile Page](https://user-images.githubusercontent.com/44736322/206134506-7c035ea7-8431-4116-b61f-f1bbe0b23713.png) ### Search for Users -![Searching for users](https://user-images.githubusercontent.com/44736322/204997074-464c3b0c-96c2-4ac0-8e28-ebc1319c0065.png) +![Searching for users](https://user-images.githubusercontent.com/44736322/206134507-c2e5c805-3c0e-4ead-9c48-b047e5fb75dc.png) + +Searched Users are sorted by first name, then last name, then username ### Create, Edit, and Delete Recipes -![Create Edit Delete Recipes](https://user-images.githubusercontent.com/44736322/204997088-cb4a34e4-9c09-408f-b15d-4b30a8409f5c.png) +![Create Edit Delete Recipes](https://user-images.githubusercontent.com/44736322/206134508-94332b3e-ea62-47e5-8426-c876e9f7ce24.png) Recipes and individual ingredients can be deleted by swiping them off of the screen ### Search for Recipes to add to Shopping List -![Search Recipes](https://user-images.githubusercontent.com/44736322/204997188-7e03d067-a26e-4629-be3f-ef9039eee54b.png) +![Search Recipes](https://user-images.githubusercontent.com/44736322/206134511-c0fd19b3-9816-4d87-bfde-54fb110c2fe5.png) ### Delete Items from List -![swipe](https://user-images.githubusercontent.com/44736322/205004229-c59ef486-edb6-4595-aa0c-846f008ab1a1.gif) +https://user-images.githubusercontent.com/44736322/206134495-b6b97f81-b77b-4068-8359-39e64d5312ca.mp4 + +### Add Additional Items +![Add Individual](https://user-images.githubusercontent.com/44736322/206134513-c6d7bedd-659f-45de-83ef-7aa4b1a9378f.png) ### Shopping List Page -![Shopping List Page](https://user-images.githubusercontent.com/44736322/204997203-5341a35e-9269-43eb-a212-6404c8119482.png) +![Shopping List Page](https://user-images.githubusercontent.com/44736322/206134515-1636e086-80a7-45df-801b-af90c2059d10.png) ### Backend Data Models -![backend-structure](https://user-images.githubusercontent.com/44736322/205004669-9f9387e0-1f76-42ac-8d49-d94cd17518aa.png) +![backend-structure](https://user-images.githubusercontent.com/44736322/206135954-63c728ce-3662-4e10-9914-557ce65f04ee.png) +