diff --git a/economic_fe/lib/data/services/remote_data_source.dart b/economic_fe/lib/data/services/remote_data_source.dart index c3673a3..15e0a40 100644 --- a/economic_fe/lib/data/services/remote_data_source.dart +++ b/economic_fe/lib/data/services/remote_data_source.dart @@ -19,10 +19,10 @@ class RemoteDataSource { Map jsonData, ) async { String apiUrl = '$baseUrl/$endPoint'; - String authToken = dotenv.env['AUTHORIZATION_KEY']!; // 환경 변수에서 가져오기 + // String authToken = dotenv.env['AUTHORIZATION_KEY']!; // 환경 변수에서 가져오기 Map headers = { 'Content-Type': 'application/json', - 'Authorization': 'Bearer $authToken', + 'Authorization': 'Bearer $accessToken', }; try { @@ -249,4 +249,163 @@ class RemoteDataSource { return response; } + + /// 틀린 문제 데이터 요청 + /// api/v1/user/wrong-quizzes + static Future fetchIncorrectQuestions(String level) async { + String endpoint = 'api/v1/user/wrong-quizzes?level=$level'; + + try { + final response = await _getApiWithHeader(endpoint, accessToken); + + if (response != null && response['isSuccess'] == true) { + debugPrint('틀린 문제 데이터 요청 성공'); + return response; + } else { + debugPrint('데이터 요청 실패: ${response?['message'] ?? '알 수 없는 에러'}'); + return null; + } + } catch (e) { + debugPrint('요청 중 예외 발생: $e'); + return null; + } + } + + /// 개별 퀴즈 조회 + /// API: api/v1/learning/learning/quiz/{quizId} + static Future fetchQuizById(int quizId) async { + try { + // API Endpoint 구성 + String endPoint = 'api/v1/learning/learning/quiz/$quizId'; + + // GET 요청 수행 + final response = await _getApiWithHeader(endPoint, accessToken); + + // 응답 데이터 처리 + if (response != null) { + print("퀴즈 데이터 응답: $response"); + return response; + } else { + print("퀴즈 데이터 요청 실패"); + return null; + } + } catch (e) { + print("fetchQuizById 요청 중 예외 발생: $e"); + return null; + } + } + + /// 스크랩 한 게시물 조회 + /// API: api/v1/user/scrap-posts + static Future fetchScrapedPosts() async { + const String endPoint = 'api/v1/user/scrap-posts'; + + final response = await _getApiWithHeader(endPoint, accessToken); + + if (response != null && response['isSuccess']) { + debugPrint("스크랩 게시글 목록 응답: ${response['results']}"); + return response['results']; + } else { + debugPrint("스크랩 게시글 목록 가져오기 실패"); + return null; + } + } + + /// 좋아요 한 게시물 조회 + /// API: api/v1/user/like-posts + static Future fetchLikedPosts() async { + const String endPoint = 'api/v1/user/like-posts'; + + final response = await _getApiWithHeader(endPoint, accessToken); + + if (response != null && response['isSuccess']) { + debugPrint("좋아요 게시글 목록 응답: ${response['results']}"); + return response['results']; + } else { + debugPrint("좋아요 게시글 목록 가져오기 실패"); + return null; + } + } + + /// 좋아요 한 댓글 조회 + /// API: api/v1/user/like-comments + static Future fetchLikedComments() async { + const String endPoint = 'api/v1/user/like-comments'; + + final response = await _getApiWithHeader(endPoint, accessToken); + + if (response != null && response['isSuccess']) { + debugPrint("좋아요 댓글 목록 응답: ${response['results']}"); + return response['results']; + } else { + debugPrint("좋아요 댓글 목록 가져오기 실패"); + return null; + } + } + + /// 스크랩 한 개념 학습 조회 + /// API: api/v1/user/scrap-concepts + static Future getScrapConcepts(String level) async { + String endpoint = 'api/v1/user/scrap-concepts?level=$level'; + + try { + // _getApiWithHeader 호출 + final response = await _getApiWithHeader(endpoint, accessToken); + + if (response != null && response is Map) { + debugPrint('스크랩한 학습 데이터 로드 성공'); + return response; + } else { + debugPrint('스크랩한 학습 데이터 로드 실패'); + return null; + } + } catch (e) { + debugPrint('getScrapConcepts Error: $e'); + return null; + } + } + + /// 스크랩 한 퀴즈 조회 + /// API: api/v1/user/scrap-quizzes + static Future getScrapQuizzes(String level) async { + String endpoint = 'api/v1/user/scrap-quizzes?level=$level'; + + try { + // _getApiWithHeader 호출 + final response = await _getApiWithHeader(endpoint, accessToken); + + if (response != null && response is Map) { + debugPrint('스크랩한 퀴즈 데이터 로드 성공'); + return response; + } else { + debugPrint('스크랩한 퀴즈 데이터 로드 실패'); + return null; + } + } catch (e) { + debugPrint('getScrapConcepts Error: $e'); + return null; + } + } + + /// 레벨별 학습 진도율 조회 + /// API: api/v1/user/progress + static Future getProgress() async { + String endpoint = 'api/v1/user/progress'; + + try { + // _getApiWithHeader 호출 + final response = await _getApiWithHeader(endpoint, accessToken); + + if (response != null && response is Map) { + debugPrint('학습 진도율 데이터 로드 성공'); + return response; + } else { + debugPrint('학습 진도율 데이터 로드 실패'); + return null; + } + } catch (e) { + debugPrint('getProgress Error: $e'); + return null; + } + } } diff --git a/economic_fe/lib/view/screens/home_page.dart b/economic_fe/lib/view/screens/home_page.dart index 1eebd77..7e7253c 100644 --- a/economic_fe/lib/view/screens/home_page.dart +++ b/economic_fe/lib/view/screens/home_page.dart @@ -22,12 +22,6 @@ class _HomePageState extends State { // 연속 학습일 const int dayCounts = 3; - // 진도율 - const double beginnerProgress = 0.92; // Beginner 진도율 - const double intermediateProgress = 0.21; // Intermediate 진도율 - const double advancedProgress = 0.02; // Advanced 진도율 - const double maxHeight = 120.0; // 그래프의 최대 높이 - // 경제 톡톡 참여자 프로필 사진 리스트 const List profileImages = [ 'assets/profile_example.png', @@ -118,163 +112,176 @@ class _HomePageState extends State { ], ), // 진도율 그래프 - SizedBox( - width: 240, - height: 156, - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - // Beginner - SizedBox( - width: 72, - height: 156, - child: Column( - mainAxisAlignment: - MainAxisAlignment.end, - children: [ - SizedBox( - width: 44, - child: Text( - '${(beginnerProgress * 100).toInt()}%', - textAlign: TextAlign.center, - style: const TextStyle( - color: Color(0xFF111111), + Obx(() { + return SizedBox( + width: 240, + height: 156, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + // Beginner + SizedBox( + width: 72, + height: 156, + child: Column( + mainAxisAlignment: + MainAxisAlignment.end, + children: [ + SizedBox( + width: 44, + child: Text( + '${(controller.beginnerProgress.value * 100).toInt()}%', + textAlign: TextAlign.center, + style: const TextStyle( + color: Color(0xFF111111), + fontSize: 12, + fontWeight: FontWeight.w400, + height: 1.50, + letterSpacing: -0.30, + ), + ), + ), + Container( + width: 36, + height: controller.maxHeight * + controller + .beginnerProgress.value, + decoration: const ShapeDecoration( + color: Color(0xFFB1F2F2), + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.only( + topLeft: Radius.circular(4), + topRight: + Radius.circular(4), + ), + ), + ), + ), + const Text( + 'Beginner', + style: TextStyle( + color: Color(0xFF404040), fontSize: 12, fontWeight: FontWeight.w400, height: 1.50, letterSpacing: -0.30, ), ), - ), - Container( - width: 36, - height: - maxHeight * beginnerProgress, - decoration: const ShapeDecoration( - color: Color(0xFFB1F2F2), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(4), - topRight: Radius.circular(4), + ], + ), + ), + // Intermediate + SizedBox( + width: 72, + height: 156, + child: Column( + mainAxisAlignment: + MainAxisAlignment.end, + children: [ + SizedBox( + width: 44, + child: Text( + '${(controller.intermediateProgress.value * 100).toInt()}%', + textAlign: TextAlign.center, + style: const TextStyle( + color: Color(0xFF111111), + fontSize: 12, + fontWeight: FontWeight.w400, + height: 1.50, + letterSpacing: -0.30, ), ), ), - ), - const Text( - 'Beginner', - style: TextStyle( - color: Color(0xFF404040), - fontSize: 12, - fontWeight: FontWeight.w400, - height: 1.50, - letterSpacing: -0.30, + Container( + width: 36, + height: controller.maxHeight * + controller + .intermediateProgress + .value, + decoration: const ShapeDecoration( + color: Color(0xFFB1F2F2), + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.only( + topLeft: Radius.circular(4), + topRight: + Radius.circular(4), + ), + ), + ), ), - ), - ], - ), - ), - // Intermediate - SizedBox( - width: 72, - height: 156, - child: Column( - mainAxisAlignment: - MainAxisAlignment.end, - children: [ - SizedBox( - width: 44, - child: Text( - '${(intermediateProgress * 100).toInt()}%', - textAlign: TextAlign.center, - style: const TextStyle( - color: Color(0xFF111111), + const Text( + 'Intermediate', + style: TextStyle( + color: Color(0xFF404040), fontSize: 12, fontWeight: FontWeight.w400, height: 1.50, letterSpacing: -0.30, ), ), - ), - Container( - width: 36, - height: maxHeight * - intermediateProgress, - decoration: const ShapeDecoration( - color: Color(0xFFB1F2F2), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(4), - topRight: Radius.circular(4), + ], + ), + ), + // Advanced + SizedBox( + width: 72, + height: 156, + child: Column( + mainAxisAlignment: + MainAxisAlignment.end, + children: [ + SizedBox( + width: 44, + child: Text( + '${(controller.advancedProgress.value * 100).toInt()}%', + textAlign: TextAlign.center, + style: const TextStyle( + color: Color(0xFF111111), + fontSize: 12, + fontWeight: FontWeight.w400, + height: 1.50, + letterSpacing: -0.30, ), ), ), - ), - const Text( - 'Intermediate', - style: TextStyle( - color: Color(0xFF404040), - fontSize: 12, - fontWeight: FontWeight.w400, - height: 1.50, - letterSpacing: -0.30, + Container( + width: 36, + height: controller.maxHeight * + controller + .advancedProgress.value, + decoration: const ShapeDecoration( + color: Color(0xFFB1F2F2), + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.only( + topLeft: Radius.circular(4), + topRight: + Radius.circular(4), + ), + ), + ), ), - ), - ], - ), - ), - // Advanced - SizedBox( - width: 72, - height: 156, - child: Column( - mainAxisAlignment: - MainAxisAlignment.end, - children: [ - SizedBox( - width: 44, - child: Text( - '${(advancedProgress * 100).toInt()}%', - textAlign: TextAlign.center, - style: const TextStyle( - color: Color(0xFF111111), + const Text( + 'Advanced', + style: TextStyle( + color: Color(0xFF404040), fontSize: 12, fontWeight: FontWeight.w400, height: 1.50, letterSpacing: -0.30, ), ), - ), - Container( - width: 36, - height: - maxHeight * advancedProgress, - decoration: const ShapeDecoration( - color: Color(0xFFB1F2F2), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(4), - topRight: Radius.circular(4), - ), - ), - ), - ), - const Text( - 'Advanced', - style: TextStyle( - color: Color(0xFF404040), - fontSize: 12, - fontWeight: FontWeight.w400, - height: 1.50, - letterSpacing: -0.30, - ), - ), - ], + ], + ), ), - ), - ], - ), - ), + ], + ), + ); + }), + Container( width: 269, height: 44, diff --git a/economic_fe/lib/view/screens/mypage/bookmarked_post_page.dart b/economic_fe/lib/view/screens/mypage/bookmarked_post_page.dart index 25200ef..05035fe 100644 --- a/economic_fe/lib/view/screens/mypage/bookmarked_post_page.dart +++ b/economic_fe/lib/view/screens/mypage/bookmarked_post_page.dart @@ -25,11 +25,10 @@ class _BookmarkedPostPageState extends State { onPress: () => Get.back(), ), body: Obx(() { - // posts가 비어있는 경우 처리 if (controller.posts.isEmpty) { return const Center( child: Text( - '데이터가 없습니다.', + '스크랩 된 데이터가 없습니다.', style: TextStyle( fontSize: 16, color: Color(0xFF767676), @@ -59,11 +58,12 @@ class _BookmarkedPostPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // 게시글 유형 및 작성일 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - post["category"]!, + post["type"] ?? '', style: const TextStyle( color: Color(0xFF767676), fontSize: 12, @@ -73,7 +73,7 @@ class _BookmarkedPostPageState extends State { ), ), Text( - post["uploadTime"]!, + post["createdDate"] ?? '', style: const TextStyle( color: Color(0xFF767676), fontSize: 12, @@ -84,15 +84,16 @@ class _BookmarkedPostPageState extends State { ), ], ), - const SizedBox( - height: 6, - ), + const SizedBox(height: 6), + // 게시글 제목 GestureDetector( onTap: () { - // 게시글 상세 페이지 이동 + // 게시글 상세 페이지 이동 (추후 구현 가능) }, child: Text( - post["title"]!, + controller.argument == "좋아요 한 댓글" + ? post["content"] ?? '' + : post["title"] ?? '', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w500, @@ -101,25 +102,23 @@ class _BookmarkedPostPageState extends State { ), ), ), - controller.argument == "좋아요 한 댓글" - ? Column( - children: [ - const SizedBox( - height: 6, - ), - Text( - post["postTitle"]!, - style: const TextStyle( - color: Color(0xFFA2A2A2), - fontSize: 12, - fontWeight: FontWeight.w400, - height: 1.30, - letterSpacing: -0.30, - ), - ), - ], - ) - : const SizedBox(), + // 좋아요 한 댓글의 경우 원문글 제목 표시 + if (controller.argument == "좋아요 한 댓글") + Column( + children: [ + const SizedBox(height: 6), + Text( + post["postName"] ?? '', + style: const TextStyle( + color: Color(0xFFA2A2A2), + fontSize: 12, + fontWeight: FontWeight.w400, + height: 1.30, + letterSpacing: -0.30, + ), + ), + ], + ), ], ), ), diff --git a/economic_fe/lib/view/screens/mypage/my_learning_page.dart b/economic_fe/lib/view/screens/mypage/my_learning_page.dart index 922913f..27d0580 100644 --- a/economic_fe/lib/view/screens/mypage/my_learning_page.dart +++ b/economic_fe/lib/view/screens/mypage/my_learning_page.dart @@ -60,9 +60,9 @@ class _MyLearningPageState extends State { controller: controller.tabController, children: [ // 스크랩 한 퀴즈 화면 - _buildQuizAndLearningTab(controller: controller), + _buildScrapQuizzesTab(controller), // 스크랩 한 학습 화면 - _buildQuizAndLearningTab(controller: controller), + _buildScrapLearningTab(controller), // 스크랩 한 단어 화면 WordListView(controller: controller), ], @@ -75,152 +75,316 @@ class _MyLearningPageState extends State { } } -class _buildQuizAndLearningTab extends StatelessWidget { - const _buildQuizAndLearningTab({ - super.key, - required this.controller, - }); - - final MyLearningController controller; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Padding( - padding: const EdgeInsets.only(top: 22, left: 15), - child: Row( - children: [ - Obx( - () => LevelContainer( - level: '초급', - isSelected: controller.selectedLevel.value == '초급', - onTap: () => controller.updateSelectedLevel('초급'), - ), +Widget _buildScrapQuizzesTab(MyLearningController controller) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 22, left: 15), + child: Row( + children: [ + Obx( + () => LevelContainer( + level: '초급', + isSelected: controller.selectedLevel.value == 'BEGINNER', + onTap: () { + controller.updateSelectedLevel('BEGINNER'); + }, ), - const SizedBox(width: 8), - Obx( - () => LevelContainer( - level: '중급', - isSelected: controller.selectedLevel.value == '중급', - onTap: () => controller.updateSelectedLevel('중급'), - ), + ), + const SizedBox(width: 8), + Obx( + () => LevelContainer( + level: '중급', + isSelected: controller.selectedLevel.value == 'INTERMEDIATE', + onTap: () { + controller.updateSelectedLevel('INTERMEDIATE'); + }, ), - const SizedBox(width: 8), - Obx( - () => LevelContainer( - level: '고급', - isSelected: controller.selectedLevel.value == '고급', - onTap: () => controller.updateSelectedLevel('고급'), - ), + ), + const SizedBox(width: 8), + Obx( + () => LevelContainer( + level: '고급', + isSelected: controller.selectedLevel.value == 'ADVANCED', + onTap: () { + controller.updateSelectedLevel('ADVANCED'); + }, ), - ], + ), + ], + ), + ), + const SizedBox(height: 18), + // 스크랩 한 모든 학습 다시 보기 버튼 + Padding( + padding: const EdgeInsets.only(left: 16), + child: GestureDetector( + onTap: () { + // "스크랩 한 모든 학습 다시 보기" 버튼 동작 추가 가능 + }, + child: Obx( + () => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + controller.buttonText.value, + style: const TextStyle( + color: Color(0xFF067BD5), + fontSize: 14, + fontWeight: FontWeight.w700, + height: 1.40, + letterSpacing: -0.35, + ), + ), + const SizedBox(width: 3), + const Icon( + Icons.arrow_circle_right, + color: Color(0xff067bd5), + size: 15, + ), + ], + ), + Container( + width: 185, + height: 1, + color: const Color(0xff067bd5), + ), + ], + ), ), ), - const SizedBox(height: 18), - // 버튼 - Padding( - padding: const EdgeInsets.only(left: 16), - child: GestureDetector( - onTap: () { - // 버튼 클릭 시 동작 - }, - child: Obx( - () => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( + ), + const SizedBox(height: 15), + // 학습 리스트 + Expanded( + child: Obx(() { + if (controller.scrapQuizzes.isEmpty) { + return const Center( + child: Text( + '데이터가 없습니다.', + style: TextStyle( + fontSize: 16, + color: Color(0xFF767676), + ), + ), + ); + } + return ListView.builder( + itemCount: controller.scrapQuizzes.length, + itemBuilder: (context, index) { + final quiz = controller.scrapQuizzes[index]; + return Padding( + padding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: const Color(0xFFD9D9D9), + width: 1, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - controller.buttonText.value, - style: const TextStyle( - color: Color(0xFF067BD5), - fontSize: 14, - fontWeight: FontWeight.w700, - height: 1.40, - letterSpacing: -0.35, - ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + quiz['learningSetName'] ?? '', + style: const TextStyle( + color: Color(0xFF767676), + fontSize: 14, + fontWeight: FontWeight.w400, + ), + ), + const SizedBox(height: 2), + Text( + quiz['quizName'] ?? '', + style: const TextStyle( + color: Color(0xFF404040), + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ], ), - const SizedBox(width: 3), - const Icon( - Icons.arrow_circle_right, - color: Color(0xff067bd5), - size: 15, + GestureDetector( + onTap: () {}, + child: const Icon( + Icons.bookmark, + color: Palette.buttonColorGreen, + ), ), ], ), - Container( - width: 185, - height: 1, - color: const Color(0xff067bd5), - ), - ], + ), + ); + }, + ); + }), + ), + ], + ); +} + +Widget _buildScrapLearningTab(MyLearningController controller) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 22, left: 15), + child: Row( + children: [ + Obx( + () => LevelContainer( + level: '초급', + isSelected: controller.selectedLevel.value == 'BEGINNER', + onTap: () { + controller.updateSelectedLevel('BEGINNER'); + }, ), ), - ), + const SizedBox(width: 8), + Obx( + () => LevelContainer( + level: '중급', + isSelected: controller.selectedLevel.value == 'INTERMEDIATE', + onTap: () { + controller.updateSelectedLevel('INTERMEDIATE'); + }, + ), + ), + const SizedBox(width: 8), + Obx( + () => LevelContainer( + level: '고급', + isSelected: controller.selectedLevel.value == 'ADVANCED', + onTap: () { + controller.updateSelectedLevel('ADVANCED'); + }, + ), + ), + ], ), - const SizedBox(height: 15), - // 탭별 데이터 리스트 - Expanded( + ), + const SizedBox(height: 18), + // 스크랩 한 모든 학습 다시 보기 버튼 + Padding( + padding: const EdgeInsets.only(left: 16), + child: GestureDetector( + onTap: () { + // "스크랩 한 모든 학습 다시 보기" 버튼 동작 추가 가능 + }, child: Obx( - () => ListView.builder( - itemCount: controller.currentData.length, - itemBuilder: (context, index) { - final item = controller.currentData[index]; - return Padding( - padding: - const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(10), - border: Border.all( - color: const Color(0xFFD9D9D9), - width: 1, + () => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + controller.buttonText.value, + style: const TextStyle( + color: Color(0xFF067BD5), + fontSize: 14, + fontWeight: FontWeight.w700, + height: 1.40, + letterSpacing: -0.35, ), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - item['category']!, - style: const TextStyle( - color: Color(0xFF767676), - fontSize: 14, - fontWeight: FontWeight.w400, - ), + const SizedBox(width: 3), + const Icon( + Icons.arrow_circle_right, + color: Color(0xff067bd5), + size: 15, + ), + ], + ), + Container( + width: 185, + height: 1, + color: const Color(0xff067bd5), + ), + ], + ), + ), + ), + ), + const SizedBox(height: 15), + // 학습 리스트 + Expanded( + child: Obx(() { + if (controller.scrapConcepts.isEmpty) { + return const Center( + child: Text( + '데이터가 없습니다.', + style: TextStyle( + fontSize: 16, + color: Color(0xFF767676), + ), + ), + ); + } + return ListView.builder( + itemCount: controller.scrapConcepts.length, + itemBuilder: (context, index) { + final concept = controller.scrapConcepts[index]; + return Padding( + padding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: const Color(0xFFD9D9D9), + width: 1, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + concept['LearningSetName'] ?? '', + style: const TextStyle( + color: Color(0xFF767676), + fontSize: 14, + fontWeight: FontWeight.w400, ), - const SizedBox(height: 8), - Text( - item['title']!, - style: const TextStyle( - color: Color(0xFF404040), - fontSize: 18, - fontWeight: FontWeight.w600, - ), + ), + const SizedBox(height: 2), + Text( + concept['name'] ?? '', + style: const TextStyle( + color: Color(0xFF404040), + fontSize: 18, + fontWeight: FontWeight.w600, ), - ], - ), - GestureDetector( - onTap: () {}, - child: const Icon( - Icons.bookmark, - color: Palette.buttonColorGreen, ), + ], + ), + GestureDetector( + onTap: () {}, + child: const Icon( + Icons.bookmark, + color: Palette.buttonColorGreen, ), - ], - ), + ), + ], ), - ); - }, - ), - ), - ), - ], - ); - } + ), + ); + }, + ); + }), + ), + ], + ); } diff --git a/economic_fe/lib/view/screens/mypage/wrong_quiz_page.dart b/economic_fe/lib/view/screens/mypage/wrong_quiz_page.dart index 4fc383b..d775866 100644 --- a/economic_fe/lib/view/screens/mypage/wrong_quiz_page.dart +++ b/economic_fe/lib/view/screens/mypage/wrong_quiz_page.dart @@ -1,3 +1,4 @@ +import 'package:economic_fe/view/screens/quiz/quiz_page.dart'; import 'package:economic_fe/view/theme/palette.dart'; import 'package:economic_fe/view/widgets/custom_app_bar.dart'; import 'package:economic_fe/view/widgets/mypage/level_container.dart'; @@ -15,7 +16,11 @@ class WrongQuizPage extends StatefulWidget { class _WrongQuizPageState extends State { final WrongQuizController controller = Get.put(WrongQuizController()); - @override + void initState() { + super.initState(); + controller.fetchIncorrectQuestions(); // 초기 데이터 로드 + } + Widget build(BuildContext context) { return Scaffold( backgroundColor: Palette.background, @@ -33,7 +38,7 @@ class _WrongQuizPageState extends State { Obx( () => LevelContainer( level: '초급', - isSelected: controller.selectedLevel.value == '초급', + isSelected: controller.selectedLevel.value == 'BEGINNER', onTap: () => controller.updateSelectedLevel('초급'), ), ), @@ -41,7 +46,8 @@ class _WrongQuizPageState extends State { Obx( () => LevelContainer( level: '중급', - isSelected: controller.selectedLevel.value == '중급', + isSelected: + controller.selectedLevel.value == 'INTERMEDIATE', onTap: () => controller.updateSelectedLevel('중급'), ), ), @@ -49,7 +55,7 @@ class _WrongQuizPageState extends State { Obx( () => LevelContainer( level: '고급', - isSelected: controller.selectedLevel.value == '고급', + isSelected: controller.selectedLevel.value == 'ADVANCED', onTap: () => controller.updateSelectedLevel('고급'), ), ), @@ -99,60 +105,63 @@ class _WrongQuizPageState extends State { const SizedBox(height: 15), // 탭별 데이터 리스트 Expanded( - child: ListView.builder( - itemCount: controller.incorrectQuestions.length, - itemBuilder: (context, index) { - final item = controller.incorrectQuestions[index]; - return Padding( - padding: - const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(10), - border: Border.all( - color: const Color(0xFFD9D9D9), - width: 1, - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - item['category']!, - style: const TextStyle( - color: Color(0xFF767676), - fontSize: 14, - fontWeight: FontWeight.w400, + child: Obx( + () => controller.incorrectQuestions.isEmpty + ? const Text('틀린 문제가 없습니다.') + : ListView.builder( + itemCount: controller.incorrectQuestions.length, + itemBuilder: (context, index) { + final item = controller.incorrectQuestions[index]; + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 8), + child: GestureDetector( + onTap: () { + // 개별 퀴즈 화면으로 이동 + Get.to( + const QuizPage(), + arguments: { + 'quizId': item['id'], + }, // 전달할 quizId + ); + }, + child: Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: const Color(0xFFD9D9D9), + width: 1, + ), ), - ), - const SizedBox(height: 8), - Text( - item['title']!, - style: const TextStyle( - color: Color(0xFF404040), - fontSize: 18, - fontWeight: FontWeight.w600, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item['category']!, + style: const TextStyle( + color: Color(0xFF767676), + fontSize: 14, + fontWeight: FontWeight.w400, + ), + ), + const SizedBox(height: 2), + Text( + item['title']!, + style: const TextStyle( + color: Color(0xFF404040), + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ], ), ), - ], - ), - GestureDetector( - onTap: () {}, - child: const Icon( - Icons.bookmark, - color: Palette.buttonColorGreen, ), - ), - ], + ); + }, ), - ), - ); - }, ), ), ], diff --git a/economic_fe/lib/view/screens/quiz/quiz_page.dart b/economic_fe/lib/view/screens/quiz/quiz_page.dart index 30a39e4..165fac6 100644 --- a/economic_fe/lib/view/screens/quiz/quiz_page.dart +++ b/economic_fe/lib/view/screens/quiz/quiz_page.dart @@ -14,15 +14,15 @@ class QuizPage extends StatefulWidget { class _QuizPageState extends State { final QuizPageController controller = Get.put(QuizPageController()); + late final int? quizId; + + @override + void initState() { + super.initState(); + quizId = Get.arguments['quizId']; // Get.arguments에서 quizId 가져오기 + controller.fetchQuizById(quizId!); // 서버에서 퀴즈 데이터 로드 + } - final question = 'Q. 다음 중 복리 효과가 경제적 결과로 나타날 수 있는 상황으로 적절한 것은?'; - // int answer = - final List options = [ - '단기간 대출을 받은 경우', - '장기간 투자한 예금 계좌의 이자가 점점 커지는 경우', - '정기적으로 단리로 계산된 이자만 지급되는 경우', - '원금만 같아도 되는 상황' - ]; @override Widget build(BuildContext context) { int? selectedOption; @@ -82,41 +82,117 @@ class _QuizPageState extends State { // ), // ), // ), - appBar: CustomAppBar( - title: '고급퀴즈', // 레벨에 따른 이름 변경 필요 - icon: Icons.close, - onPress: () => controller.showModal(), - currentIndex: 1, - totalIndex: 3, - ), - body: Stack( - children: [ - QuizCard( - screenHeight: screenHeight, - screenWidth: screenWidth, - onPress: () {}, - option: 0, - question: question, - answerOptions: options, - isLast: false, - isQuiz: true, - answer: 2, - ), - // 모달창 - Obx(() { - return controller.isModalVisible.value - ? StopOptionModal( - closeModal: () => controller.hideModal(), - contents: '정말 퀴즈를 중단하시겠어요?', - keepBtnText: '계속할래요', - stopBtnText: '그만할래요', - keepFunc: () => controller.hideModal(), - stopFunc: () => controller.stopBtn(), - ) - : const SizedBox(); - }), - ], - ), + appBar: quizId == null + ? CustomAppBar( + title: '고급퀴즈', // 레벨에 따른 이름 변경 필요 + icon: Icons.close, + onPress: () => controller.showModal(), + currentIndex: 1, + totalIndex: 3, + ) + : CustomAppBar( + title: '학습 세트 이름', // 수정 필요 + icon: Icons.close, + onPress: () => controller.showModal(), + ), + body: Obx(() { + // 서버에서 받아온 퀴즈 데이터가 없고 quizId도 없는 경우 + if (quizId == null && controller.quizData.isEmpty) { + // 예시 데이터 사용 + const question = 'Q. 다음 중 복리 효과가 경제적 결과로 나타날 수 있는 상황으로 적절한 것은?'; + const options = [ + '단기간 대출을 받은 경우', + '장기간 투자한 예금 계좌의 이자가 점점 커지는 경우', + '정기적으로 단리로 계산된 이자만 지급되는 경우', + '원금만 같아도 되는 상황' + ]; + const int option = 0; // 기본값으로 설정 + const int correctAnswerIndex = 1; // 예시 데이터의 정답 인덱스 + + return Stack( + children: [ + QuizCard( + screenHeight: screenHeight, + screenWidth: screenWidth, + onPress: () {}, + option: option, + question: question, + answerOptions: options, + isLast: false, + isQuiz: true, + answer: correctAnswerIndex, + ), + // 모달창 + Obx(() { + return controller.isModalVisible.value + ? StopOptionModal( + closeModal: () => controller.hideModal(), + contents: '정말 퀴즈를 중단하시겠어요?', + keepBtnText: '계속할래요', + stopBtnText: '그만할래요', + keepFunc: () => controller.hideModal(), + stopFunc: () => controller.stopBtn(), + ) + : const SizedBox(); + }), + ], + ); + } + + // 서버에서 받아온 퀴즈 데이터가 있는 경우 + if (controller.quizData.isEmpty) { + return const Center( + child: CircularProgressIndicator(), + ); + } + + // 서버에서 가져온 퀴즈 데이터 + final question = controller.quizData['question'] ?? ''; + + // choiceList 처리 + final List choiceList = + controller.quizData['choiceList']?['choiceList'] ?? []; + final List answerOptions = choiceList + .map((choice) => choice['content'].toString()) // 명시적으로 String 변환 + .toList(); + + // 정답 인덱스 계산 + final correctAnswerIndex = answerOptions + .indexWhere((option) => option == controller.quizData['answer']); + + // 퀴즈 타입에 따른 옵션 설정 + final quizType = controller.quizData['type'] ?? ''; + final option = (quizType == "OX") ? 1 : 2; + + return Stack( + children: [ + QuizCard( + screenHeight: screenHeight, + screenWidth: screenWidth, + onPress: () {}, + option: option, // 타입에 따른 옵션 값 전달 + question: question, + answerOptions: answerOptions, + isLast: false, + isQuiz: true, + answer: correctAnswerIndex, + ), + // 모달창 + Obx(() { + return controller.isModalVisible.value + ? StopOptionModal( + closeModal: () => controller.hideModal(), + contents: '정말 퀴즈를 중단하시겠어요?', + keepBtnText: '계속할래요', + stopBtnText: '그만할래요', + keepFunc: () => controller.hideModal(), + stopFunc: () => controller.stopBtn(), + ) + : const SizedBox(); + }), + ], + ); + }), ); } } diff --git a/economic_fe/lib/view/widgets/quiz_card.dart b/economic_fe/lib/view/widgets/quiz_card.dart index 3196fac..3e93445 100644 --- a/economic_fe/lib/view/widgets/quiz_card.dart +++ b/economic_fe/lib/view/widgets/quiz_card.dart @@ -98,7 +98,7 @@ class _QuizCardState extends State { Container( width: MediaQuery.of(context).size.width - 32, padding: const EdgeInsets.symmetric( - horizontal: 32, vertical: 24), + horizontal: 16, vertical: 24), decoration: const BoxDecoration( borderRadius: BorderRadius.only( bottomLeft: Radius.circular(10), @@ -124,7 +124,9 @@ class _QuizCardState extends State { return controller.clickCheckBtn.value ? Padding( padding: const EdgeInsets.symmetric( - vertical: 8), + vertical: 8, + horizontal: 16, + ), child: MultipleOptionContainer( widget: widget, optionNum: index + 1, @@ -148,7 +150,9 @@ class _QuizCardState extends State { child: Padding( padding: const EdgeInsets.symmetric( - vertical: 8), + vertical: 8, + horizontal: 16, + ), child: MultipleOptionContainer( widget: widget, optionNum: index + 1, @@ -465,8 +469,8 @@ class OXOptionContainer extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - width: 120, - height: 120, + width: (MediaQuery.of(context).size.width - 80) / 2, + height: (MediaQuery.of(context).size.width - 80) / 2, decoration: isSelected ? isQuiz ? ShapeDecoration( @@ -506,7 +510,6 @@ class OXOptionContainer extends StatelessWidget { fontWeight: FontWeight.w500, height: 0.18, ), - textAlign: TextAlign.center, ), ), ); diff --git a/economic_fe/lib/view_model/home_controller.dart b/economic_fe/lib/view_model/home_controller.dart index 84a5a09..176dfc2 100644 --- a/economic_fe/lib/view_model/home_controller.dart +++ b/economic_fe/lib/view_model/home_controller.dart @@ -1,3 +1,5 @@ +import 'package:economic_fe/data/services/remote_data_source.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart'; // GoRouter import class HomeController extends GetxController { @@ -41,4 +43,41 @@ class HomeController extends GetxController { goalSets[index]++; } } + + // 진도율 (초기값은 0) + RxDouble beginnerProgress = 0.0.obs; + RxDouble intermediateProgress = 0.0.obs; + RxDouble advancedProgress = 0.0.obs; + + // 최대 그래프 높이 + final double maxHeight = 120.0; + + @override + void onInit() { + super.onInit(); + fetchProgress(); + } + + /// 레벨별 학습 진도율 조회 + Future fetchProgress() async { + try { + final response = await RemoteDataSource.getProgress(); + + if (response != null && response['isSuccess'] == true) { + final progressData = response['results']['progress'] ?? {}; + + // 서버 응답 값이 존재하면 업데이트 + beginnerProgress.value = (progressData['BEGINNER'] ?? 0.0).toDouble(); + intermediateProgress.value = + (progressData['INTERMEDIATE'] ?? 0.0).toDouble(); + advancedProgress.value = (progressData['ADVANCED'] ?? 0.0).toDouble(); + + debugPrint('학습 진도율 데이터 업데이트 완료'); + } else { + debugPrint('fetchProgress 실패: 응답이 null이거나 isSuccess가 false'); + } + } catch (e) { + debugPrint('fetchProgress Error: $e'); + } + } } diff --git a/economic_fe/lib/view_model/mypage/bookmarked_posts_controller.dart b/economic_fe/lib/view_model/mypage/bookmarked_posts_controller.dart index 5e64d80..5e49ad4 100644 --- a/economic_fe/lib/view_model/mypage/bookmarked_posts_controller.dart +++ b/economic_fe/lib/view_model/mypage/bookmarked_posts_controller.dart @@ -1,58 +1,68 @@ +import 'package:economic_fe/data/services/remote_data_source.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart'; class BookmarkedPostsController extends GetxController { final String argument = Get.arguments ?? '스크랩 한 글'; - // 게시글 데이터 리스트 - final RxList> posts = >[].obs; - - // Mock 데이터 - final List> bookmarkedPosts = [ - { - "category": "일반 게시판", - "title": "스크랩한 게시글 제목 1", - "uploadTime": "2시간 전", - }, - { - "category": "경제 톡톡", - "title": "스크랩한 게시글 제목 2", - "uploadTime": "1일 전", - }, - ]; - - final List> likedPosts = [ - { - "category": "일반 게시판", - "title": "좋아요한 게시글 제목 1", - "uploadTime": "3시간 전", - }, - { - "category": "경제 톡톡", - "title": "좋아요한 게시글 제목 2", - "uploadTime": "2일 전", - }, - ]; - - final List> likedComments = [ - { - "category": "일반 게시판", - "title": "좋아요한 댓글 내용", - "uploadTime": "4시간 전", - "postTitle": "원문글 제목", - }, - ]; + RxList posts = [].obs; + RxInt totalPage = 0.obs; + RxInt currentPage = 0.obs; @override void onInit() { super.onInit(); + fetchData(); + } + + /// Get.arguments 값에 따라 다른 API 호출 + Future fetchData() async { + try { + dynamic response; + + if (argument == '스크랩 한 글') { + response = await RemoteDataSource.fetchScrapedPosts(); + } else if (argument == '좋아요 한 글') { + response = await RemoteDataSource.fetchLikedPosts(); + } else if (argument == '좋아요 한 댓글') { + response = await RemoteDataSource.fetchLikedComments(); + } else { + debugPrint("fetchData Error: 잘못된 argument 값입니다."); + return; + } + + if (response != null) { + final List rawPosts; + + if (argument == '좋아요 한 댓글') { + // 좋아요 한 댓글 API의 경우 + rawPosts = response['likeCommentResponses'] ?? []; + } else { + // 다른 API들의 경우 + rawPosts = response['postList'] ?? []; + } + + // type 변환 로직 적용 + final List> processedPosts = rawPosts.map((post) { + final postMap = Map.from(post); + final type = postMap['type'] ?? ''; + final transformedType = (type == 'ECONOMY_TALK') ? '경제 톡톡' : '일반 게시판'; + + return { + ...postMap, // 기존 데이터 + 'type': transformedType, // 변환된 type 값 + if (argument == '좋아요 한 댓글') 'postTitle': postMap['postName'] ?? '' + }; + }).toList(); - // arguments에 따라 posts 초기화 - if (argument == '스크랩 한 글') { - posts.assignAll(bookmarkedPosts); - } else if (argument == '좋아요 한 글') { - posts.assignAll(likedPosts); - } else if (argument == '좋아요 한 댓글') { - posts.assignAll(likedComments); + posts.value = processedPosts; + totalPage.value = response['results']['totalPage'] ?? 0; + currentPage.value = response['results']['currentPage'] ?? 0; + } else { + debugPrint("fetchData Error: 응답이 null이거나 성공하지 않았습니다."); + } + } catch (e) { + debugPrint("fetchData Error: $e"); } } } diff --git a/economic_fe/lib/view_model/mypage/my_learning_controller.dart b/economic_fe/lib/view_model/mypage/my_learning_controller.dart index aeef65d..ee763a6 100644 --- a/economic_fe/lib/view_model/mypage/my_learning_controller.dart +++ b/economic_fe/lib/view_model/mypage/my_learning_controller.dart @@ -7,20 +7,12 @@ class MyLearningController extends GetxController with GetSingleTickerProviderStateMixin { late TabController tabController; - // 탭별 데이터 - final List> quizzes = [ - {'category': '금융', 'title': '저축과 이자'}, - {'category': '경제', 'title': '수요와 공급'}, - ]; - - final List> learningMaterials = [ - {'category': '금융', 'title': '주식의 기초'}, - {'category': '경제', 'title': '물가와 환율'}, - ]; - var selectedConsonant = "ㄱ".obs; var keyword = "".obs; var typeValue = true.obs; //false : 검색 + var selectedLevel = 'BEGINNER'.obs; // 초기값 설정 + var scrapConcepts = >[].obs; // 스크랩한 개념 학습 목록 + var scrapQuizzes = >[].obs; // 스크랩한 퀴즈 목록 //용어 사전 데이터 불러오기 Future> getDictionaryList( @@ -29,7 +21,7 @@ class MyLearningController extends GetxController try { print("start"); dynamic response; - + if (type) { response = await RemoteDataSource.getDictionary(page, text); print("response :: $response"); @@ -98,36 +90,6 @@ class MyLearningController extends GetxController 'ㅎ' ]; - // 스크랩한 단어 리스트 (임시 데이터 포함) - var scrapedWords = [ - DictionaryModel( - termName: "인플레이션", - termDescription: "물가 상승을 의미합니다.", - ), - DictionaryModel( - termName: "GDP", - termDescription: "국내총생산으로 한 국가에서 일정 기간 동안 생산된 총 상품과 서비스의 시장 가치입니다.", - ), - DictionaryModel( - termName: "환율", - termDescription: "다른 나라 화폐와의 교환 비율을 나타냅니다.", - ), - DictionaryModel( - termName: "금리", - termDescription: "돈을 빌리거나 맡길 때의 이자 비율을 의미합니다.", - ), - DictionaryModel( - termName: "리세션", - termDescription: "경제 활동이 줄어들고 경제 성장률이 감소하는 경기 후퇴를 뜻합니다.", - ), - ].obs; - - // 현재 선택된 탭 데이터 - RxList> currentData = >[].obs; - - // 현재 선택된 레벨 - RxString selectedLevel = '초급'.obs; - // 버튼 텍스트 RxString buttonText = '스크랩 한 모든 퀴즈 다시 풀기'.obs; @@ -138,6 +100,8 @@ class MyLearningController extends GetxController // 초기 데이터 설정 updateCurrentData(0); + fetchScrapConcepts(); + fetchScrapQuizzes(); // 탭 변경 시 데이터와 버튼 텍스트 업데이트 tabController.addListener(() { @@ -150,11 +114,9 @@ class MyLearningController extends GetxController void updateCurrentData(int index) { switch (index) { case 0: - currentData.assignAll(quizzes); buttonText.value = '스크랩 한 모든 퀴즈 다시 풀기'; break; case 1: - currentData.assignAll(learningMaterials); buttonText.value = '스크랩 한 모든 학습 다시 보기'; break; } @@ -162,5 +124,47 @@ class MyLearningController extends GetxController void updateSelectedLevel(String level) { selectedLevel.value = level; + fetchScrapConcepts(); // 레벨 변경 시 데이터 로드 + fetchScrapQuizzes(); + } + + Future fetchScrapConcepts() async { + try { + final response = + await RemoteDataSource.getScrapConcepts(selectedLevel.value); + if (response != null && response['isSuccess'] == true) { + final List rawConcepts = + response['results']['scrapConceptList'] ?? []; + scrapConcepts.value = rawConcepts.map((concept) { + return Map.from(concept); + }).toList(); + } else { + scrapConcepts.clear(); + debugPrint('fetchScrapConcepts 실패: ${response?['message']}'); + } + } catch (e) { + debugPrint('fetchScrapConcepts Error: $e'); + scrapConcepts.clear(); + } + } + + Future fetchScrapQuizzes() async { + try { + final response = + await RemoteDataSource.getScrapQuizzes(selectedLevel.value); + if (response != null && response['isSuccess'] == true) { + final List rawQuizzes = + response['results']['scrapQuizList'] ?? []; + scrapQuizzes.value = rawQuizzes.map((concept) { + return Map.from(concept); + }).toList(); + } else { + scrapQuizzes.clear(); + debugPrint('fetchScrapQuizzes 실패: ${response?['message']}'); + } + } catch (e) { + debugPrint('fetchScrapQuizzes Error: $e'); + scrapQuizzes.clear(); + } } } diff --git a/economic_fe/lib/view_model/mypage/wrong_quiz_controller.dart b/economic_fe/lib/view_model/mypage/wrong_quiz_controller.dart index b0e8de3..0d4ebb1 100644 --- a/economic_fe/lib/view_model/mypage/wrong_quiz_controller.dart +++ b/economic_fe/lib/view_model/mypage/wrong_quiz_controller.dart @@ -1,15 +1,75 @@ +import 'package:economic_fe/data/services/remote_data_source.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart'; class WrongQuizController extends GetxController { - // 현재 선택된 레벨 - RxString selectedLevel = '초급'.obs; + var selectedLevel = 'BEGINNER'.obs; // 초기값 설정 + var incorrectQuestions = >[].obs; + /// 레벨 선택 업데이트 void updateSelectedLevel(String level) { - selectedLevel.value = level; + selectedLevel.value = _convertLevelToApiValue(level); + fetchIncorrectQuestions(); // 레벨 변경 시 데이터 가져오기 } - final List> incorrectQuestions = [ - {'category': '금융', 'title': '복리 계산'}, - {'category': '경제', 'title': 'GDP와 GNI'}, - ]; + // // 임시 데이터 리스트 + // final List> incorrectQuestions = [ + // {'category': '금융', 'title': '복리 계산'}, + // {'category': '경제', 'title': 'GDP와 GNI'}, + // ]; + + // 틀린 문제 가져오기 + Future fetchIncorrectQuestions() async { + try { + final response = + await RemoteDataSource.fetchIncorrectQuestions(selectedLevel.value); + + if (response != null && response['isSuccess'] == true) { + final failQuizList = response['results']['failQuizList'] as List; + + incorrectQuestions.value = failQuizList.map((quiz) { + return { + 'id': int.tryParse(quiz['id'].toString()) ?? 0, // `id`를 정수로 변환 + 'title': quiz['name'] ?? '', // `name` 값이 없으면 빈 문자열로 처리 + 'category': quiz['learningSet'] ?? '' // `learningSet` 값 처리 + }; + }).toList(); + } else { + debugPrint('Fetch failed: ${response?['message']}'); + incorrectQuestions.clear(); + } + } catch (e) { + debugPrint('fetchIncorrectQuestions Error: $e'); + } + } + + // // 개별 퀴즈 화면으로 이동 + // Future fetchQuizDetails(int quizId) async { + // try { + // final response = await RemoteDataSource.fetchQuizById(quizId); + // if (response != null && response['isSuccess'] == true) { + // final quizData = response['results']; + // // 퀴즈 화면으로 이동 + // Get.toNamed('/quiz', arguments: quizData); + // } else { + // Get.snackbar('Error', '퀴즈 데이터를 불러올 수 없습니다.'); + // } + // } catch (e) { + // Get.snackbar('Error', '퀴즈 데이터를 가져오는 중 오류가 발생했습니다.'); + // } + // } + + /// UI에서 사용되는 레벨을 API 값으로 변환 + String _convertLevelToApiValue(String level) { + switch (level) { + case '초급': + return 'BEGINNER'; + case '중급': + return 'INTERMEDIATE'; + case '고급': + return 'ADVANCED'; + default: + return 'BEGINNER'; + } + } } diff --git a/economic_fe/lib/view_model/quiz/quiz_page_controller.dart b/economic_fe/lib/view_model/quiz/quiz_page_controller.dart index 5aace3e..2dafda2 100644 --- a/economic_fe/lib/view_model/quiz/quiz_page_controller.dart +++ b/economic_fe/lib/view_model/quiz/quiz_page_controller.dart @@ -1,8 +1,10 @@ +import 'package:economic_fe/data/services/remote_data_source.dart'; import 'package:get/get.dart'; class QuizPageController extends GetxController { // 학습 중단 확인창 표시 여부 관리 var isModalVisible = false.obs; + var quizData = {}.obs; void showModal() { isModalVisible.value = true; @@ -13,6 +15,20 @@ class QuizPageController extends GetxController { } void stopBtn() { - Get.toNamed('/learning_list'); + Get.back(); + } + + // 퀴즈 데이터 가져오기 + Future fetchQuizById(int quizId) async { + try { + final response = await RemoteDataSource.fetchQuizById(quizId); + if (response != null && response['isSuccess'] == true) { + quizData.value = response['results']; + } else { + print('퀴즈 데이터 로드 실패: ${response?['message']}'); + } + } catch (e) { + print('fetchQuizById 에러: $e'); + } } }