Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] : Complete Purchase Process #52

Merged
merged 5 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 25 additions & 26 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,30 @@ class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return const ScreenController();
// return MaterialApp(
// title: 'Saphy',
// theme: ThemeData(
// colorScheme: ColorScheme.fromSeed(seedColor: altBlack),
// useMaterial3: true,
// ),
// debugShowCheckedModeBanner: false,
// initialRoute: WelcomeScreen.id,
// routes: {
// WelcomeScreen.id: (context) => const WelcomeScreen(),
// SignupScreen.id: (context) => const SignupScreen(
// socialType: '',
// userEmail: '',
// userName: '',
// userPhotoUrl: '',
// ),
// OtpScreen.id: (context) => OtpScreen(
// verificationId: '',
// phoneNumber: '',
// onVerificationSuccess: () {},
// ),
// SplashSellingScreen.id: (context) => const SplashSellingScreen(),
// ScreenController.id: (context) => const ScreenController(),
// },
// );
return MaterialApp(
title: 'Saphy',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: altBlack),
useMaterial3: true,
),
debugShowCheckedModeBanner: false,
initialRoute: WelcomeScreen.id,
routes: {
WelcomeScreen.id: (context) => const WelcomeScreen(),
SignupScreen.id: (context) => const SignupScreen(
socialType: '',
userEmail: '',
userName: '',
userPhotoUrl: '',
),
OtpScreen.id: (context) => OtpScreen(
verificationId: '',
phoneNumber: '',
onVerificationSuccess: () {},
),
SplashSellingScreen.id: (context) => const SplashSellingScreen(),
ScreenController.id: (context) => const ScreenController(),
},
);
}
}
35 changes: 20 additions & 15 deletions lib/models/product.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Product {
String color;
String storage;
String grade;
ImageModel descriptionImage;

Product({
required this.id,
Expand All @@ -23,24 +24,27 @@ class Product {
required this.color,
required this.storage,
required this.grade,
required this.descriptionImage,
});

factory Product.fromJson(Map<String, dynamic> json) {
return Product(
id: json['id'],
deviceType: json['deviceType'] ?? "",
name: json['name'] ?? "",
description: json['description'] ?? "",
price: json['price'],
stock: json['stock'] ?? "",
images: (json['images'] as List)
.map((item) => ImageModel.fromJson(item))
.toList(),
brand: json['brand'] ?? "",
color: json['color'] ?? "",
storage: json['storage'] ?? "",
grade: json['grade'] ?? "",
);
id: json['id'],
deviceType: json['deviceType'] ?? "",
name: json['name'] ?? "",
description: json['description'] ?? "",
price: json['price'],
stock: json['stock'] ?? "",
images: (json['images'] as List)
.map((item) => ImageModel.fromJson(item))
.toList(),
brand: json['brand'] ?? "",
color: json['color'] ?? "",
storage: json['storage'] ?? "",
grade: json['grade'] ?? "",
descriptionImage: json['descriptionImage'] != null
? ImageModel.fromJson(json['descriptionImage'])
: ImageModel(name: "", url: ""));
}

Map<String, dynamic> toJson() {
Expand All @@ -51,11 +55,12 @@ class Product {
'description': description,
'price': price,
'stock': stock,
'images': images, // 직접 저장
'images': images,
'brand': brand,
'color': color,
'storage': storage,
'grade': grade,
"descriptionImage": images,
};
}
}
Expand Down
3 changes: 1 addition & 2 deletions lib/screens/main/main_screen.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:saphy/models/product.dart';
Expand Down Expand Up @@ -146,7 +145,7 @@ class _MainScreenState extends State<MainScreen> {
} else if (!snapshot.hasData ||
snapshot.data!.isEmpty) {
return const Center(
child: Text('No products found')); // 데이터 없음 메시지
child: Text('상품이 없습니다')); // 데이터 없음 메시지
} else {
final products = snapshot.data!; // 데이터 가져오기
return Wrap(
Expand Down
79 changes: 76 additions & 3 deletions lib/screens/products/liked_list_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import 'package:flutter/widgets.dart';
// import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:intl/intl.dart';
import 'package:saphy/models/product.dart';
import 'package:saphy/service/authentication/secure_storage.dart';
import 'package:saphy/utils/textstyles.dart';
import 'package:saphy/widgets/product_card.dart';
import 'package:saphy/utils/colors.dart';
import 'package:saphy/widgets/app_bar.dart';
import 'package:dio/dio.dart';

class LikedListPage extends StatefulWidget {
const LikedListPage({super.key});
Expand All @@ -17,6 +19,53 @@ class LikedListPage extends StatefulWidget {

class _LikedListPageState extends State<LikedListPage> {
final NumberFormat numberFormat = NumberFormat('###,###,###,###');
late Future<List<Product>> _products;
int cnt = 0;

Future<List<Product>> getProducts() async {
final dio = Dio();
String? accessToken = await readAccessToken();

try {
final response = await dio.get(
'https://saphy.site/item-wishes/',
options: Options(
headers: {
'Authorization': 'Bearer $accessToken', // 필요한 헤더 추가
},
),
);
if (response.statusCode == 200) {
final data = response.data as Map<String, dynamic>;
if (data['results'] != null) {
List<Product> products = (data['results'] as List)
.map((item) => Product.fromJson(item))
.toList();
return products;
} else {
throw Exception('No results found in the response');
}
} else {
throw Exception('Failed to load products');
}
} catch (e) {
return [];
}
}

@override
void initState() {
super.initState();
_products = getProducts();
countProducts();
}

Future<void> countProducts() async {
List<Product> products = await getProducts();
setState(() {
cnt = products.length;
});
}

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -53,15 +102,39 @@ class _LikedListPageState extends State<LikedListPage> {
),
),
),
const SliverPadding(
padding: EdgeInsets.all(20),
SliverPadding(
padding: const EdgeInsets.all(20),
sliver: SliverToBoxAdapter(
child: Wrap(
direction: Axis.horizontal,
alignment: WrapAlignment.spaceBetween,
spacing: 15,
runSpacing: 15,
children: [],
children: [
FutureBuilder(
future: _products,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error.toString()}');
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
return const Center(child: Text('상품이 없습니다'));
} else {
final products = snapshot.data!;
return Wrap(
direction: Axis.horizontal,
alignment: WrapAlignment.spaceBetween,
spacing: 15,
runSpacing: 15,
children: products
.map((product) => ProductCard(product: product))
.toList(),
);
}
},
)
],
),
),
),
Expand Down
62 changes: 60 additions & 2 deletions lib/screens/products/product_detail_page.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:saphy/screens/purchase/purchase_page.dart';
import 'package:saphy/service/authentication/secure_storage.dart';
import 'package:saphy/utils/colors.dart';
import 'package:saphy/utils/number_format.dart';
import 'package:saphy/utils/textstyles.dart';
Expand All @@ -17,6 +18,7 @@ class ProductDetail extends StatefulWidget {

class _ProductDetailState extends State<ProductDetail> {
Product? productDetail;
bool isWished = false; // 아이템이 찜되었는지 상태를 관리

@override
void initState() {
Expand Down Expand Up @@ -62,10 +64,58 @@ class _ProductDetailState extends State<ProductDetail> {
color: "color",
storage: "storage",
grade: "grade",
descriptionImage: ImageModel(name: "name", url: "url"),
);
}
}

Future<void> toggleWish() async {
final dio = Dio();
try {
String token = await readJwt();
token = token.toString().split(" ")[2];

if (!isWished) {
// 아이템을 찜하는 POST 요청
final response = await dio.post(
'https://saphy.site/item-wishes?itemId=${widget.product.id}',
options: Options(
headers: {
'Authorization': token,
},
),
);

if (response.statusCode == 201) {
// 요청이 성공적으로 처리된 경우
setState(() {
isWished = true; // 아이템이 찜 상태로 변경
});
}
} else {
// 아이템 찜 해제하는 POST 요청
final response = await dio.delete(
'https://saphy.site/item-wishes/${widget.product.id}', // 삭제 요청을 보낼 URL
options: Options(
headers: {
'Authorization': 'Bearer $token',
},
),
);

if (response.statusCode == 200) {
// 요청이 성공적으로 처리된 경우
setState(() {
isWished = false; // 아이템이 찜 해제 상태로 변경
});
}
}
} catch (e) {
print('Error: $e');
// 에러 처리 로직 추가 가능
}
}

@override
Widget build(BuildContext context) {
var screenWidth = MediaQuery.of(context).size.width;
Expand Down Expand Up @@ -350,8 +400,16 @@ class _ProductDetailState extends State<ProductDetail> {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.favorite_outline)),
onPressed: toggleWish,
icon: Icon(
isWished
? Icons.favorite
: Icons.favorite_outline, // 찜 상태에 따라 아이콘 변경
color: isWished
? Colors.red
: Colors.black, // 찜 상태에 따라 색상 변경
),
),
const SizedBox(width: 10),
Flexible(flex: 1, child: button("구매하기")),
const SizedBox(width: 10),
Expand Down
Loading