upd: redesign

Deskripsi:
- login dan konfirmasi kode otp
- firebase code env

No Issues
This commit is contained in:
2026-02-14 15:43:38 +08:00
parent 6ca935483a
commit 8c63c08bc3
16 changed files with 128 additions and 98 deletions

View File

@@ -79,6 +79,12 @@ export default {
URL_FIREBASE_DB: process.env.URL_FIREBASE_DB,
PASS_ENC: process.env.PASS_ENC,
WA_SERVER_TOKEN: process.env.WA_SERVER_TOKEN,
FIREBASE_API_KEY: process.env.FIREBASE_API_KEY,
FIREBASE_AUTH_DOMAIN: process.env.FIREBASE_AUTH_DOMAIN,
FIREBASE_PROJECT_ID: process.env.FIREBASE_PROJECT_ID,
FIREBASE_STORAGE_BUCKET: process.env.FIREBASE_STORAGE_BUCKET,
FIREBASE_MESSAGING_SENDER_ID: process.env.FIREBASE_MESSAGING_SENDER_ID,
FIREBASE_APP_ID: process.env.FIREBASE_APP_ID,
}
}
};

View File

@@ -20,10 +20,16 @@ export default function Index() {
const { signIn } = useAuthSession();
const login = (): void => {
const random: string = 'contohLoginMobileDarmasaba';
var mytexttoEncryption = "contohLoginMobileDarmasaba"
const encrypted = CryptoES.AES.encrypt(mytexttoEncryption, ConstEnv.pass_encrypt).toString();
signIn(encrypted);
// WARNING: This is a hardcoded bypass for development purposes.
// It should be removed or secured before production release.
if (__DEV__) {
const random: string = 'contohLoginMobileDarmasaba';
var mytexttoEncryption = "contohLoginMobileDarmasaba"
const encrypted = CryptoES.AES.encrypt(mytexttoEncryption, ConstEnv.pass_encrypt).toString();
signIn(encrypted);
} else {
console.warn("Bypass login disabled in production.");
}
}
return (
<View style={[Styles.wrapLogin, { backgroundColor: colors.background }]} >

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
assets/images/logo-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@@ -53,11 +53,10 @@ export default function ViewLogin({ onValidate }: Props) {
return (
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
<StatusBar style={theme === 'dark' ? 'light' : 'dark'} translucent={false} backgroundColor={colors.background} />
<ToastCustom />
<View style={[Styles.p20, Styles.h100]}>
<View style={{ alignItems: "center", marginTop: 70, marginBottom: 50 }}>
<Image
source={require("../../assets/images/logo.png")}
source={theme === 'dark' ? require("../../assets/images/logo-dark.png") : require("../../assets/images/logo.png")}
style={[{ width: 300, height: 150 }]}
width={270}
height={110}
@@ -71,18 +70,28 @@ export default function ViewLogin({ onValidate }: Props) {
}}
type="numeric"
placeholder="XXX-XXX-XXXX"
round
itemLeft={<Text style={[Platform.OS === 'ios' && Styles.mt02]}>+62</Text>}
info="Kami akan mengirim kode verifikasi melalui WhatsApp, guna mengonfirmasikan nomor Anda." />
<ButtonForm
text="MASUK"
onPress={() => { handleCheckPhone() }}
disabled={disableLogin}
/>
<View style={[{ width: '50%', alignSelf: 'center' }]}>
<ButtonForm
text="MASUK"
onPress={() => { handleCheckPhone() }}
disabled={disableLogin}
/>
</View>
<View style={{ flex: 1 }} />
<View style={{ alignItems: 'center' }}>
<Image
source={theme === 'dark' ? require("../../assets/images/cogniti-dark.png") : require("../../assets/images/cogniti-light.png")}
style={{ width: 86, height: 27 }}
resizeMode="contain"
/>
</View>
</View>
{
loadingLogin && <ModalLoading isVisible={true} setVisible={setLoadingLogin} />
}
<ToastCustom position="bottom" />
</SafeAreaView>
)

View File

@@ -5,7 +5,7 @@ import { useTheme } from "@/providers/ThemeProvider";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { StatusBar } from "expo-status-bar";
import { useState } from "react";
import { Image, Platform, View } from "react-native";
import { Image, SafeAreaView, View } from "react-native";
import { OtpInput } from "react-native-otp-entry";
import Toast from 'react-native-toast-message';
import { ButtonForm } from "../buttonForm";
@@ -58,13 +58,12 @@ export default function ViewVerification({ phone, otp }: Props) {
}
return (
<>
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
<StatusBar style={theme === 'dark' ? 'light' : 'dark'} translucent={false} backgroundColor={colors.background} />
<ToastCustom />
<View style={[Styles.wrapLogin, { backgroundColor: colors.background }]} >
<View style={[Styles.p20, Styles.h100]} >
<View style={{ alignItems: "center", marginTop: 70, marginBottom: 50 }}>
<Image
source={require("../../assets/images/logo.png")}
source={theme === 'dark' ? require("../../assets/images/logo-dark.png") : require("../../assets/images/logo.png")}
style={[{ width: 300, height: 150 }]}
width={270}
height={110}
@@ -90,14 +89,25 @@ export default function ViewVerification({ phone, otp }: Props) {
pinCodeTextStyle: { color: colors.text }
}}
/>
<ButtonForm
text="SUBMIT"
onPress={() => { onCheckOtp() }}
/>
<Text style={[Styles.textInformation, Styles.mt05, Styles.cDefault, { textAlign: 'center', color: colors.tint }]}>
Tidak Menerima kode verifikasi? <Text onPress={() => { resendOtp() }}>Kirim Ulang</Text>
<View style={[{ width: '50%', alignSelf: 'center' }]}>
<ButtonForm
text="SUBMIT"
onPress={() => { onCheckOtp() }}
/>
</View>
<Text style={[Styles.textInformation, Styles.mt05, { textAlign: 'center', color: colors.dimmed }]}>
Tidak Menerima kode verifikasi? <Text onPress={() => { resendOtp() }} style={[{ color: colors.tint }]}>Kirim Ulang</Text>
</Text>
<View style={{ flex: 1 }} />
<View style={[{ alignItems: 'center' }]}>
<Image
source={theme === 'dark' ? require("../../assets/images/cogniti-dark.png") : require("../../assets/images/cogniti-light.png")}
style={{ width: 86, height: 27 }}
resizeMode="contain"
/>
</View>
</View>
</>
<ToastCustom position="bottom" />
</SafeAreaView>
)
}

