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

74
App.js
View File

@ -36,23 +36,75 @@ class App extends Component {
this._setDataFromInitState = this._setDataFromInitState.bind(this)
}
componentDidMount = () => {
componentDidMount = async () => {
SplashScreen.hide()
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 () => {
await this.setPermission()
const fcmToken = await messaging().getAPNSToken();
if (fcmToken) {
store.dispatch(appSetPushToken(fcmToken))
const resultSendDevice = await registerDevice(fcmToken)
console.log(' re sult register_device =>', resultSendDevice)
if (resultSendDevice.ok && resultSendDevice.data.success) {
store.dispatch(appSetDevice(resultSendDevice.data.device))
}
}
try {
await this.setPermission()
// ใช้ getToken() แทน getAPNSToken()
const fcmToken = await messaging().getToken();
console.log('FCM Token:', fcmToken);
if (fcmToken) {
store.dispatch(appSetPushToken(fcmToken))
const resultSendDevice = await registerDevice(fcmToken)
console.log('register_device result =>', resultSendDevice)
if (resultSendDevice.ok && resultSendDevice.data.success) {
store.dispatch(appSetDevice(resultSendDevice.data.device))
}
}
// เพิ่ม notification listeners
this.setupNotificationListeners();
} catch (error) {
console.log('initNotification error:', error);
}
}
setPermission = async () => {

Binary file not shown.

View File

@ -17,4 +17,4 @@
}
],
"elementType": "File"
}
}

View File

@ -8,12 +8,16 @@
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <UserNotifications/UserNotifications.h>
#import <Firebase.h>
#import "RNSplashScreen.h"
#import <FBSDKCoreKit/FBSDKCoreKit.h>
@interface AppDelegate () <UNUserNotificationCenterDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
@ -23,6 +27,10 @@
[FIRApp configure];
}
// เพิ่ม notification center delegate
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"csareactrn60"
@ -68,4 +76,17 @@
#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

View File

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

View File

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

