SA-178 product selection added

This commit is contained in:
Mykhailo Bilodid 2023-12-17 01:34:22 +02:00
parent 7bcab820ae
commit 947154c59a
10 changed files with 352 additions and 48 deletions

View File

@ -0,0 +1,10 @@
<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_295_534)">
<path d="M10.8696 2.77884L12.263 5.5655C12.453 5.95342 12.9596 6.3255 13.3871 6.39675L15.9126 6.81634C17.5276 7.0855 17.9075 8.25717 16.7438 9.413L14.7805 11.3763C14.448 11.7088 14.2659 12.3501 14.3688 12.8093L14.9309 15.2397C15.3742 17.1634 14.353 17.9076 12.6509 16.9022L10.2838 15.5009C9.8563 15.2476 9.15172 15.2476 8.7163 15.5009L6.34922 16.9022C4.65505 17.9076 3.62588 17.1555 4.06922 15.2397L4.6313 12.8093C4.73422 12.3501 4.55213 11.7088 4.21963 11.3763L2.2563 9.413C1.10047 8.25717 1.47255 7.0855 3.08755 6.81634L5.61297 6.39675C6.03255 6.3255 6.53922 5.95342 6.72922 5.5655L8.12255 2.77884C8.88255 1.26675 10.1176 1.26675 10.8696 2.77884Z" stroke="#FFC700" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_295_534">
<rect width="19" height="19" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 980 B

View File

@ -0,0 +1,9 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.47409 1.91065L8.43216 3.82679C8.56281 4.09353 8.9112 4.34937 9.20515 4.39837L10.9417 4.68688C12.0521 4.87196 12.3134 5.67761 11.5132 6.47237L10.1632 7.82238C9.93459 8.05101 9.80939 8.49194 9.88015 8.80766L10.2666 10.4788C10.5715 11.8016 9.86927 12.3133 8.6989 11.622L7.07127 10.6585C6.77731 10.4843 6.29284 10.4843 5.99344 10.6585L4.36581 11.622C3.20088 12.3133 2.49322 11.7962 2.79806 10.4788L3.18455 8.80766C3.25532 8.49194 3.13012 8.05101 2.90149 7.82238L1.55148 6.47237C0.756716 5.67761 1.01256 4.87196 2.12305 4.68688L3.85956 4.39837C4.14807 4.34937 4.49645 4.09353 4.6271 3.82679L5.58517 1.91065C6.10775 0.870929 6.95695 0.870929 7.47409 1.91065Z" fill="url(#paint0_linear_554_62)" stroke="#FFC700" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<defs>
<linearGradient id="paint0_linear_554_62" x1="1" y1="6" x2="12" y2="6" gradientUnits="userSpaceOnUse">
<stop offset="0.53125" stop-color="#FFC700"/>
<stop offset="0.557292" stop-color="#FFC700" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,3 +1,3 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.775 26.0125C15.35 26.1625 14.65 26.1625 14.225 26.0125C10.6 24.775 2.5 19.6125 2.5 10.8625C2.5 7 5.6125 3.875 9.45 3.875C11.725 3.875 13.7375 4.975 15 6.675C15.6422 5.80734 16.4787 5.10216 17.4425 4.61593C18.4063 4.1297 19.4705 3.87595 20.55 3.875C24.3875 3.875 27.5 7 27.5 10.8625C27.5 19.6125 19.4 24.775 15.775 26.0125Z" stroke="#009FFF" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<svg width="28" height="26" viewBox="0 0 28 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.775 24.0125C14.35 24.1625 13.65 24.1625 13.225 24.0125C9.6 22.775 1.5 17.6125 1.5 8.8625C1.5 5 4.6125 1.875 8.45 1.875C10.725 1.875 12.7375 2.975 14 4.675C14.6422 3.80734 15.4787 3.10216 16.4425 2.61593C17.4063 2.1297 18.4705 1.87595 19.55 1.875C23.3875 1.875 26.5 5 26.5 8.8625C26.5 17.6125 18.4 22.775 14.775 24.0125Z" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 525 B

After

Width:  |  Height:  |  Size: 520 B

10
assets/icons/star.svg Normal file
View File

@ -0,0 +1,10 @@
<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_295_532)">
<path d="M10.8696 2.77884L12.263 5.56551C12.453 5.95342 12.9596 6.3255 13.3871 6.39675L15.9125 6.81634C17.5276 7.0855 17.9075 8.25717 16.7438 9.413L14.7805 11.3763C14.448 11.7088 14.2659 12.3501 14.3688 12.8093L14.9309 15.2397C15.3742 17.1634 14.353 17.9076 12.6509 16.9022L10.2838 15.5009C9.8563 15.2476 9.15172 15.2476 8.7163 15.5009L6.34922 16.9022C4.65505 17.9076 3.62588 17.1555 4.06922 15.2397L4.6313 12.8093C4.73422 12.3501 4.55213 11.7088 4.21963 11.3763L2.2563 9.413C1.10047 8.25717 1.47255 7.0855 3.08755 6.81634L5.61297 6.39675C6.03255 6.3255 6.53922 5.95342 6.72922 5.56551L8.12255 2.77884C8.88255 1.26675 10.1175 1.26675 10.8696 2.77884Z" fill="#FFC700" stroke="#FFC700" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_295_532">
<rect width="19" height="19" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 997 B

