Files
mobile-darmasaba/app/(application)/edit-profile.tsx
amaliadwiy d3802ca26c upd: redesign
Deskripsi:
- fitur ganti mode tema
- penerapan tema pada semua fitur

NO Issues
2026-02-09 17:49:25 +08:00

377 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import ButtonBackHeader from "@/components/buttonBackHeader";
import ButtonSaveHeader from "@/components/buttonSaveHeader";
import { InputForm } from "@/components/inputForm";
import ModalSelect from "@/components/modalSelect";
import SelectForm from "@/components/selectForm";
import Text from "@/components/Text";
import { ConstEnv } from "@/constants/ConstEnv";
import Styles from "@/constants/Styles";
import { apiEditProfile, apiGetProfile } from "@/lib/api";
import { setEntities } from "@/lib/entitiesSlice";
import { validateName } from "@/lib/fun_validateName";
import { useAuthSession } from "@/providers/AuthProvider";
import { useTheme } from "@/providers/ThemeProvider";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import { useHeaderHeight } from "@react-navigation/elements";
import * as ImagePicker from "expo-image-picker";
import { router, Stack } from "expo-router";
import { useEffect, useState } from "react";
import {
Image,
KeyboardAvoidingView,
Platform,
Pressable,
SafeAreaView,
ScrollView,
View
} from "react-native";
import Toast from "react-native-toast-message";
import { useDispatch, useSelector } from "react-redux";
type Props = {
id: string;
name: string;
nik: string;
email: string;
phone: string;
gender: string;
img: string;
isActive: boolean;
idGroup: string;
idPosition: string;
};
export default function EditProfile() {
const headerHeight = useHeaderHeight()
const dispatch = useDispatch()
const { colors } = useTheme();
const entities = useSelector((state: any) => state.entities)
const { token, decryptToken } = useAuthSession()
const [errorImg, setErrorImg] = useState(false)
// ... keeping state same ...
const [selectedImage, setSelectedImage] = useState<string | undefined | { uri: string }>(undefined);
const [choosePosition, setChoosePosition] = useState({ val: entities.idPosition, label: entities.position });
const [chooseGender, setChooseGender] = useState({ val: entities.gender, label: entities.gender == "F" ? 'Perempuan' : 'Laki-laki' });
const [valSelect, setValSelect] = useState<"position" | "gender">("position");
const [isSelect, setSelect] = useState(false);
const [disableBtn, setDisableBtn] = useState(false)
const [valChoose, setValChoose] = useState("")
const [imgForm, setImgForm] = useState<any>()
const [loading, setLoading] = useState(false)
const [data, setData] = useState<Props>({
id: entities.id,
name: entities.name,
nik: entities.nik,
email: entities.email,
phone: entities.phone,
gender: entities.gender,
img: entities.img,
isActive: entities.isActive,
idGroup: entities.idGroup,
idPosition: entities.idPosition
});
const [error, setError] = useState({
position: false,
nik: false,
name: false,
phone: false,
email: false,
gender: false,
});
async function handleLoad() {
try {
const response = await apiGetProfile({ id: entities.id });
dispatch(setEntities(response.data))
} catch (error) {
console.error(error);
}
}
useEffect(() => {
if (entities.id == undefined) {
handleLoad();
}
}, [entities]);
function validationForm(cat: string, val: any, label?: string) {
if (cat == "position") {
setChoosePosition({ val, label: String(label) });
setData({ ...data, idPosition: val });
if (val == "" || val == "null") {
setError({ ...error, position: true });
} else {
setError({ ...error, position: false });
}
} else if (cat == "nik") {
setData({ ...data, nik: val });
if (val == "" || val.length !== 16) {
setError({ ...error, nik: true });
} else {
setError({ ...error, nik: false });
}
} else if (cat == "name") {
setData({ ...data, name: val });
if (!validateName(val)) {
setError({ ...error, name: true });
} else {
setError({ ...error, name: false });
}
} else if (cat == "email") {
setData({ ...data, email: val });
if (
val == "" ||
!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(val)
) {
setError({ ...error, email: true });
} else {
setError({ ...error, email: false });
}
} else if (cat == "phone") {
setData({ ...data, phone: val });
if (val == "" || !(val.length >= 9 && val.length <= 16)) {
setError({ ...error, phone: true });
} else {
setError({ ...error, phone: false });
}
} else if (cat == "gender") {
setChooseGender({ val, label: String(label) });
setData({ ...data, gender: val });
if (val == "" || val == "null") {
setError({ ...error, gender: true });
} else {
setError({ ...error, gender: false });
}
}
}
function checkForm() {
if (Object.values(error).some((v) => v == true) || Object.values(data).some((v) => v == "")) {
setDisableBtn(true)
} else {
setDisableBtn(false)
}
}
useEffect(() => {
checkForm()
}, [error, data])
async function handleEdit() {
try {
setLoading(true)
const hasil = await decryptToken(String(token?.current))
const fd = new FormData()
if (imgForm != undefined) {
fd.append("file", {
uri: imgForm.uri,
type: imgForm.mimeType || "image/jpeg",
name: imgForm.fileName || "image.jpg",
} as any);
} else {
fd.append("file", "undefined",);
}
fd.append("data", JSON.stringify(
{ user: hasil, ...data }
))
const response = await apiEditProfile(fd)
if (response.success) {
Toast.show({ type: 'small', text1: 'Berhasil mengupdate data', })
await handleLoad()
router.back()
} else {
Toast.show({ type: 'small', text1: response.message, })
}
} catch (error) {
console.error(error)
Toast.show({ type: 'small', text1: 'Gagal mengupdate data', })
} finally {
setLoading(false)
}
}
const pickImageAsync = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ["images"],
allowsEditing: true,
quality: 0.9,
aspect: [1, 1]
});
if (!result.canceled) {
setErrorImg(false)
setSelectedImage(result.assets[0].uri);
setImgForm(result.assets[0]);
} else {
setErrorImg(false)
}
};
return (
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
<Stack.Screen
options={{
headerLeft: () => (
<ButtonBackHeader
onPress={() => {
router.back();
}}
/>
),
headerTitle: "Edit Profile",
headerTitleAlign: "center",
headerRight: () => (
<ButtonSaveHeader
disable={disableBtn || loading ? true : false}
category="update"
onPress={() => {
handleEdit()
}}
/>
),
}}
/>
<KeyboardAvoidingView
style={[Styles.h100]}
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
keyboardVerticalOffset={headerHeight}
>
<ScrollView showsVerticalScrollIndicator={false}>
<View style={[Styles.p15]}>
<View style={{ justifyContent: "center", alignItems: "center" }}>
{
selectedImage != undefined ? (
<Pressable onPress={pickImageAsync}>
<Image
src={
typeof selectedImage === "string"
? selectedImage
: selectedImage.uri
}
style={[Styles.userProfileBig]}
onError={() => { setErrorImg(true) }}
/>
<View style={[Styles.absoluteIconPicker]}>
<MaterialCommunityIcons name="image" color={'white'} size={15} />
</View>
</Pressable>
) : (
<Pressable onPress={pickImageAsync}>
<Image
source={errorImg ? require("../../assets/images/user.jpg") : { uri: `${ConstEnv.url_storage}/files/${data?.img}` }}
style={[Styles.userProfileBig]}
onError={() => { setErrorImg(true) }}
/>
<View style={[Styles.absoluteIconPicker]}>
<MaterialCommunityIcons name="image" color={'white'} size={15} />
</View>
</Pressable>
)
}
</View>
<SelectForm
label="Jabatan"
placeholder="Pilih Jabatan"
value={choosePosition.label}
required
onPress={() => {
setValChoose(choosePosition.val);
setValSelect("position");
setSelect(true);
}}
error={error.position}
errorText="Jabatan tidak boleh kosong"
/>
<InputForm
label="NIK"
type="numeric"
placeholder="NIK"
required
value={data?.nik}
error={error.nik}
errorText="NIK Harus 16 Karakter"
onChange={val => {
validationForm("nik", val)
}}
/>
<InputForm
label="Nama"
type="default"
placeholder="Nama"
required
value={data?.name}
error={error.name}
errorText="Nama harus 350 karakter (huruf, angka, spasi, dan simbol ringan (. , ' _ -))"
onChange={val => {
validationForm("name", val)
}}
/>
<InputForm
label="Email"
type="default"
placeholder="Email"
required
value={data?.email}
error={error.email}
errorText="Email tidak valid"
onChange={val => {
validationForm("email", val)
}}
/>
<InputForm
label="Nomor Telepon"
type="numeric"
placeholder="8XX-XXX-XXX"
required
itemLeft={<Text style={[Platform.OS === 'ios' && Styles.mt02]}>+62</Text>}
value={data?.phone}
error={error.phone}
errorText="Nomor Telepon tidak valid"
onChange={val => {
validationForm("phone", val)
}}
/>
<SelectForm
label="Jenis Kelamin"
placeholder="Pilih Jenis Kelamin"
value={chooseGender.label}
required
onPress={() => {
setValChoose(chooseGender.val);
setValSelect("gender");
setSelect(true);
}}
error={error.gender}
errorText="Jenis Kelamin tidak boleh kosong"
/>
</View>
</ScrollView>
</KeyboardAvoidingView>
<ModalSelect
category={valSelect}
close={setSelect}
onSelect={(value) => {
validationForm(valSelect, value.val, value.label);
}}
title={
valSelect == "position"
? "Jabatan"
: "Jenis Kelamin"
}
open={isSelect}
idParent={valSelect == "position" ? data?.idGroup : ""}
valChoose={valChoose}
/>
</SafeAreaView>
);
}