View File

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

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' }]}>
<TouchableOpacity
// onPress={openSecretChamber}
onPress={openSecretChamber}
activeOpacity={1}>
<Image
style={{ width: 35, height: 35, }}
@ -720,13 +720,17 @@ const AppStack = createStackNavigator({
screen: RepairService,
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'),
RepairConfirm: screenConfig(RepairConfirm, 'confirm_info'),
RepairHistory: screenConfig(RepairHistory, 'repair_history'),
RepairSuccess: screenConfig(RepairSuccess, 'request_sent'),
// RepairHistoryDetail: screenConfig(RepairHistoryDetail, 'repair_history_detail'),
RepairHistoryDetail: {
RepairHistoryDetail: screenConfig(RepairHistoryDetail, 'repair_history_detail'),
/*RepairHistoryDetail: {
screen: RepairHistoryDetail,
navigationOptions: ({navigation}) => ({
title: t('repair_history_detail'),
@ -772,7 +776,7 @@ const AppStack = createStackNavigator({
</TouchableOpacity>
)
})
},
},*/
//Reward
Reward: screenConfig(RewardScreen, 'redeem_reward'),
RewardDetailScreen: screenConfig(RewardDetailScreen, 'product_detail'),

View File

@ -36,7 +36,7 @@ class LoginScreen extends Component {
async componentDidMount() {
await messaging().deleteToken()
console.log('deletetoken');
}
async _login () {
@ -74,7 +74,7 @@ class LoginScreen extends Component {
// const fcmToken = await messaging().getAPNSToken()
console.log('await messaging().getAPNSToken() <<<<<< ', fcmToken)
let params = {
// mobile: this.state.username,
@ -114,7 +114,7 @@ class LoginScreen extends Component {
this.setState({ isLoading: false })
}
})
};
render() {
@ -149,7 +149,7 @@ class LoginScreen extends Component {
<View style={styles.form}>
<View style={styles.row}>
<CustomInput
maxLength={10}
maxLength={13}
keyboardType="numeric"
onChangeText={(e) => {
this.setState({

View File

@ -23,36 +23,16 @@ import {getRepairList} from '../../api/UserApi';
import IndicatorLoading from '../../components/IndicatorLoading';
import { t } from '../../utils/i18n'
import moment from "moment";
import {connect} from "react-redux";
export default class RepairHistory extends Component {
class RepairHistory extends Component {
constructor(props) {
super(props)
this.state = {
isLoading: false,
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'
// },
]
history_list:[]
}
this.onRefresh = this.onRefresh.bind(this)
this.convertStatus = this.convertStatus.bind(this)
@ -66,50 +46,53 @@ export default class RepairHistory extends Component {
getRepairHistoryList(){
this.setState({
isLoading: true
})
getRepairList()
.then(res => {
if(res.ok){
console.log('check data response ----------> ',res.data.data);
}, () => {
let project_id = this.props.user && this.props.user.project_id;
getRepairList(project_id)
.then(res => {
if(res.ok){
console.log('check data response ----------> ',res.data.data);
this.setState({
history_list: res.data.data,
})
}
}).finally(() => {
this.setState({
history_list: res.data.data,
isLoading: false
})
}
this.setState({
isLoading: false
})
})
}
convertStatus(statusName){
switch(statusName){
case 'Pending':
return t('request_repair')
case 'InProgress':
return 'ยืนยันนัด'
case '1':
return 'รอซ่อม'
case '2':
return 'กำลังซ่อม'
case 'NewAppointment':
return 'นัดหมายใหม่'
case 'Cancel':
case '4':
return t('cancel')
case 'Success':
return 'ซ่อมสำเร็จ'
case '3':
return 'เสร็จสิ้น'
default:
return t('request_repair')
}
}
convertColorStatus(statusName){
switch(statusName){
case 'Pending':
convertColorStatus(statusCode){
switch(statusCode){
case '1': //รอซ่อม
return '#FF9500'
case 'InProgress':
case '2': //กำลังซ่อม
return '#007AFF'
// return '#FFCC00'
case 'NewAppointment':
return '#145EB3'
case 'Cancel':
case '4': //ยกเลิก
return '#666666'
case 'Success':
case '3': //เสร็จสิ้น
return '#2C7C0B'
default:
return '#C4C4C4'
@ -120,28 +103,26 @@ export default class RepairHistory extends Component {
return <TouchableOpacity style={{ borderBottomColor: '#00000040', borderBottomWidth: 1, backgroundColor: 'white' }}
onPress={() => {this.props.navigation.navigate('RepairHistoryDetail',{
repair_id:item.id,
project_id: this.props.user && this.props.user.project_id,
getRepairHistoryList: () => {this.getRepairHistoryList()}
})}}>
<View style={{height: 104,justifyContent:'center',paddingHorizontal:16}}>
<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={{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>
</View>
</View>
<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>
</View>
<View style={{flexDirection:'row',justifyContent:'space-between'}}>
<View style={{flexDirection:'row',alignItems:'center'}}>
<Icon name="ic_calendar_alt" size={14} color="#00000080" />
<Text style={{marginLeft: 10, fontSize:12}}>
{ item.date ?
`${moment(item.date).format('DD-MM-YYYY')}, ${item.time.slice(0,-3)}`
: item.status === 'Cancel' ? '-' : 'รอการนัดหมายใหม่'
}
{ item.date ? item.date : item.status_name }
</Text>
</View>
<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,
repair_id: this.props.navigation.getParam('repair_id','NO_ITEM'),
type: this.props.navigation.getParam('type','NO_ITEM'), //params from Notification.js
project_id: this.props.navigation.getParam('project_id',null),
repair:{},
visibleCancel: false,
rating_point: [0,0,0,0,0],
@ -121,7 +122,7 @@ export default class RepairHistoryDetail extends Component {
this.setState({
isLoading: true
})
getRepairById(this.state.repair_id, this.state.type)
getRepairById(this.state.project_id, this.state.repair_id)
.then(res => {
console.log('repair data -------------> ',res.data.data)
this.setState({

View File

@ -1,5 +1,5 @@
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 Select2 from "react-select2-native";
import Text from "../../components/Text";
@ -45,10 +45,10 @@ class RepairIndex extends Component {
super(props);
this.state = {
data: {
repair_list: [
{name: '', material: '', value: 0}
],
maintenanceList: [],
// repair_list: [
// {name: '', material: '', value: 0}
// ],
// maintenanceList: [],
work_date: moment(new Date()).format('DD-MM-YYYY'),
details: ''
},
@ -94,7 +94,7 @@ class RepairIndex extends Component {
let data = this.state.data;
if(data.header_dropdown_id
&& data.room_id
&& data.group_dropdown_id
// && data.group_dropdown_id
// && data.details
&& data.work_date
&& data.work_time
@ -322,7 +322,7 @@ class RepairIndex extends Component {
width: null,
height: null,
backgroundColor: '#EAEAF4',
padding: 16
padding: 16,
}}>
<ScrollView>
<SelectionData
@ -337,19 +337,19 @@ class RepairIndex extends Component {
zIndex={5}
/>
<SelectionData
items={this.state.rooms}
title={'room2'}
open={this.state.openRoom}
setOpen={(open) => this.setOpen(open, 'openRoom')}
value={data && data.room_id ? data.room_id : ''}
setValue={(callback) => this.setRepairData(callback(this), 'room_id')}
placeholder={t('select_room')}
viewStyle={isIos ? {zIndex: 4} : {}}
zIndex={4}
/>
<SelectionData
items={this.state.rooms}
title={'room2'}
open={this.state.openRoom}
setOpen={(open) => this.setOpen(open, 'openRoom')}
value={data && data.room_id ? data.room_id : ''}
setValue={(callback) => this.setRepairData(callback(this), 'room_id')}
placeholder={t('select_room')}
viewStyle={isIos ? {zIndex: 4} : {}}
zIndex={4}
/>
<SelectionData
{/*<SelectionData
items={this.state.repair_types}
title={'repair_type'}
open={this.state.openRoomType}
@ -359,50 +359,50 @@ class RepairIndex extends Component {
placeholder={t('select_repair_type')}
viewStyle={isIos ? {zIndex: 3} : {}}
zIndex={3}
/>
/>*/}
<View style={Styles.mbt16}>
<Text style={Styles.Label}>{t('repair_detail')}</Text>
<View style={Styles.textAreaBox}>
<TextInput
placeholder={t('detail')}
style={Styles.textArea}
onChangeText={text => this.setRepairData(text, 'details')}
value={data && data.details ? data.details : ''}
multiline={true}
numberOfLines={4}
textAlignVertical={'top'}
/>
<View style={Styles.mbt16}>
<Text style={Styles.Label}>{t('repair_detail')}</Text>
<View style={Styles.textAreaBox}>
<TextInput
placeholder={t('detail')}
style={Styles.textArea}
onChangeText={text => this.setRepairData(text, 'details')}
value={data && data.details ? data.details : ''}
multiline={true}
numberOfLines={4}
textAlignVertical={'top'}
/>
</View>
</View>
</View>
<View style={Styles.mbt16}>
<Text style={Styles.Label}>{t('repair_date')}</Text>
<View style={{flex: 1}}>
<TouchableOpacity style={Styles.DateComponent} onPress={() => this.setState({visibleDate: true})}>
<View style={Styles.DatePlaceholder}>
<Text style={{fontSize: 14, color: '#000000', marginHorizontal: 10,}}>{moment(this.state.select_date).format('DD/MM/YYYY')}</Text>
</View>
<View>
<Icon name="ic_calendar_alt" size={20} color="rgba(0,0,0,.25)"/>
</View>
</TouchableOpacity>
<View style={Styles.mbt16}>
<Text style={Styles.Label}>{t('repair_date')}</Text>
<View style={{flex: 1}}>
<TouchableOpacity style={Styles.DateComponent} onPress={() => this.setState({visibleDate: true})}>
<View style={Styles.DatePlaceholder}>
<Text style={{fontSize: 14, color: '#000000', marginHorizontal: 10,}}>{moment(this.state.select_date).format('DD/MM/YYYY')}</Text>
</View>
<View>
<Icon name="ic_calendar_alt" size={20} color="rgba(0,0,0,.25)"/>
</View>
</TouchableOpacity>
</View>
</View>
</View>
<SelectionData
items={this.state.period}
title={'period'}
open={this.state.openRepairType}
setOpen={(open) => this.setOpen(open, 'openRepairType')}
value={data && data.work_time ? data.work_time : ''}
setValue={(callback) => this.setRepairData(callback(this), 'work_time')}
placeholder={t('period')}
viewStyle={isIos ? {zIndex: 2} : {}}
zIndex={2}
/>
<SelectionData
items={this.state.period}
title={'period'}
open={this.state.openRepairType}
setOpen={(open) => this.setOpen(open, 'openRepairType')}
value={data && data.work_time ? data.work_time : ''}
setValue={(callback) => this.setRepairData(callback(this), 'work_time')}
placeholder={t('period')}
viewStyle={isIos ? {zIndex: 3} : {paddingBottom: 100}}
zIndex={3}
/>
{/*{
{/*{
data.repair_list.map((repair, index) =>
<View style={[Styles.RepairListContainer, Styles.mbt16]} key={'repair_list' + index}>
<SelectionData
@ -440,12 +440,11 @@ class RepairIndex extends Component {
)
}*/}
{/*<TouchableOpacity onPress={() => this.addRepairList()}>
{/*<TouchableOpacity onPress={() => this.addRepairList()}>
<View style={[Styles.BtnAddList, Styles.mbt16]}>
<Text style={{color: '#00420A', textAlign: 'center'}}>+ {t('add_list')}</Text>
</View>
</TouchableOpacity>*/}
</ScrollView>
{