Merge pull request 'amalia/03-okt-25' (#45) from amalia/03-okt-25 into join
Reviewed-on: bip/mobile-darmasaba#45
This commit is contained in:
@@ -4,7 +4,7 @@ export default {
|
|||||||
expo: {
|
expo: {
|
||||||
name: "Desa+",
|
name: "Desa+",
|
||||||
slug: "mobile-darmasaba",
|
slug: "mobile-darmasaba",
|
||||||
version: "1.0.5", // Versi aplikasi (App Store)
|
version: "2.0.1", // Versi aplikasi (App Store)
|
||||||
jsEngine: "jsc",
|
jsEngine: "jsc",
|
||||||
orientation: "portrait",
|
orientation: "portrait",
|
||||||
icon: "./assets/images/logo-icon-small.png",
|
icon: "./assets/images/logo-icon-small.png",
|
||||||
@@ -14,7 +14,7 @@ export default {
|
|||||||
ios: {
|
ios: {
|
||||||
supportsTablet: true,
|
supportsTablet: true,
|
||||||
bundleIdentifier: "mobiledarmasaba.app",
|
bundleIdentifier: "mobiledarmasaba.app",
|
||||||
buildNumber: "2",
|
buildNumber: "3",
|
||||||
infoPlist: {
|
infoPlist: {
|
||||||
ITSAppUsesNonExemptEncryption: false,
|
ITSAppUsesNonExemptEncryption: false,
|
||||||
CFBundleDisplayName: "Desa+"
|
CFBundleDisplayName: "Desa+"
|
||||||
@@ -23,7 +23,7 @@ export default {
|
|||||||
},
|
},
|
||||||
android: {
|
android: {
|
||||||
package: "mobiledarmasaba.app",
|
package: "mobiledarmasaba.app",
|
||||||
versionCode: 9,
|
versionCode: 10,
|
||||||
adaptiveIcon: {
|
adaptiveIcon: {
|
||||||
foregroundImage: "./assets/images/logo-icon-small.png",
|
foregroundImage: "./assets/images/logo-icon-small.png",
|
||||||
backgroundColor: "#ffffff"
|
backgroundColor: "#ffffff"
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ export default function AddMemberDivision() {
|
|||||||
setData(responsemember.data.filter((i: any) => i.idUserRole != 'supadmin'))
|
setData(responsemember.data.filter((i: any) => i.idUserRole != 'supadmin'))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { useAuthSession } from "@/providers/AuthProvider"
|
|||||||
import { Feather, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"
|
import { Feather, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router"
|
import { router, Stack, useLocalSearchParams } from "expo-router"
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { Pressable, SafeAreaView, ScrollView, View } from "react-native"
|
import { Pressable, RefreshControl, SafeAreaView, ScrollView, View } from "react-native"
|
||||||
import Toast from "react-native-toast-message"
|
import Toast from "react-native-toast-message"
|
||||||
import { useSelector } from "react-redux"
|
import { useSelector } from "react-redux"
|
||||||
|
|
||||||
@@ -39,6 +39,7 @@ type PropsMember = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function InformationDivision() {
|
export default function InformationDivision() {
|
||||||
|
const [refreshing, setRefreshing] = useState(false)
|
||||||
const entityUser = useSelector((state: any) => state.user)
|
const entityUser = useSelector((state: any) => state.user)
|
||||||
const { id } = useLocalSearchParams<{ id: string }>()
|
const { id } = useLocalSearchParams<{ id: string }>()
|
||||||
const [isModal, setModal] = useState(false)
|
const [isModal, setModal] = useState(false)
|
||||||
@@ -116,6 +117,14 @@ export default function InformationDivision() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleRefresh = async () => {
|
||||||
|
setRefreshing(true)
|
||||||
|
handleLoad(false)
|
||||||
|
handleCheckMember()
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
setRefreshing(false)
|
||||||
|
};
|
||||||
|
|
||||||
async function handleCheckMember() {
|
async function handleCheckMember() {
|
||||||
try {
|
try {
|
||||||
const hasil = await decryptToken(String(token?.current));
|
const hasil = await decryptToken(String(token?.current));
|
||||||
@@ -161,7 +170,15 @@ export default function InformationDivision() {
|
|||||||
headerRight: () => ((entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision) && <HeaderRightDivisionInfo id={id} active={dataDetail?.isActive} />,
|
headerRight: () => ((entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision) && <HeaderRightDivisionInfo id={id} active={dataDetail?.isActive} />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ScrollView style={[Styles.h100]}>
|
<ScrollView
|
||||||
|
refreshControl={
|
||||||
|
<RefreshControl
|
||||||
|
refreshing={refreshing}
|
||||||
|
onRefresh={handleRefresh}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
style={[Styles.h100]}
|
||||||
|
>
|
||||||
<View style={[Styles.p15]}>
|
<View style={[Styles.p15]}>
|
||||||
{
|
{
|
||||||
dataDetail?.isActive == false && (
|
dataDetail?.isActive == false && (
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { ConstEnv } from "@/constants/ConstEnv";
|
|||||||
import Styles from "@/constants/Styles";
|
import Styles from "@/constants/Styles";
|
||||||
import { apiEditProfile, apiGetProfile } from "@/lib/api";
|
import { apiEditProfile, apiGetProfile } from "@/lib/api";
|
||||||
import { setEntities } from "@/lib/entitiesSlice";
|
import { setEntities } from "@/lib/entitiesSlice";
|
||||||
|
import { validateName } from "@/lib/fun_validateName";
|
||||||
import { useAuthSession } from "@/providers/AuthProvider";
|
import { useAuthSession } from "@/providers/AuthProvider";
|
||||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||||
import { useHeaderHeight } from "@react-navigation/elements";
|
import { useHeaderHeight } from "@react-navigation/elements";
|
||||||
@@ -109,7 +110,7 @@ export default function EditProfile() {
|
|||||||
}
|
}
|
||||||
} else if (cat == "name") {
|
} else if (cat == "name") {
|
||||||
setData({ ...data, name: val });
|
setData({ ...data, name: val });
|
||||||
if (val == "") {
|
if (!validateName(val)) {
|
||||||
setError({ ...error, name: true });
|
setError({ ...error, name: true });
|
||||||
} else {
|
} else {
|
||||||
setError({ ...error, name: false });
|
setError({ ...error, name: false });
|
||||||
@@ -164,8 +165,8 @@ export default function EditProfile() {
|
|||||||
if (imgForm != undefined) {
|
if (imgForm != undefined) {
|
||||||
fd.append("file", {
|
fd.append("file", {
|
||||||
uri: imgForm.uri,
|
uri: imgForm.uri,
|
||||||
type: imgForm.mimeType,
|
type: imgForm.mimeType || "image/jpeg",
|
||||||
name: imgForm.fileName,
|
name: imgForm.fileName || "image.jpg",
|
||||||
} as any);
|
} as any);
|
||||||
} else {
|
} else {
|
||||||
fd.append("file", "undefined",);
|
fd.append("file", "undefined",);
|
||||||
@@ -197,9 +198,9 @@ export default function EditProfile() {
|
|||||||
const pickImageAsync = async () => {
|
const pickImageAsync = async () => {
|
||||||
let result = await ImagePicker.launchImageLibraryAsync({
|
let result = await ImagePicker.launchImageLibraryAsync({
|
||||||
mediaTypes: ["images"],
|
mediaTypes: ["images"],
|
||||||
allowsEditing: false,
|
allowsEditing: true,
|
||||||
quality: 1,
|
quality: 0.9,
|
||||||
aspect: [1, 1],
|
aspect: [1, 1]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.canceled) {
|
if (!result.canceled) {
|
||||||
@@ -305,7 +306,7 @@ export default function EditProfile() {
|
|||||||
required
|
required
|
||||||
value={data?.name}
|
value={data?.name}
|
||||||
error={error.name}
|
error={error.name}
|
||||||
errorText="Nama tidak boleh kosong"
|
errorText="Nama harus 3–50 karakter (huruf, angka, spasi, dan simbol ringan (. , ' _ -))"
|
||||||
onChange={val => {
|
onChange={val => {
|
||||||
validationForm("name", val)
|
validationForm("name", val)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Text from "@/components/Text";
|
|||||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||||
import Styles from "@/constants/Styles";
|
import Styles from "@/constants/Styles";
|
||||||
import { apiCreateUser } from "@/lib/api";
|
import { apiCreateUser } from "@/lib/api";
|
||||||
|
import { validateName } from "@/lib/fun_validateName";
|
||||||
import { setUpdateMember } from "@/lib/memberSlice";
|
import { setUpdateMember } from "@/lib/memberSlice";
|
||||||
import { useAuthSession } from "@/providers/AuthProvider";
|
import { useAuthSession } from "@/providers/AuthProvider";
|
||||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||||
@@ -100,7 +101,7 @@ export default function CreateMember() {
|
|||||||
}
|
}
|
||||||
} else if (cat == "name") {
|
} else if (cat == "name") {
|
||||||
setDataForm({ ...dataForm, name: val });
|
setDataForm({ ...dataForm, name: val });
|
||||||
if (val == "") {
|
if (!validateName(val)) {
|
||||||
setError({ ...error, name: true });
|
setError({ ...error, name: true });
|
||||||
} else {
|
} else {
|
||||||
setError({ ...error, name: false });
|
setError({ ...error, name: false });
|
||||||
@@ -166,8 +167,8 @@ export default function CreateMember() {
|
|||||||
if (imgForm != undefined) {
|
if (imgForm != undefined) {
|
||||||
fd.append("file", {
|
fd.append("file", {
|
||||||
uri: imgForm.uri,
|
uri: imgForm.uri,
|
||||||
type: imgForm.mimeType,
|
type: imgForm.mimeType || "image/jpeg",
|
||||||
name: imgForm.fileName,
|
name: imgForm.fileName || "image.jpg",
|
||||||
} as any)
|
} as any)
|
||||||
} else {
|
} else {
|
||||||
fd.append("file", "undefined")
|
fd.append("file", "undefined")
|
||||||
@@ -193,9 +194,9 @@ export default function CreateMember() {
|
|||||||
const pickImageAsync = async () => {
|
const pickImageAsync = async () => {
|
||||||
let result = await ImagePicker.launchImageLibraryAsync({
|
let result = await ImagePicker.launchImageLibraryAsync({
|
||||||
mediaTypes: ["images"],
|
mediaTypes: ["images"],
|
||||||
allowsEditing: false,
|
allowsEditing: true,
|
||||||
quality: 1,
|
quality: 0.9,
|
||||||
aspect: [1, 1],
|
aspect: [1, 1]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.canceled) {
|
if (!result.canceled) {
|
||||||
@@ -309,7 +310,7 @@ export default function CreateMember() {
|
|||||||
placeholder="Nama"
|
placeholder="Nama"
|
||||||
required
|
required
|
||||||
error={error.name}
|
error={error.name}
|
||||||
errorText="Nama tidak boleh kosong"
|
errorText="Nama harus 3–50 karakter (huruf, angka, spasi, dan simbol ringan (. , ' _ -))"
|
||||||
onChange={val => {
|
onChange={val => {
|
||||||
validationForm("name", val)
|
validationForm("name", val)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Text from "@/components/Text";
|
|||||||
import { ConstEnv } from "@/constants/ConstEnv";
|
import { ConstEnv } from "@/constants/ConstEnv";
|
||||||
import Styles from "@/constants/Styles";
|
import Styles from "@/constants/Styles";
|
||||||
import { apiEditUser, apiGetProfile } from "@/lib/api";
|
import { apiEditUser, apiGetProfile } from "@/lib/api";
|
||||||
|
import { validateName } from "@/lib/fun_validateName";
|
||||||
import { setUpdateMember } from "@/lib/memberSlice";
|
import { setUpdateMember } from "@/lib/memberSlice";
|
||||||
import { useAuthSession } from "@/providers/AuthProvider";
|
import { useAuthSession } from "@/providers/AuthProvider";
|
||||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||||
@@ -132,7 +133,7 @@ export default function EditMember() {
|
|||||||
}
|
}
|
||||||
} else if (cat == "name") {
|
} else if (cat == "name") {
|
||||||
setData({ ...data, name: val });
|
setData({ ...data, name: val });
|
||||||
if (val == "") {
|
if (!validateName(val)) {
|
||||||
setError({ ...error, name: true });
|
setError({ ...error, name: true });
|
||||||
} else {
|
} else {
|
||||||
setError({ ...error, name: false });
|
setError({ ...error, name: false });
|
||||||
@@ -187,8 +188,8 @@ export default function EditMember() {
|
|||||||
if (imgForm != undefined) {
|
if (imgForm != undefined) {
|
||||||
fd.append("file", {
|
fd.append("file", {
|
||||||
uri: imgForm.uri,
|
uri: imgForm.uri,
|
||||||
type: imgForm.mimeType,
|
type: imgForm.mimeType || "image/jpeg",
|
||||||
name: imgForm.fileName,
|
name: imgForm.fileName || "image.jpg",
|
||||||
} as any);
|
} as any);
|
||||||
} else {
|
} else {
|
||||||
fd.append("file", "undefined",);
|
fd.append("file", "undefined",);
|
||||||
@@ -220,9 +221,9 @@ export default function EditMember() {
|
|||||||
const pickImageAsync = async () => {
|
const pickImageAsync = async () => {
|
||||||
let result = await ImagePicker.launchImageLibraryAsync({
|
let result = await ImagePicker.launchImageLibraryAsync({
|
||||||
mediaTypes: ["images"],
|
mediaTypes: ["images"],
|
||||||
allowsEditing: false,
|
allowsEditing: true,
|
||||||
quality: 1,
|
quality: 0.9,
|
||||||
aspect: [1, 1],
|
aspect: [1, 1]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.canceled) {
|
if (!result.canceled) {
|
||||||
@@ -348,7 +349,7 @@ export default function EditMember() {
|
|||||||
required
|
required
|
||||||
value={data?.name}
|
value={data?.name}
|
||||||
error={error.name}
|
error={error.name}
|
||||||
errorText="Nama tidak boleh kosong"
|
errorText="Nama harus 3–50 karakter (huruf, angka, spasi, dan simbol ringan (. , ' _ -))"
|
||||||
onChange={val => {
|
onChange={val => {
|
||||||
validationForm("name", val)
|
validationForm("name", val)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ export function InputDate({ label, value, placeholder, onChange, info, disable,
|
|||||||
display="spinner"
|
display="spinner"
|
||||||
onChange={(event, date) => { onChangeDate(event.type, date) }}
|
onChange={(event, date) => { onChangeDate(event.type, date) }}
|
||||||
onTouchCancel={() => setModal(false)}
|
onTouchCancel={() => setModal(false)}
|
||||||
|
textColor="black"
|
||||||
/>
|
/>
|
||||||
</ModalFloat>
|
</ModalFloat>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -394,7 +394,7 @@
|
|||||||
);
|
);
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = mobiledarmasaba.app;
|
PRODUCT_BUNDLE_IDENTIFIER = mobiledarmasaba.app;
|
||||||
PRODUCT_NAME = Desa;
|
PRODUCT_NAME = "Desa";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Desa/Desa-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Desa/Desa-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
@@ -429,7 +429,7 @@
|
|||||||
);
|
);
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = mobiledarmasaba.app;
|
PRODUCT_BUNDLE_IDENTIFIER = mobiledarmasaba.app;
|
||||||
PRODUCT_NAME = Desa;
|
PRODUCT_NAME = "Desa";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Desa/Desa-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Desa/Desa-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>aps-environment</key>
|
<key>aps-environment</key>
|
||||||
<string>development</string>
|
<string>development</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.0.5</string>
|
<string>2.0.1</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>2</string>
|
<string>3</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
@@ -95,5 +95,5 @@
|
|||||||
<string>Automatic</string>
|
<string>Automatic</string>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
18
lib/fun_validateName.ts
Normal file
18
lib/fun_validateName.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Validasi Display Name
|
||||||
|
* Aturan:
|
||||||
|
* - 2 sampai 50 karakter
|
||||||
|
* - Huruf, angka, spasi, titik, koma, apostrof, underscore, dan dash
|
||||||
|
* - Tidak boleh semua spasi
|
||||||
|
*/
|
||||||
|
export const validateName = (name: string): boolean => {
|
||||||
|
const trimmed = name.trim();
|
||||||
|
|
||||||
|
// Jika kosong setelah di-trim → invalid
|
||||||
|
if (!trimmed) return false;
|
||||||
|
|
||||||
|
// Regex: hanya huruf, angka, spasi, titik, koma, apostrof, underscore, dash
|
||||||
|
const regex = /^[a-zA-Z0-9\s._,'-]{3,50}$/;
|
||||||
|
|
||||||
|
return regex.test(trimmed);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user