diff --git a/mobile/lib/api/komentar.dart b/mobile/lib/api/komentar.dart new file mode 100644 index 0000000..122c026 --- /dev/null +++ b/mobile/lib/api/komentar.dart @@ -0,0 +1,37 @@ +import 'dart:convert'; + +import 'package:dio/dio.dart'; +import 'package:mobile/model/komentar.dart'; +import 'package:mobile/model/profile_secure.dart'; +import 'package:mobile/model/user.dart'; + +Future postKomentar(int reviewId, String komentar) async { + var profile = await SecureProfile.getStorage(); + var formData = FormData.fromMap({"komentar": komentar}); + try { + var response = await Dio() + .post("https://travelliu.yaudahlah.my.id/api/komentar/$reviewId", + options: Options( + headers: { + 'Authorization': 'Bearer ${profile.getApiKey()}', + "Accept": "application/json", + }, + ), + data: formData); + var data = response.data; + var user = User.empty(name: profile.name!); + return Komentar(id: data["id"] as int, komentar: komentar, user: user); + } on DioError catch (e) { + if (e.response != null) { + var response = e.response!; + if (response.statusCode == 401) { + await profile.setLoggedOut(); + return Future.error("Session expired"); + } + if (response.statusCode == 400) { + return Future.error(response.data); + } + } + return Future.error("Gagal untuk mempost review"); + } +} diff --git a/mobile/lib/api/review.dart b/mobile/lib/api/review.dart index 2cc18b1..4d79858 100644 --- a/mobile/lib/api/review.dart +++ b/mobile/lib/api/review.dart @@ -12,7 +12,7 @@ Future> getAllReview() async { List decoded = jsonDecode(response.body); List reviews = []; for (var review in decoded) { - reviews.add(Review.fromJson(review, true)); + reviews.add(Review.fromJson(review)); } return reviews; } else { @@ -20,6 +20,20 @@ Future> getAllReview() async { } } +Future getReviewById(int id) async { + try { + var response = await http + .get(Uri.parse("https://travelliu.yaudahlah.my.id/api/review/$id")); + if (response.statusCode == 200) { + return Review.fromJson(jsonDecode(response.body)); + } + Future.error("Tidak dapat mengambil review"); + } catch (e) { + return Future.error(e); + } + return Future.error("Tidak dapat mengambil review"); +} + Future createReview( {required String nama, required String alamat, diff --git a/mobile/lib/api/user.dart b/mobile/lib/api/user.dart index aa49bb7..a6f825a 100644 --- a/mobile/lib/api/user.dart +++ b/mobile/lib/api/user.dart @@ -18,9 +18,10 @@ Future loginUser(String email, String password) async { var decoded = jsonDecode(response.body); var token = decoded["token"]; var id = decoded["user"]["id"]; + var name = decoded["user"]["name"]; var profile = await SecureProfile.getStorage(); - await profile.setLoggedIn(id, token); + await profile.setLoggedIn(id, token, name); } else if (response.statusCode == 400) { return Future.error("Email atau password salah"); } else { diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 6602ddc..f341ab4 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -54,7 +54,7 @@ class MyApp extends StatelessWidget { ), routes: { HomeScreen.routeName: (context) => const HomeScreen(), - ReviewDetailScreen.routeName: (context) => const ReviewDetailScreen(), + ReviewDetailScreen.routeName: (context) => ReviewDetailScreen(), FormReviewScreen.routeName: (context) => const FormReviewScreen(), FormTikumScreen.routeName: (context) => const FormTikumScreen(), RegisterScreen.routeName: (context) => RegisterScreen(), diff --git a/mobile/lib/model/komentar.dart b/mobile/lib/model/komentar.dart new file mode 100644 index 0000000..c8c1168 --- /dev/null +++ b/mobile/lib/model/komentar.dart @@ -0,0 +1,14 @@ +import 'package:mobile/model/user.dart'; + +class Komentar { + final int id; + final String komentar; + User? user; + + Komentar({required this.id, required this.komentar, this.user}); + + factory Komentar.fromJson(Map json) { + var user = User.empty(id: json["user"]["id"], name: json["user"]["name"]); + return Komentar(id: json["id"], komentar: json["komentar"], user: user); + } +} diff --git a/mobile/lib/model/profile_secure.dart b/mobile/lib/model/profile_secure.dart index a0a1ba4..e787fb3 100644 --- a/mobile/lib/model/profile_secure.dart +++ b/mobile/lib/model/profile_secure.dart @@ -25,30 +25,32 @@ class SecureProfile { bool isLoggedIn; int? userId; - String? apiKey; + String? apiKey, name; SecureProfile( {required this.storage, required this.isLoggedIn, this.userId, - this.apiKey}); + this.apiKey, + this.name}); static Future getStorage() async { var storage = await SharedPreferences.getInstance(); var userId = storage.getInt("user_id"); var apiKey = storage.getString("api_key"); + var name = storage.getString("user_name"); bool loggedIn = false; - - if ((userId == null || userId == -1) || (apiKey == null || apiKey == "")) { - await storage.setInt("user_id", -1); - await storage.setString("api_key", ""); - } else { + if (userId != null && apiKey != null && name != null) { loggedIn = true; } return SecureProfile( - storage: storage, isLoggedIn: loggedIn, apiKey: apiKey, userId: userId); + storage: storage, + isLoggedIn: loggedIn, + apiKey: apiKey, + userId: userId, + name: name); } bool getLoggedInStatus() { @@ -63,15 +65,21 @@ class SecureProfile { return userId; } - Future setLoggedIn(int userId, String apiKey) async { + String? getUserName() { + return name; + } + + Future setLoggedIn(int userId, String apiKey, String name) async { await storage.setInt("user_id", userId); await storage.setString("api_key", apiKey); + await storage.setString("user_name", name); isLoggedIn = true; } Future setLoggedOut() async { await storage.remove("api_key"); await storage.remove("user_id"); + await storage.remove("user_name"); isLoggedIn = false; } } diff --git a/mobile/lib/model/review.dart b/mobile/lib/model/review.dart index 3233be8..8d231c4 100644 --- a/mobile/lib/model/review.dart +++ b/mobile/lib/model/review.dart @@ -1,3 +1,5 @@ +import 'package:mobile/model/komentar.dart'; + import "./user.dart"; class Review { @@ -6,8 +8,9 @@ class Review { final String namaTempat, alamat, review, photo; final User user; final double? longitude, latitude; + List komentar = []; - const Review( + Review( {required this.userId, required this.id, required this.photo, @@ -18,17 +21,13 @@ class Review { this.user = const User.empty(), this.longitude, this.latitude, + this.komentar = const [], this.numKomentar = 0}); - factory Review.fromJson(Map json, bool concatReview) { + factory Review.fromJson(Map json) { var user = User.fromJson(json["user"]); - String review = json["review"]; - if (concatReview) { - if (review.length > 200) { - review = review.substring(0, 195); - review = review + " ..."; - } - } + List finalKomentar = []; + int komentarcount = 0; if (json["latitude"] != null) { if (json["latitude"] is int) { @@ -46,6 +45,12 @@ class Review { json["longitude"] = json["longitude"] as double; } + if (json.containsKey("komentar")) { + List komentarTemp = json["komentar"]; + for (var komentar in komentarTemp) { + finalKomentar.add(Komentar.fromJson(komentar)); + } + } return Review( userId: json['user_id'], id: json['id'], @@ -53,11 +58,12 @@ class Review { namaTempat: json["nama_tempat"], photo: json["photo"], rating: json["rating"].toDouble(), - review: review, + review: json["review"], latitude: json["latitude"], longitude: json["longitude"], - numKomentar: json["komentar_count"], - user: user); + numKomentar: json["komentar_count"] ?? finalKomentar.length, + user: user, + komentar: finalKomentar); } } diff --git a/mobile/lib/screen/_global/components/shimmer/detail_review_shimmer.dart b/mobile/lib/screen/_global/components/shimmer/detail_review_shimmer.dart new file mode 100644 index 0000000..58144da --- /dev/null +++ b/mobile/lib/screen/_global/components/shimmer/detail_review_shimmer.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; +import 'package:mobile/screen/_global/components/shimmer/shimmer_container.dart'; + +class ShimmerDetailReview extends StatelessWidget { + const ShimmerDetailReview({Key? key}) : super(key: key); + final double textHeight = 10; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ShimmerContainer( + height: 200, + ), + const SizedBox( + height: 20, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(50), + child: const ShimmerContainer( + width: 40, + height: 40, + ), + ), + const SizedBox( + width: 15, + ), + const ShimmerContainer( + width: 200, + ) + ], + ), + const SizedBox( + height: 20, + ), + const ShimmerContainer( + width: 150, + ), + const SizedBox( + height: 20, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ShimmerContainer( + width: MediaQuery.of(context).size.width * .7, + height: textHeight, + ), + const SizedBox( + height: 5, + ), + ShimmerContainer( + height: textHeight, + width: MediaQuery.of(context).size.width * .8, + ), + const SizedBox( + height: 5, + ), + ShimmerContainer( + height: textHeight, + width: MediaQuery.of(context).size.width * .6, + ), + const SizedBox( + height: 5, + ), + ShimmerContainer( + height: textHeight, + width: MediaQuery.of(context).size.width * .9, + ), + const SizedBox( + height: 5, + ), + ShimmerContainer( + height: textHeight, + width: MediaQuery.of(context).size.width * .9, + ), + ], + ), + const SizedBox(height: 20), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ShimmerContainer( + height: textHeight, + width: MediaQuery.of(context).size.width * .8, + ), + const SizedBox( + height: 5, + ), + ShimmerContainer( + width: MediaQuery.of(context).size.width * .6, + height: textHeight, + ), + const SizedBox( + height: 5, + ), + ShimmerContainer( + height: textHeight, + width: MediaQuery.of(context).size.width * .9, + ), + const SizedBox( + height: 5, + ), + ShimmerContainer( + height: textHeight, + width: MediaQuery.of(context).size.width * .9, + ), + ], + ) + ], + )) + ], + ); + } +} diff --git a/mobile/lib/screen/form_login/login_screen.dart b/mobile/lib/screen/form_login/login_screen.dart index e4ef1f6..95cde0c 100644 --- a/mobile/lib/screen/form_login/login_screen.dart +++ b/mobile/lib/screen/form_login/login_screen.dart @@ -81,17 +81,6 @@ class _LoginScreenState extends State { padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 30), child: Stack( children: [ - TextButton( - style: ButtonStyle( - overlayColor: MaterialStateProperty.all(Colors.black12)), - onPressed: _handleKembaliButton, - child: const Text( - "Kembali", - style: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 15), - )), Center( child: SingleChildScrollView( reverse: true, @@ -217,6 +206,20 @@ class _LoginScreenState extends State { ), ), ), + ElevatedButton( + style: ButtonStyle( + overlayColor: MaterialStateProperty.all(Colors.black12), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: _handleKembaliButton, + child: const Text( + "Kembali", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 15), + ), + ), ], ), ))); diff --git a/mobile/lib/screen/form_register/register_screen.dart b/mobile/lib/screen/form_register/register_screen.dart index cea088c..544078f 100644 --- a/mobile/lib/screen/form_register/register_screen.dart +++ b/mobile/lib/screen/form_register/register_screen.dart @@ -95,18 +95,6 @@ class RegisterScreenState extends State { padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 30), child: Stack( children: [ - TextButton( - style: ButtonStyle( - overlayColor: - MaterialStateProperty.all(Colors.black12)), - onPressed: _handleKembaliButton, - child: const Text( - "Kembali", - style: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 15), - )), Center( child: SingleChildScrollView( reverse: true, @@ -266,7 +254,21 @@ class RegisterScreenState extends State { ), ), ), - ) + ), + ElevatedButton( + style: ButtonStyle( + overlayColor: MaterialStateProperty.all(Colors.black12), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: _handleKembaliButton, + child: const Text( + "Kembali", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 15), + ), + ), ], )), ), diff --git a/mobile/lib/screen/form_review/form_review_screen.dart b/mobile/lib/screen/form_review/form_review_screen.dart index 3b494f4..62d72d0 100644 --- a/mobile/lib/screen/form_review/form_review_screen.dart +++ b/mobile/lib/screen/form_review/form_review_screen.dart @@ -144,17 +144,6 @@ class _FormReviewScreenState extends State { padding: const EdgeInsets.all(12.0), child: Stack( children: [ - TextButton( - style: ButtonStyle( - overlayColor: MaterialStateProperty.all(Colors.black12)), - onPressed: _handleKembaliButton, - child: const Text( - "Kembali", - style: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 15), - )), Center( child: SingleChildScrollView( reverse: true, @@ -349,7 +338,21 @@ class _FormReviewScreenState extends State { ), ), ), - ) + ), + ElevatedButton( + style: ButtonStyle( + overlayColor: MaterialStateProperty.all(Colors.black12), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: _handleKembaliButton, + child: const Text( + "Kembali", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 15), + ), + ), ], ), ), diff --git a/mobile/lib/screen/form_tikum/form_tikum_screen.dart b/mobile/lib/screen/form_tikum/form_tikum_screen.dart index 3a2fbe9..41f3919 100644 --- a/mobile/lib/screen/form_tikum/form_tikum_screen.dart +++ b/mobile/lib/screen/form_tikum/form_tikum_screen.dart @@ -87,18 +87,6 @@ class _FormTikumScreenState extends State { padding: const EdgeInsets.all(12.0), child: Stack( children: [ - TextButton( - style: ButtonStyle( - overlayColor: MaterialStateProperty.all(Colors.black12)), - onPressed: _handleKembaliButton, - child: const Text( - "Kembali", - style: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 15), - ), - ), Center( child: SingleChildScrollView( reverse: true, @@ -347,7 +335,21 @@ class _FormTikumScreenState extends State { ), ), ), - ) + ), + ElevatedButton( + style: ButtonStyle( + overlayColor: MaterialStateProperty.all(Colors.black12), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: _handleKembaliButton, + child: const Text( + "Kembali", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 15), + ), + ), ], ), ), diff --git a/mobile/lib/screen/home/components/timeline/timeline_card.dart b/mobile/lib/screen/home/components/timeline/timeline_card.dart index 9222300..fffcd25 100644 --- a/mobile/lib/screen/home/components/timeline/timeline_card.dart +++ b/mobile/lib/screen/home/components/timeline/timeline_card.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:mobile/model/review.dart'; import 'package:mobile/screen/_global/components/image_network.dart'; -import 'package:mobile/screen/form_review/form_review_screen.dart'; import 'package:mobile/screen/profile_detail/profile_detail_screen.dart'; import "dart:math" as math; @@ -15,6 +14,8 @@ class TimelineCard extends StatelessWidget { @override Widget build(BuildContext context) { + String review = data.review; + if (review.length > 200) review = data.review.substring(0, 195) + "..."; return Container( margin: const EdgeInsets.only(bottom: 5), child: InkWell( @@ -80,7 +81,7 @@ class TimelineCard extends StatelessWidget { children: [ Expanded( child: Text( - data.review, + review, textAlign: TextAlign.left, )) ], diff --git a/mobile/lib/screen/profile_detail/profile_detail_screen.dart b/mobile/lib/screen/profile_detail/profile_detail_screen.dart index c675aa4..bfd71f2 100644 --- a/mobile/lib/screen/profile_detail/profile_detail_screen.dart +++ b/mobile/lib/screen/profile_detail/profile_detail_screen.dart @@ -1,10 +1,8 @@ import 'package:flutter/material.dart'; -import "dart:math" as math; import 'package:mobile/model/profile.dart'; import 'package:mobile/api/user.dart'; import 'package:mobile/screen/_global/components/profile_card.dart'; import 'package:mobile/screen/_global/components/shimmer/profile_shimmer.dart'; -import 'package:mobile/screen/home/components/profile/myprofile/my_profile.dart'; class ProfilePeopleScreenArguments { final int id; diff --git a/mobile/lib/screen/review_detail/components/komentar_card.dart b/mobile/lib/screen/review_detail/components/komentar_card.dart new file mode 100644 index 0000000..d92f8a1 --- /dev/null +++ b/mobile/lib/screen/review_detail/components/komentar_card.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:mobile/model/komentar.dart'; + +class KomentarCard extends StatelessWidget { + final Komentar data; + + const KomentarCard({Key? key, required this.data}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: Colors.black, + )), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text( + data.user!.name, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + Text(data.komentar) + ]), + ); + } +} diff --git a/mobile/lib/screen/review_detail/components/review_detail.dart b/mobile/lib/screen/review_detail/components/review_detail.dart new file mode 100644 index 0000000..875ea08 --- /dev/null +++ b/mobile/lib/screen/review_detail/components/review_detail.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; +import 'package:mobile/model/review.dart'; +import 'package:mobile/screen/_global/components/image_network.dart'; +import 'package:mobile/screen/review_detail/components/review_komentar.dart'; +import "dart:math" as math; + +import 'package:url_launcher/url_launcher.dart'; + +class ReviewDetail extends StatelessWidget { + final Review data; + ReviewDetail({Key? key, required this.data}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ListView( + children: [ + ImageNetworkWShimmer( + link: data.photo[0] == "/" + ? "https://travelliu.yaudahlah.my.id${data.photo}" + : data.photo, + ), + const SizedBox( + height: 10, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Column( + children: [ + __ReviewSection(data: data), + const SizedBox( + height: 10, + ), + DetailReviewKomentarSection(data: data) + ], + )), + ], + ); + } +} + +class __ReviewSection extends StatelessWidget { + final Review data; + final int randomForProfile = math.Random().nextInt(1000); + __ReviewSection({Key? key, required this.data}) : super(key: key); + + void _handleMapsButton() async { + if (data.latitude == null || data.longitude == null) { + return; + } + String link = + "https://www.google.com/maps/search/?api=1&query=${data.latitude}%2C${data.longitude}"; + final Uri _url = Uri.parse(link); + if (!await launchUrl(_url, mode: LaunchMode.externalApplication)) { + throw const AlertDialog(title: Text("Failed to open the link")); + } + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(50), + child: ImageNetworkWShimmer( + link: + "https://www.thiswaifudoesnotexist.net/example-$randomForProfile.jpg", + width: 40, + height: 40, + ), + ), + const SizedBox( + width: 15, + ), + Text(data.user.name) + ], + ), + const SizedBox( + height: 20, + ), + Row( + children: [ + const Icon(Icons.star_border_outlined, color: Colors.yellow), + Text(data.rating.toStringAsFixed(2)), + const SizedBox( + width: 10, + ), + Text( + data.namaTempat, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ], + ), + const SizedBox( + height: 10, + ), + Text( + data.review, + textAlign: TextAlign.justify, + ), + const SizedBox( + height: 10, + ), + Row( + children: [ + ElevatedButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all(Colors.black)), + onPressed: _handleMapsButton, + child: const Center( + child: Icon( + Icons.map_outlined, + color: Colors.white, + ), + ), + ), + const SizedBox(width: 20), + Expanded(child: Text(data.alamat)) + ], + ) + ], + ); + } +} diff --git a/mobile/lib/screen/review_detail/components/review_komentar.dart b/mobile/lib/screen/review_detail/components/review_komentar.dart new file mode 100644 index 0000000..1797af2 --- /dev/null +++ b/mobile/lib/screen/review_detail/components/review_komentar.dart @@ -0,0 +1,135 @@ +import 'package:flutter/material.dart'; +import 'package:mobile/api/komentar.dart'; +import 'package:mobile/model/komentar.dart'; +import 'package:mobile/model/profile_secure.dart'; +import 'package:mobile/model/review.dart'; +import 'package:mobile/screen/review_detail/components/komentar_card.dart'; +import 'package:mobile/utils/show_snackbar.dart'; + +class DetailReviewKomentarSection extends StatefulWidget { + final Review data; + DetailReviewKomentarSection({Key? key, required this.data}) : super(key: key); + + @override + State createState() => + _DetailReviewKomentarSectionState(); +} + +class _DetailReviewKomentarSectionState + extends State { + bool isLoggedIn = false; + final TextEditingController _commentController = TextEditingController(); + final _formKey = GlobalKey(); + late List comments; + + void _checkLogin() async { + var profile = await SecureProfile.getStorage(); + setState(() { + isLoggedIn = profile.getLoggedInStatus(); + }); + } + + String? _validRequired(String? txt) { + if (txt == null || txt.isEmpty) { + return "Komentar harus diisi"; + } + return null; + } + + @override + void initState() { + super.initState(); + setState(() { + comments = widget.data.komentar; + }); + _checkLogin(); + } + + @override + Widget build(BuildContext context) { + void _handlePostComment() async { + if (_formKey.currentState!.validate()) { + try { + showDialog( + context: context, + builder: (context) => const Center( + child: SizedBox( + height: 100, + width: 100, + child: CircularProgressIndicator(), + ), + )); + String komentar = _commentController.text; + Komentar sentKomentar = await postKomentar(widget.data.id, komentar); + _commentController.clear(); + setState(() { + comments.insert(0, sentKomentar); + }); + Navigator.pop(context); + } catch (e) { + Navigator.pop(context); + ShowSnackBar(context, e.toString()); + } + } + } + + return Column( + children: [ + isLoggedIn + ? Form( + key: _formKey, + child: Row( + children: [ + Expanded( + child: TextFormField( + controller: _commentController, + validator: _validRequired, + decoration: InputDecoration( + hintText: "Masukkan komentar", + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: + const BorderSide(color: Colors.black)), + contentPadding: + const EdgeInsets.symmetric(horizontal: 12), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + ), + ), + const SizedBox( + width: 13, + ), + ElevatedButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all(Colors.black)), + onPressed: _handlePostComment, + child: const Center( + child: Text( + "Kirim", + style: TextStyle( + color: Colors.white, fontWeight: FontWeight.bold), + ), + ), + ), + ], + )) + : const SizedBox.shrink(), + for (var komentar in comments) + Column( + children: [ + const SizedBox( + height: 10, + ), + KomentarCard(data: komentar), + ], + ), + const SizedBox( + height: 20, + ), + ], + ); + } +} diff --git a/mobile/lib/screen/review_detail/review_detail_screen.dart b/mobile/lib/screen/review_detail/review_detail_screen.dart index 1d53664..877a295 100644 --- a/mobile/lib/screen/review_detail/review_detail_screen.dart +++ b/mobile/lib/screen/review_detail/review_detail_screen.dart @@ -1,4 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:mobile/api/review.dart'; +import 'package:mobile/model/review.dart'; +import 'package:mobile/screen/_global/components/shimmer/detail_review_shimmer.dart'; +import 'package:mobile/screen/home/home_screen.dart'; +import 'package:mobile/screen/review_detail/components/review_detail.dart'; +import 'package:mobile/utils/show_snackbar.dart'; class ReviewDetailScreenArguments { final int id; @@ -8,14 +14,14 @@ class ReviewDetailScreenArguments { class ReviewDetailScreen extends StatelessWidget { static String routeName = "/review-detail"; - const ReviewDetailScreen({Key? key}) : super(key: key); + ReviewDetailScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { var arg = ModalRoute.of(context)!.settings.arguments as ReviewDetailScreenArguments; - var id = arg.id; + Future futureReview = getReviewById(id); return Scaffold( appBar: AppBar( @@ -23,13 +29,49 @@ class ReviewDetailScreen extends StatelessWidget { backgroundColor: Colors.white, ), body: Center( - child: ElevatedButton( - child: Text("Hello from $id Go Back"), - onPressed: () { - Navigator.pop(context); + child: FutureBuilder( + future: futureReview, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + return ReviewDetail(data: snapshot.data!); + } + if (snapshot.hasError) { + return const _GagalDetailReview(); + } + return const ShimmerDetailReview(); }, ), ), ); } } + +class _GagalDetailReview extends StatelessWidget { + const _GagalDetailReview({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("Gagal dalam mengambil review"), + ElevatedButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all(Colors.black)), + onPressed: () { + Navigator.pop(context); + }, + child: const Text( + "Back to timeline", + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ], + ), + ); + } +}