Compare commits

...

2 Commits

Author SHA1 Message Date
86eec87b8c update ios platform push notification 2025-11-06 16:26:57 +07:00
5447279eee update api get repair list 2025-11-06 16:18:44 +07:00
12 changed files with 206 additions and 138 deletions

58
App.js
View File

@ -36,23 +36,75 @@ class App extends Component {
this._setDataFromInitState = this._setDataFromInitState.bind(this) this._setDataFromInitState = this._setDataFromInitState.bind(this)
} }
componentDidMount = () => { componentDidMount = async () => {
SplashScreen.hide() SplashScreen.hide()
Settings.initializeSDK(); Settings.initializeSDK();
// Test FCM token
try {
const token = await messaging().getToken();
console.log('Current FCM Token:', token);
console.log('Token length:', token ? token.length : 0);
// ตรวจสอบ permission status
const authStatus = await messaging().requestPermission();
console.log('Permission status:', authStatus);
} catch (error) {
console.log('Error getting FCM token:', error);
}
}
// เพิ่ม method ใหม่สำหรับ setup listeners
setupNotificationListeners = () => {
// Foreground message handler
messaging().onMessage(async remoteMessage => {
console.log('Foreground notification:', remoteMessage);
});
// Background/Quit message handler
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Background notification:', remoteMessage);
});
// Notification opened app handler
messaging().onNotificationOpenedApp(remoteMessage => {
console.log('Notification opened app:', remoteMessage);
});
// Check if app was opened from notification
messaging()
.getInitialNotification()
.then(remoteMessage => {
if (remoteMessage) {
console.log('App opened from notification:', remoteMessage);
}
});
} }
initNotification = async () => { initNotification = async () => {
try {
await this.setPermission() await this.setPermission()
const fcmToken = await messaging().getAPNSToken();
// ใช้ getToken() แทน getAPNSToken()
const fcmToken = await messaging().getToken();
console.log('FCM Token:', fcmToken);
if (fcmToken) { if (fcmToken) {
store.dispatch(appSetPushToken(fcmToken)) store.dispatch(appSetPushToken(fcmToken))
const resultSendDevice = await registerDevice(fcmToken) const resultSendDevice = await registerDevice(fcmToken)
console.log(' re sult register_device =>', resultSendDevice) console.log('register_device result =>', resultSendDevice)
if (resultSendDevice.ok && resultSendDevice.data.success) { if (resultSendDevice.ok && resultSendDevice.data.success) {
store.dispatch(appSetDevice(resultSendDevice.data.device)) store.dispatch(appSetDevice(resultSendDevice.data.device))
} }
} }
// เพิ่ม notification listeners
this.setupNotificationListeners();
} catch (error) {
console.log('initNotification error:', error);
}
} }
setPermission = async () => { setPermission = async () => {

Binary file not shown.

View File

@ -8,12 +8,16 @@
#import "AppDelegate.h" #import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h> #import <React/RCTBundleURLProvider.h>
#import <UserNotifications/UserNotifications.h>
#import <Firebase.h> #import <Firebase.h>
#import "RNSplashScreen.h" #import "RNSplashScreen.h"
#import <FBSDKCoreKit/FBSDKCoreKit.h> #import <FBSDKCoreKit/FBSDKCoreKit.h>
@interface AppDelegate () <UNUserNotificationCenterDelegate>
@end
@implementation AppDelegate @implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
@ -23,6 +27,10 @@
[FIRApp configure]; [FIRApp configure];
} }
// เพิ่ม notification center delegate
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"csareactrn60" moduleName:@"csareactrn60"
@ -68,4 +76,17 @@
#endif #endif
} }
// เพิ่ม methods สำหรับ handle notifications
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound);
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler {
completionHandler();
}
@end @end

View File

@ -83,6 +83,8 @@
<string>no use</string> <string>no use</string>
<key>NSSpeechRecognitionUsageDescription</key> <key>NSSpeechRecognitionUsageDescription</key>
<string>no use</string> <string>no use</string>
<key>NSUserNotificationUsageDescription</key>
<string>This app needs permission to send push notifications.</string>
<key>UIAppFonts</key> <key>UIAppFonts</key>
<array> <array>
<string>arial.ttf</string> <string>arial.ttf</string>