View File

@ -1,3 +1,5 @@
import 'dart:ffi';
class Product {
Product({
required this.id,
@ -5,8 +7,10 @@ class Product {
required this.url,
required this.imageUrls,
required this.rating,
required this.price
});
required this.price,
required this.description,
required this.wasOpened,
});
String id;
String name;
@ -14,6 +18,8 @@ class Product {
List<String> imageUrls;
double rating;
double price;
String description;
bool wasOpened;
Product.fromJson(Map<String, dynamic> json)
: id = json['id'] as String,
@ -21,5 +27,7 @@ class Product {
url = json['url'] as String,
imageUrls = json['imageUrls'] as List<String>,
rating = json['rating'] as double,
price = json['name'] as double;
price = json['name'] as double,
description = json['description'] as String,
wasOpened = json['wasOpened'] as bool;
}

View File

@ -0,0 +1,31 @@
import 'package:graphql/client.dart';
import 'package:shopping_assistant_mobile_client/models/product.dart';
import 'package:shopping_assistant_mobile_client/network/api_client.dart';
class ProductService {
final ApiClient client = ApiClient();
Future<void> addProductToPersonalWishlist(Product product, String wishlisId) async {
final options = MutationOptions(
document: gql('''
mutation AddProductToPersonalWishlist(\$wishlistId: String!, \$dto: ProductCreateDtoInput!) {
addProductToPersonalWishlist(wishlistId: \$wishlistId, dto: \$dto) {
}
}
'''),
variables: {'wishlistId': wishlisId,
'dto': {
'wasOpened': product.wasOpened,
'url': product.url,
'rating': product.rating,
'price': product.price,
'name': product.name,
'imagesUrls': product.imageUrls,
'description': product.description,
}},
);
final result = await client.mutate(options);
}
}

View File

@ -1,18 +1,86 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:shopping_assistant_mobile_client/models/product.dart';
import 'package:shopping_assistant_mobile_client/network/product_service.dart';
class Cards extends StatefulWidget {
final String wishlistId;
final String wishlistName;
List<String> products = [];
List<String> inputProducts = [];
List<Product> products = [];
Cards({required this.wishlistName, required this.products});
Cards({required this.wishlistName, required this.inputProducts, required this.wishlistId});
@override
_CardsState createState() => _CardsState();
}
class _CardsState extends State<Cards> {
int currentProduct = 0;
ProductService productService = ProductService();
List<Product> cart = [];
@override
void initState(){
for(String productName in this.widget.inputProducts){
this.widget.products.add(
Product(
id: '',
name: productName,
url: 'link',
imageUrls: [],
rating: 0.0,
price: 0.0,
description: '',
wasOpened: false,
)
);
}
}
Widget buildRatingStars(double rating) {
int whole = rating.floor();
double fractal = rating - whole;
List<Widget> stars = [];
for (int i = 0; i < 5; i++) {
if (i < whole) {
// Whole star
stars.add(SvgPicture.asset(
'assets/icons/star.svg',
width: 19,
height: 19,
));
} else if (fractal != 0.0) {
// Half star
stars.add(SvgPicture.asset(
'assets/icons/half-star.svg',
width: 19,
height: 19,
));
fractal -= fractal;
} else {
// Empty star
stars.add(SvgPicture.asset(
'assets/icons/empty-star.svg',
width: 19,
height: 19,
));
}
}
return Row(
children: stars,
);
}
@override
Widget build(BuildContext context) {
bool hasCards = widget.products.isNotEmpty;
bool isLastProduct = currentProduct == widget.products.length;
return Scaffold(
appBar: AppBar(
title: Text(widget.wishlistName),
@ -21,33 +89,177 @@ class _CardsState extends State<Cards> {
body: Center(
child: Stack(
children: [
// Back Card with increased rotation effect
if (currentProduct < widget.products.length - 1)
Positioned(
left: 10, // Adjust the left position as needed
child: Transform.rotate(
angle: -5 * 3.1415926535 / 180, // Rotation angle
angle: -5 * 3.1415926535 / 180,
child: Container(
width: 400, // Increased width
height: 600, // Increased height
width: 300,
height: 600,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
border: Border.all(
color: Color(0xFF009FFF),
),
boxShadow: [
BoxShadow(
color: Color(0xFF0165FF),
blurRadius: 8.0,
spreadRadius: 0.0,
offset: Offset(0, 0),
),
],
),
),
),
),
// Main Card
Positioned(
child: Container(
width: 300,
height: 600,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
border: Border.all(
color: Color(0xFF009FFF),
),
boxShadow: [
BoxShadow(
color: Color(0xFF0165FF),
blurRadius: 8.0,
spreadRadius: 0.0,
offset: Offset(0, 0),
),
],
),
child: Column(
children: [
Container(
width: 300, // Set the width of the main card
height: 500, // Set the height of the main card
height: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
border: Border.all(
color: Color(0xFF009FFF),
image: DecorationImage(
image: AssetImage('assets/images/product_image_placeholder.jpg'), // Replace with your image path
fit: BoxFit.cover,
),
),
),
SizedBox(height: 10),
Text(
currentProduct < widget.products.length
? widget.products[currentProduct].name
: "The cards ended.",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
if (currentProduct == widget.products.length)
Text(
"Swipe right to show more or left to exit.",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w400,
),
),
SizedBox(height: 10),
if (!isLastProduct)
Column(
children: [
Text(
currentProduct < widget.products.length
? widget.products[currentProduct].description
: "Product description not available.",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w400,
),
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
buildRatingStars(currentProduct < widget.products.length
? widget.products[currentProduct].rating
: 0.0),
SizedBox(width: 20),
Text(
'\$${currentProduct < widget.products.length ? widget.products[currentProduct].price : "-"}',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
SizedBox(height: 50),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: currentProduct < widget.products.length
? SvgPicture.asset(
'assets/icons/x.svg',
width: 30,
height: 30,
)
: SvgPicture.asset(
'assets/icons/exit-cards.svg',
width: 30,
height: 30,
),
onPressed: () {
if (currentProduct < widget.products.length) {
showNextProduct();
} else {
Navigator.of(context).pop();
}
},
),
IconButton(
icon: SvgPicture.asset(
'assets/icons/back.svg',
width: 30,
height: 30,
),
onPressed: () {
showPreviousProduct();
},
),
IconButton(
icon: currentProduct < widget.products.length
? SvgPicture.asset(
'assets/icons/heart.svg',
width: 30,
height: 30,
color: Colors.blue,
)
: SvgPicture.asset(
'assets/icons/add-products.svg',
width: 30,
height: 30,
),
onPressed: () async {
if (currentProduct < widget.products.length) {
if (!cart.contains(widget.products[currentProduct])) {
await productService.addProductToPersonalWishlist(
widget.products[currentProduct],
widget.wishlistId,
);
cart.add(widget.products[currentProduct]);
}
showNextProduct();
}
},
),
],
),
],
),
),
),
@ -56,5 +268,20 @@ class _CardsState extends State<Cards> {
),
);
}
}
void showNextProduct() {
setState(() {
if(currentProduct < widget.products.length) {
++currentProduct;
}
});
}
void showPreviousProduct() {
setState(() {
if(currentProduct > 0 ) {
--currentProduct;
}
});
}
}

View File

@ -206,10 +206,11 @@ class ChatScreenState extends State<ChatScreen> {
setState(() {
if(_searchService.checkerForProduct()){
messages.removeLast();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Cards(wishlistName: this.widget.wishlistName, products: this._searchService.products,),
builder: (context) => Cards(wishlistName: this.widget.wishlistName, inputProducts: this._searchService.products, wishlistId: this.widget.wishlistId,),
),
);
}

View File

@ -244,10 +244,10 @@ packages:
dependency: "direct main"
description:
name: http
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.1.2"
http_parser:
dependency: transitive
description:
@ -396,10 +396,10 @@ packages:
dependency: transitive
description:
name: petitparser
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev"
source: hosted
version: "5.4.0"
version: "6.0.2"
platform:
dependency: transitive
description:
@ -468,10 +468,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_web
sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf
sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "2.2.2"
shared_preferences_windows:
dependency: transitive
description:
@ -569,10 +569,10 @@ packages:
dependency: transitive
description:
name: url_launcher_linux
sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd"
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "3.1.1"
url_launcher_macos:
dependency: transitive
description:
@ -593,18 +593,18 @@ packages:
dependency: transitive
description:
name: url_launcher_web
sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2"
sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.2.2"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc"
sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "3.1.1"
uuid:
dependency: "direct main"
description:
@ -681,10 +681,10 @@ packages:
dependency: transitive
description:
name: xml
sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev"
source: hosted
version: "6.3.0"
version: "6.5.0"
yaml:
dependency: transitive
description:
@ -694,5 +694,5 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.2.0-194.0.dev <4.0.0"
flutter: ">=3.13.0"
dart: ">=3.2.0 <4.0.0"
flutter: ">=3.16.0"

View File

@ -74,6 +74,14 @@ flutter:
- assets/icons/wishlists.svg
- assets/icons/start-new-search.svg
- assets/icons/settings.svg
- assets/icons/heart.svg
- assets/icons/x.svg
- assets/icons/exit-cards.svg
- assets/icons/back.svg
- assets/icons/add-products.svg
- assets/icons/star.svg
- assets/icons/half-star.svg
- assets/icons/empty-star.svg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware