sort/app/(tabs)/home.tsx

644 lines
27 KiB
TypeScript
Raw Permalink Normal View History

2024-09-24 00:17:23 +00:00
import React, { useCallback, useEffect, useRef, useState } from 'react';
2024-09-07 01:22:11 +00:00
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';
2024-09-24 00:17:23 +00:00
import { useAppContext } from '@/components/AppContext';
import { useFocusEffect } from 'expo-router';
import { formatRupiah } from '@/components/Helper';
2024-10-16 03:37:18 +00:00
import LinearGradient from 'react-native-linear-gradient';
import { Ionicons } from '@expo/vector-icons';
import lang from '../../components/data/lang.json';
2024-09-07 01:22:11 +00:00
const screenWidth = Dimensions.get("window").width;
const HomeScreen = () => {
2024-09-24 00:17:23 +00:00
const {loadData:profileLoad}:any = useAppContext();
const profileScreenRef = useRef(null);
2024-09-07 01:22:11 +00:00
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('');
2024-09-24 00:17:23 +00:00
const [order, setOrder] = useState(0)
const [invoice, setInvoice] = useState(0)
const [order2, setOrder2] = useState(0)
const [invoice2, setInvoice2] = useState(0)
2024-09-07 01:22:11 +00:00
2024-09-24 00:17:23 +00:00
useEffect(() => {
2024-09-07 01:22:11 +00:00
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;
};
2024-09-24 00:17:23 +00:00
(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)))))]);
})();
2024-09-07 01:22:11 +00:00
const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
return () => backHandler.remove();
2024-09-24 00:17:23 +00:00
}, [setSvgData, setInvoice,setOrder]);
2024-09-07 01:22:11 +00:00
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);
}
};
2024-09-24 00:17:23 +00:00
2024-09-07 01:22:11 +00:00
const getGreeting = () => {
const hour = new Date().getHours();
if (hour < 12) {
2024-10-16 03:37:18 +00:00
return 'Good Morning';
2024-09-07 01:22:11 +00:00
} else if (hour < 15) {
2024-10-16 03:37:18 +00:00
return 'Good Afternoon';
2024-09-07 01:22:11 +00:00
} else if (hour < 18) {
2024-10-16 03:37:18 +00:00
return 'Good Evening';
2024-09-07 01:22:11 +00:00
} else {
2024-10-16 03:37:18 +00:00
return 'Good Night';
2024-09-07 01:22:11 +00:00
}
};
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 = [
2024-10-16 03:37:18 +00:00
// { 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: "STOCK\n ITEMS", uri: "https://www.svgrepo.com/show/375469/os-inventory-management.svg" },
{ img: require('../../assets/images/icons/icon_sort_ListPesanan.png') , action: "history", text: "ORDER\n HISTORY", uri: "https://www.svgrepo.com/show/375491/retail-api.svg" },
{ img: require('../../assets/images/icons/icon_sort_Pelunasan.png') , action: "pelunasan", text: "PAYMENT", uri: "https://www.svgrepo.com/show/375438/gce-systems-management.svg" },
{ img: require('../../assets/images/icons/icon_sort_OrderLunas.png') , action: "orderlunas", text: "PAID ORDERS", uri: "https://www.svgrepo.com/show/375438/gce-systems-management.svg" },
{ img: require('../../assets/images/icons/icon_sort_OrderBelumLunas.png') , action: "orderbelumlunas", text: "UNPAID ORDERS", uri: "https://www.svgrepo.com/show/375438/gce-systems-management.svg" },
{ img: require('../../assets/images/icons/icon_sort_ListCustomer.png') , action: "customer", text: "CUSTOMER\n LIST", uri: "https://www.svgrepo.com/show/375359/cloud-for-marketing.svg" },
// { img: require('../../assets/images/icons/icon_sort_Profil.png') , action: "profile", text: "PROFILE", 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" }
2024-09-07 01:22:11 +00:00
];
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={() => {
2024-09-24 00:17:23 +00:00
// profileLoad()
2024-09-07 01:22:11 +00:00
navigation.navigate(item.action);
}} key={`item-${i}-${index}`}>
<View style={[styles.cardMenu]}>
<View style={styles.menu}>
2024-10-16 03:37:18 +00:00
<View style={{
backgroundColor:'white',
padding: 10,
borderRadius: 20
}}>
<Image
source={item.img}
style={{ width: 70,height:70 }}
resizeMode="contain" // You can adjust this based on how you want the image to scale
/>
</View>
2024-09-07 01:22:11 +00:00
<Text style={{
2024-10-16 03:37:18 +00:00
fontSize:12
2024-09-07 01:22:11 +00:00
, 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}>
2024-10-16 03:37:18 +00:00
<View style={{
backgroundColor: "#fff",
paddingTop:30
}}>
<View style={{
flexDirection:"row",
paddingHorizontal: 20,
paddingVertical: 5,
backgroundColor: "#fff",
}}>
<View style={{
flexDirection:"column",
alignItems:"center",
justifyContent:"center",
}}>
<Image style={styles.avatar} source={require('../../assets/images/profile/profile.jpg')} />
</View>
<View style={[{flex: 1},{
paddingVertical: 10,
paddingHorizontal: 10
}]}>
<Text style={{ fontSize: 18, color: "#333", fontWeight: '700' }}>Hi, {employeeData.name.toUpperCase()} !</Text>
<Text style={{ fontSize: 14, color: "#777", fontWeight: '700' }}>{lang.en.login.selamat_datang}</Text>
</View>
<View style={{ flexDirection: "column", alignItems: "center", justifyContent: "center" }}>
<Ionicons onPress={() => navigation.navigate('setting')} name="ellipsis-vertical" size={30} color={config.color.primary} />
2024-09-07 01:22:11 +00:00
</View>
2024-10-16 03:37:18 +00:00
</View>
2024-09-07 01:22:11 +00:00
</View>
<ScrollView>
2024-10-16 03:37:18 +00:00
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
<View style={styles.gaugeRow}>
<View style={{
overflow: 'hidden'
, borderRadius: 20
, margin: 10
}}>
<LinearGradient
start={{ x: 0, y: 0.5 }}
end={{ x: 1, y: 0.5 }}
colors={["#0000ff", config.color.primary, config.color.primary]}
>
<View style={[styles.card,{
overflow: 'hidden',
shadowOffset: {
width: 0,
height: 2,
},
marginVertical: 10,
marginHorizontal: 10,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center"
}]}>
<View style={{ flexDirection: "row" }}>
<View style={{ flex:1, flexDirection:"column" }}>
<Text style={{
fontSize: 16,
fontWeight: "bold",
color: "white",
flex:1
}}>Total Sales</Text>
<Text style={{fontSize: 24, color: "white", fontWeight: "bold"}}>67</Text>
</View>
<View style={{flexDirection:"column", alignItems:"center", justifyContent:"center"}}>
<ProgressChart
data={{ data: orderData }}
width={120}
height={120}
strokeWidth={10}
radius={50}
chartConfig={{
backgroundGradientFrom: config.color.textbiru,
backgroundGradientTo: config.color.textbiru,
color: (opacity = 1) => `rgba(255, 215, 255, ${opacity})`,
}}
hideLegend={true}
style={{backgroundColor: 'transparent'}}
/>
<Text style={{
fontSize: 28
, position:"absolute"
, fontWeight: "bold"
, color:"#fff"
}}>60%</Text>
</View>
</View>
</View>
</LinearGradient>
2024-09-07 01:22:11 +00:00
</View>
2024-10-16 03:37:18 +00:00
<View style={{
overflow: 'hidden'
, borderRadius: 20
, margin: 10
}}>
<LinearGradient
colors={[config.color.logohijau, config.color.logohijau]}
>
<View style={[styles.card,{
overflow: 'hidden',
shadowOffset: {
width: 0,
height: 2,
},
marginVertical: 10,
marginHorizontal: 10,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center"
}]}>
<View style={{flexDirection: "row"}}>
<View style={{flex:1, flexDirection:"column"}}>
<Text style={{
fontSize: 16,
fontWeight: "bold",
color: "#333",
flex:1
}}>Total Visit</Text>
<Text style={{fontSize: 24, fontWeight: "bold"}}>13</Text>
</View>
<View style={{flexDirection:"column", alignItems:"center", justifyContent:"center"}}>
<ProgressChart
data={{ data: visitData }}
width={120}
height={120}
strokeWidth={10}
radius={50}
chartConfig={{
backgroundGradientFrom: config.color.logohijau,
backgroundGradientTo: config.color.logohijau,
color: (opacity = 1) => `rgba(255, 255, 255, ${opacity})`,
}}
hideLegend={true}
style={{backgroundColor: 'transparent'}}
/>
<Text style={{
fontSize: 28
, position:"absolute"
, fontWeight: "bold"
}}>30%</Text>
</View>
</View>
</View>
</LinearGradient>
2024-09-07 01:22:11 +00:00
</View>
</View>
2024-10-16 03:37:18 +00:00
</ScrollView>
2024-09-07 01:22:11 +00:00
<View style={{ backgroundColor: '#f0f0f0', paddingTop: 20 }}>
<View style={{paddingHorizontal:20}} >
<View style={{
2024-10-16 03:37:18 +00:00
backgroundColor: config.color.textbiru,
2024-09-07 01:22:11 +00:00
borderRadius: 20,
paddingHorizontal: 20,
paddingVertical:10,
flexDirection:"row"
}}>
<View style={{width:140}}>
<View>
<Text style={{ textAlign:"center", fontSize: 60 }}>🏆</Text>
</View>
<View>
2024-10-16 03:37:18 +00:00
<Text style={{ textAlign: "center", fontSize: 24, fontWeight:700, color: '#fff' }}>RANK 1</Text>
2024-09-07 01:22:11 +00:00
</View>
</View>
<View style={{flex:1}}>
<View style={{padding:10, borderBottomColor:"#ddd", borderBottomWidth:1}}>
<Text style={styles.statLabel}>TOTAL ORDER</Text>
2024-09-24 00:17:23 +00:00
<Text style={styles.statValue}>Rp {formatRupiah(order.toString())}</Text>
2024-09-07 01:22:11 +00:00
</View>
<View style={{ padding: 10}}>
<Text style={styles.statLabel}>TOTAL COLLECTED</Text>
2024-09-24 00:17:23 +00:00
<Text style={styles.statValue}>Rp {formatRupiah(invoice.toString())}</Text>
2024-09-07 01:22:11 +00:00
</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>
2024-10-16 03:37:18 +00:00
<View style={{
height: 60,
backgroundColor: "#fff",
flexDirection: "row",
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
}}>
<TouchableOpacity onPress={() => navigation.navigate('home')} style={{ width: 140}}>
<View style={{ justifyContent: "center", height: 60, flexDirection: "column", alignItems: "center" }}>
<Ionicons name="home" size={24} color={config.color.textbiru} />
<Text style={{color:config.color.textbiru}}>Home</Text>
</View>
</TouchableOpacity>
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={() => navigation.navigate('order')} activeOpacity={1} style={{ justifyContent: "center", height: 60, flexDirection: "column", alignItems: "center" }}>
<Image style={{
width: 60,
height: 60,
marginBottom: 30,
resizeMode: 'contain'
}} source={require('../../assets/images/g42.png')} />
</TouchableOpacity>
</View>
<TouchableOpacity onPress={() => navigation.navigate('profile')} style={{ width: 140}}>
<View style={{ justifyContent: "center", height: 60, flexDirection: "column", alignItems: "center" }}>
<Ionicons name="person" size={24} color={config.color.textbiru} />
<Text style={{color:config.color.textbiru}}>Profile</Text>
</View>
</TouchableOpacity>
</View>
2024-09-07 01:22:11 +00:00
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f0f0f0',
},
header: {
flexDirection: 'row',
alignItems: 'center',
2024-10-16 03:37:18 +00:00
marginBottom : 10,
2024-09-07 01:22:11 +00:00
},
avatar: {
2024-10-16 03:37:18 +00:00
width: 55,
height: 55,
borderRadius: 40,
2024-09-07 01:22:11 +00:00
},
info: {
flex:1,
marginLeft: 20,
},
name: {
fontSize: 25,
fontWeight: 'bold',
2024-10-16 03:37:18 +00:00
color: "white",
2024-09-07 01:22:11 +00:00
},
greeting: {
2024-10-16 03:37:18 +00:00
color: "white",
fontSize: 16,
2024-09-07 01:22:11 +00:00
fontWeight:'600'
},
trophyRow: {
flex: 1,
flexDirection:'column',
alignItems: 'flex-end',
paddingHorizontal:20
},
gaugeRow: {
padding: 10,
flexDirection: 'row',
},
card: {
2024-10-16 03:37:18 +00:00
width: 280,
height: 140,
2024-09-07 01:22:11 +00:00
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,
2024-10-16 03:37:18 +00:00
margin: 1,
2024-09-07 01:22:11 +00:00
borderRadius: 10,
shadowColor: '#000',
shadowOpacity: 0.2,
shadowRadius: 5,
},
menu: {
alignItems: 'center',
fontSize: screenWidth > 411 ? 14 : 18,
},
});
export default HomeScreen;