View File

@ -3,7 +3,7 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>aps-environment</key> <key>aps-environment</key>
<string>development</string> <string>production</string>
<key>com.apple.developer.applesignin</key> <key>com.apple.developer.applesignin</key>
<array> <array>
<string>Default</string> <string>Default</string>

View File

@ -162,12 +162,12 @@ export const getServicesDetail = (service_id) => {
return Api.get('/service/'+service_id); return Api.get('/service/'+service_id);
} }
export const getRepairList = (service_id) => { export const getRepairList = (project_id) => {
return Api.get('/repair'); return Api.get(`/api/project/${project_id}/maintenance`);
} }
export const getRepairById = (repair_id, type = '') => { export const getRepairById = (project_id, id) => {
return Api.get(`/repair/${repair_id}?self_room=${(type === 'effect_responsible' ? 'true' : 'false')}`); return Api.get(`/api/project/${project_id}/maintenance/${id}`);
} }
export const postRepair = (param) => { export const postRepair = (param) => {

View File

@ -363,7 +363,7 @@ const MainHeader = ({ navigation }) => {
}, []) }, [])
return <View style={[{ flex: 1, alignItems: 'center', flexDirection: 'row', marginTop: 0, paddingLeft: 10, backgroundColor: server_mode === 'develop' ? '#ff0000' : 'transparent' }]}> return <View style={[{ flex: 1, alignItems: 'center', flexDirection: 'row', marginTop: 0, paddingLeft: 10, backgroundColor: server_mode === 'develop' ? '#ff0000' : 'transparent' }]}>
<TouchableOpacity <TouchableOpacity
// onPress={openSecretChamber} onPress={openSecretChamber}
activeOpacity={1}> activeOpacity={1}>
<Image <Image
style={{ width: 35, height: 35, }} style={{ width: 35, height: 35, }}
@ -720,13 +720,17 @@ const AppStack = createStackNavigator({
screen: RepairService, screen: RepairService,
navigationOptions: NavWithRightIcon('request_repair_and_Other', 'ic_clock_history', 'RepairHistory') navigationOptions: NavWithRightIcon('request_repair_and_Other', 'ic_clock_history', 'RepairHistory')
},*/ },*/
Repair: screenConfig(RepairIndex, 'request_repair'), Repair: {
screen: RepairIndex,
navigationOptions: NavWithRightIcon('request_repair', 'ic_clock_history', 'RepairHistory')
},
// Repair: screenConfig(RepairIndex, 'request_repair'),
RepairDetail: screenConfig(RepairServiceDetail, 'service_detail'), RepairDetail: screenConfig(RepairServiceDetail, 'service_detail'),
RepairConfirm: screenConfig(RepairConfirm, 'confirm_info'), RepairConfirm: screenConfig(RepairConfirm, 'confirm_info'),
RepairHistory: screenConfig(RepairHistory, 'repair_history'), RepairHistory: screenConfig(RepairHistory, 'repair_history'),
RepairSuccess: screenConfig(RepairSuccess, 'request_sent'), RepairSuccess: screenConfig(RepairSuccess, 'request_sent'),
// RepairHistoryDetail: screenConfig(RepairHistoryDetail, 'repair_history_detail'), RepairHistoryDetail: screenConfig(RepairHistoryDetail, 'repair_history_detail'),
RepairHistoryDetail: { /*RepairHistoryDetail: {
screen: RepairHistoryDetail, screen: RepairHistoryDetail,
navigationOptions: ({navigation}) => ({ navigationOptions: ({navigation}) => ({
title: t('repair_history_detail'), title: t('repair_history_detail'),
@ -772,7 +776,7 @@ const AppStack = createStackNavigator({
</TouchableOpacity> </TouchableOpacity>
) )
}) })
}, },*/
//Reward //Reward
Reward: screenConfig(RewardScreen, 'redeem_reward'), Reward: screenConfig(RewardScreen, 'redeem_reward'),
RewardDetailScreen: screenConfig(RewardDetailScreen, 'product_detail'), RewardDetailScreen: screenConfig(RewardDetailScreen, 'product_detail'),

View File

@ -149,7 +149,7 @@ class LoginScreen extends Component {
<View style={styles.form}> <View style={styles.form}>
<View style={styles.row}> <View style={styles.row}>
<CustomInput <CustomInput
maxLength={10} maxLength={13}
keyboardType="numeric" keyboardType="numeric"
onChangeText={(e) => { onChangeText={(e) => {
this.setState({ this.setState({

View File

@ -23,36 +23,16 @@ import {getRepairList} from '../../api/UserApi';
import IndicatorLoading from '../../components/IndicatorLoading'; import IndicatorLoading from '../../components/IndicatorLoading';
import { t } from '../../utils/i18n' import { t } from '../../utils/i18n'
import moment from "moment"; import moment from "moment";
import {connect} from "react-redux";
export default class RepairHistory extends Component { class RepairHistory extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
this.state = { this.state = {
isLoading: false, isLoading: false,
history_list:[ history_list:[]
// {
// title: 'A1-1',
// status: 'รอดำเนินการ',
// tag: '#124456'
// },
// {
// title: 'A1-2',
// status: 'แจ้งซ่อม',
// tag: '#546315'
// },
// {
// title: 'A1-2',
// status: 'เสร็จสิ้น',
// tag: '#148521'
// },
// {
// title: 'A1-2',
// status: 'เสร็จสิ้น',
// tag: '#986521'
// },
]
} }
this.onRefresh = this.onRefresh.bind(this) this.onRefresh = this.onRefresh.bind(this)
this.convertStatus = this.convertStatus.bind(this) this.convertStatus = this.convertStatus.bind(this)
@ -66,8 +46,9 @@ export default class RepairHistory extends Component {
getRepairHistoryList(){ getRepairHistoryList(){
this.setState({ this.setState({
isLoading: true isLoading: true
}) }, () => {
getRepairList() let project_id = this.props.user && this.props.user.project_id;
getRepairList(project_id)
.then(res => { .then(res => {
if(res.ok){ if(res.ok){
console.log('check data response ----------> ',res.data.data); console.log('check data response ----------> ',res.data.data);
@ -75,41 +56,43 @@ export default class RepairHistory extends Component {
history_list: res.data.data, history_list: res.data.data,
}) })
} }
}).finally(() => {
this.setState({ this.setState({
isLoading: false isLoading: false
}) })
}) })
})
} }
convertStatus(statusName){ convertStatus(statusName){
switch(statusName){ switch(statusName){
case 'Pending': case '1':
return t('request_repair') return 'รอซ่อม'
case 'InProgress': case '2':
return 'ยืนยันนัด' return 'กำลังซ่อม'
case 'NewAppointment': case 'NewAppointment':
return 'นัดหมายใหม่' return 'นัดหมายใหม่'
case 'Cancel': case '4':
return t('cancel') return t('cancel')
case 'Success': case '3':
return 'ซ่อมสำเร็จ' return 'เสร็จสิ้น'
default: default:
return t('request_repair') return t('request_repair')
} }
} }
convertColorStatus(statusName){ convertColorStatus(statusCode){
switch(statusName){ switch(statusCode){
case 'Pending': case '1': //รอซ่อม
return '#FF9500' return '#FF9500'
case 'InProgress': case '2': //กำลังซ่อม
return '#007AFF' return '#007AFF'
// return '#FFCC00' // return '#FFCC00'
case 'NewAppointment': case 'NewAppointment':
return '#145EB3' return '#145EB3'
case 'Cancel': case '4': //ยกเลิก
return '#666666' return '#666666'
case 'Success': case '3': //เสร็จสิ้น
return '#2C7C0B' return '#2C7C0B'
default: default:
return '#C4C4C4' return '#C4C4C4'
@ -120,28 +103,26 @@ export default class RepairHistory extends Component {
return <TouchableOpacity style={{ borderBottomColor: '#00000040', borderBottomWidth: 1, backgroundColor: 'white' }} return <TouchableOpacity style={{ borderBottomColor: '#00000040', borderBottomWidth: 1, backgroundColor: 'white' }}
onPress={() => {this.props.navigation.navigate('RepairHistoryDetail',{ onPress={() => {this.props.navigation.navigate('RepairHistoryDetail',{
repair_id:item.id, repair_id:item.id,
project_id: this.props.user && this.props.user.project_id,
getRepairHistoryList: () => {this.getRepairHistoryList()} getRepairHistoryList: () => {this.getRepairHistoryList()}
})}}> })}}>
<View style={{height: 104,justifyContent:'center',paddingHorizontal:16}}> <View style={{height: 104,justifyContent:'center',paddingHorizontal:16}}>
<View style={{flexDirection:'row',justifyContent:'space-between'}}> <View style={{flexDirection:'row',justifyContent:'space-between'}}>
<Text style={{color: Color.green_title, fontSize: 16,}}>{t('room')} {item.room_no}</Text> <Text style={{color: Color.green_title, fontSize: 16,}}>{t('room')} {item.room_sap_code}</Text>
<View style={{flexDirection:'row',alignItems:'center'}}> <View style={{flexDirection:'row',alignItems:'center'}}>
<View style={{height: 10,width: 10,borderRadius: 5,backgroundColor:this.convertColorStatus(item.status)}}/> <View style={{height: 10,width: 10,borderRadius: 5,backgroundColor:this.convertColorStatus(item.status)}}/>
<Text style={{marginLeft:10, fontSize:14, color:this.convertColorStatus(item.status)}}>{this.convertStatus(item.status)}</Text> <Text style={{marginLeft:10, fontSize:14, color:this.convertColorStatus(item.status)}}>{this.convertStatus(item.status)}</Text>
</View> </View>
</View> </View>
<View style={{flexDirection:'row',justifyContent:'space-between',marginVertical:5}}> <View style={{flexDirection:'row',justifyContent:'space-between',marginVertical:5}}>
<Text style={{fontSize:14}}>{item.services_name}</Text> <Text style={{fontSize:14}}>{item.name}</Text>
<Text style={{fontSize:14}}>#{item.id}</Text> <Text style={{fontSize:14}}>#{item.id}</Text>
</View> </View>
<View style={{flexDirection:'row',justifyContent:'space-between'}}> <View style={{flexDirection:'row',justifyContent:'space-between'}}>
<View style={{flexDirection:'row',alignItems:'center'}}> <View style={{flexDirection:'row',alignItems:'center'}}>
<Icon name="ic_calendar_alt" size={14} color="#00000080" /> <Icon name="ic_calendar_alt" size={14} color="#00000080" />
<Text style={{marginLeft: 10, fontSize:12}}> <Text style={{marginLeft: 10, fontSize:12}}>
{ item.date ? { item.date ? item.date : item.status_name }
`${moment(item.date).format('DD-MM-YYYY')}, ${item.time.slice(0,-3)}`
: item.status === 'Cancel' ? '-' : 'รอการนัดหมายใหม่'
}
</Text> </Text>
</View> </View>
<Text style={{color: '#145EB3', fontSize: 12}}>{item.status === 'Success' && item.points ? item.points + ' คะแนน' : ''}</Text> <Text style={{color: '#145EB3', fontSize: 12}}>{item.status === 'Success' && item.points ? item.points + ' คะแนน' : ''}</Text>
@ -183,3 +164,11 @@ export default class RepairHistory extends Component {
) )
} }
} }
const mapStateToProps = state => {
return {
user: state.app.user
}
}
export default connect(mapStateToProps)(RepairHistory)

View File

@ -53,6 +53,7 @@ export default class RepairHistoryDetail extends Component {
isLoading: false, isLoading: false,
repair_id: this.props.navigation.getParam('repair_id','NO_ITEM'), repair_id: this.props.navigation.getParam('repair_id','NO_ITEM'),
type: this.props.navigation.getParam('type','NO_ITEM'), //params from Notification.js type: this.props.navigation.getParam('type','NO_ITEM'), //params from Notification.js
project_id: this.props.navigation.getParam('project_id',null),
repair:{}, repair:{},
visibleCancel: false, visibleCancel: false,
rating_point: [0,0,0,0,0], rating_point: [0,0,0,0,0],
@ -121,7 +122,7 @@ export default class RepairHistoryDetail extends Component {
this.setState({ this.setState({
isLoading: true isLoading: true
}) })
getRepairById(this.state.repair_id, this.state.type) getRepairById(this.state.project_id, this.state.repair_id)
.then(res => { .then(res => {
console.log('repair data -------------> ',res.data.data) console.log('repair data -------------> ',res.data.data)
this.setState({ this.setState({

View File

@ -1,5 +1,5 @@
import React, {Component} from 'react' import React, {Component} from 'react'
import {Alert, Platform, ScrollView, TextInput, TouchableOpacity, View} from "react-native"; import {Alert, KeyboardAvoidingView, Platform, ScrollView, TextInput, TouchableOpacity, View} from "react-native";
import {t} from "../../utils/i18n"; import {t} from "../../utils/i18n";
import Select2 from "react-select2-native"; import Select2 from "react-select2-native";
import Text from "../../components/Text"; import Text from "../../components/Text";
@ -45,10 +45,10 @@ class RepairIndex extends Component {
super(props); super(props);
this.state = { this.state = {
data: { data: {
repair_list: [ // repair_list: [
{name: '', material: '', value: 0} // {name: '', material: '', value: 0}
], // ],
maintenanceList: [], // maintenanceList: [],
work_date: moment(new Date()).format('DD-MM-YYYY'), work_date: moment(new Date()).format('DD-MM-YYYY'),
details: '' details: ''
}, },
@ -94,7 +94,7 @@ class RepairIndex extends Component {
let data = this.state.data; let data = this.state.data;
if(data.header_dropdown_id if(data.header_dropdown_id
&& data.room_id && data.room_id
&& data.group_dropdown_id // && data.group_dropdown_id
// && data.details // && data.details
&& data.work_date && data.work_date
&& data.work_time && data.work_time
@ -322,7 +322,7 @@ class RepairIndex extends Component {
width: null, width: null,
height: null, height: null,
backgroundColor: '#EAEAF4', backgroundColor: '#EAEAF4',
padding: 16 padding: 16,
}}> }}>
<ScrollView> <ScrollView>
<SelectionData <SelectionData
@ -349,7 +349,7 @@ class RepairIndex extends Component {
zIndex={4} zIndex={4}
/> />
<SelectionData {/*<SelectionData
items={this.state.repair_types} items={this.state.repair_types}
title={'repair_type'} title={'repair_type'}
open={this.state.openRoomType} open={this.state.openRoomType}
@ -359,7 +359,7 @@ class RepairIndex extends Component {
placeholder={t('select_repair_type')} placeholder={t('select_repair_type')}
viewStyle={isIos ? {zIndex: 3} : {}} viewStyle={isIos ? {zIndex: 3} : {}}
zIndex={3} zIndex={3}
/> />*/}
<View style={Styles.mbt16}> <View style={Styles.mbt16}>
<Text style={Styles.Label}>{t('repair_detail')}</Text> <Text style={Styles.Label}>{t('repair_detail')}</Text>
@ -398,8 +398,8 @@ class RepairIndex extends Component {
value={data && data.work_time ? data.work_time : ''} value={data && data.work_time ? data.work_time : ''}
setValue={(callback) => this.setRepairData(callback(this), 'work_time')} setValue={(callback) => this.setRepairData(callback(this), 'work_time')}
placeholder={t('period')} placeholder={t('period')}
viewStyle={isIos ? {zIndex: 2} : {}} viewStyle={isIos ? {zIndex: 3} : {paddingBottom: 100}}
zIndex={2} zIndex={3}
/> />
{/*{ {/*{
@ -445,7 +445,6 @@ class RepairIndex extends Component {
<Text style={{color: '#00420A', textAlign: 'center'}}>+ {t('add_list')}</Text> <Text style={{color: '#00420A', textAlign: 'center'}}>+ {t('add_list')}</Text>
</View> </View>
</TouchableOpacity>*/} </TouchableOpacity>*/}
</ScrollView> </ScrollView>
{ {