View File

@@ -1,5 +1,5 @@
import { ColorsStatus } from "@/constants/ColorsStatus";
import Styles from "@/constants/Styles";
import { useTheme } from "@/providers/ThemeProvider";
import { TouchableOpacity } from "react-native";
import Text from './Text';
@@ -10,8 +10,9 @@ type Props = {
};
export function ButtonForm({ text, onPress, disabled }: Props) {
const { colors } = useTheme();
return (
<TouchableOpacity style={[Styles.btnRound, { marginTop: 30,}, disabled && ColorsStatus.gray]} onPress={onPress} disabled={disabled}>
<TouchableOpacity style={[Styles.btnRound, Styles.round05, Styles.mt30, { backgroundColor: colors.primary }, disabled && { backgroundColor: colors.tabIconDefault }]} onPress={onPress} disabled={disabled}>
<Text style={[Styles.textDefaultSemiBold, Styles.cWhite]}>{text}</Text>
</TouchableOpacity>
);

View File

@@ -74,7 +74,7 @@ export function InputForm({ label, value, placeholder, onChange, info, disable,
/>
</View>
{error && (<Text style={[Styles.textInformation, { color: colors.error }, Styles.mt05]}>{errorText}</Text>)}
{info != undefined && (<Text style={[Styles.textInformation, { color: colors.icon }, Styles.mt05]}>{info}</Text>)}
{info != undefined && (<Text style={[Styles.textInformation, { color: colors.dimmed }, Styles.mt05]}>{info}</Text>)}
</View>
)
}

View File

@@ -7,12 +7,19 @@ import Text from "./Text";
export default function ToastCustom({ position }: { position?: 'top' | 'bottom' }) {
const { colors } = useTheme()
return (
<Toast autoHide onPress={() => Toast.hide()} visibilityTime={1500} position={position || 'bottom'} config={{
small: ({ text1 }) => (
<View style={[Styles.toastContainer, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
<Text style={{ fontSize: 12 }}>{text1}</Text>
</View>
)
}} />
<Toast
autoHide
onPress={() => Toast.hide()}
visibilityTime={1500}
position={position || 'bottom'}
bottomOffset={80}
config={{
small: ({ text1 }) => (
<View style={[Styles.toastContainer, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
<Text style={{ fontSize: 12 }}>{text1}</Text>
</View>
)
}}
/>
)
}

View File

@@ -6,7 +6,7 @@ export const Colors = {
text: '#11181C',
background: '#f7f7f7ff',
tint: tintColorLight,
primary: '#19345E',
primary: '#1F3C88',
icon: '#1F3C88',
card: '#ffffff',
tabIconDefault: '#687076',
@@ -24,7 +24,7 @@ export const Colors = {
text: '#ECEDEE',
background: '#0F1B2D',
tint: tintColorDark,
primary: '#19345E',
primary: '#123A6F',
icon: '#9DB9E8',
card: '#16233A', // slightly lighter than background #151718
tabIconDefault: '#9BA1A6',

View File

@@ -2,5 +2,14 @@ import Constants from 'expo-constants';
export const ConstEnv = {
url_storage: Constants?.expoConfig?.extra?.URL_STORAGE,
pass_encrypt: Constants?.expoConfig?.extra?.PASS_ENC
pass_encrypt: Constants?.expoConfig?.extra?.PASS_ENC,
firebase: {
apiKey: Constants?.expoConfig?.extra?.FIREBASE_API_KEY,
authDomain: Constants?.expoConfig?.extra?.FIREBASE_AUTH_DOMAIN,
projectId: Constants?.expoConfig?.extra?.FIREBASE_PROJECT_ID,
storageBucket: Constants?.expoConfig?.extra?.FIREBASE_STORAGE_BUCKET,
messagingSenderId: Constants?.expoConfig?.extra?.FIREBASE_MESSAGING_SENDER_ID,
appId: Constants?.expoConfig?.extra?.FIREBASE_APP_ID,
databaseURL: Constants?.expoConfig?.extra?.URL_FIREBASE_DB,
}
}

View File

@@ -88,6 +88,9 @@ const Styles = StyleSheet.create({
mb15: {
marginBottom: 15
},
mb20: {
marginBottom: 20
},
mb30: {
marginBottom: 30
},
@@ -130,6 +133,9 @@ const Styles = StyleSheet.create({
mt15: {
marginTop: 15
},
mt30: {
marginTop: 30
},
mr05: {
marginRight: 5
},
@@ -291,9 +297,9 @@ const Styles = StyleSheet.create({
borderWidth: 1,
},
btnRound: {
backgroundColor: '#19345E',
backgroundColor: '#1F3C88',
borderWidth: 0,
borderColor: '#19345E',
borderColor: '#1F3C88',
alignItems: 'center',
borderRadius: 30,
marginTop: 15,
@@ -309,7 +315,7 @@ const Styles = StyleSheet.create({
},
btnLainnya: {
alignSelf: 'flex-start',
backgroundColor: '#19345E',
backgroundColor: '#1F3C88',
paddingVertical: 5,
marginVertical: 5
},

View File

@@ -96,30 +96,18 @@ export const apiGetGroup = async ({ user, active, search }: { user: string, acti
};
export const apiCreateGroup = async (data: { user: string, name: string }) => {
await api.post('mobile/group', data).then(response => {
return response.data;
})
.catch(error => {
console.error('Error:', error);
});
const response = await api.post('mobile/group', data);
return response.data;
};
export const apiEditGroup = async (data: { user: string, name: string }, id: string) => {
await api.put(`mobile/group/${id}`, data).then(response => {
return response.data;
})
.catch(error => {
console.error('Error:', error);
});
const response = await api.put(`mobile/group/${id}`, data);
return response.data;
};
export const apiDeleteGroup = async (data: { user: string, isActive: boolean }, id: string) => {
await api.delete(`mobile/group/${id}`, { data }).then(response => {
return response.data;
})
.catch(error => {
console.error('Error:', error);
});
const response = await api.delete(`mobile/group/${id}`, { data });
return response.data;
};
export const apiGetPosition = async ({ user, active, search, group }: { user: string, active: string, search: string, group?: string }) => {
@@ -128,22 +116,14 @@ export const apiGetPosition = async ({ user, active, search, group }: { user: st
};
export const apiCreatePosition = async (data: { user: string, name: string, idGroup: string }) => {
await api.post('mobile/position', data).then(response => {
return response.data;
})
.catch(error => {
console.error('Error:', error);
});
const response = await api.post('mobile/position', data);
return response.data;
};
export const apiDeletePosition = async (data: { user: string, isActive: boolean }, id: string) => {
await api.delete(`mobile/position/${id}`, { data }).then(response => {
return response.data;
})
.catch(error => {
console.error('Error:', error);
});
const response = await api.delete(`mobile/position/${id}`, { data });
return response.data;
};
export const apiEditPosition = async (data: { user: string, name: string, idGroup: string }, id: string) => {
@@ -207,12 +187,8 @@ export const apiUpdateDiscussionGeneralCommentar = async ({ id, data }: { id: st
export const apiDeleteMemberDiscussionGeneral = async (data: { user: string, idUser: string }, id: string) => {
await api.delete(`mobile/discussion-general/${id}/member`, { data }).then(response => {
return response.data;
})
.catch(error => {
console.error('Error:', error);
});
const response = await api.delete(`mobile/discussion-general/${id}/member`, { data });
return response.data;
};
@@ -222,19 +198,10 @@ export const apiUpdateStatusDiscussionGeneral = async ({ id, data }: { id: strin
};
export const apiDeleteDiscussionGeneral = async (data: { user: string, active: boolean }, id: string) => {
await api.delete(`mobile/discussion-general/${id}`, { data }).then(response => {
return response.data;
})
.catch(error => {
console.error('Error:', error);
});
const response = await api.delete(`mobile/discussion-general/${id}`, { data });
return response.data;
};
// export const apiEditDiscussionGeneral = async (data: { user: string, title: string, desc: string }, id: string) => {
// const response = await api.put(`/mobile/discussion-general/${id}`, data)
// return response.data;
// };
export const apiEditDiscussionGeneral = async (data: FormData, id: string) => {
const response = await api.put(`/mobile/discussion-general/${id}`, data, {
headers: {

View File

@@ -23,6 +23,10 @@ export function pushToPage(category: string, idContent: string) {
return router.push(`/member/${idContent}`)
} else if (cat[0] == 'project') {
return router.push(`/project/${idContent}`)
} else if (cat[0] == 'group') {
return router.push(`/group`)
} else if (cat[0] == 'position') {
return router.push(`/position`)
}
}
}

View File

@@ -8,23 +8,28 @@ import * as Notifications from 'expo-notifications';
import { useEffect } from 'react';
import { PermissionsAndroid, Platform } from 'react-native';
// Firebase config
import { ConstEnv } from '@/constants/ConstEnv';
const RNfirebaseConfig = {
apiKey: "AIzaSyB2hbsW91J3oRQx4_jgrCCNY0tNt5-21e8",
authDomain: "googleapis.com",
projectId: "mobile-darmasaba",
storageBucket: "mobile-darmasaba.appspot.com",
messagingSenderId: "867439221179",
appId: "1:867439221179:android:4509f77478c8dce99b0c9e",
databaseURL: "https://mobile-darmasaba-default-rtdb.asia-southeast1.firebasedatabase.app/"
apiKey: ConstEnv.firebase.apiKey,
authDomain: ConstEnv.firebase.authDomain,
projectId: ConstEnv.firebase.projectId,
storageBucket: ConstEnv.firebase.storageBucket,
messagingSenderId: ConstEnv.firebase.messagingSenderId,
appId: ConstEnv.firebase.appId,
databaseURL: ConstEnv.firebase.databaseURL
};
const initializeFirebase = async () => {
try {
const app = getApps().length ? getApp() : initializeApp(RNfirebaseConfig);
let app;
const apps = getApps();
if (apps.length) {
app = getApp() as any;
} else {
app = initializeApp(RNfirebaseConfig) as any;
}
const mess = getMessaging(app);
// await registerDeviceForRemoteMessages(mess);
// `registerDeviceForRemoteMessages` tidak perlu lagi
await setAutoInitEnabled(mess, true);
return mess;