[MAPS]
This commit is contained in:
parent
bdca218c7f
commit
f5ba03de84
2
.babelrc
2
.babelrc
@ -17,7 +17,7 @@
|
||||
"services": "./src/services",
|
||||
"store": "./src/store",
|
||||
"global-styles": "./src/styles",
|
||||
"utilites": "./src/utilites"
|
||||
"utilities": "./src/utilities"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -17,7 +17,7 @@ module.exports = {
|
||||
services: './src/services',
|
||||
store: './src/store',
|
||||
'global-styles': './src/styles',
|
||||
utilites: './src/utilites',
|
||||
utilities: './src/utilities',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -12,7 +12,7 @@
|
||||
"services": ["src/services/*"],
|
||||
"store": ["src/store/*"],
|
||||
"global-styles": ["src/styles/*"],
|
||||
"utilites": ["src/utilites/*"]
|
||||
"utilities": ["src/utilities/*"]
|
||||
}
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@
|
||||
"eslint-import-resolver-babel-module": "^5.3.1",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"i18next": "^22.0.6",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"moment": "^2.29.4",
|
||||
"react": "18.1.0",
|
||||
"react-i18next": "^12.0.0",
|
||||
|
BIN
src/assets/images/marker.png
Normal file
BIN
src/assets/images/marker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.5 KiB |
@ -1,4 +1,4 @@
|
||||
import React, {useState} from 'react';
|
||||
import React, {useEffect, useState, useMemo} from 'react';
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
@ -6,6 +6,8 @@ import {
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
TextInput,
|
||||
FlatList,
|
||||
Image,
|
||||
} from 'react-native';
|
||||
import {Colors, FONTS} from 'global-styles';
|
||||
import {RFValue} from 'react-native-responsive-fontsize';
|
||||
@ -14,19 +16,44 @@ import Modal from 'react-native-modal';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import MapView, {PROVIDER_GOOGLE, Marker} from 'react-native-maps';
|
||||
const {width, height} = Dimensions.get('window');
|
||||
import debounce from 'lodash.debounce';
|
||||
import {searchPlace} from 'services';
|
||||
|
||||
let coordinateX = {
|
||||
latitude: 37.78825,
|
||||
longitude: -122.4324,
|
||||
latitudeDelta: 0.0922,
|
||||
longitudeDelta: 0.0421,
|
||||
};
|
||||
const ASPECT_RATIO = width / height;
|
||||
const LATITUDE_DELTA = 0.001;
|
||||
|
||||
const _Maps = props => {
|
||||
const {t} = useTranslation();
|
||||
const [modal, setModal] = useState(false);
|
||||
const [coordinate, setCoordinate] = useState({});
|
||||
const {label, note, error, required} = props;
|
||||
const [modal, setModal] = useState(false);
|
||||
const [location, setLocation] = useState({
|
||||
name: '',
|
||||
address: '',
|
||||
coordinate: {},
|
||||
});
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [places, setPlaces] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
debouncedResults.cancel();
|
||||
};
|
||||
});
|
||||
|
||||
const handleSearchPlace = async e => {
|
||||
setKeyword(e);
|
||||
const {_data, _error} = await searchPlace(e);
|
||||
if (_error) {
|
||||
console.log(_error);
|
||||
return;
|
||||
}
|
||||
setPlaces(_data);
|
||||
};
|
||||
|
||||
const debouncedResults = useMemo(() => {
|
||||
return debounce(handleSearchPlace, 300);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{label && (
|
||||
@ -70,9 +97,11 @@ const _Maps = props => {
|
||||
<Text style={styles.title}>
|
||||
{t('select_outlet_location_text')}
|
||||
</Text>
|
||||
{coordinate?.latitude ? (
|
||||
{location.coordinate?.latitude ? (
|
||||
<TouchableOpacity
|
||||
onPress={() => setCoordinate({})}
|
||||
onPress={() =>
|
||||
setLocation({name: '', address: '', coordinate: {}})
|
||||
}
|
||||
style={styles.buttonSearch}>
|
||||
<Icon
|
||||
name="search"
|
||||
@ -82,7 +111,11 @@ const _Maps = props => {
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
<View style={styles.inputSearchSection}>
|
||||
<TextInput style={styles.input} placeholder="Cari" />
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
onChangeText={debouncedResults}
|
||||
placeholder={t('search_placeholder')}
|
||||
/>
|
||||
<Icon
|
||||
name="search"
|
||||
type="Feather"
|
||||
@ -92,63 +125,71 @@ const _Maps = props => {
|
||||
)}
|
||||
</View>
|
||||
|
||||
{!coordinate?.latitude && (
|
||||
<View style={styles.locationContainer}>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={() =>
|
||||
setCoordinate({
|
||||
latitude: 37.78825,
|
||||
longitude: -122.4324,
|
||||
latitudeDelta: 0.0922,
|
||||
longitudeDelta: 0.0421,
|
||||
})
|
||||
}
|
||||
style={styles.locationSection}>
|
||||
<Icon
|
||||
name="my-location"
|
||||
type="MaterialIcons"
|
||||
style={styles.myLoationIcon}
|
||||
/>
|
||||
<View style={styles.locationInfoSection(true)}>
|
||||
<Text style={styles.locationInfoName}>
|
||||
Warung Sop & Sate Sapi Pak Bayu
|
||||
</Text>
|
||||
<Text numberOfLines={2} style={styles.locationInfoAddress}>
|
||||
Jl. Yudistiro No.2, RT.05/RW.11, Palgading, Sinduharjo,
|
||||
Kec. Ngaglik, Kabupaten Sleman, Daerah Istimewa Yogyakarta
|
||||
55581
|
||||
</Text>
|
||||
{!location.coordinate?.latitude && (
|
||||
<FlatList
|
||||
data={places}
|
||||
keyExtractor={(item, index) => index.toString()}
|
||||
renderItem={({item, index}) => (
|
||||
<View style={styles.locationContainer}>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={() =>
|
||||
setLocation({
|
||||
name: item.text_id,
|
||||
address: item.place_name_id,
|
||||
coordinate: {
|
||||
latitude: item.geometry.coordinates[1],
|
||||
longitude: item.geometry.coordinates[0],
|
||||
latitudeDelta: LATITUDE_DELTA,
|
||||
longitudeDelta: LATITUDE_DELTA * ASPECT_RATIO,
|
||||
},
|
||||
})
|
||||
}
|
||||
style={styles.locationSection}>
|
||||
<Icon
|
||||
name="my-location"
|
||||
type="MaterialIcons"
|
||||
style={styles.myLoationIcon}
|
||||
/>
|
||||
<View style={styles.locationInfoSection(true)}>
|
||||
<Text style={styles.locationInfoName}>
|
||||
{item.text_id}
|
||||
</Text>
|
||||
<Text
|
||||
numberOfLines={2}
|
||||
style={styles.locationInfoAddress}>
|
||||
{`${item.place_name_id}`}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{coordinate?.latitude && (
|
||||
{location.coordinate?.latitude && (
|
||||
<React.Fragment>
|
||||
<View style={styles.mapSection}>
|
||||
<MapView
|
||||
provider={PROVIDER_GOOGLE} // remove if not using Google Maps
|
||||
provider={PROVIDER_GOOGLE}
|
||||
style={styles.map}
|
||||
region={coordinate}>
|
||||
<Marker
|
||||
coordinate={coordinate}
|
||||
title={'title'}
|
||||
description={'description'}
|
||||
/>
|
||||
zoomEnabled={true}
|
||||
initialRegion={location.coordinate}
|
||||
onRegionChangeComplete={v =>
|
||||
setLocation({...location, coordinate: v})
|
||||
}>
|
||||
<Marker coordinate={location.coordinate}>
|
||||
<Image
|
||||
source={require('assets/images/marker.png')}
|
||||
style={styles.marker}
|
||||
/>
|
||||
</Marker>
|
||||
</MapView>
|
||||
</View>
|
||||
<View style={styles.buttonSection}>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={() =>
|
||||
setCoordinate({
|
||||
latitude: 37.78825,
|
||||
longitude: -122.4324,
|
||||
latitudeDelta: 0.0922,
|
||||
longitudeDelta: 0.0421,
|
||||
})
|
||||
}
|
||||
disabled
|
||||
style={styles.locationSection}>
|
||||
<Icon
|
||||
name="my-location"
|
||||
@ -157,14 +198,12 @@ const _Maps = props => {
|
||||
/>
|
||||
<View style={styles.locationInfoSection(false)}>
|
||||
<Text style={styles.locationInfoName}>
|
||||
Warung Sop & Sate Sapi Pak Bayu
|
||||
{location.name}
|
||||
</Text>
|
||||
<Text
|
||||
numberOfLines={2}
|
||||
style={styles.locationInfoAddress}>
|
||||
Jl. Yudistiro No.2, RT.05/RW.11, Palgading, Sinduharjo,
|
||||
Kec. Ngaglik, Kabupaten Sleman, Daerah Istimewa
|
||||
Yogyakarta 55581
|
||||
{location.address}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
@ -334,8 +373,9 @@ const styles = StyleSheet.create({
|
||||
width: width,
|
||||
},
|
||||
map: {
|
||||
flex: 1,
|
||||
height: height - 0.5,
|
||||
// flex: 1,
|
||||
// height: 500,
|
||||
height: height - width * 1,
|
||||
},
|
||||
buttonClose: {
|
||||
backgroundColor: Colors.WHITE,
|
||||
@ -379,6 +419,19 @@ const styles = StyleSheet.create({
|
||||
fontSize: RFValue(12),
|
||||
marginTop: width * 0.005,
|
||||
},
|
||||
marker: {
|
||||
width: width * 0.15,
|
||||
height: width * 0.15,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 3.84,
|
||||
|
||||
elevation: 5,
|
||||
},
|
||||
});
|
||||
|
||||
export default _Maps;
|
||||
|
@ -1,10 +1,14 @@
|
||||
import React from 'react';
|
||||
import {View, Text, Image} from 'react-native';
|
||||
import {View, Text, Image, TouchableOpacity} from 'react-native';
|
||||
import styles from './styles';
|
||||
|
||||
const ProductRaw = props => {
|
||||
const {onPress} = props;
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
activeOpacity={0.5}
|
||||
style={styles.container}>
|
||||
<Image
|
||||
source={require('assets/images/product-1.png')}
|
||||
style={styles.image}
|
||||
@ -16,7 +20,7 @@ const ProductRaw = props => {
|
||||
</Text>
|
||||
<Text style={styles.price}>Rp15.000</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -48,9 +48,11 @@ export default {
|
||||
menu_category_title: 'Menu Category',
|
||||
category_list_text: 'Category list',
|
||||
add_category_title: 'Add Category',
|
||||
update_category_title: 'Change Category',
|
||||
category_name_text: 'Category name',
|
||||
change_text: 'Change',
|
||||
add_new_menu_title: 'Add Menus',
|
||||
update_new_menu_title: 'Update Menus',
|
||||
menu_photo_text: 'Menu photo',
|
||||
menu_photo_note: 'Upload attractive photos to make potential customers more interested.',
|
||||
upload_image_text: 'Upload photos',
|
||||
@ -59,4 +61,5 @@ export default {
|
||||
price_text: 'Price (Rp)',
|
||||
name_food_placeholder: 'Ex: Chicken Teriyaki Rice Set',
|
||||
description_food_placeholder: 'Ex: Chicken Katsu + Steam Rice + Salad + Katsu Sauce + Spicy Mayonnaise',
|
||||
search_placeholder: 'Search . . .',
|
||||
};
|
||||
|
@ -48,9 +48,11 @@ export default {
|
||||
menu_category_title: 'Kategori Menu',
|
||||
category_list_text: 'Daftar kategori',
|
||||
add_category_title: 'Tambah Kategori',
|
||||
update_category_title: 'Ubah Kategori',
|
||||
category_name_text: 'Nama kategori',
|
||||
change_text: 'Ubah',
|
||||
add_new_menu_title: 'Tambah Menu',
|
||||
update_new_menu_title: 'Update Menu',
|
||||
menu_photo_text: 'Foto menu',
|
||||
menu_photo_note: 'Upload foto yang menarik agar calon pelangganmu makin tertarik.',
|
||||
upload_image_text: 'Upload foto',
|
||||
@ -59,4 +61,5 @@ export default {
|
||||
price_text: 'Harga (Rp)',
|
||||
name_food_placeholder: 'Cth: Chicken Teriyaki Rice Set',
|
||||
description_food_placeholder: 'Cth: Chicken Katsu + Steam Rice + Salad + Katsu Sauce + Spicy Mayonnaise',
|
||||
search_placeholder: 'Cari . . .',
|
||||
};
|
||||
|
@ -5,12 +5,18 @@ import {Colors} from 'global-styles';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import styles from './styles';
|
||||
|
||||
const AddMenuCategory = ({navigation}) => {
|
||||
const AddMenuCategory = ({route, navigation}) => {
|
||||
const {t} = useTranslation();
|
||||
const [category, setCategory] = useState('');
|
||||
const {type} = route.params;
|
||||
return (
|
||||
<Container backgroundColor={Colors.WHITE}>
|
||||
<Header navigation={navigation} smTitle={t('add_category_title')} />
|
||||
<Header
|
||||
navigation={navigation}
|
||||
smTitle={t(
|
||||
type === 'add' ? 'add_category_title' : 'update_category_title',
|
||||
)}
|
||||
/>
|
||||
<View style={styles.container}>
|
||||
<ScrollView>
|
||||
<View style={styles.spacing} />
|
||||
|
@ -20,7 +20,9 @@ const MenuCategory = ({navigation}) => {
|
||||
<View style={[styles.sceneTopSection, styles.horizontal]}>
|
||||
<Text style={styles.sceneLabel}>{t('category_list_text')}</Text>
|
||||
<TouchableOpacity
|
||||
onPress={() => navigation.navigate('AddMenuCategory')}
|
||||
onPress={() =>
|
||||
navigation.navigate('AddMenuCategory', {type: 'add'})
|
||||
}
|
||||
style={styles.buttonAddCategory}>
|
||||
<Text style={styles.buttonAddCategoryTitle}>Tambah</Text>
|
||||
</TouchableOpacity>
|
||||
|
@ -5,12 +5,18 @@ import {Colors} from 'global-styles';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import styles from './styles';
|
||||
|
||||
const AddProduct = ({navigation}) => {
|
||||
const AddProduct = ({route, navigation}) => {
|
||||
const {t} = useTranslation();
|
||||
const [price, setPrice] = useState('0');
|
||||
const {type} = route.params;
|
||||
return (
|
||||
<Container backgroundColor={Colors.WHITE}>
|
||||
<Header navigation={navigation} smTitle={t('add_new_menu_title')} />
|
||||
<Header
|
||||
navigation={navigation}
|
||||
smTitle={t(
|
||||
type === 'add' ? 'add_new_menu_title' : 'update_new_menu_title',
|
||||
)}
|
||||
/>
|
||||
<View style={styles.container}>
|
||||
<ScrollView>
|
||||
<View style={styles.spacing} />
|
||||
|
@ -13,18 +13,24 @@ const ProductList = ({navigation}) => {
|
||||
<Header
|
||||
navigation={navigation}
|
||||
smTitle={'Mie Ayam'}
|
||||
onButtonPress={() => navigation.navigate('AddProduct')}
|
||||
onButtonPress={() =>
|
||||
navigation.navigate('AddMenuCategory', {type: 'update'})
|
||||
}
|
||||
/>
|
||||
<View style={styles.container}>
|
||||
<ScrollView>
|
||||
<View style={styles.spacing} />
|
||||
<ProductRaw />
|
||||
<ProductRaw
|
||||
onPress={() => navigation.navigate('AddProduct', {type: 'update'})}
|
||||
/>
|
||||
<View style={styles.separator} />
|
||||
<ProductRaw />
|
||||
<ProductRaw
|
||||
onPress={() => navigation.navigate('AddProduct', {type: 'update'})}
|
||||
/>
|
||||
</ScrollView>
|
||||
<Button
|
||||
title="Tambah"
|
||||
onPress={() => navigation.navigate('AddProduct')}
|
||||
onPress={() => navigation.navigate('AddProduct', {type: 'add'})}
|
||||
/>
|
||||
</View>
|
||||
</Container>
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './auth-services';
|
||||
export * from './master-service';
|
||||
|
30
src/services/master-service.js
Normal file
30
src/services/master-service.js
Normal file
@ -0,0 +1,30 @@
|
||||
import {HttpRequest, RestConstant, StatusCode} from 'constants';
|
||||
|
||||
const initialState = {
|
||||
_data: undefined,
|
||||
_error: undefined,
|
||||
};
|
||||
|
||||
export const searchPlace = async keyword => {
|
||||
const newState = {...initialState};
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
const access_token = 'pk.eyJ1IjoiYW5kaXdhd2FuayIsImEiOiJja2ZuZmViejUxbHltMnFrdTFreHZ1MDIyIn0.n7s5b5vF6YqcKrMHxB8p1Q';
|
||||
const config = `country=id&types=place,address,country,postcode,region,locality,district,neighborhood,poi&language=id&access_token=${access_token}`;
|
||||
const url = `https://api.mapbox.com/geocoding/v5/mapbox.places/${keyword}.json?${config}`;
|
||||
try {
|
||||
const response = await HttpRequest.get(url);
|
||||
switch (response.request.status) {
|
||||
case StatusCode.OK:
|
||||
newState._data = response.data.features;
|
||||
return newState;
|
||||
case StatusCode.UNAUTHORIZED:
|
||||
newState._error = response.response.data;
|
||||
return newState;
|
||||
case StatusCode.SERVER_ERROR:
|
||||
newState._error = response.response.data;
|
||||
return newState;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user