511 lines
20 KiB
TypeScript
511 lines
20 KiB
TypeScript
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
import { View, Image, Text, StyleSheet, Dimensions, ScrollView, BackHandler, Alert, TouchableOpacity, ImageBackground } from 'react-native';
|
|
import { useNavigation, NavigationProp } from '@react-navigation/native';
|
|
import { ProgressChart } from 'react-native-chart-kit';
|
|
import { SvgUri } from 'react-native-svg';
|
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
import { addStorage, cfg } from '@/components/lib/cfg';
|
|
import { DB } from '@/components/lib/db';
|
|
import { Asset } from 'expo-asset';
|
|
import config from '../../components/data/config.json';
|
|
import TimeLive from '@/components/Time';
|
|
import { useAppContext } from '@/components/AppContext';
|
|
import { useFocusEffect } from 'expo-router';
|
|
import { formatRupiah } from '@/components/Helper';
|
|
|
|
const screenWidth = Dimensions.get("window").width;
|
|
|
|
const HomeScreen = () => {
|
|
const {loadData:profileLoad}:any = useAppContext();
|
|
const profileScreenRef = useRef(null);
|
|
const navigation = useNavigation<NavigationProp<any>>();
|
|
const [greeting, setGreeting] = useState('');
|
|
const [employeeData, setEmployeeData] = useState({job_title:'', name: '', work_email: '', work_phone: '' });
|
|
const [orderData, setOrderData] = useState([0]);
|
|
const [visitData, setVisitData] = useState([0]);
|
|
const [svgData, setSvgData] = useState('');
|
|
const [order, setOrder] = useState(0)
|
|
const [invoice, setInvoice] = useState(0)
|
|
const [order2, setOrder2] = useState(0)
|
|
const [invoice2, setInvoice2] = useState(0)
|
|
|
|
|
|
useEffect(() => {
|
|
const loadSvg = async () => {
|
|
const asset = Asset.fromModule(require('../../assets/images/bg/SORT_bg_Home.svg'));
|
|
await asset.downloadAsync();
|
|
const response = await fetch(asset.uri);
|
|
const svgText = await response.text();
|
|
setSvgData(svgText);
|
|
};
|
|
|
|
loadSvg();
|
|
|
|
setGreeting(getGreeting());
|
|
getEmployeeData();
|
|
animateProgress();
|
|
|
|
const backAction = () => {
|
|
Alert.alert(
|
|
"Konfirmasi Keluar",
|
|
"Apakah Anda yakin ingin keluar dari aplikasi?",
|
|
[
|
|
{
|
|
text: "Tidak",
|
|
onPress: () => null,
|
|
style: "cancel"
|
|
},
|
|
{ text: "Ya", onPress: () => {
|
|
(async function(){
|
|
await AsyncStorage.clear();
|
|
navigation.reset({
|
|
index:0,
|
|
routes:[{name:'index'}]
|
|
})
|
|
BackHandler.exitApp()
|
|
})();
|
|
}
|
|
}
|
|
]
|
|
);
|
|
return true;
|
|
};
|
|
|
|
(async function(){
|
|
let [ord, inv]:any = await DB(`SELECT * FROM total_order`);
|
|
let [ord2, inv2]: any = await DB(`SELECT * FROM total_order_count`);
|
|
setInvoice(Number(inv.total))
|
|
setOrder(Number(ord.total))
|
|
setInvoice2(Number(inv2.total))
|
|
setOrder2(Number(ord2.total))
|
|
setOrderData([Math.min(100, ((Number(ord2.total) / (Number(ord2.total) + Number(inv2.total)) )) )]);
|
|
setVisitData([Math.min(100, ((Number(inv2.total) / (Number(ord2.total) + Number(inv2.total)))))]);
|
|
})();
|
|
|
|
const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
|
|
|
|
return () => backHandler.remove();
|
|
}, [setSvgData, setInvoice,setOrder]);
|
|
|
|
|
|
|
|
const getEmployeeData = async () => {
|
|
try {
|
|
const jsonValue = await AsyncStorage.getItem('login');
|
|
if (jsonValue !== null) {
|
|
const data = JSON.parse(jsonValue);
|
|
setEmployeeData(data[0]);
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
const getGreeting = () => {
|
|
const hour = new Date().getHours();
|
|
if (hour < 12) {
|
|
return 'Selamat Pagi';
|
|
} else if (hour < 15) {
|
|
return 'Selamat Siang';
|
|
} else if (hour < 18) {
|
|
return 'Selamat Sore';
|
|
} else {
|
|
return 'Selamat Malam';
|
|
}
|
|
};
|
|
|
|
const animateProgress = () => {
|
|
let orderProgress = 0;
|
|
let visitProgress = 0;
|
|
const interval = setInterval(() => {
|
|
orderProgress += 0.05;
|
|
visitProgress += 0.05;
|
|
setOrderData([Math.min(orderProgress, 0.75)]);
|
|
setVisitData([Math.min(visitProgress, 0.65)]);
|
|
if (orderProgress >= 0.75 && visitProgress >= 0.65) {
|
|
clearInterval(interval);
|
|
}
|
|
}, 100);
|
|
};
|
|
|
|
const orderConfig = {
|
|
backgroundGradientFrom: '#fff',
|
|
backgroundGradientTo: '#fff',
|
|
color: (opacity = 1) => `rgba(255, 205, 26, ${opacity})`,
|
|
};
|
|
|
|
const visitConfig = {
|
|
backgroundGradientFrom: '#fff',
|
|
backgroundGradientTo: '#fff',
|
|
color: (opacity = 1) => `rgba(26, 255, 146, ${opacity})`,
|
|
};
|
|
|
|
const renderMenuItems = () => {
|
|
|
|
const menuItems = [
|
|
{ img: require('../../assets/images/icons/icon_sort_BuatPesanan.png') , action: "order", text: "BUAT\n PESANAN", uri: "https://www.svgrepo.com/show/375440/google-cloud-marketplace.svg" },
|
|
{ img: require('../../assets/images/icons/icon_sort_StokBarang1.png') , action: "stock", text: "STOK\n BARANG", uri: "https://www.svgrepo.com/show/375469/os-inventory-management.svg" },
|
|
{ img: require('../../assets/images/icons/icon_sort_ListPesanan.png') , action: "history", text: "LIST\n PESANAN", uri: "https://www.svgrepo.com/show/375491/retail-api.svg" },
|
|
{ img: require('../../assets/images/icons/icon_sort_Pelunasan.png') , action: "pelunasan", text: "PELUNASAN", uri: "https://www.svgrepo.com/show/375438/gce-systems-management.svg" },
|
|
{ img: require('../../assets/images/icons/icon_sort_OrderLunas.png') , action: "orderlunas", text: "ORDER LUNAS", uri: "https://www.svgrepo.com/show/375438/gce-systems-management.svg" },
|
|
{ img: require('../../assets/images/icons/icon_sort_OrderBelumLunas.png') , action: "orderbelumlunas", text: "ORDER BELUM\nLUNAS", uri: "https://www.svgrepo.com/show/375438/gce-systems-management.svg" },
|
|
{ img: require('../../assets/images/icons/icon_sort_ListCustomer.png') , action: "customer", text: "LIST\n CUSTOMER", uri: "https://www.svgrepo.com/show/375359/cloud-for-marketing.svg" },
|
|
{ img: require('../../assets/images/icons/icon_sort_Profil.png') , action: "profile", text: "PROFIL", uri: "https://www.svgrepo.com/show/375473/permissions.svg" },
|
|
{ img: require('../../assets/images/icons/icon_sort_Pengaturan.png') , action: "setting", text: "PENGATURAN", uri: "https://www.svgrepo.com/show/375438/gce-systems-management.svg" }
|
|
];
|
|
|
|
const numColumns = 3 ;
|
|
const menuRows = [];
|
|
|
|
for (let i = 0; i < menuItems.length; i += numColumns) {
|
|
const items = menuItems.slice(i, i + numColumns);
|
|
menuRows.push(
|
|
<View style={styles.menuRow} key={`row-${i}`}>
|
|
{items.map((item, index) => (
|
|
<TouchableOpacity style={{ padding: 0, margin: 0 }} onPress={() => {
|
|
// profileLoad()
|
|
navigation.navigate(item.action);
|
|
}} key={`item-${i}-${index}`}>
|
|
<View style={[styles.cardMenu]}>
|
|
<View style={styles.menu}>
|
|
<Image
|
|
source={item.img}
|
|
style={{ width: 75,height:75 }}
|
|
resizeMode="contain" // You can adjust this based on how you want the image to scale
|
|
/>
|
|
<Text style={{
|
|
fontSize:14
|
|
, paddingTop: 10
|
|
, fontWeight: 700
|
|
, padding: 5
|
|
, width:'100%'
|
|
, textAlign:'center'
|
|
, paddingHorizontal: 5
|
|
, height: 60
|
|
}}>{item.text}</Text>
|
|
</View>
|
|
</View>
|
|
</TouchableOpacity>
|
|
))}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
return menuRows;
|
|
};
|
|
|
|
const getData = async () => {
|
|
DB('SELECT * FROM res_partner').then((data: any) => {
|
|
cfg.dataPilihan.customer = data;
|
|
})
|
|
}
|
|
|
|
const getDataMaster: any = {
|
|
uom: function () {
|
|
return new Promise(async (resolve, reject) => {
|
|
let uom = await DB(`
|
|
SELECT C
|
|
.NAME ->> 'en_US' kat,
|
|
u.create_uid,
|
|
u.NAME,
|
|
u.factor
|
|
FROM
|
|
"public".uom_uom u
|
|
LEFT JOIN "public".uom_category C ON C.create_uid = u.create_uid
|
|
WHERE C.name->>'en_US' = 'Rokok'
|
|
ORDER BY u.factor DESC
|
|
`);
|
|
addStorage("master oum", uom);
|
|
cfg.dataPilihan.uom = uom;
|
|
resolve(uom);
|
|
})
|
|
},
|
|
uomkat: function () {
|
|
return new Promise(async (resolve, reject) => {
|
|
let uom = await DB(`
|
|
SELECT create_uid, name FROM uom_category
|
|
`);
|
|
cfg.dataPilihan.uomkat = uom;
|
|
addStorage('master uomkat', uom)
|
|
resolve(uom);
|
|
})
|
|
},
|
|
tax: function () {
|
|
return new Promise(async (resolve, reject) => {
|
|
let uom = await DB(`
|
|
SELECT id, country_id, type_tax_use, amount_type, name, amount FROm account_tax
|
|
`);
|
|
cfg.dataPilihan.tax = uom;
|
|
addStorage('master tax', uom)
|
|
resolve(uom);
|
|
})
|
|
}
|
|
, product: function () {
|
|
return new Promise(async (resolve, reject) => {
|
|
let uom = await DB(`
|
|
SELECT P
|
|
.ID,
|
|
P.NAME ->> 'en_US' AS NAME,
|
|
P.list_price * factor AS list_price,
|
|
P.uom_id,
|
|
u.factor
|
|
FROM
|
|
product_template
|
|
P LEFT JOIN product_tag_product_template_rel T ON P.ID = T.product_template_id
|
|
LEFT JOIN product_tag pt ON T.product_tag_id = pt.ID
|
|
LEFT JOIN uom_uom u ON u.id = p.uom_id
|
|
WHERE
|
|
pt.NAME ->> 'en_US' = 'rokok' ORDER BY id ASC;
|
|
`);
|
|
cfg.dataPilihan.product = uom;
|
|
addStorage('master product', uom)
|
|
resolve(uom);
|
|
})
|
|
}
|
|
}
|
|
|
|
cfg.action['home'] = function () {
|
|
getDataMaster.tax();
|
|
getData();
|
|
};
|
|
|
|
function capitalizeWords(str:any) {
|
|
return str.split(' ').map((word:any) => {
|
|
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
}).join(' ');
|
|
}
|
|
|
|
useEffect(() => {
|
|
getDataMaster.product();
|
|
getDataMaster.tax();
|
|
getData();
|
|
},[])
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={{ marginBottom: -88 }}>
|
|
<ImageBackground
|
|
source={require('../../assets/images/bg/SORT_bg_Home.png')}
|
|
>
|
|
<View style={[styles.header]}>
|
|
<View style={[styles.info]}>
|
|
<Text style={[styles.greeting,{marginBottom:10}]}>{greeting},</Text>
|
|
<Text style={styles.name}>{employeeData.name.toUpperCase()}</Text>
|
|
<Text style={{ color: 'white' }}>{capitalizeWords(employeeData.job_title ? employeeData.job_title:'')}</Text>
|
|
</View>
|
|
<View style={[styles.trophyRow]}>
|
|
<TimeLive />
|
|
<Image style={styles.avatar} source={{ uri: 'https://www.bootdey.com/img/Content/avatar/avatar6.png' }} />
|
|
</View>
|
|
</View>
|
|
</ImageBackground>
|
|
</View>
|
|
<ScrollView>
|
|
<View style={styles.gaugeRow}>
|
|
<View style={styles.card}>
|
|
<Text style={{ fontWeight: '700', fontSize: 16 }}>{order2} Orders</Text>
|
|
<ProgressChart
|
|
data={{ data: orderData }}
|
|
width={(screenWidth / 2) - 50}
|
|
height={(screenWidth / 2) - 50}
|
|
strokeWidth={10}
|
|
radius={50}
|
|
chartConfig={orderConfig}
|
|
hideLegend={true}
|
|
/>
|
|
<View style={styles.chartCenterText}>
|
|
<Text style={{ fontSize:20, textAlign: 'center', fontWeight: '900', color: '#2563eb' }}>75%</Text>
|
|
<Text style={{ fontWeight: '600', textAlign: 'center', color: '#2563eb' }}>Goal</Text>
|
|
</View>
|
|
<Text style={{ fontSize: 16, fontWeight: '600', textAlign: 'center' }}>22 Pending</Text>
|
|
</View>
|
|
<View style={styles.card}>
|
|
<Text style={{ fontWeight: '700', fontSize: 16 }}>{invoice2} Visits</Text>
|
|
<ProgressChart
|
|
data={{ data: visitData }}
|
|
width={(screenWidth / 2) - 50}
|
|
height={(screenWidth / 2) - 50}
|
|
strokeWidth={10}
|
|
radius={50}
|
|
chartConfig={visitConfig}
|
|
hideLegend={true}
|
|
/>
|
|
<View style={styles.chartCenterText}>
|
|
<Text style={{ fontSize: 20, textAlign: 'center', fontWeight: '900', color: '#2563eb' }}>65%</Text>
|
|
<Text style={{ fontWeight:'600', textAlign: 'center', color: '#2563eb' }}>Goal</Text>
|
|
</View>
|
|
<Text style={{ fontSize: 16, fontWeight: '600' , textAlign: 'center' }}>19 Pending</Text>
|
|
</View>
|
|
</View>
|
|
|
|
<View style={{ backgroundColor: '#f0f0f0', paddingTop: 20 }}>
|
|
<View style={{paddingHorizontal:20}} >
|
|
<View style={{
|
|
backgroundColor: config.color.primary,
|
|
borderRadius: 20,
|
|
paddingHorizontal: 20,
|
|
paddingVertical:10,
|
|
flexDirection:"row"
|
|
}}>
|
|
<View style={{width:140}}>
|
|
<View>
|
|
<Text style={{ textAlign:"center", fontSize: 60 }}>🏆</Text>
|
|
</View>
|
|
<View>
|
|
<Text style={{ textAlign: "center", fontSize: 24, fontWeight:700, color: '#fff' }}>RANK 8</Text>
|
|
</View>
|
|
</View>
|
|
<View style={{flex:1}}>
|
|
<View style={{padding:10, borderBottomColor:"#ddd", borderBottomWidth:1}}>
|
|
<Text style={styles.statLabel}>TOTAL ORDER</Text>
|
|
<Text style={styles.statValue}>Rp {formatRupiah(order.toString())}</Text>
|
|
</View>
|
|
<View style={{ padding: 10}}>
|
|
<Text style={styles.statLabel}>TOTAL COLLECTED</Text>
|
|
<Text style={styles.statValue}>Rp {formatRupiah(invoice.toString())}</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
{/* <View style={styles.syncRow}>
|
|
<Text style={styles.bio}>
|
|
Synchronize File
|
|
</Text>
|
|
<SvgUri style={{ marginRight: 12 }} width={40} height={40} uri="https://www.svgrepo.com/show/375410/data-transfer.svg"></SvgUri>
|
|
</View> */}
|
|
<View style={{paddingVertical:20}}>
|
|
{renderMenuItems()}
|
|
</View>
|
|
</View>
|
|
</ScrollView>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: '#f0f0f0',
|
|
},
|
|
header: {
|
|
paddingTop: 75,
|
|
paddingBottom: 100,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
padding: 20,
|
|
},
|
|
avatar: {
|
|
width: 50,
|
|
height: 50,
|
|
borderRadius: 25,
|
|
marginTop:10
|
|
},
|
|
info: {
|
|
flex:1,
|
|
marginLeft: 20,
|
|
},
|
|
name: {
|
|
fontSize: 25,
|
|
fontWeight: 'bold',
|
|
color: '#fff',
|
|
},
|
|
greeting: {
|
|
color: '#fff',
|
|
fontSize: 14,
|
|
fontWeight:'600'
|
|
},
|
|
trophyRow: {
|
|
flex: 1,
|
|
flexDirection:'column',
|
|
alignItems: 'flex-end',
|
|
paddingHorizontal:20
|
|
},
|
|
gaugeRow: {
|
|
padding: 10,
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
},
|
|
card: {
|
|
backgroundColor: '#fff',
|
|
flex: 1,
|
|
marginBottom: 10,
|
|
padding: 10,
|
|
marginHorizontal: 10,
|
|
borderRadius: 10,
|
|
shadowColor: '#000',
|
|
shadowOpacity: 0.2,
|
|
shadowOffset: { width: 0, height: 2 },
|
|
shadowRadius: 5,
|
|
},
|
|
chartCenterText: {
|
|
position: 'absolute',
|
|
width:'100%',
|
|
left:10,
|
|
top: '45%',
|
|
flexDirection:'column',
|
|
justifyContent:'center',
|
|
alignItems:'center'
|
|
},
|
|
stats: {
|
|
flexDirection: 'column',
|
|
alignItems: 'center',
|
|
padding: 15,
|
|
marginHorizontal: 20,
|
|
marginTop: 0,
|
|
borderRadius: 10,
|
|
shadowColor: '#000',
|
|
shadowOpacity: 0.2,
|
|
shadowOffset: { width: 0, height: 2 },
|
|
shadowRadius: 5,
|
|
},
|
|
stat: {
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
},
|
|
statLabel: {
|
|
color: 'white',
|
|
fontWeight: '500',
|
|
fontSize: 12,
|
|
},
|
|
statValue: {
|
|
fontSize: 18,
|
|
fontWeight:'700',
|
|
color:'white'
|
|
},
|
|
syncRow: {
|
|
paddingTop: 15,
|
|
marginHorizontal: 10,
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
},
|
|
bio: {
|
|
paddingHorizontal: 20,
|
|
fontSize: 17,
|
|
fontWeight: '700',
|
|
color: '#333',
|
|
},
|
|
menuRow: {
|
|
paddingTop: 5,
|
|
flexDirection: 'row',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
cardMenu: {
|
|
width: (screenWidth/3)-20,
|
|
margin: 5,
|
|
borderRadius: 10,
|
|
shadowColor: '#000',
|
|
shadowOpacity: 0.2,
|
|
shadowRadius: 5,
|
|
},
|
|
menu: {
|
|
alignItems: 'center',
|
|
fontSize: screenWidth > 411 ? 14 : 18,
|
|
},
|
|
});
|
|
|
|
export default HomeScreen; |