Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Hoàng Văn Đạt
mypoint_flutter_app
Commits
efb4662c
Commit
efb4662c
authored
Aug 08, 2025
by
DatHV
Browse files
update campaign 7day
parent
4c376d38
Changes
94
Show whitespace changes
Inline
Side-by-side
lib/configs/api_paths.dart
View file @
efb4662c
...
@@ -78,4 +78,19 @@ class APIPaths {
...
@@ -78,4 +78,19 @@ class APIPaths {
static
const
String
inviteFriendCampaigns
=
"/campaign/api/v3.0/invite-friend/campaigns"
;
static
const
String
inviteFriendCampaigns
=
"/campaign/api/v3.0/invite-friend/campaigns"
;
static
const
String
phoneInviteFriend
=
"/campaign/api/v3.0/invite-friend/invite"
;
static
const
String
phoneInviteFriend
=
"/campaign/api/v3.0/invite-friend/invite"
;
static
const
String
rewardOpportunityGetList
=
"/rewardOpportunityGetList/1.0.0"
;
static
const
String
rewardOpportunityGetList
=
"/rewardOpportunityGetList/1.0.0"
;
static
const
String
rewardOpportunityOpenRequest
=
"/rewardOpportunityOpenRequest/1.0.0"
;
static
const
String
productsCustomerLikes
=
"/product/api/v2.0/customer/likes"
;
static
const
String
orderHistoryCategories
=
"/order/api/v1.0/orders/categories"
;
static
const
String
getTransactionOrderHistory
=
"/order/api/v1.0/my-orders"
;
static
const
String
customerContractRequestSearch
=
"/customerContractRequestSearch/1.0.0"
;
static
const
String
customerContractSearchHistoryGetList
=
"/customerContractSearchHistoryGetList/1.0.0"
;
static
const
String
customerContractDelete
=
"/customerContractDelete/1.0.0"
;
static
const
String
getProductVnTraSold
=
"/product/api/v2.0/vntra_sold"
;
static
const
String
detailMyPackageVnTra
=
"/product/api/v2.0/vntra_sold/%@"
;
static
const
String
getCampaignLiveTransactions
=
"/campaign/api/v3.0/%@/live-transactions"
;
static
const
String
getCampaignMissions
=
"/campaign/api/v3.0/%@/missions"
;
static
const
String
getCampaignReward
=
"/campaign/api/v3.0/%@/reward"
;
static
const
String
submitCampaignMission
=
"/campaign/api/v3.0/%@/mission/%@/submit"
;
static
const
String
getQuizCampaign
=
"/quiz/api/v1.0/quiz/%@/"
;
static
const
String
quizSubmitCampaign
=
"/quiz/api/v1.0/quiz/%@/submit/"
;
}
}
\ No newline at end of file
lib/directional/directional_action_type.dart
View file @
efb4662c
...
@@ -43,7 +43,7 @@ enum DirectionalScreenName {
...
@@ -43,7 +43,7 @@ enum DirectionalScreenName {
memberShip
,
memberShip
,
ranking
,
ranking
,
mobileTopup
,
mobileTopup
,
carrierPackage
,
mobileTopupData
,
topup
,
topup
,
register
,
register
,
gifts
,
gifts
,
...
@@ -96,6 +96,7 @@ enum DirectionalScreenName {
...
@@ -96,6 +96,7 @@ enum DirectionalScreenName {
pointHunting
,
pointHunting
,
orderMenu
,
orderMenu
,
unknown
,
unknown
,
transactionHistories
,
}
}
extension
DirectionalScreenRouterExtension
on
DirectionalScreenName
{
extension
DirectionalScreenRouterExtension
on
DirectionalScreenName
{
...
@@ -208,7 +209,7 @@ extension DirectionalScreenNameExtension on DirectionalScreenName {
...
@@ -208,7 +209,7 @@ extension DirectionalScreenNameExtension on DirectionalScreenName {
return
"APP_SCREEN_RANKING_PROGRAM"
;
return
"APP_SCREEN_RANKING_PROGRAM"
;
case
DirectionalScreenName
.
mobileTopup
:
case
DirectionalScreenName
.
mobileTopup
:
return
"APP_SCREEN_PRODUCT_MOBILE_TOPUP"
;
return
"APP_SCREEN_PRODUCT_MOBILE_TOPUP"
;
case
DirectionalScreenName
.
carrierPackage
:
case
DirectionalScreenName
.
mobileTopupData
:
return
"APP_SCREEN_MOBILE_TOPUP_DATA"
;
return
"APP_SCREEN_MOBILE_TOPUP_DATA"
;
case
DirectionalScreenName
.
topup
:
case
DirectionalScreenName
.
topup
:
return
"APP_SCREEN_TOPUP"
;
return
"APP_SCREEN_TOPUP"
;
...
@@ -314,6 +315,8 @@ extension DirectionalScreenNameExtension on DirectionalScreenName {
...
@@ -314,6 +315,8 @@ extension DirectionalScreenNameExtension on DirectionalScreenName {
return
"UNKNOWN"
;
return
"UNKNOWN"
;
case
DirectionalScreenName
.
orderMenu
:
case
DirectionalScreenName
.
orderMenu
:
return
"APP_SCREEN_ORDER_MENU"
;
return
"APP_SCREEN_ORDER_MENU"
;
case
DirectionalScreenName
.
transactionHistories
:
return
"APP_SCREEN_TRANSACTION_HISTORIES"
;
}
}
}
}
...
...
lib/directional/directional_screen.dart
View file @
efb4662c
...
@@ -82,14 +82,14 @@ class DirectionalScreen {
...
@@ -82,14 +82,14 @@ class DirectionalScreen {
case
DirectionalScreenName
.
topup
||
DirectionalScreenName
.
mobileTopup
:
case
DirectionalScreenName
.
topup
||
DirectionalScreenName
.
mobileTopup
:
Get
.
toNamed
(
phoneTopUpScreen
);
Get
.
toNamed
(
phoneTopUpScreen
);
return
true
;
return
true
;
case
DirectionalScreenName
.
carrierPackage
:
BaseWebViewInput
input
=
BaseWebViewInput
(
url:
clickActionParam
??
""
);
Get
.
toNamed
(
baseWebViewScreen
,
arguments:
input
);
return
true
;
case
DirectionalScreenName
.
productMobileCard
:
case
DirectionalScreenName
.
productMobileCard
:
Get
.
toNamed
(
productMobileCardScreen
);
Get
.
toNamed
(
productMobileCardScreen
);
return
true
;
return
true
;
case
DirectionalScreenName
.
simService
:
case
DirectionalScreenName
.
simService
:
BaseWebViewInput
input
=
BaseWebViewInput
(
url:
"https://mypoint.uudaigoicuoc.com/"
);
Get
.
toNamed
(
baseWebViewScreen
,
arguments:
input
);
return
true
;
case
DirectionalScreenName
.
mobileTopupData
:
Get
.
toNamed
(
dataNetworkServiceScreen
);
Get
.
toNamed
(
dataNetworkServiceScreen
);
return
true
;
return
true
;
case
DirectionalScreenName
.
pointBack
:
case
DirectionalScreenName
.
pointBack
:
...
@@ -98,14 +98,13 @@ class DirectionalScreen {
...
@@ -98,14 +98,13 @@ class DirectionalScreen {
case
DirectionalScreenName
.
gamesBundle
:
case
DirectionalScreenName
.
gamesBundle
:
Get
.
toNamed
(
gameTabScreen
,
arguments:
{
"can_back_button"
:
true
});
Get
.
toNamed
(
gameTabScreen
,
arguments:
{
"can_back_button"
:
true
});
return
true
;
return
true
;
case
DirectionalScreenName
.
vnTraPackage
||
DirectionalScreenName
.
familyMedon
:
case
DirectionalScreenName
.
vnTraPackage
||
DirectionalScreenName
.
familyMedon
||
DirectionalScreenName
.
voucher
:
Get
.
toNamed
(
voucherDetailScreen
,
arguments:
{
"productId"
:
int
.
parse
(
clickActionParam
??
""
)});
Get
.
toNamed
(
voucherDetailScreen
,
arguments:
{
"productId"
:
int
.
parse
(
clickActionParam
??
""
)});
return
true
;
return
true
;
case
DirectionalScreenName
.
viewDeepLink
||
DirectionalScreenName
.
viewDeepLinkInApp
:
case
DirectionalScreenName
.
viewDeepLink
||
DirectionalScreenName
.
viewDeepLinkInApp
:
if
((
clickActionParam
??
""
).
isEmpty
)
return
true
;
if
((
clickActionParam
??
""
).
isEmpty
)
return
true
;
const
replaceId1
=
'{{customerId}}'
;
const
replaceId1
=
'{{customerId}}'
;
const
replaceId2
=
'{customerId}'
;
const
replaceId2
=
'{customerId}'
;
final
customerId
=
DataPreference
.
instance
.
profile
?.
workerSite
?.
customerId
??
""
;
final
customerId
=
DataPreference
.
instance
.
profile
?.
workerSite
?.
customerId
??
""
;
String
urlString
=
clickActionParam
!.
urlDecoded
;
String
urlString
=
clickActionParam
!.
urlDecoded
;
if
(
urlString
.
contains
(
replaceId1
))
{
if
(
urlString
.
contains
(
replaceId1
))
{
...
@@ -118,11 +117,9 @@ class DirectionalScreen {
...
@@ -118,11 +117,9 @@ class DirectionalScreen {
Uri
?
uri
=
Uri
.
tryParse
(
urlString
);
Uri
?
uri
=
Uri
.
tryParse
(
urlString
);
if
(
uri
==
null
)
return
true
;
if
(
uri
==
null
)
return
true
;
final
requestId
=
const
Uuid
().
v4
();
// Cần package `uuid`
final
requestId
=
const
Uuid
().
v4
();
// Cần package `uuid`
final
updatedUri
=
uri
.
replace
(
queryParameters:
{
final
updatedUri
=
uri
.
replace
(
queryParameters:
{...
uri
.
queryParameters
,
'aff_sub3'
:
requestId
});
...
uri
.
queryParameters
,
LaunchMode
mode
=
'aff_sub3'
:
requestId
,
type
==
DirectionalScreenName
.
viewDeepLink
?
LaunchMode
.
externalApplication
:
LaunchMode
.
platformDefault
;
});
LaunchMode
mode
=
type
==
DirectionalScreenName
.
viewDeepLink
?
LaunchMode
.
externalApplication
:
LaunchMode
.
platformDefault
;
forceOpen
(
url:
updatedUri
,
mode:
mode
);
forceOpen
(
url:
updatedUri
,
mode:
mode
);
return
true
;
return
true
;
case
DirectionalScreenName
.
refundHistory
:
case
DirectionalScreenName
.
refundHistory
:
...
@@ -131,9 +128,34 @@ class DirectionalScreen {
...
@@ -131,9 +128,34 @@ class DirectionalScreen {
case
DirectionalScreenName
.
inviteFriend
:
case
DirectionalScreenName
.
inviteFriend
:
Get
.
toNamed
(
inviteFriendCampaignScreen
);
Get
.
toNamed
(
inviteFriendCampaignScreen
);
return
true
;
return
true
;
case
DirectionalScreenName
.
dailyCheckin
:
case
DirectionalScreenName
.
dailyCheckin
||
DirectionalScreenName
.
dailyCheckinScreen
:
Get
.
toNamed
(
dailyCheckInScreen
);
Get
.
toNamed
(
dailyCheckInScreen
);
return
true
;
return
true
;
case
DirectionalScreenName
.
favorite
:
Get
.
toNamed
(
vouchersScreen
,
arguments:
{
"favorite"
:
true
});
return
true
;
case
DirectionalScreenName
.
transactionHistories
:
Get
.
toNamed
(
transactionHistoryScreen
);
return
true
;
case
DirectionalScreenName
.
electricBill
:
Get
.
toNamed
(
electricPaymentScreen
);
return
true
;
case
DirectionalScreenName
.
listPaymentOfElectric
:
Get
.
toNamed
(
electricPaymentHistoryScreen
);
return
true
;
case
DirectionalScreenName
.
myVnTraPackage
:
Get
.
toNamed
(
trafficServiceScreen
);
return
true
;
case
DirectionalScreenName
.
campaignSevenDayScreen
:
Get
.
toNamed
(
campaignSevenDayScreen
);
return
true
;
case
DirectionalScreenName
.
workerProfile
:
Get
.
toNamed
(
personalEditScreen
);
return
true
;
case
DirectionalScreenName
.
surveyCampaign
:
// if ((clickActionParam ?? '').isEmpty) return false;
Get
.
toNamed
(
surveyQuestionScreen
,
arguments:
{
"quizId"
:
"1"
});
return
true
;
default
:
default
:
print
(
"Không nhận diện được action type:
$clickActionType
"
);
print
(
"Không nhận diện được action type:
$clickActionType
"
);
return
false
;
return
false
;
...
@@ -147,10 +169,7 @@ Future<bool> forceOpen({required Uri url, LaunchMode mode = LaunchMode.platformD
...
@@ -147,10 +169,7 @@ Future<bool> forceOpen({required Uri url, LaunchMode mode = LaunchMode.platformD
await
launchUrl
(
await
launchUrl
(
url
,
url
,
mode:
LaunchMode
.
externalApplication
,
mode:
LaunchMode
.
externalApplication
,
webViewConfiguration:
const
WebViewConfiguration
(
webViewConfiguration:
const
WebViewConfiguration
(
enableJavaScript:
true
,
headers:
<
String
,
String
>{}),
enableJavaScript:
true
,
headers:
<
String
,
String
>{},
),
);
);
return
true
;
return
true
;
}
}
...
@@ -162,7 +181,7 @@ Future<void> openAppStore(String url) async {
...
@@ -162,7 +181,7 @@ Future<void> openAppStore(String url) async {
if
(
await
canLaunchUrl
(
uri
))
{
if
(
await
canLaunchUrl
(
uri
))
{
await
launchUrl
(
await
launchUrl
(
uri
,
uri
,
mode:
LaunchMode
.
externalApplication
,
// ⚠️ Phải dùng mode này
mode:
LaunchMode
.
externalApplication
,
);
);
}
else
{
}
else
{
debugPrint
(
"⚠️ Không thể mở URL:
$url
"
);
debugPrint
(
"⚠️ Không thể mở URL:
$url
"
);
...
...
lib/extensions/color_extension.dart
View file @
efb4662c
import
'dart:ui'
;
import
'dart:ui'
;
import
'package:flutter/material.dart'
;
extension
ColorExtension
on
Color
{
extension
ColorExtension
on
Color
{
Color
get
invert
=>
Color
.
fromARGB
(
Color
get
invert
=>
Color
.
fromARGB
(
alpha
,
alpha
,
...
@@ -7,4 +9,10 @@ extension ColorExtension on Color {
...
@@ -7,4 +9,10 @@ extension ColorExtension on Color {
255
-
green
,
255
-
green
,
255
-
blue
,
255
-
blue
,
);
);
Color
get
contrastTextColor
{
//(luminance perceived brightness)
final
brightness
=
(
0.299
*
red
+
0.587
*
green
+
0.114
*
blue
);
return
brightness
>
186
?
Colors
.
black
:
Colors
.
white
;
}
}
}
\ No newline at end of file
lib/extensions/date_format.dart
View file @
efb4662c
...
@@ -2,4 +2,5 @@ class DateFormat {
...
@@ -2,4 +2,5 @@ class DateFormat {
static
const
String
serverTimezone
=
"yyyy-MM-dd'T'HH:mm:ssZ"
;
static
const
String
serverTimezone
=
"yyyy-MM-dd'T'HH:mm:ssZ"
;
static
const
String
server
=
"yyyy-MM-dd HH:mm:ss"
;
static
const
String
server
=
"yyyy-MM-dd HH:mm:ss"
;
static
const
String
ddMMyyyy
=
"dd/MM/yyyy"
;
static
const
String
ddMMyyyy
=
"dd/MM/yyyy"
;
static
const
String
ddMMyyyyhhmm
=
"dd/MM/yyyy hh:mm"
;
}
}
lib/main.dart
View file @
efb4662c
import
'package:flutter/material.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_localizations/flutter_localizations.dart'
;
import
'package:get/get.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/preference/data_preference.dart'
;
import
'package:mypoint_flutter_app/preference/data_preference.dart'
;
import
'package:mypoint_flutter_app/preference/point/point_manager.dart'
;
import
'package:mypoint_flutter_app/preference/point/point_manager.dart'
;
import
'package:mypoint_flutter_app/resouce/base_color.dart'
;
import
'package:mypoint_flutter_app/shared/router_gage.dart'
;
import
'package:mypoint_flutter_app/shared/router_gage.dart'
;
void
main
(
)
async
{
void
main
(
)
async
{
...
@@ -20,9 +22,18 @@ class MyApp extends StatelessWidget {
...
@@ -20,9 +22,18 @@ class MyApp extends StatelessWidget {
debugShowCheckedModeBanner:
false
,
debugShowCheckedModeBanner:
false
,
initialRoute:
'/splash'
,
initialRoute:
'/splash'
,
theme:
ThemeData
(
theme:
ThemeData
(
colorScheme:
ColorScheme
.
fromSwatch
(
primarySwatch:
Colors
.
deepPurple
),
colorScheme:
ColorScheme
.
fromSwatch
(
primarySwatch:
Colors
.
brown
),
primaryColor:
Color
s
.
deepPurple
,
primaryColor:
Base
Color
.
primary500
,
),
),
locale:
const
Locale
(
'vi'
),
supportedLocales:
const
[
Locale
(
'vi'
,
'VN'
),
// Vietnamese
],
localizationsDelegates:
const
[
GlobalMaterialLocalizations
.
delegate
,
GlobalWidgetsLocalizations
.
delegate
,
GlobalCupertinoLocalizations
.
delegate
,
],
// home: SplashScreen(),
// home: SplashScreen(),
getPages:
RouterPage
.
pages
(),
getPages:
RouterPage
.
pages
(),
);
);
...
...
lib/networking/restful_api_request.dart
View file @
efb4662c
...
@@ -18,8 +18,12 @@ import '../screen/affiliate/model/affiliate_category_model.dart';
...
@@ -18,8 +18,12 @@ import '../screen/affiliate/model/affiliate_category_model.dart';
import
'../screen/affiliate/model/affiliate_product_top_sale_model.dart'
;
import
'../screen/affiliate/model/affiliate_product_top_sale_model.dart'
;
import
'../screen/affiliate/model/cashback_overview_model.dart'
;
import
'../screen/affiliate/model/cashback_overview_model.dart'
;
import
'../screen/affiliate_brand_detail/models/affiliate_brand_detail_model.dart'
;
import
'../screen/affiliate_brand_detail/models/affiliate_brand_detail_model.dart'
;
import
'../screen/campaign7day/models/campaign_7day_info_model.dart'
;
import
'../screen/campaign7day/models/campaign_7day_mission_model.dart'
;
import
'../screen/campaign7day/models/campaign_7day_reward_model.dart'
;
import
'../screen/daily_checkin/daily_checkin_models.dart'
;
import
'../screen/daily_checkin/daily_checkin_models.dart'
;
import
'../screen/data_network_service/product_network_data_model.dart'
;
import
'../screen/data_network_service/product_network_data_model.dart'
;
import
'../screen/electric_payment/customer_contract_object_model.dart'
;
import
'../screen/faqs/faqs_model.dart'
;
import
'../screen/faqs/faqs_model.dart'
;
import
'../screen/game/models/game_bundle_item_model.dart'
;
import
'../screen/game/models/game_bundle_item_model.dart'
;
import
'../screen/history_point_cashback/models/history_point_cashback_model.dart'
;
import
'../screen/history_point_cashback/models/history_point_cashback_model.dart'
;
...
@@ -45,10 +49,14 @@ import '../screen/otp/model/create_otp_response_model.dart';
...
@@ -45,10 +49,14 @@ import '../screen/otp/model/create_otp_response_model.dart';
import
'../screen/otp/model/otp_verify_response_model.dart'
;
import
'../screen/otp/model/otp_verify_response_model.dart'
;
import
'../screen/pageDetail/model/campaign_detail_model.dart'
;
import
'../screen/pageDetail/model/campaign_detail_model.dart'
;
import
'../screen/pageDetail/model/detail_page_rule_type.dart'
;
import
'../screen/pageDetail/model/detail_page_rule_type.dart'
;
import
'../screen/quiz_campaign/quiz_campaign_model.dart'
;
import
'../screen/register_campaign/model/registration_form_package_model.dart'
;
import
'../screen/register_campaign/model/registration_form_package_model.dart'
;
import
'../screen/splash/splash_screen_viewmodel.dart'
;
import
'../screen/splash/splash_screen_viewmodel.dart'
;
import
'../screen/topup/models/brand_network_model.dart'
;
import
'../screen/topup/models/brand_network_model.dart'
;
import
'../screen/traffic_service/traffic_service_model.dart'
;
import
'../screen/transaction/history/transaction_category_model.dart'
;
import
'../screen/transaction/history/transaction_history_model.dart'
;
import
'../screen/transaction/history/transaction_history_model.dart'
;
import
'../screen/transaction/history/transaction_history_response_model.dart'
;
import
'../screen/transaction/model/order_product_payment_response_model.dart'
;
import
'../screen/transaction/model/order_product_payment_response_model.dart'
;
import
'../screen/transaction/model/payment_bank_account_info_model.dart'
;
import
'../screen/transaction/model/payment_bank_account_info_model.dart'
;
import
'../screen/transaction/model/payment_method_model.dart'
;
import
'../screen/transaction/model/payment_method_model.dart'
;
...
@@ -299,6 +307,13 @@ extension RestfullAPIClientAllApi on RestfulAPIClient {
...
@@ -299,6 +307,13 @@ extension RestfullAPIClientAllApi on RestfulAPIClient {
);
);
}
}
Future
<
BaseResponseModel
<
List
<
ProductModel
>>>
productsCustomerLikes
(
Json
body
)
async
{
return
requestNormal
(
APIPaths
.
productsCustomerLikes
,
Method
.
GET
,
body
,
(
data
)
{
final
list
=
data
as
List
<
dynamic
>;
return
list
.
map
((
e
)
=>
ProductModel
.
fromJson
(
e
)).
toList
();
});
}
Future
<
BaseResponseModel
<
ProductModel
>>
getProduct
(
int
id
)
async
{
Future
<
BaseResponseModel
<
ProductModel
>>
getProduct
(
int
id
)
async
{
final
path
=
APIPaths
.
getProductDetail
.
replaceAll
(
"%@"
,
id
.
toString
());
final
path
=
APIPaths
.
getProductDetail
.
replaceAll
(
"%@"
,
id
.
toString
());
return
requestNormal
(
path
,
Method
.
GET
,
{},
(
data
)
=>
ProductModel
.
fromJson
(
data
as
Json
));
return
requestNormal
(
path
,
Method
.
GET
,
{},
(
data
)
=>
ProductModel
.
fromJson
(
data
as
Json
));
...
@@ -722,4 +737,122 @@ extension RestfullAPIClientAllApi on RestfulAPIClient {
...
@@ -722,4 +737,122 @@ extension RestfullAPIClientAllApi on RestfulAPIClient {
return
CheckInDataModel
.
fromJson
(
data
as
Json
);
return
CheckInDataModel
.
fromJson
(
data
as
Json
);
});
});
}
}
Future
<
BaseResponseModel
<
SubmitCheckInData
>>
submitCheckIn
()
async
{
String
?
token
=
DataPreference
.
instance
.
token
??
""
;
final
body
=
{
"access_token"
:
token
,
};
return
requestNormal
(
APIPaths
.
rewardOpportunityOpenRequest
,
Method
.
POST
,
body
,
(
data
)
{
return
SubmitCheckInData
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
TransactionHistoryResponse
>>
getTransactionHistoryResponse
(
Json
body
)
async
{
return
requestNormal
(
APIPaths
.
getTransactionOrderHistory
,
Method
.
GET
,
body
,
(
data
)
{
return
TransactionHistoryResponse
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
List
<
TransactionCategoryModel
>>>
getTransactionHistoryCategories
()
async
{
return
requestNormal
(
APIPaths
.
orderHistoryCategories
,
Method
.
GET
,
{},
(
data
)
{
final
list
=
data
as
List
<
dynamic
>;
return
list
.
map
((
e
)
=>
TransactionCategoryModel
.
fromJson
(
e
)).
toList
();
});
}
Future
<
BaseResponseModel
<
CustomerContractModel
>>
customerContractRequestSearch
(
String
maKH
)
async
{
String
?
token
=
DataPreference
.
instance
.
token
??
""
;
final
body
=
{
"access_token"
:
token
,
'ma_khang'
:
maKH
,
};
return
requestNormal
(
APIPaths
.
customerContractRequestSearch
,
Method
.
POST
,
body
,
(
data
)
{
return
CustomerContractModel
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
List
<
CustomerContractModel
>>>
customerContractSearchHistoryGetList
()
async
{
String
?
token
=
DataPreference
.
instance
.
token
??
""
;
final
body
=
{
"access_token"
:
token
,
};
return
requestNormal
(
APIPaths
.
customerContractSearchHistoryGetList
,
Method
.
POST
,
body
,
(
data
)
{
final
list
=
data
as
List
<
dynamic
>;
return
list
.
map
((
e
)
=>
CustomerContractModel
.
fromJson
(
e
)).
toList
();
});
}
Future
<
BaseResponseModel
<
bool
>>
customerContractDelete
(
String
maKHs
)
async
{
String
?
token
=
DataPreference
.
instance
.
token
??
""
;
final
body
=
{
"access_token"
:
token
,
'ma_khang'
:
maKHs
,
};
return
requestNormal
(
APIPaths
.
customerContractDelete
,
Method
.
POST
,
body
,
(
data
)
{
return
data
==
true
;
});
}
Future
<
BaseResponseModel
<
TrafficServiceResponseModel
>>
getProductVnTraSold
(
Json
body
)
async
{
return
requestNormal
(
APIPaths
.
getProductVnTraSold
,
Method
.
GET
,
body
,
(
data
)
{
return
TrafficServiceResponseModel
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
TrafficServiceDetailModel
>>
getDetailMyPackageVnTra
(
String
id
)
async
{
final
path
=
APIPaths
.
detailMyPackageVnTra
.
replaceAll
(
"%@"
,
id
);
return
requestNormal
(
path
,
Method
.
GET
,
{},
(
data
)
{
return
TrafficServiceDetailModel
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
EmptyCodable
>>
submitPerformMission
(
Campaign7DayMissionModel
mission
,
String
id
)
async
{
final
path
=
APIPaths
.
submitCampaignMission
.
replaceFirst
(
'%@'
,
id
)
.
replaceFirst
(
'%@'
,
mission
.
id
.
toString
());
return
requestNormal
(
path
,
Method
.
POST
,
{},
(
data
)
{
return
EmptyCodable
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
List
<
Campaign7DayRewardModel
>>>
getCampaignRewards
(
String
id
)
async
{
final
path
=
APIPaths
.
getCampaignReward
.
replaceFirst
(
'%@'
,
id
);
return
requestNormal
(
path
,
Method
.
GET
,
{},
(
data
)
{
final
list
=
data
as
List
<
dynamic
>;
return
list
.
map
((
e
)
=>
Campaign7DayRewardModel
.
fromJson
(
e
)).
toList
();
});
}
Future
<
BaseResponseModel
<
List
<
String
>>>
getCampaignLiveTransactions
(
String
id
)
async
{
final
path
=
APIPaths
.
getCampaignLiveTransactions
.
replaceFirst
(
'%@'
,
id
);
return
requestNormal
(
path
,
Method
.
GET
,
{},
(
data
)
{
if
(
data
is
List
)
{
return
data
.
map
((
e
)
=>
e
.
toString
()).
toList
();
}
return
<
String
>[];
});
}
Future
<
BaseResponseModel
<
Campaign7DayInfoModel
>>
getCampaignMissions
(
String
id
)
async
{
final
path
=
APIPaths
.
getCampaignMissions
.
replaceFirst
(
'%@'
,
id
);
return
requestNormal
(
path
,
Method
.
GET
,
{},
(
data
)
{
return
Campaign7DayInfoModel
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
SurveyCampaignInfoModel
>>
getCampaignQuizSurvey
(
String
id
)
async
{
final
path
=
APIPaths
.
getQuizCampaign
.
replaceFirst
(
'%@'
,
id
);
return
requestNormal
(
path
,
Method
.
GET
,
{},
(
data
)
{
return
SurveyCampaignInfoModel
.
fromJson
(
data
as
Json
);
});
}
Future
<
BaseResponseModel
<
QuizCampaignSubmitResponseModel
>>
quizSubmitCampaign
(
String
id
,
Json
body
)
async
{
final
path
=
APIPaths
.
quizSubmitCampaign
.
replaceFirst
(
'%@'
,
id
);
return
requestNormal
(
path
,
Method
.
POST
,
body
,
(
data
)
{
return
QuizCampaignSubmitResponseModel
.
fromJson
(
data
as
Json
);
});
}
}
}
\ No newline at end of file
lib/screen/campaign7day/campaign_7day_guide_dialog.dart
0 → 100644
View file @
efb4662c
import
'package:flutter/material.dart'
;
import
'package:flutter_widget_from_html/flutter_widget_from_html.dart'
;
import
'../../resouce/base_color.dart'
;
class
Campaign7DayGuideDialog
extends
StatelessWidget
{
final
String
title
;
final
String
htmlContent
;
final
VoidCallback
onClose
;
const
Campaign7DayGuideDialog
({
super
.
key
,
required
this
.
title
,
required
this
.
htmlContent
,
required
this
.
onClose
});
@override
Widget
build
(
BuildContext
context
)
{
final
widthScreen
=
MediaQuery
.
of
(
context
).
size
.
width
;
final
heightScreen
=
MediaQuery
.
of
(
context
).
size
.
height
;
return
Stack
(
children:
[
Container
(
padding:
const
EdgeInsets
.
only
(
top:
32
),
child:
Stack
(
clipBehavior:
Clip
.
none
,
children:
[
Container
(
decoration:
BoxDecoration
(
color:
const
Color
(
0xFFFCECEF
),
borderRadius:
BorderRadius
.
circular
(
24
)),
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
Padding
(
padding:
const
EdgeInsets
.
only
(
top:
24
,
left:
16
,
right:
16
,
bottom:
16
),
child:
Row
(
children:
[
Expanded
(
child:
Text
(
title
,
style:
TextStyle
(
color:
BaseColor
.
primary400
,
fontSize:
24
,
fontWeight:
FontWeight
.
w800
),
),
),
SizedBox
(
width:
widthScreen
/
2.5
),
],
),
),
const
SizedBox
(
height:
8
),
Container
(
margin:
const
EdgeInsets
.
symmetric
(
horizontal:
16
),
padding:
const
EdgeInsets
.
all
(
16
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
20
)),
constraints:
BoxConstraints
(
maxHeight:
heightScreen
*
0.6
),
child:
SingleChildScrollView
(
child:
HtmlWidget
(
htmlContent
,
textStyle:
const
TextStyle
(
fontSize:
16
,
height:
1.5
)),
),
),
const
SizedBox
(
height:
24
),
],
),
),
Positioned
(
top:
-
30
,
right:
0
,
child:
Image
.
asset
(
'assets/images/bg_header_campaign_info.png'
,
width:
widthScreen
/
1.6
),
),
],
),
),
Positioned
(
top:
0
,
right:
8
,
child:
GestureDetector
(
onTap:
onClose
,
child:
Container
(
padding:
const
EdgeInsets
.
all
(
2
),
decoration:
const
BoxDecoration
(
shape:
BoxShape
.
circle
,
border:
Border
.
fromBorderSide
(
BorderSide
(
color:
Colors
.
white
,
width:
1.5
)),
),
child:
const
Icon
(
Icons
.
close_outlined
,
color:
Colors
.
white
,
size:
20
),
),
),
),
],
);
}
}
lib/screen/campaign7day/campaign_7day_mission_info_dialog.dart
0 → 100644
View file @
efb4662c
import
'package:flutter/material.dart'
;
import
'package:flutter_widget_from_html/flutter_widget_from_html.dart'
;
import
'../../resouce/base_color.dart'
;
import
'models/campaign_7day_mission_model.dart'
;
class
Campaign7DayMissionInfoDialog
extends
StatelessWidget
{
final
Campaign7DayMissionModel
mission
;
final
VoidCallback
onConfirm
;
final
VoidCallback
onClose
;
const
Campaign7DayMissionInfoDialog
({
super
.
key
,
required
this
.
mission
,
required
this
.
onConfirm
,
required
this
.
onClose
,
});
@override
Widget
build
(
BuildContext
context
)
{
return
Stack
(
children:
[
_buildContentBox
(),
_buildCloseButton
()]);
}
Widget
_buildContentBox
()
{
return
Container
(
padding:
const
EdgeInsets
.
only
(
top:
24
),
child:
Stack
(
clipBehavior:
Clip
.
none
,
children:
[
_buildInnerContainer
(),
_buildHeaderBanner
()]),
);
}
Widget
_buildInnerContainer
()
{
return
Container
(
margin:
const
EdgeInsets
.
only
(
top:
9
,
left:
8
,
right:
8
),
padding:
const
EdgeInsets
.
only
(
top:
16
,
left:
16
,
right:
16
,
bottom:
32
),
decoration:
BoxDecoration
(
color:
const
Color
(
0xFFFEF1F2
),
borderRadius:
BorderRadius
.
circular
(
16
)),
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
const
SizedBox
(
height:
36
),
HtmlWidget
(
mission
.
info
??
''
,
textStyle:
const
TextStyle
(
fontSize:
16
,
color:
Colors
.
black87
)),
const
SizedBox
(
height:
36
),
if
(
mission
.
isReady
)
_buildConfirmButton
(),
],
),
);
}
Widget
_buildHeaderBanner
()
{
return
Positioned
(
top:
0
,
left:
0
,
right:
0
,
child:
Center
(
child:
Stack
(
alignment:
Alignment
.
center
,
children:
[
Image
.
asset
(
'assets/images/ic_header_alert_campaign.png'
,
height:
48
,
fit:
BoxFit
.
contain
),
const
Text
(
'Thông tin nhiệm vụ'
,
style:
TextStyle
(
color:
Colors
.
white
,
fontWeight:
FontWeight
.
w800
,
fontSize:
17
,
shadows:
[
Shadow
(
color:
Colors
.
black26
,
offset:
Offset
(
2
,
2
),
blurRadius:
2
)],
),
),
],
),
),
);
}
Widget
_buildCloseButton
()
{
return
Positioned
(
top:
0
,
right:
8
,
child:
GestureDetector
(
onTap:
onClose
,
child:
Container
(
padding:
const
EdgeInsets
.
all
(
2
),
decoration:
const
BoxDecoration
(
shape:
BoxShape
.
circle
,
border:
Border
.
fromBorderSide
(
BorderSide
(
color:
Colors
.
white
,
width:
1.5
)),
),
child:
const
Icon
(
Icons
.
close_outlined
,
color:
Colors
.
white
,
size:
20
),
),
),
);
}
Widget
_buildConfirmButton
()
{
return
ElevatedButton
(
onPressed:
onConfirm
,
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
BaseColor
.
primary400
,
minimumSize:
const
Size
(
double
.
infinity
,
48
),
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
24
)),
),
child:
const
Text
(
'THỰC HIỆN'
,
style:
TextStyle
(
fontWeight:
FontWeight
.
w800
,
color:
Colors
.
white
,
fontSize:
18
)),
);
}
}
lib/screen/campaign7day/campaign_7day_reward_dialog.dart
0 → 100644
View file @
efb4662c
import
'package:flutter/material.dart'
;
import
'custom_widgets/campaign_7day_reward_item.dart'
;
import
'models/campaign_7day_reward_model.dart'
;
class
Campaign7DayRewardDialog
extends
StatelessWidget
{
final
List
<
Campaign7DayRewardModel
>
rewards
;
final
VoidCallback
onClose
;
const
Campaign7DayRewardDialog
({
super
.
key
,
required
this
.
rewards
,
required
this
.
onClose
});
@override
Widget
build
(
BuildContext
context
)
{
return
Dialog
(
backgroundColor:
Colors
.
transparent
,
insetPadding:
const
EdgeInsets
.
all
(
16
),
child:
Stack
(
children:
[
Container
(
padding:
const
EdgeInsets
.
only
(
top:
32
),
child:
Stack
(
clipBehavior:
Clip
.
none
,
children:
[
Container
(
decoration:
BoxDecoration
(
color:
const
Color
(
0xFFFCECEF
),
borderRadius:
BorderRadius
.
circular
(
24
)),
padding:
const
EdgeInsets
.
only
(
top:
48
,
left:
16
,
right:
16
,
bottom:
24
),
child:
ConstrainedBox
(
constraints:
BoxConstraints
(
maxHeight:
MediaQuery
.
of
(
context
).
size
.
height
*
0.7
,
// 70% chiều cao màn hình
),
child:
ListView
.
separated
(
shrinkWrap:
true
,
physics:
const
BouncingScrollPhysics
(),
itemCount:
rewards
.
length
,
separatorBuilder:
(
_
,
__
)
=>
const
SizedBox
(
height:
12
),
itemBuilder:
(
context
,
index
)
{
return
Campaign7DayRewardItem
(
model:
rewards
[
index
]);
},
),
),
),
Positioned
(
top:
-
9
,
left:
0
,
right:
0
,
child:
Center
(
child:
Stack
(
alignment:
Alignment
.
center
,
children:
[
Image
.
asset
(
'assets/images/ic_header_alert_campaign.png'
,
height:
48
,
fit:
BoxFit
.
contain
),
const
Text
(
'Lịch sử phần thưởng'
,
style:
TextStyle
(
color:
Colors
.
white
,
fontWeight:
FontWeight
.
w800
,
fontSize:
17
),
),
],
),
),
),
],
),
),
Positioned
(
top:
0
,
right:
8
,
child:
GestureDetector
(
onTap:
onClose
,
child:
Container
(
padding:
const
EdgeInsets
.
all
(
2
),
decoration:
const
BoxDecoration
(
shape:
BoxShape
.
circle
,
border:
Border
.
fromBorderSide
(
BorderSide
(
color:
Colors
.
white
,
width:
1.5
)),
),
child:
const
Icon
(
Icons
.
close_outlined
,
color:
Colors
.
white
,
size:
20
),
),
),
),
],
),
);
}
}
lib/screen/campaign7day/campaign_7day_screen.dart
0 → 100644
View file @
efb4662c
import
'package:flutter/material.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/widgets/custom_empty_widget.dart'
;
import
'../../base/base_screen.dart'
;
import
'../../base/basic_state.dart'
;
import
'../../resouce/base_color.dart'
;
import
'../../widgets/bottom_sheet_helper.dart'
;
import
'../home/custom_widget/hover_view.dart'
;
import
'campaign_7day_guide_dialog.dart'
;
import
'campaign_7day_mission_info_dialog.dart'
;
import
'campaign_7day_reward_dialog.dart'
;
import
'campaign_7day_viewmodel.dart'
;
import
'custom_widgets/campaign_7day_marguee_bar.dart'
;
import
'custom_widgets/campaign_7day_mission_card_item.dart'
;
import
'custom_widgets/campaign_7day_progress_box.dart'
;
import
'custom_widgets/campaign_7day_special_misssion_card.dart'
;
import
'custom_widgets/campaign_7day_top_buttons.dart'
;
class
Campaign7DayScreen
extends
BaseScreen
{
const
Campaign7DayScreen
({
super
.
key
});
@override
State
<
Campaign7DayScreen
>
createState
()
=>
_Campaign7DayScreenState
();
}
class
_Campaign7DayScreenState
extends
BaseState
<
Campaign7DayScreen
>
with
BasicState
{
late
final
Campaign7DayViewModel
_viewModel
;
@override
void
initState
()
{
super
.
initState
();
String
?
campaignId
=
'1'
;
final
args
=
Get
.
arguments
;
if
(
args
is
String
)
{
campaignId
=
args
;
}
if
(
campaignId
==
null
)
{
WidgetsBinding
.
instance
.
addPostFrameCallback
((
_
)
{
Get
.
back
();
});
return
;
}
_viewModel
=
Get
.
put
(
Campaign7DayViewModel
(
campaignId:
campaignId
));
_viewModel
.
getLiveTransactions
();
_viewModel
.
getCampaign7DayInfo
();
_viewModel
.
onShowAlertError
=
(
message
)
{
if
(
message
.
isNotEmpty
)
{
showAlertError
(
content:
message
);
}
};
_viewModel
.
submitPerformMissionResponse
=
(
mission
)
{
final
popup
=
mission
.
popup
;
if
(
popup
!=
null
)
{
showAlert
(
data:
popup
.
dataAlertModel
);
}
};
_viewModel
.
getCampaignRewardsResponse
=
(
rewards
)
{
BottomSheetHelper
.
showBottomSheetPopup
(
backgroundContainerColor:
Colors
.
transparent
,
horizontalContainerPadding:
0
,
child:
Campaign7DayRewardDialog
(
rewards:
rewards
,
onClose:
()
{
Get
.
back
();
},
),
);
};
}
@override
Widget
createBody
()
{
return
Scaffold
(
body:
Stack
(
children:
[
RefreshIndicator
(
onRefresh:
()
async
{
_viewModel
.
getLiveTransactions
();
_viewModel
.
getCampaign7DayInfo
();
},
child:
SingleChildScrollView
(
physics:
const
AlwaysScrollableScrollPhysics
(),
child:
Column
(
children:
[
_buildHeaderWithInfo
(),
const
SizedBox
(
height:
88
),
Obx
(()
=>
_buildMissionList
()),
const
SizedBox
(
height:
24
),
],
),
),
),
Campaign7dayTopButtons
(
onBack:
()
{
Get
.
back
();
},
onInfo:
()
{
BottomSheetHelper
.
showBottomSheetPopup
(
backgroundContainerColor:
Colors
.
transparent
,
horizontalContainerPadding:
0
,
bottomContainerPadding:
0
,
child:
Campaign7DayGuideDialog
(
title:
_viewModel
.
campaign7DayInfo
.
value
?.
name
??
'Chiến dịch 7 ngày'
,
htmlContent:
_viewModel
.
campaign7DayInfo
.
value
?.
description
??
''
,
onClose:
()
{
Get
.
back
();
},
),
);
},
onGift:
()
{
_viewModel
.
getCampaignRewards
();
},
),
Positioned
.
fill
(
child:
Obx
(()
// print('_viewModel.campaign7DayInfo.value?.countDownTime: ${_viewModel.campaign7DayInfo.value?.countDownTime}');
=>
HoverView
(
imagePath:
_viewModel
.
campaign7DayInfo
.
value
?.
config
?.
mainIcon
??
'assets/images/ic_pipi_04.png'
,
onTap:
null
,
onClose:
null
,
backgroundColor:
Colors
.
transparent
,
size:
80
,
countDownTime:
_viewModel
.
campaign7DayInfo
.
value
?.
countDownTime
??
0.0
,
),
),
),
],
),
backgroundColor:
BaseColor
.
primary400
,
);
}
Widget
_buildHeaderWithInfo
()
{
final
heightImageBG
=
MediaQuery
.
of
(
context
).
size
.
width
*
1589
/
1125
;
return
Stack
(
clipBehavior:
Clip
.
none
,
children:
[
Image
.
asset
(
'assets/images/bg_campaign_7day.png'
,
fit:
BoxFit
.
fill
,
width:
double
.
infinity
,
height:
heightImageBG
,
),
Positioned
(
left:
16
,
right:
16
,
child:
Transform
.
translate
(
offset:
Offset
(
0
,
heightImageBG
-
90
),
child:
Obx
(()
{
final
text
=
_viewModel
.
liveTransactions
.
value
;
final
missions
=
_viewModel
.
campaign7DayInfo
.
value
?.
missions
??
[];
return
Column
(
children:
[
if
(
text
.
isNotEmpty
)
CampaignMarqueeNoticeBar
(
text:
text
.
join
(
' • '
)),
const
SizedBox
(
height:
14
),
if
(
missions
.
isNotEmpty
)
Campaign7DayProgressBox
(
missions:
missions
),
const
SizedBox
(
height:
16
),
],
);
}),
),
),
],
);
}
Widget
_buildMissionList
()
{
final
missions
=
_viewModel
.
campaign7DayInfo
.
value
?.
missions
??
[];
if
(
missions
.
isEmpty
)
{
return
const
Center
(
child:
EmptyWidget
(
content:
'Không có nhiệm vụ nào'
));
}
final
normalMissions
=
missions
.
where
((
mission
)
=>
!
mission
.
isSpecial
).
toList
();
final
specialMissions
=
missions
.
where
((
mission
)
=>
mission
.
isSpecial
).
toList
();
return
Stack
(
clipBehavior:
Clip
.
none
,
children:
[
Container
(
margin:
const
EdgeInsets
.
only
(
top:
9
,
left:
16
,
right:
16
),
// để chừa chỗ ảnh header
padding:
const
EdgeInsets
.
all
(
12
),
decoration:
BoxDecoration
(
color:
Color
(
0xFFFEF1F2
),
borderRadius:
BorderRadius
.
circular
(
16
)),
child:
Column
(
children:
[
const
SizedBox
(
height:
30
),
...
normalMissions
.
map
(
(
mission
)
=>
Campaign7DayMissionCardItem
(
mission:
mission
,
onTapInfo:
()
{
BottomSheetHelper
.
showBottomSheetPopup
(
backgroundContainerColor:
Colors
.
transparent
,
child:
Campaign7DayMissionInfoDialog
(
mission:
mission
,
onConfirm:
()
{
Get
.
back
();
_viewModel
.
submitPerformMission
(
mission
);
},
onClose:
()
{
Get
.
back
();
},
),
);
},
onTapClaimReward:
()
{
_viewModel
.
submitPerformMission
(
mission
);
},
),
),
if
(
specialMissions
.
isNotEmpty
)
...
specialMissions
.
map
(
(
mission
)
=>
Campaign7DaySpecialMissionCard
(
mission:
mission
,
onTap:
()
{
_viewModel
.
submitPerformMission
(
mission
);
},
),
),
],
),
),
Positioned
(
top:
0
,
left:
0
,
right:
0
,
child:
Center
(
child:
Stack
(
alignment:
Alignment
.
center
,
children:
[
Image
.
asset
(
'assets/images/ic_header_alert_campaign.png'
,
height:
48
,
fit:
BoxFit
.
contain
),
const
Text
(
'Danh sách nhiệm vụ'
,
style:
TextStyle
(
color:
Colors
.
white
,
fontWeight:
FontWeight
.
w800
,
fontSize:
17
,
shadows:
[
Shadow
(
color:
Colors
.
black26
,
offset:
Offset
(
2
,
2
),
blurRadius:
2
)],
),
),
],
),
),
),
],
);
}
}
lib/screen/campaign7day/campaign_7day_viewmodel.dart
0 → 100644
View file @
efb4662c
import
'package:get/get_rx/src/rx_types/rx_types.dart'
;
import
'package:mypoint_flutter_app/networking/restful_api_request.dart'
;
import
'../../base/restful_api_viewmodel.dart'
;
import
'../../configs/constants.dart'
;
import
'models/campaign_7day_info_model.dart'
;
import
'models/campaign_7day_mission_model.dart'
;
import
'models/campaign_7day_reward_model.dart'
;
class
Campaign7DayViewModel
extends
RestfulApiViewModel
{
String
campaignId
;
var
liveTransactions
=
RxList
<
String
>();
var
campaign7DayInfo
=
Rxn
<
Campaign7DayInfoModel
>();
void
Function
(
String
message
)?
onShowAlertError
;
void
Function
(
Campaign7DayMissionModel
mission
)?
submitPerformMissionResponse
;
void
Function
(
List
<
Campaign7DayRewardModel
>
rewards
)?
getCampaignRewardsResponse
;
Campaign7DayViewModel
({
required
this
.
campaignId
});
void
getLiveTransactions
()
{
client
.
getCampaignLiveTransactions
(
campaignId
).
then
((
value
)
{
liveTransactions
.
value
=
value
.
data
??
[];
});
}
void
getCampaignRewards
()
{
showLoading
();
client
.
getCampaignRewards
(
campaignId
).
then
((
value
)
{
hideLoading
();
final
data
=
value
.
data
??
[];
if
(!
value
.
isSuccess
)
{
onShowAlertError
?.
call
(
value
.
errorMessage
??
Constants
.
commonError
);
return
;
}
if
(
data
.
isEmpty
)
{
onShowAlertError
?.
call
(
"Bạn chưa có phần thưởng nào. Vui lòng hoàn thành các nhiệm vụ để nhận thưởng!"
);
}
else
{
getCampaignRewardsResponse
?.
call
(
data
);
}
});
}
void
getCampaign7DayInfo
()
{
client
.
getCampaignMissions
(
campaignId
).
then
((
value
)
{
if
(!
value
.
isSuccess
)
{
onShowAlertError
?.
call
(
value
.
errorMessage
??
Constants
.
commonError
);
}
campaign7DayInfo
.
value
=
value
.
data
;
});
}
void
submitPerformMission
(
Campaign7DayMissionModel
mission
)
{
if
(!
mission
.
isReady
)
return
;
showLoading
();
client
.
submitPerformMission
(
mission
,
campaignId
).
then
((
value
)
{
hideLoading
();
if
(
value
.
isSuccess
)
{
// getCampaign7DayInfo();
if
(
mission
.
popup
!=
null
)
{
submitPerformMissionResponse
?.
call
(
mission
);
}
else
{
mission
.
directionScreen
?.
begin
();
}
}
else
{
onShowAlertError
?.
call
(
value
.
errorMessage
??
Constants
.
commonError
);
}
});
}
}
lib/screen/campaign7day/custom_widgets/campaign_7day_marguee_bar.dart
0 → 100644
View file @
efb4662c
import
'package:flutter/material.dart'
;
import
'package:marquee/marquee.dart'
;
class
CampaignMarqueeNoticeBar
extends
StatelessWidget
{
final
String
text
;
final
String
iconAsset
;
// ví dụ: assets/icons/ic_speaker.png
const
CampaignMarqueeNoticeBar
({
super
.
key
,
required
this
.
text
,
this
.
iconAsset
=
'assets/images/ic_speaker_toast.png'
,
});
@override
Widget
build
(
BuildContext
context
)
{
return
Stack
(
clipBehavior:
Clip
.
none
,
children:
[
Container
(
height:
28
,
margin:
const
EdgeInsets
.
only
(
left:
8
,
right:
8
,
top:
6
),
padding:
const
EdgeInsets
.
only
(
left:
36
,
right:
12
),
decoration:
BoxDecoration
(
color:
Colors
.
black
.
withOpacity
(
0.5
),
borderRadius:
BorderRadius
.
circular
(
20
),
),
child:
Center
(
child:
Marquee
(
text:
text
,
style:
const
TextStyle
(
color:
Colors
.
white
,
fontSize:
14
),
scrollAxis:
Axis
.
horizontal
,
crossAxisAlignment:
CrossAxisAlignment
.
center
,
blankSpace:
60.0
,
velocity:
40.0
,
pauseAfterRound:
Duration
.
zero
,
startPadding:
0.0
,
accelerationDuration:
Duration
(
seconds:
1
),
accelerationCurve:
Curves
.
linear
,
decelerationDuration:
Duration
(
seconds:
1
),
decelerationCurve:
Curves
.
linear
,
),
),
),
// Icon loa nằm ngoài trái
Positioned
(
left:
8
,
top:
0
,
child:
Image
.
asset
(
iconAsset
,
width:
40
,
height:
40
,
),
),
],
);
}
}
lib/screen/campaign7day/custom_widgets/campaign_7day_mission_card_item.dart
0 → 100644
View file @
efb4662c
import
'package:flutter/material.dart'
;
import
'package:mypoint_flutter_app/widgets/image_loader.dart'
;
import
'../../../resouce/base_color.dart'
;
import
'../models/campaign_7day_mission_model.dart'
;
class
Campaign7DayMissionCardItem
extends
StatelessWidget
{
final
Campaign7DayMissionModel
mission
;
final
VoidCallback
?
onTapInfo
;
final
VoidCallback
?
onTapClaimReward
;
const
Campaign7DayMissionCardItem
({
super
.
key
,
required
this
.
mission
,
this
.
onTapInfo
,
this
.
onTapClaimReward
});
@override
Widget
build
(
BuildContext
context
)
{
return
Container
(
margin:
const
EdgeInsets
.
symmetric
(
vertical:
8
,
horizontal:
4
),
padding:
const
EdgeInsets
.
symmetric
(
vertical:
16
,
horizontal:
8
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
12
)),
child:
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
loadNetworkImage
(
url:
mission
.
avatar
,
width:
48
,
height:
48
,
fit:
BoxFit
.
cover
,
placeholderAsset:
'assets/images/ic_point.png'
,
),
const
SizedBox
(
width:
12
),
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
mission
.
title
??
''
,
style:
const
TextStyle
(
fontWeight:
FontWeight
.
bold
,
fontSize:
18
)),
const
SizedBox
(
height:
16
),
Row
(
children:
[
Image
.
asset
(
mission
.
reward
?.
imageAsset
??
'assets/images/ic_campaign_gift.png'
,
width:
24
,
height:
24
,
),
const
SizedBox
(
width:
4
),
Text
(
mission
.
reward
?.
value
??
''
,
style:
const
TextStyle
(
fontWeight:
FontWeight
.
w700
,
color:
Colors
.
orange
),
),
],
),
],
),
),
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
end
,
mainAxisAlignment:
MainAxisAlignment
.
start
,
children:
[
GestureDetector
(
onTap:
onTapInfo
,
child:
Icon
(
Icons
.
info_outline
,
size:
20
,
color:
BaseColor
.
primary400
)),
const
SizedBox
(
height:
16
),
ElevatedButton
(
onPressed:
mission
.
isReady
?
onTapClaimReward
:
null
,
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
mission
.
isReady
?
BaseColor
.
primary400
:
BaseColor
.
second500
,
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
20
)),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
0
),
),
child:
const
Text
(
'Thực hiện'
,
style:
TextStyle
(
fontSize:
14
,
fontWeight:
FontWeight
.
w800
,
color:
Colors
.
white
),
),
),
],
),
],
),
);
}
}
lib/screen/campaign7day/custom_widgets/campaign_7day_progress_box.dart
0 → 100644
View file @
efb4662c
import
'package:flutter/material.dart'
;
import
'../models/campaign_7day_config_model.dart'
;
import
'../models/campaign_7day_mission_model.dart'
;
class
Campaign7DayProgressBox
extends
StatelessWidget
{
final
List
<
Campaign7DayMissionModel
>?
missions
;
const
Campaign7DayProgressBox
({
super
.
key
,
required
this
.
missions
});
@override
Widget
build
(
BuildContext
context
)
{
final
normalMissions
=
missions
?.
where
((
mission
)
=>
!
mission
.
isSpecial
).
toList
()
??
[];
final
completedCount
=
(
missions
?.
where
((
mission
)
=>
mission
.
mStatus
==
MissionStatus
.
completed
).
toList
()
??
[]).
length
;
final
specialMission
=
missions
?.
lastWhere
((
mission
)
=>
mission
.
isSpecial
);
return
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
12
,
vertical:
16
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
16
)),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
'Hoàn thành
${normalMissions.length}
nhiệm vụ để mở thưởng lớn'
,
style:
TextStyle
(
fontSize:
16
,
fontWeight:
FontWeight
.
w600
),
),
const
SizedBox
(
height:
12
),
Row
(
children:
[
Expanded
(
child:
_buildLine
(
0
<
completedCount
)),
for
(
int
i
=
0
;
i
<
(
missions
?.
length
??
0
);
i
++)
...[
_buildIcon
(
i
<
completedCount
),
Expanded
(
child:
_buildLine
(
i
<
completedCount
)),
],
if
(
specialMission
!=
null
)
Image
.
asset
(
specialMission
.
mStatus
==
MissionStatus
.
completed
?
'assets/images/ic_campaign_mission_gift_open.png'
:
'assets/images/ic_campaign_mission.png'
,
width:
48
,
height:
48
,
),
],
),
],
),
);
}
Widget
_buildIcon
(
bool
isDone
)
{
return
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
2
),
child:
Image
.
asset
(
isDone
?
'assets/images/ic_point.png'
:
'assets/images/ic_point_gray.png'
,
width:
24
,
height:
24
,
),
);
}
Widget
_buildLine
(
bool
isDone
)
{
return
Container
(
height:
3
,
color:
isDone
?
Colors
.
orange
:
Colors
.
grey
.
shade300
);
}
}
lib/screen/campaign7day/custom_widgets/campaign_7day_reward_item.dart
0 → 100644
View file @
efb4662c
import
'package:flutter/material.dart'
;
import
'package:flutter_widget_from_html/flutter_widget_from_html.dart'
;
import
'package:get/get.dart'
;
import
'../../../resouce/base_color.dart'
;
import
'../models/campaign_7day_reward_model.dart'
;
class
Campaign7DayRewardItem
extends
StatelessWidget
{
final
Campaign7DayRewardModel
model
;
const
Campaign7DayRewardItem
({
super
.
key
,
required
this
.
model
});
@override
Widget
build
(
BuildContext
context
)
{
return
Container
(
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
12
),
boxShadow:
[
BoxShadow
(
color:
Colors
.
black
.
withOpacity
(
0.08
),
// màu bóng
blurRadius:
8
,
// độ mờ
offset:
const
Offset
(
0
,
4
),
// hướng đổ bóng
),
],
),
padding:
const
EdgeInsets
.
only
(
left:
12
,
right:
12
,
top:
12
,
bottom:
6
),
child:
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Image
.
asset
(
model
.
imageAsset
,
width:
48
,
height:
48
),
const
SizedBox
(
width:
12
),
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
HtmlWidget
(
model
.
textDisplay
??
''
,
textStyle:
const
TextStyle
(
fontSize:
15
,
color:
Colors
.
black
)),
const
SizedBox
(
height:
8
),
Divider
(
color:
Colors
.
grey
.
shade200
,
thickness:
1
,
height:
2
),
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
Expanded
(
child:
Text
(
model
.
createdAt
??
''
,
style:
TextStyle
(
fontSize:
12
,
color:
Colors
.
black54
))),
ElevatedButton
(
onPressed:
()
{
Get
.
back
();
model
.
direction
?.
begin
();
},
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
BaseColor
.
primary400
,
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
2
),
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
24
)),
minimumSize:
const
Size
(
0
,
32
),
),
child:
Text
(
model
.
buttonText
??
'Sử dụng ngay'
,
style:
const
TextStyle
(
fontSize:
14
,
fontWeight:
FontWeight
.
bold
,
color:
Colors
.
white
),
),
),
],
),
],
),
),
],
),
);
}
}
lib/screen/campaign7day/custom_widgets/campaign_7day_special_misssion_card.dart
0 → 100644
View file @
efb4662c
import
'package:flutter/material.dart'
;
import
'../models/campaign_7day_config_model.dart'
;
import
'../models/campaign_7day_mission_model.dart'
;
class
Campaign7DaySpecialMissionCard
extends
StatelessWidget
{
final
Campaign7DayMissionModel
mission
;
final
VoidCallback
?
onTap
;
const
Campaign7DaySpecialMissionCard
({
super
.
key
,
required
this
.
mission
,
this
.
onTap
,
});
@override
Widget
build
(
BuildContext
context
)
{
final
width
=
MediaQuery
.
of
(
context
).
size
.
width
;
final
height
=
(
width
)*
300
/
957
;
final
imagePath
=
makeImageBGSubmitMission
(
mission
.
mStatus
);
return
GestureDetector
(
onTap:
onTap
,
child:
Container
(
width:
width
,
height:
height
,
margin:
const
EdgeInsets
.
symmetric
(
horizontal:
0
,
vertical:
4
),
decoration:
BoxDecoration
(
image:
DecorationImage
(
image:
AssetImage
(
imagePath
),
fit:
BoxFit
.
contain
,
),
),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
),
child:
Row
(
children:
[
Expanded
(
child:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
mission
.
title
??
''
,
style:
const
TextStyle
(
fontSize:
17
,
fontWeight:
FontWeight
.
w800
,
color:
Colors
.
white
,
shadows:
[
Shadow
(
offset:
Offset
(
2
,
2
),
blurRadius:
0.1
,
color:
Colors
.
black54
,
),
],
),
),
const
SizedBox
(
height:
2
),
Text
(
mission
.
description
??
''
,
style:
const
TextStyle
(
fontSize:
14
,
color:
Colors
.
white
,
),
),
],
),
),
SizedBox
(
width:
width
/
4.5
,),
],
),
),
);
}
String
makeImageBGSubmitMission
(
MissionStatus
status
)
{
late
final
String
imageName
;
switch
(
status
)
{
case
MissionStatus
.
ready
:
imageName
=
'assets/images/bg_misstion_submit_campaign_7day_ready.png'
;
break
;
case
MissionStatus
.
pending
:
imageName
=
'assets/images/bg_misstion_submit_campaign_7day_disable.png'
;
break
;
case
MissionStatus
.
completed
:
imageName
=
'assets/images/bg_misstion_submit_campaign_7day_complete.png'
;
break
;
}
return
imageName
;
}
}
lib/screen/campaign7day/custom_widgets/campaign_7day_top_buttons.dart
0 → 100644
View file @
efb4662c
import
'package:flutter/material.dart'
;
class
Campaign7dayTopButtons
extends
StatelessWidget
{
final
VoidCallback
?
onBack
;
final
VoidCallback
?
onInfo
;
final
VoidCallback
?
onGift
;
const
Campaign7dayTopButtons
({
super
.
key
,
this
.
onBack
,
this
.
onInfo
,
this
.
onGift
,
});
@override
Widget
build
(
BuildContext
context
)
{
return
SafeArea
(
child:
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16.0
,
vertical:
8
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
children:
[
_buildIconButton
(
"assets/images/ic_campaign_back.png"
,
onBack
),
Row
(
children:
[
_buildIconButton
(
"assets/images/ic_campaign_info.png"
,
onInfo
),
const
SizedBox
(
width:
12
),
_buildIconButton
(
"assets/images/ic_campaign_gift.png"
,
onGift
),
],
),
],
),
),
);
}
Widget
_buildIconButton
(
String
assetName
,
VoidCallback
?
onPressed
)
{
return
GestureDetector
(
onTap:
onPressed
,
child:
SizedBox
(
width:
40
,
height:
40
,
child:
Image
.
asset
(
assetName
,
// color: Colors.white,
width:
22
,
height:
22
,
),
),
);
}
}
lib/screen/campaign7day/models/campaign_7day_config_model.dart
0 → 100644
View file @
efb4662c
import
'dart:ui'
;
import
'package:json_annotation/json_annotation.dart'
;
import
'package:mypoint_flutter_app/resouce/base_color.dart'
;
part
'campaign_7day_config_model.g.dart'
;
@JsonSerializable
()
class
Campaign7dayConfigModel
{
@JsonKey
(
name:
'main_icon'
)
final
String
?
mainIcon
;
@JsonKey
(
name:
'progress_bar_text'
)
final
String
?
progressBarText
;
@JsonKey
(
name:
'header_img'
)
final
String
?
headerImg
;
@JsonKey
(
name:
'main_color'
)
final
String
?
mainColorHex
;
@JsonKey
(
name:
'main_img_type'
)
final
String
?
mainImgType
;
Campaign7dayConfigModel
({
this
.
mainIcon
,
this
.
progressBarText
,
this
.
headerImg
,
this
.
mainColorHex
,
this
.
mainImgType
,
});
factory
Campaign7dayConfigModel
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
=>
_$Campaign7dayConfigModelFromJson
(
json
);
Map
<
String
,
dynamic
>
toJson
()
=>
_$Campaign7dayConfigModelToJson
(
this
);
String
get
suffixImageName
{
final
style
=
CampaignStyleImage
.
values
.
firstWhere
(
(
e
)
=>
e
.
name
.
toUpperCase
()
==
(
mainImgType
??
''
).
toUpperCase
(),
orElse:
()
=>
CampaignStyleImage
.
RED
,
);
return
style
.
name
;
}
Color
get
mainColor
{
if
((
mainColorHex
??
''
).
isEmpty
)
{
return
BaseColor
.
primary400
;
}
return
_parseHexColor
(
mainColorHex
!)
??
BaseColor
.
primary400
;
}
String
get
headerMissionBGAsset
=>
'assets/images/bg_header_alert_campaign_7day_
$suffixImageName
.png'
;
Color
?
_parseHexColor
(
String
hex
)
{
try
{
final
buffer
=
StringBuffer
();
if
(
hex
.
startsWith
(
'#'
))
hex
=
hex
.
substring
(
1
);
if
(
hex
.
length
==
6
)
buffer
.
write
(
'ff'
);
buffer
.
write
(
hex
);
return
Color
(
int
.
parse
(
buffer
.
toString
(),
radix:
16
));
}
catch
(
_
)
{
return
null
;
}
}
}
enum
MissionStatus
{
ready
,
pending
,
completed
}
enum
TypeReward
{
point
,
voucher
}
enum
CampaignStyleImage
{
RED
,
ORANGE
}
\ No newline at end of file
lib/screen/campaign7day/models/campaign_7day_config_model.g.dart
0 → 100644
View file @
efb4662c
// GENERATED CODE - DO NOT MODIFY BY HAND
part of
'campaign_7day_config_model.dart'
;
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Campaign7dayConfigModel
_$Campaign7dayConfigModelFromJson
(
Map
<
String
,
dynamic
>
json
,
)
=>
Campaign7dayConfigModel
(
mainIcon:
json
[
'main_icon'
]
as
String
?,
progressBarText:
json
[
'progress_bar_text'
]
as
String
?,
headerImg:
json
[
'header_img'
]
as
String
?,
mainColorHex:
json
[
'main_color'
]
as
String
?,
mainImgType:
json
[
'main_img_type'
]
as
String
?,
);
Map
<
String
,
dynamic
>
_$Campaign7dayConfigModelToJson
(
Campaign7dayConfigModel
instance
,
)
=>
<
String
,
dynamic
>{
'main_icon'
:
instance
.
mainIcon
,
'progress_bar_text'
:
instance
.
progressBarText
,
'header_img'
:
instance
.
headerImg
,
'main_color'
:
instance
.
mainColorHex
,
'main_img_type'
:
instance
.
mainImgType
,
};
Prev
1
2
3
4
5
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment