Compare commits
2 Commits
api-donati
...
api-admin/
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f36620988 | |||
| f750d158be |
@@ -1,54 +1,226 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
MapCustom,
|
||||
Spacing,
|
||||
TextInputCustom,
|
||||
ViewWrapper
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import API_IMAGE from "@/constants/api-storage";
|
||||
import DIRECTORY_ID from "@/constants/directory-id";
|
||||
import { apiMapsGetOne, apiMapsUpdate } from "@/service/api-client/api-maps";
|
||||
import { uploadFileService } from "@/service/upload-service";
|
||||
import pickFile, { IFileData } from "@/utils/pickFile";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
import MapView, { LatLng, Marker } from "react-native-maps";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
const defaultRegion = {
|
||||
latitude: -8.737109,
|
||||
longitude: 115.1756897,
|
||||
latitudeDelta: 0.1,
|
||||
longitudeDelta: 0.1,
|
||||
};
|
||||
export default function MapsEdit() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [data, setData] = useState<any | null>({
|
||||
id: "",
|
||||
namePin: "",
|
||||
latitude: "",
|
||||
longitude: "",
|
||||
imageId: "",
|
||||
});
|
||||
const [selectedLocation, setSelectedLocation] = useState<LatLng | null>(null);
|
||||
const [image, setImage] = useState<IFileData | null>(null);
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiMapsGetOne({ id: id as string });
|
||||
|
||||
if (response.success) {
|
||||
setData({
|
||||
id: response.data.id,
|
||||
namePin: response.data.namePin,
|
||||
latitude: response.data.latitude,
|
||||
longitude: response.data.longitude,
|
||||
imageId: response.data.imageId,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMapPress = (event: any) => {
|
||||
const { latitude, longitude } = event.nativeEvent.coordinate;
|
||||
const location = { latitude, longitude };
|
||||
setSelectedLocation(location);
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
let newData: any;
|
||||
if (!data.namePin) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Nama pin harus diisi",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
newData = {
|
||||
namePin: data?.namePin,
|
||||
latitude: selectedLocation?.latitude || data?.latitude,
|
||||
longitude: selectedLocation?.longitude || data?.longitude,
|
||||
};
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
if (image) {
|
||||
const responseUpload = await uploadFileService({
|
||||
dirId: DIRECTORY_ID.map_image,
|
||||
imageUri: image?.uri,
|
||||
});
|
||||
|
||||
if (!responseUpload?.data?.id) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal mengunggah gambar",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const imageId = responseUpload?.data?.id;
|
||||
|
||||
newData = {
|
||||
namePin: data?.namePin,
|
||||
latitude: selectedLocation?.latitude,
|
||||
longitude: selectedLocation?.longitude,
|
||||
newImageId: imageId,
|
||||
};
|
||||
}
|
||||
|
||||
const responseUpdate = await apiMapsUpdate({
|
||||
id: data?.id,
|
||||
data: newData,
|
||||
});
|
||||
|
||||
if (!responseUpdate.success) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal mengupdate map",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Map berhasil diupdate",
|
||||
});
|
||||
router.back();
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log(`Simpan maps ${id}`);
|
||||
router.back()
|
||||
}}
|
||||
disabled={!data.namePin}
|
||||
onPress={handleSubmit}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
Simpan
|
||||
Update
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
|
||||
|
||||
<BaseBox>
|
||||
<MapCustom />
|
||||
</BaseBox>
|
||||
<View style={[styles.container, { height: 400 }]}>
|
||||
<MapView
|
||||
style={styles.map}
|
||||
initialRegion={
|
||||
data?.latitude && data?.longitude
|
||||
? {
|
||||
latitude: data?.latitude,
|
||||
longitude: data?.longitude,
|
||||
latitudeDelta: 0.1,
|
||||
longitudeDelta: 0.1,
|
||||
}
|
||||
: defaultRegion
|
||||
}
|
||||
onPress={handleMapPress}
|
||||
showsUserLocation={true}
|
||||
showsMyLocationButton={true}
|
||||
loadingEnabled={true}
|
||||
loadingIndicatorColor="#666"
|
||||
loadingBackgroundColor="#f0f0f0"
|
||||
>
|
||||
{selectedLocation ? (
|
||||
<Marker
|
||||
coordinate={selectedLocation}
|
||||
title="Lokasi Dipilih"
|
||||
description={`Lat: ${selectedLocation.latitude.toFixed(
|
||||
6
|
||||
)}, Lng: ${selectedLocation.longitude.toFixed(6)}`}
|
||||
pinColor="red"
|
||||
/>
|
||||
) : (
|
||||
<Marker
|
||||
coordinate={defaultRegion}
|
||||
title="Lokasi Dipilih"
|
||||
description={`Lat: ${defaultRegion.latitude.toFixed(
|
||||
6
|
||||
)}, Lng: ${defaultRegion.longitude.toFixed(6)}`}
|
||||
pinColor="red"
|
||||
/>
|
||||
)}
|
||||
</MapView>
|
||||
</View>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Nama Pin"
|
||||
placeholder="Masukkan nama pin maps"
|
||||
value={data?.namePin}
|
||||
onChangeText={(value) => setData({ ...data, namePin: value })}
|
||||
/>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
|
||||
<LandscapeFrameUploaded />
|
||||
<LandscapeFrameUploaded
|
||||
image={
|
||||
image
|
||||
? image?.uri
|
||||
: API_IMAGE.GET({ fileId: data?.imageId as string })
|
||||
}
|
||||
/>
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => {
|
||||
console.log("Upload foto ");
|
||||
router.navigate(`/take-picture/${id}`);
|
||||
pickFile({
|
||||
allowedType: "image",
|
||||
setImageUri(file) {
|
||||
setImage(file);
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Upload
|
||||
@@ -57,3 +229,16 @@ export default function MapsEdit() {
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
width: "100%",
|
||||
backgroundColor: "#f5f5f5",
|
||||
overflow: "hidden",
|
||||
borderRadius: 8,
|
||||
marginBottom: 20,
|
||||
},
|
||||
map: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -6,21 +6,96 @@ import {
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
Spacing,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import MapSelected from "@/components/Map/MapSelected";
|
||||
import DIRECTORY_ID from "@/constants/directory-id";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiMapsCreate } from "@/service/api-client/api-maps";
|
||||
import { uploadFileService } from "@/service/upload-service";
|
||||
import pickFile, { IFileData } from "@/utils/pickFile";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { LatLng } from "react-native-maps";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function MapsCreate() {
|
||||
const { user } = useAuth();
|
||||
const { id } = useLocalSearchParams();
|
||||
const [selectedLocation, setSelectedLocation] = useState<LatLng | null>(null);
|
||||
const [name, setName] = useState<string>("");
|
||||
const [image, setImage] = useState<IFileData | null>(null);
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
let newData: any;
|
||||
newData = {
|
||||
authorId: user?.id,
|
||||
portofolioId: id,
|
||||
namePin: name,
|
||||
latitude: selectedLocation?.latitude,
|
||||
longitude: selectedLocation?.longitude,
|
||||
};
|
||||
|
||||
if (image) {
|
||||
const responseUpload = await uploadFileService({
|
||||
dirId: DIRECTORY_ID.map_image,
|
||||
imageUri: image?.uri,
|
||||
});
|
||||
|
||||
if (!responseUpload?.data?.id) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal mengunggah gambar",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const imageId = responseUpload?.data?.id;
|
||||
|
||||
newData = {
|
||||
authorId: user?.id,
|
||||
portofolioId: id,
|
||||
namePin: name,
|
||||
latitude: selectedLocation?.latitude,
|
||||
longitude: selectedLocation?.longitude,
|
||||
imageId: imageId,
|
||||
};
|
||||
}
|
||||
|
||||
const response = await apiMapsCreate({
|
||||
data: newData,
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal menambahkan map",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Map berhasil ditambahkan",
|
||||
});
|
||||
router.back();
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log(`Simpan maps ${id}`);
|
||||
router.replace(`/portofolio/${id}`);
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
disabled={!selectedLocation || name === ""}
|
||||
onPress={handleSubmit}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
@@ -30,25 +105,34 @@ export default function MapsCreate() {
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
|
||||
|
||||
<BaseBox style={{ height: 400 }}>
|
||||
<TextCustom>Maps Her</TextCustom>
|
||||
<BaseBox>
|
||||
<MapSelected
|
||||
selectedLocation={selectedLocation as any}
|
||||
setSelectedLocation={setSelectedLocation}
|
||||
/>
|
||||
</BaseBox>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Nama Pin"
|
||||
placeholder="Masukkan nama pin maps"
|
||||
value={name}
|
||||
onChangeText={setName}
|
||||
/>
|
||||
|
||||
<Spacing height={50} />
|
||||
|
||||
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
|
||||
<LandscapeFrameUploaded />
|
||||
<LandscapeFrameUploaded image={image?.uri} />
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => {
|
||||
console.log("Upload foto ");
|
||||
router.navigate(`/take-picture/${id}`);
|
||||
pickFile({
|
||||
allowedType: "image",
|
||||
setImageUri(file) {
|
||||
setImage(file);
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Upload
|
||||
|
||||
@@ -1,19 +1,234 @@
|
||||
import { MapCustom, ViewWrapper } from "@/components";
|
||||
import {
|
||||
ButtonCustom,
|
||||
DrawerCustom,
|
||||
DummyLandscapeImage,
|
||||
Grid,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import API_IMAGE from "@/constants/api-storage";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { apiMapsGetAll } from "@/service/api-client/api-maps";
|
||||
import { openInDeviceMaps } from "@/utils/openInDeviceMaps";
|
||||
import { FontAwesome, Ionicons } from "@expo/vector-icons";
|
||||
import { Image } from "expo-image";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import MapView from "react-native-maps";
|
||||
import MapView, { Marker } from "react-native-maps";
|
||||
|
||||
const defaultRegion = {
|
||||
latitude: -8.737109,
|
||||
longitude: 115.1756897,
|
||||
latitudeDelta: 0.1,
|
||||
longitudeDelta: 0.1,
|
||||
height: 300,
|
||||
};
|
||||
|
||||
export interface LocationItem {
|
||||
id: string | number;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
name: string;
|
||||
imageId?: string;
|
||||
}
|
||||
|
||||
export default function Maps() {
|
||||
const [list, setList] = useState<any[] | null>(null);
|
||||
const [loadList, setLoadList] = useState(false);
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [selected, setSelected] = useState({
|
||||
id: "",
|
||||
bidangBisnis: "",
|
||||
nomorTelepon: "",
|
||||
alamatBisnis: "",
|
||||
namePin: "",
|
||||
imageId: "",
|
||||
portofolioId: "",
|
||||
latitude: 0,
|
||||
longitude: 0,
|
||||
});
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
handlerLoadList();
|
||||
}, [])
|
||||
);
|
||||
|
||||
const handlerLoadList = async () => {
|
||||
try {
|
||||
setLoadList(true);
|
||||
const response = await apiMapsGetAll();
|
||||
|
||||
if (response.success) {
|
||||
setList(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadList(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
|
||||
{/* <MapCustom height={"100%"} /> */}
|
||||
<View style={{ flex: 1 }}>
|
||||
<MapView
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</ViewWrapper>
|
||||
<>
|
||||
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
|
||||
{/* <MapCustom height={"100%"} /> */}
|
||||
<View style={{ flex: 1 }}>
|
||||
{loadList ? (
|
||||
<MapView
|
||||
initialRegion={defaultRegion}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<MapView
|
||||
initialRegion={defaultRegion}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
{list?.map((item: any, index: number) => {
|
||||
return (
|
||||
<Marker
|
||||
key={item?.id}
|
||||
coordinate={{
|
||||
latitude: item?.latitude,
|
||||
longitude: item?.longitude,
|
||||
}}
|
||||
title={item?.namePin}
|
||||
onPress={() => {
|
||||
setOpenDrawer(true);
|
||||
setSelected({
|
||||
id: item?.id,
|
||||
bidangBisnis:
|
||||
item?.Portofolio?.MasterBidangBisnis?.name,
|
||||
nomorTelepon: item?.Portofolio?.tlpn,
|
||||
alamatBisnis: item?.Portofolio?.alamatKantor,
|
||||
namePin: item?.namePin,
|
||||
imageId: item?.imageId,
|
||||
portofolioId: item?.Portofolio?.id,
|
||||
latitude: item?.latitude,
|
||||
longitude: item?.longitude,
|
||||
});
|
||||
}}
|
||||
// Gunakan gambar kustom jika tersedia
|
||||
>
|
||||
<View style={{}}>
|
||||
<Image
|
||||
source={{
|
||||
uri: API_IMAGE.GET({
|
||||
fileId: item?.Portofolio?.logoId,
|
||||
}),
|
||||
}}
|
||||
style={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 100,
|
||||
borderWidth: 1,
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</Marker>
|
||||
);
|
||||
})}
|
||||
</MapView>
|
||||
)}
|
||||
</View>
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<DummyLandscapeImage height={200} imageId={selected.imageId} />
|
||||
<Spacing />
|
||||
<StackCustom gap={"xs"}>
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
<FontAwesome
|
||||
name="building-o"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{selected.namePin}</TextCustom>}
|
||||
/>
|
||||
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
<Ionicons
|
||||
name="list-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{selected.bidangBisnis}</TextCustom>}
|
||||
/>
|
||||
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
<Ionicons
|
||||
name="call-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{selected.nomorTelepon}</TextCustom>}
|
||||
/>
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
<Ionicons
|
||||
name="location-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{selected.alamatBisnis}</TextCustom>}
|
||||
/>
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={6} style={{ paddingRight: 10 }}>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
setOpenDrawer(false);
|
||||
router.push(`/portofolio/${selected.portofolioId}`);
|
||||
}}
|
||||
>
|
||||
Detail
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ paddingLeft: 10 }}>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
openInDeviceMaps({
|
||||
latitude: selected.latitude,
|
||||
longitude: selected.longitude,
|
||||
title: selected.namePin,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Buka Maps
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</StackCustom>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { DrawerCustom, LoaderCustom, Spacing, StackCustom } from "@/components";
|
||||
import {
|
||||
ButtonCustom,
|
||||
DrawerCustom,
|
||||
DummyLandscapeImage,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import Portofolio_BusinessLocation from "@/screens/Portofolio/BusinessLocationSection";
|
||||
import Portofolio_ButtonDelete from "@/screens/Portofolio/ButtonDelete";
|
||||
@@ -13,19 +23,20 @@ import Portofolio_SocialMediaSection from "@/screens/Portofolio/SocialMediaSecti
|
||||
import { apiGetOnePortofolio } from "@/service/api-client/api-portofolio";
|
||||
import { apiUser } from "@/service/api-client/api-user";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { openInDeviceMaps } from "@/utils/openInDeviceMaps";
|
||||
import { FontAwesome, Ionicons } from "@expo/vector-icons";
|
||||
import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
|
||||
export default function Portofolio() {
|
||||
const { user } = useAuth();
|
||||
const { id } = useLocalSearchParams();
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
const [isLoadingDelete, setIsLoadingDelete] = useState(false);
|
||||
const [data, setData] = useState<any>();
|
||||
const [profileId, setProfileId] = useState<any>();
|
||||
|
||||
const { user } = useAuth();
|
||||
const [openDrawerLocation, setOpenDrawerLocation] = useState(false);
|
||||
|
||||
const openDrawer = () => {
|
||||
setIsDrawerOpen(true);
|
||||
@@ -43,19 +54,13 @@ export default function Portofolio() {
|
||||
|
||||
async function onLoadData(id: string) {
|
||||
const response = await apiGetOnePortofolio({ id: id });
|
||||
console.log(
|
||||
"[PROFILE ID]>>",
|
||||
JSON.stringify(response.data.Profile.id, null, 2)
|
||||
);
|
||||
|
||||
setData(response.data);
|
||||
}
|
||||
|
||||
const onLoadUserByToken = async () => {
|
||||
const response = await apiUser(user?.id as string);
|
||||
console.log(
|
||||
"[PROFILE LOGIN]>>",
|
||||
JSON.stringify(response.data?.Profile.id, null, 2)
|
||||
);
|
||||
|
||||
setProfileId(response?.data?.Profile?.id);
|
||||
};
|
||||
|
||||
@@ -89,7 +94,11 @@ export default function Portofolio() {
|
||||
data={data}
|
||||
listSubBidang={data?.Portofolio_BidangDanSubBidangBisnis as any[]}
|
||||
/>
|
||||
<Portofolio_BusinessLocation />
|
||||
<Portofolio_BusinessLocation
|
||||
data={data?.BusinessMaps}
|
||||
imageId={data?.logoId}
|
||||
setOpenDrawerLocation={setOpenDrawerLocation}
|
||||
/>
|
||||
<Portofolio_SocialMediaSection
|
||||
data={data?.Portofolio_MediaSosial}
|
||||
/>
|
||||
@@ -110,10 +119,93 @@ export default function Portofolio() {
|
||||
height={"auto"}
|
||||
>
|
||||
<Portofolio_MenuDrawerSection
|
||||
drawerItems={drawerItemsPortofolio({ id: id as string })}
|
||||
drawerItems={drawerItemsPortofolio({
|
||||
id: id as string,
|
||||
maps: data?.BusinessMaps,
|
||||
})}
|
||||
setIsDrawerOpen={setIsDrawerOpen}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* Drawer Lokasi */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerLocation}
|
||||
closeDrawer={() => setOpenDrawerLocation(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<DummyLandscapeImage
|
||||
height={200}
|
||||
imageId={data?.BusinessMaps?.imageId}
|
||||
/>
|
||||
<Spacing />
|
||||
<StackCustom gap={"xs"}>
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
<FontAwesome
|
||||
name="building-o"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{data?.BusinessMaps?.namePin}</TextCustom>}
|
||||
/>
|
||||
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
<Ionicons
|
||||
name="list-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={
|
||||
<TextCustom>{data?.MasterBidangBisnis?.name}</TextCustom>
|
||||
}
|
||||
/>
|
||||
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
<Ionicons
|
||||
name="call-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{data?.tlpn}</TextCustom>}
|
||||
/>
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
<Ionicons
|
||||
name="location-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{data?.alamatKantor}</TextCustom>}
|
||||
/>
|
||||
|
||||
<Spacing />
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
openInDeviceMaps({
|
||||
latitude: data?.BusinessMaps?.latitude,
|
||||
longitude: data?.BusinessMaps?.longitude,
|
||||
title: data?.BusinessMaps?.namePin,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Buka Maps
|
||||
</ButtonCustom>
|
||||
</StackCustom>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function Profile() {
|
||||
const [dataToken, setDataToken] = useState<IProfile>();
|
||||
const [listPortofolio, setListPortofolio] = useState<any[]>();
|
||||
|
||||
const { logout, isAdmin, user } = useAuth();
|
||||
const { token, logout, isAdmin, user, userData } = useAuth();
|
||||
|
||||
const openDrawer = () => {
|
||||
setIsDrawerOpen(true);
|
||||
@@ -42,7 +42,8 @@ export default function Profile() {
|
||||
onLoadPortofolio(id as string);
|
||||
onLoadUserByToken();
|
||||
isUserCheck();
|
||||
}, [id])
|
||||
userData(token as string);
|
||||
}, [id, token])
|
||||
);
|
||||
|
||||
const isUserCheck = () => {
|
||||
@@ -136,10 +137,7 @@ const ButtonnDot = ({
|
||||
}) => {
|
||||
const isId = id === undefined || id === null;
|
||||
|
||||
console.log("ID CHECK", id);
|
||||
|
||||
if (isId) {
|
||||
console.log("ID UNDEFINED", id);
|
||||
return (
|
||||
<>
|
||||
<TouchableOpacity onPress={logout}>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from "@/components";
|
||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiUser } from "@/service/api-client/api-user";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import Toast from "react-native-toast-message";
|
||||
@@ -19,12 +20,25 @@ export default function WaitingRoom() {
|
||||
async function handleCheck() {
|
||||
try {
|
||||
const response = await userData(token as string);
|
||||
|
||||
if (response.active) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Akun anda telah aktif", // text2: "Anda berhasil login",
|
||||
});
|
||||
router.replace(`/(application)/(user)/profile/create`);
|
||||
const checkProfile = await apiUser(response.id);
|
||||
|
||||
if (checkProfile?.data?.Profile) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Akun anda telah aktif kembali", // text2: "Anda berhasil login",
|
||||
});
|
||||
router.replace(`/(application)/(user)/home`);
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Akun anda telah aktif", // text2: "Anda berhasil login",
|
||||
});
|
||||
router.replace(`/(application)/(user)/profile/create`);
|
||||
}
|
||||
|
||||
// router.replace(`/(application)/(user)/profile/create`);
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
|
||||
@@ -15,7 +15,10 @@ import {
|
||||
ICON_SIZE_XLARGE,
|
||||
} from "@/constants/constans-value";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { adminListMenu } from "@/screens/Admin/listPageAdmin";
|
||||
import {
|
||||
adminListMenu,
|
||||
superAdminListMenu,
|
||||
} from "@/screens/Admin/listPageAdmin";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { FontAwesome6, Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack } from "expo-router";
|
||||
@@ -24,8 +27,11 @@ import { useState } from "react";
|
||||
export default function AdminLayout() {
|
||||
const [openDrawerNavbar, setOpenDrawerNavbar] = useState(false);
|
||||
const [openDrawerUser, setOpenDrawerUser] = useState(false);
|
||||
// const [user, setUser] = useState(null);
|
||||
|
||||
const { logout } = useAuth();
|
||||
const { logout, user } = useAuth();
|
||||
|
||||
console.log("[USER LAYOUT]", JSON.stringify(user, null, 2));
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -56,58 +62,58 @@ export default function AdminLayout() {
|
||||
),
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="dashboard" />
|
||||
{/* <Stack.Screen name="dashboard" /> */}
|
||||
{/* ================== Investment Start ================== */}
|
||||
<Stack.Screen name="investment/index" />
|
||||
{/* <Stack.Screen name="investment/index" /> */}
|
||||
{/* ================== Investment End ================== */}
|
||||
|
||||
{/* ================== Maps Start ================== */}
|
||||
<Stack.Screen name="maps" />
|
||||
{/* <Stack.Screen name="maps" /> */}
|
||||
{/* ================== Maps End ================== */}
|
||||
|
||||
{/* ================== App Information Start ================== */}
|
||||
<Stack.Screen name="app-information/index" />
|
||||
{/* <Stack.Screen name="app-information/index" /> */}
|
||||
{/* ================== App Information End ================== */}
|
||||
|
||||
{/* ================== Job Start ================== */}
|
||||
<Stack.Screen name="job/index" />
|
||||
{/* <Stack.Screen name="job/index" /> */}
|
||||
{/* <Stack.Screen name="job/publish" />
|
||||
<Stack.Screen name="job/review" />
|
||||
<Stack.Screen name="job/reject" /> */}
|
||||
<Stack.Screen name="job/[status]/status" />
|
||||
<Stack.Screen name="job/[id]/[status]/index" />
|
||||
{/* <Stack.Screen name="job/[status]/status" />
|
||||
<Stack.Screen name="job/[id]/[status]/index" /> */}
|
||||
|
||||
{/* ================== Collaboration Start ================== */}
|
||||
<Stack.Screen name="collaboration/index" />
|
||||
<Stack.Screen name="collaboration/publish" />
|
||||
{/* <Stack.Screen name="collaboration/index" /> */}
|
||||
{/* <Stack.Screen name="collaboration/publish" />
|
||||
<Stack.Screen name="collaboration/group" />
|
||||
<Stack.Screen name="collaboration/reject" />
|
||||
<Stack.Screen name="collaboration/[id]/[status]" />
|
||||
<Stack.Screen name="collaboration/[id]/group" />
|
||||
<Stack.Screen name="collaboration/[id]/group" /> */}
|
||||
{/* ================== Collaboration End ================== */}
|
||||
|
||||
{/* ================== Forum Start ================== */}
|
||||
<Stack.Screen name="forum/index" />
|
||||
<Stack.Screen name="forum/[id]/index" />
|
||||
{/* <Stack.Screen name="forum/index" /> */}
|
||||
{/* <Stack.Screen name="forum/[id]/index" />
|
||||
<Stack.Screen name="forum/report-comment" />
|
||||
<Stack.Screen name="forum/report-posting" />
|
||||
<Stack.Screen name="forum/[id]/list-report-posting" />
|
||||
<Stack.Screen name="forum/[id]/list-report-comment" />
|
||||
<Stack.Screen name="forum/[id]/list-report-comment" /> */}
|
||||
{/* ================== Forum End ================== */}
|
||||
|
||||
{/* ================== Voting Start ================== */}
|
||||
<Stack.Screen name="voting/index" />
|
||||
{/* <Stack.Screen name="voting/index" />
|
||||
<Stack.Screen name="voting/[status]/status" />
|
||||
<Stack.Screen name="voting/[id]/[status]/index" />
|
||||
<Stack.Screen name="voting/[id]/reject-input" />
|
||||
<Stack.Screen name="voting/[id]/reject-input" /> */}
|
||||
{/* ================== Voting End ================== */}
|
||||
|
||||
{/* ================== Event Start ================== */}
|
||||
<Stack.Screen name="event/index" />
|
||||
{/* <Stack.Screen name="event/index" />
|
||||
<Stack.Screen name="event/[status]/status" />
|
||||
<Stack.Screen name="event/type-of-event" />
|
||||
<Stack.Screen name="event/type-create" />
|
||||
<Stack.Screen name="event/type-update" />
|
||||
<Stack.Screen name="event/type-update" /> */}
|
||||
{/* <Stack.Screen name="event/[id]/[status]/index" />
|
||||
<Stack.Screen name="event/[id]/reject-input"/> */}
|
||||
{/* ================== Event End ================== */}
|
||||
@@ -134,7 +140,11 @@ export default function AdminLayout() {
|
||||
/>
|
||||
|
||||
<NavbarMenu
|
||||
items={adminListMenu}
|
||||
items={
|
||||
user?.masterUserRoleId === "2"
|
||||
? adminListMenu
|
||||
: superAdminListMenu
|
||||
}
|
||||
onClose={() => setOpenDrawerNavbar(false)}
|
||||
/>
|
||||
</StackCustom>
|
||||
@@ -155,7 +165,7 @@ export default function AdminLayout() {
|
||||
/>
|
||||
}
|
||||
>
|
||||
<TextCustom>Username</TextCustom>
|
||||
<TextCustom>{user?.username || "-"}</TextCustom>
|
||||
</GridComponentView>
|
||||
<GridComponentView
|
||||
leftIcon={
|
||||
@@ -166,7 +176,13 @@ export default function AdminLayout() {
|
||||
/>
|
||||
}
|
||||
>
|
||||
<TextCustom>User Role</TextCustom>
|
||||
<TextCustom>
|
||||
{user
|
||||
? user?.masterUserRoleId === "2"
|
||||
? "Admin"
|
||||
: "Super Admin"
|
||||
: "-"}
|
||||
</TextCustom>
|
||||
</GridComponentView>
|
||||
|
||||
<MenuDrawerDynamicGrid
|
||||
|
||||
@@ -1,13 +1,31 @@
|
||||
import {
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { StackCustom, TextCustom, ViewWrapper } from "@/components";
|
||||
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { apiAdminMainDashboardGetAll } from "@/service/api-admin/api-admin-main-dashboard";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function AdminDashboard() {
|
||||
const [countUser, setCountUser] = useState(0);
|
||||
const [countPortofolio, setCountPortofolio] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
onLoadData();
|
||||
}, []);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiAdminMainDashboardGetAll();
|
||||
|
||||
if (response.success) {
|
||||
setCountUser(response.data.user);
|
||||
setCountPortofolio(response.data.portofolio);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR LOAD DATA]", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
@@ -15,7 +33,7 @@ export default function AdminDashboard() {
|
||||
<TextCustom bold size={30}>
|
||||
Main Dashboard
|
||||
</TextCustom>
|
||||
{listData.map((item, i) => (
|
||||
{listData(countUser, countPortofolio).map((item, i) => (
|
||||
<AdminComp_BoxDashboard key={i} item={item} />
|
||||
))}
|
||||
</StackCustom>
|
||||
@@ -24,15 +42,15 @@ export default function AdminDashboard() {
|
||||
);
|
||||
}
|
||||
|
||||
const listData = [
|
||||
const listData = (countUser: number, countPortofolio: number) => [
|
||||
{
|
||||
label: "User",
|
||||
value: 4,
|
||||
value: countUser,
|
||||
icon: <Ionicons name="people" size={30} color={MainColor.yellow} />,
|
||||
},
|
||||
{
|
||||
label: "Portofolio",
|
||||
value: 7,
|
||||
value: countPortofolio,
|
||||
icon: (
|
||||
<Ionicons name="id-card-outline" size={30} color={MainColor.yellow} />
|
||||
),
|
||||
|
||||
128
app/(application)/admin/super-admin/[id]/index.tsx
Normal file
128
app/(application)/admin/super-admin/[id]/index.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
LoaderCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import {
|
||||
apiAdminUserAccessGetById,
|
||||
apiAdminUserAccessUpdateStatus,
|
||||
} from "@/service/api-admin/api-admin-user-access";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function SuperAdminDetail() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [data, setData] = useState<any | null>(null);
|
||||
const [loadData, setLoadData] = useState(false);
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadData(true);
|
||||
const response = await apiAdminUserAccessGetById({ id: id as string });
|
||||
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR LOAD DATA]", error);
|
||||
} finally {
|
||||
setLoadData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlerSubmit = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await apiAdminUserAccessUpdateStatus({
|
||||
id: id as string,
|
||||
role: data?.masterUserRoleId === "2" ? "user" : "admin",
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Update role gagal",
|
||||
});
|
||||
return;
|
||||
}
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Update role berhasil ",
|
||||
});
|
||||
router.back();
|
||||
} catch (error) {
|
||||
console.log("[ERROR UPDATE STATUS]", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
headerComponent={<AdminBackButtonAntTitle title={`Detail User`} />}
|
||||
footerComponent={
|
||||
data && (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
isLoading={isLoading}
|
||||
backgroundColor={
|
||||
data?.masterUserRoleId === "2" ? "red" : "green"
|
||||
}
|
||||
textColor={"white"}
|
||||
onPress={handlerSubmit}
|
||||
>
|
||||
{data?.masterUserRoleId === "2"
|
||||
? "Hapus akses admin"
|
||||
: "Tambah sebagai admin"}
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
)
|
||||
}
|
||||
>
|
||||
{loadData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<StackCustom>
|
||||
{listData(data && data)?.map((item: any, index: number) => (
|
||||
<GridTwoView
|
||||
key={index}
|
||||
spanLeft={4}
|
||||
spanRight={8}
|
||||
leftIcon={<TextCustom bold>{item?.label}</TextCustom>}
|
||||
rightIcon={<TextCustom>{item?.value}</TextCustom>}
|
||||
/>
|
||||
))}
|
||||
</StackCustom>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const listData = (data: any) => [
|
||||
{
|
||||
label: "Username",
|
||||
value: (data && data?.username) || "-",
|
||||
},
|
||||
{
|
||||
label: "Role",
|
||||
value: data && data?.masterUserRoleId === "2" ? "Admin" : "User",
|
||||
},
|
||||
{
|
||||
label: "Nomor",
|
||||
value: (data && `+${data?.nomor}`) || "-",
|
||||
},
|
||||
];
|
||||
148
app/(application)/admin/super-admin/index.tsx
Normal file
148
app/(application)/admin/super-admin/index.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BadgeCustom,
|
||||
CenterCustom,
|
||||
Divider,
|
||||
SearchInput,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import { GridViewCustomSpan } from "@/components/_ShareComponent/GridViewCustomSpan";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||
import { apiAdminUserAccessGetAll } from "@/service/api-admin/api-admin-user-access";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function SuperAdmin_ListUser() {
|
||||
const [listData, setListData] = useState<any[] | null>(null);
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [search])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiAdminUserAccessGetAll({
|
||||
search: search,
|
||||
category: "all-role",
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR LOAD DATA]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const rightComponent = () => {
|
||||
return (
|
||||
<>
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari Username"
|
||||
onChangeText={(text) => setSearch(text)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
headerComponent={
|
||||
<AdminComp_BoxTitle
|
||||
title={"Super Admin"}
|
||||
rightComponent={rightComponent()}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<GridViewCustomSpan
|
||||
span1={2}
|
||||
span2={5}
|
||||
span3={5}
|
||||
component1={
|
||||
<TextCustom align="center" bold>
|
||||
Aksi
|
||||
</TextCustom>
|
||||
}
|
||||
component2={
|
||||
<TextCustom align="center" bold>
|
||||
Username
|
||||
</TextCustom>
|
||||
}
|
||||
component3={
|
||||
<TextCustom align="center" bold>
|
||||
Role
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
|
||||
<Divider />
|
||||
|
||||
<StackCustom>
|
||||
{_.isEmpty(listData) ? (
|
||||
<TextCustom align="center" color="gray" size={"small"}>
|
||||
Tidak ada data
|
||||
</TextCustom>
|
||||
) : (
|
||||
listData?.map((item: any, index: number) => (
|
||||
<GridViewCustomSpan
|
||||
key={index}
|
||||
span1={2}
|
||||
span2={5}
|
||||
span3={5}
|
||||
component1={
|
||||
<CenterCustom>
|
||||
<Ionicons
|
||||
onPress={() =>
|
||||
router.push(`/admin/super-admin/${item?.id}`)
|
||||
}
|
||||
name="open"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.yellow}
|
||||
/>
|
||||
</CenterCustom>
|
||||
|
||||
// <ButtonCustom
|
||||
// onPress={() =>
|
||||
// router.push(`/admin/super-admin/${item?.id}`)
|
||||
// }
|
||||
// >
|
||||
// Detail
|
||||
// </ButtonCustom>
|
||||
}
|
||||
component2={
|
||||
<TextCustom bold truncate>
|
||||
{item?.username || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
component3={
|
||||
<CenterCustom>
|
||||
{item?.masterUserRoleId === "2" ? (
|
||||
<BadgeCustom color={AccentColor.blue}>Admin</BadgeCustom>
|
||||
) : (
|
||||
<BadgeCustom color={AccentColor.softblue}>
|
||||
User
|
||||
</BadgeCustom>
|
||||
)}
|
||||
</CenterCustom>
|
||||
}
|
||||
style3={{ alignItems: "center", justifyContent: "center" }}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
import {
|
||||
ButtonCustom,
|
||||
Divider,
|
||||
Grid,
|
||||
SearchInput,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export default function AdminUserAccess() {
|
||||
const rightComponent = () => {
|
||||
return (
|
||||
<>
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari User"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
headerComponent={
|
||||
<AdminComp_BoxTitle
|
||||
title="User Access"
|
||||
rightComponent={rightComponent()}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Grid>
|
||||
<Grid.Col span={4} style={{ alignItems: "center" }}>
|
||||
<TextCustom bold>Aksi</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={4} style={{ alignItems: "center" }}>
|
||||
<TextCustom bold>Username</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={4} style={{ alignItems: "center" }}>
|
||||
<TextCustom bold>Nomor</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
<Divider />
|
||||
|
||||
<StackCustom>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Grid key={index}>
|
||||
<Grid.Col
|
||||
span={4}
|
||||
style={{
|
||||
alignItems: "center",
|
||||
paddingLeft: 5,
|
||||
paddingRight: 5,
|
||||
}}
|
||||
>
|
||||
<TextCustom bold>
|
||||
<ButtonCustom
|
||||
iconLeft={
|
||||
<Ionicons
|
||||
name={
|
||||
index % 2 === 0
|
||||
? "checkmark-outline"
|
||||
: "close-circle-outline"
|
||||
}
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color="black"
|
||||
/>
|
||||
}
|
||||
onPress={() => {}}
|
||||
backgroundColor={
|
||||
index % 2 === 0 ? MainColor.green : MainColor.red
|
||||
}
|
||||
>
|
||||
<TextCustom size={"small"} color={"black"}>
|
||||
{index % 2 === 0 ? "Berikan Akses" : "Hapus Akses"}
|
||||
</TextCustom>
|
||||
</ButtonCustom>
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={4}
|
||||
style={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
paddingLeft: 5,
|
||||
paddingRight: 5,
|
||||
}}
|
||||
>
|
||||
<TextCustom bold truncate>
|
||||
Useraname
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={4}
|
||||
style={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
paddingLeft: 5,
|
||||
paddingRight: 5,
|
||||
}}
|
||||
>
|
||||
<TextCustom bold truncate>
|
||||
08123456789
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
))}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
124
app/(application)/admin/user-access/[id]/index.tsx
Normal file
124
app/(application)/admin/user-access/[id]/index.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
LoaderCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import {
|
||||
apiAdminUserAccessGetById,
|
||||
apiAdminUserAccessUpdateStatus,
|
||||
} from "@/service/api-admin/api-admin-user-access";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function AdminUserAccessDetail() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [data, setData] = useState<any | null>(null);
|
||||
const [loadData, setLoadData] = useState(false);
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadData(true);
|
||||
const response = await apiAdminUserAccessGetById({ id: id as string });
|
||||
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR LOAD DATA]", error);
|
||||
} finally {
|
||||
setLoadData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlerSubmit = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await apiAdminUserAccessUpdateStatus({
|
||||
id: id as string,
|
||||
active: !data?.active,
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Update aktifasi gagal",
|
||||
});
|
||||
return;
|
||||
}
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Update aktifasi berhasil ",
|
||||
});
|
||||
router.back();
|
||||
} catch (error) {
|
||||
console.log("[ERROR UPDATE STATUS]", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
headerComponent={<AdminBackButtonAntTitle title={`Detail User`} />}
|
||||
footerComponent={
|
||||
data && (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
isLoading={isLoading}
|
||||
backgroundColor={data?.active ? "red" : "green"}
|
||||
textColor={"white"}
|
||||
onPress={handlerSubmit}
|
||||
>
|
||||
{data?.active ? "Hapus Akses" : "Berikan Akses"}
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
)
|
||||
}
|
||||
>
|
||||
{loadData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<StackCustom>
|
||||
{listData(data && data)?.map((item: any, index: number) => (
|
||||
<GridTwoView
|
||||
key={index}
|
||||
spanLeft={4}
|
||||
spanRight={8}
|
||||
leftIcon={<TextCustom bold>{item?.label}</TextCustom>}
|
||||
rightIcon={<TextCustom>{item?.value}</TextCustom>}
|
||||
/>
|
||||
))}
|
||||
</StackCustom>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const listData = (data: any) => [
|
||||
{
|
||||
label: "Username",
|
||||
value: (data && data?.username) || "-",
|
||||
},
|
||||
{
|
||||
label: "Aktivasi",
|
||||
value: data && data?.active ? "Aktif" : "Tidak Aktif",
|
||||
},
|
||||
{
|
||||
label: "Nomor",
|
||||
value: (data && `+${data?.nomor}`) || "-",
|
||||
},
|
||||
];
|
||||
143
app/(application)/admin/user-access/index.tsx
Normal file
143
app/(application)/admin/user-access/index.tsx
Normal file
@@ -0,0 +1,143 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BadgeCustom,
|
||||
CenterCustom,
|
||||
Divider,
|
||||
SearchInput,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import { GridViewCustomSpan } from "@/components/_ShareComponent/GridViewCustomSpan";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||
import { apiAdminUserAccessGetAll } from "@/service/api-admin/api-admin-user-access";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function AdminUserAccess() {
|
||||
const [listData, setListData] = useState<any[] | null>(null);
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [search])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiAdminUserAccessGetAll({
|
||||
search: search,
|
||||
category: "only-user",
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR LOAD DATA]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const rightComponent = () => {
|
||||
return (
|
||||
<>
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari User"
|
||||
onChangeText={(text) => setSearch(text)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
headerComponent={
|
||||
<AdminComp_BoxTitle
|
||||
title="User Access"
|
||||
rightComponent={rightComponent()}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<GridViewCustomSpan
|
||||
span1={2}
|
||||
span2={5}
|
||||
span3={5}
|
||||
component1={
|
||||
<TextCustom align="center" bold>
|
||||
Aksi
|
||||
</TextCustom>
|
||||
}
|
||||
component2={
|
||||
<TextCustom align="center" bold>
|
||||
Username
|
||||
</TextCustom>
|
||||
}
|
||||
component3={
|
||||
<TextCustom align="center" bold>
|
||||
Status Akses
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
|
||||
<Divider />
|
||||
|
||||
<StackCustom>
|
||||
{_.isEmpty(listData) ? (
|
||||
<TextCustom align="center" color="gray" size={"small"}>
|
||||
Tidak ada data
|
||||
</TextCustom>
|
||||
) : (
|
||||
listData?.map((item: any, index: number) => (
|
||||
<GridViewCustomSpan
|
||||
key={index}
|
||||
span1={2}
|
||||
span2={5}
|
||||
span3={5}
|
||||
component1={
|
||||
<CenterCustom>
|
||||
<Ionicons
|
||||
onPress={() =>
|
||||
router.push(`/admin/user-access/${item?.id}`)
|
||||
}
|
||||
name="open"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.yellow}
|
||||
/>
|
||||
</CenterCustom>
|
||||
// <ButtonCustom
|
||||
// onPress={() =>
|
||||
// router.push(`/admin/user-access/${item?.id}`)
|
||||
// }
|
||||
// >
|
||||
// Detail
|
||||
// </ButtonCustom>
|
||||
}
|
||||
component2={
|
||||
<TextCustom bold truncate>
|
||||
{item?.username || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
component3={
|
||||
<CenterCustom>
|
||||
{item?.active ? (
|
||||
<BadgeCustom color="green">Aktif</BadgeCustom>
|
||||
) : (
|
||||
<BadgeCustom color="red">Tidak Aktif</BadgeCustom>
|
||||
)}
|
||||
</CenterCustom>
|
||||
}
|
||||
style3={{ alignItems: "center", justifyContent: "center" }}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { Href, router } from "expo-router";
|
||||
import { DimensionValue, TouchableOpacity } from "react-native";
|
||||
import { DimensionValue, StyleProp, TouchableOpacity, ViewStyle } from "react-native";
|
||||
|
||||
type SizeType = "xs" | "sm" | "md" | "lg" | "xl" | number | string | undefined;
|
||||
|
||||
@@ -10,12 +10,14 @@ export default function ActionIcon({
|
||||
icon,
|
||||
size = "md",
|
||||
disabled = false,
|
||||
style,
|
||||
}: {
|
||||
href?: Href;
|
||||
onPress?: () => void;
|
||||
icon: React.ReactNode;
|
||||
size?: SizeType;
|
||||
disabled?: boolean;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}) {
|
||||
const sizeMap = {
|
||||
xs: 22,
|
||||
@@ -39,7 +41,7 @@ export default function ActionIcon({
|
||||
<TouchableOpacity
|
||||
disabled={disabled}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
style={[{
|
||||
backgroundColor: disabled ? MainColor.disabled : MainColor.yellow,
|
||||
padding: 5,
|
||||
borderRadius: 50,
|
||||
@@ -47,7 +49,7 @@ export default function ActionIcon({
|
||||
justifyContent: "center",
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
}}
|
||||
}, style]}
|
||||
onPress={() => {
|
||||
if (disabled) return;
|
||||
if (href) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// components/MapComponent.js
|
||||
|
||||
import API_IMAGE from "@/constants/api-storage";
|
||||
import { Image } from "expo-image";
|
||||
import React from "react";
|
||||
import { DimensionValue, StyleSheet, View } from "react-native";
|
||||
import MapView, { Marker } from "react-native-maps";
|
||||
@@ -10,6 +12,9 @@ interface MapComponentProps {
|
||||
latitudeDelta?: number;
|
||||
longitudeDelta?: number;
|
||||
height?: DimensionValue;
|
||||
namePin?: string;
|
||||
imageId?: string;
|
||||
onPress?: () => void;
|
||||
}
|
||||
|
||||
const MapCustom = ({
|
||||
@@ -18,6 +23,9 @@ const MapCustom = ({
|
||||
latitudeDelta = 0.1,
|
||||
longitudeDelta = 0.1,
|
||||
height = 300,
|
||||
namePin = "Bali",
|
||||
imageId,
|
||||
onPress,
|
||||
}: MapComponentProps) => {
|
||||
const initialRegion = {
|
||||
latitude,
|
||||
@@ -40,9 +48,22 @@ const MapCustom = ({
|
||||
latitude,
|
||||
longitude,
|
||||
}}
|
||||
title="Bali"
|
||||
description="Badung, Bali, Indonesia"
|
||||
/>
|
||||
title={namePin}
|
||||
onPress={onPress}
|
||||
// Gunakan gambar kustom jika tersedia
|
||||
>
|
||||
<View style={{}}>
|
||||
<Image
|
||||
source={{ uri: API_IMAGE.GET({ fileId: imageId as string }) }}
|
||||
style={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 100,
|
||||
borderWidth: 1,
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</Marker>
|
||||
</MapView>
|
||||
</View>
|
||||
);
|
||||
|
||||
79
components/Map/MapSelected.tsx
Normal file
79
components/Map/MapSelected.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import React from "react";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
import MapView, { LatLng, Marker } from "react-native-maps";
|
||||
|
||||
interface MapSelectedProps {
|
||||
initialRegion?: {
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
latitudeDelta?: number;
|
||||
longitudeDelta?: number;
|
||||
};
|
||||
onLocationSelect?: (location: LatLng) => void;
|
||||
height?: number; // Opsional: tinggi peta dalam piksel
|
||||
selectedLocation: LatLng;
|
||||
setSelectedLocation: (location: LatLng) => void;
|
||||
}
|
||||
|
||||
const MapSelected: React.FC<MapSelectedProps> = ({
|
||||
initialRegion,
|
||||
onLocationSelect,
|
||||
selectedLocation,
|
||||
setSelectedLocation,
|
||||
height = 400, // Default height: 400
|
||||
}) => {
|
||||
const handleMapPress = (event: any) => {
|
||||
const { latitude, longitude } = event.nativeEvent.coordinate;
|
||||
const location = { latitude, longitude };
|
||||
setSelectedLocation(location);
|
||||
onLocationSelect?.(location);
|
||||
};
|
||||
|
||||
// Default region sesuai permintaan
|
||||
const defaultRegion = {
|
||||
latitude: -8.737109,
|
||||
longitude: 115.1756897,
|
||||
latitudeDelta: 0.1,
|
||||
longitudeDelta: 0.1,
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={[styles.container, { height }]}>
|
||||
<MapView
|
||||
style={styles.map}
|
||||
initialRegion={(initialRegion as any) || defaultRegion}
|
||||
onPress={handleMapPress}
|
||||
showsUserLocation={true}
|
||||
showsMyLocationButton={true}
|
||||
loadingEnabled={true}
|
||||
loadingIndicatorColor="#666"
|
||||
loadingBackgroundColor="#f0f0f0"
|
||||
>
|
||||
{selectedLocation && (
|
||||
<Marker
|
||||
coordinate={selectedLocation}
|
||||
title="Lokasi Dipilih"
|
||||
description={`Lat: ${selectedLocation.latitude.toFixed(
|
||||
6
|
||||
)}, Lng: ${selectedLocation.longitude.toFixed(6)}`}
|
||||
pinColor="red"
|
||||
/>
|
||||
)}
|
||||
</MapView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
width: "100%",
|
||||
backgroundColor: "#f5f5f5",
|
||||
overflow: "hidden",
|
||||
borderRadius: 8,
|
||||
},
|
||||
map: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export default MapSelected;
|
||||
37
components/_ShareComponent/GridTwoView.tsx
Normal file
37
components/_ShareComponent/GridTwoView.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { ViewStyle } from "react-native";
|
||||
import Grid from "../Grid/GridCustom";
|
||||
|
||||
export default function GridTwoView({
|
||||
spanLeft = 6,
|
||||
spanRight = 6,
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
styleLeft,
|
||||
styleRight,
|
||||
}: {
|
||||
spanLeft?: number;
|
||||
spanRight?: number;
|
||||
leftIcon?: React.ReactNode;
|
||||
rightIcon?: React.ReactNode;
|
||||
styleLeft?: ViewStyle;
|
||||
styleRight?: ViewStyle;
|
||||
}) {
|
||||
const baseStyle: ViewStyle = { justifyContent: "center" };
|
||||
|
||||
return (
|
||||
<Grid containerStyle={{ marginBottom: 0 }}>
|
||||
<Grid.Col
|
||||
span={spanLeft}
|
||||
style={styleLeft ? { ...baseStyle, ...styleLeft } : baseStyle}
|
||||
>
|
||||
{leftIcon}
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={spanRight}
|
||||
style={styleRight ? { ...baseStyle, ...styleRight } : baseStyle}
|
||||
>
|
||||
{rightIcon}
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Grid } from "@/components";
|
||||
import { StyleProp, ViewStyle } from "react-native";
|
||||
|
||||
export const GridViewCustomSpan = ({
|
||||
span1,
|
||||
@@ -7,31 +8,52 @@ export const GridViewCustomSpan = ({
|
||||
component1,
|
||||
component2,
|
||||
component3,
|
||||
style1,
|
||||
style2,
|
||||
style3,
|
||||
}: {
|
||||
span1: number;
|
||||
span2: number;
|
||||
span3: number;
|
||||
span1?: number;
|
||||
span2?: number;
|
||||
span3?: number;
|
||||
component1: React.ReactNode;
|
||||
component2: React.ReactNode;
|
||||
component3: React.ReactNode;
|
||||
style1?: StyleProp<ViewStyle>;
|
||||
style2?: StyleProp<ViewStyle>;
|
||||
style3?: StyleProp<ViewStyle>;
|
||||
}) => {
|
||||
return (
|
||||
<Grid>
|
||||
<Grid.Col
|
||||
span={span1 || 4}
|
||||
style={{ justifyContent: "center", paddingRight: 5, paddingLeft: 5 }}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
paddingRight: 10,
|
||||
paddingLeft: 10,
|
||||
...(style1 as object)
|
||||
} as ViewStyle}
|
||||
>
|
||||
{component1}
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={span2 || 4}
|
||||
style={{ justifyContent: "center", paddingRight: 5, paddingLeft: 5 }}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
paddingRight: 10,
|
||||
paddingLeft: 10,
|
||||
...(style2 as object)
|
||||
} as ViewStyle}
|
||||
>
|
||||
{component2}
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={span3 || 4}
|
||||
style={{ justifyContent: "center", paddingRight: 5, paddingLeft: 5 }}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
paddingRight: 10,
|
||||
paddingLeft: 10,
|
||||
...(style3 as object)
|
||||
} as ViewStyle}
|
||||
>
|
||||
{component3}
|
||||
</Grid.Col>
|
||||
|
||||
@@ -135,7 +135,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const userData = async (token: string) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await apiConfig.get(`/mobile/user?token=${token}`, {
|
||||
const response = await apiConfig.get(`/mobile?token=${token}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NavbarItem } from "@/components/Drawer/NavbarMenu";
|
||||
|
||||
export { adminListMenu }
|
||||
export { adminListMenu, superAdminListMenu }
|
||||
|
||||
const adminListMenu: NavbarItem[] = [
|
||||
{
|
||||
@@ -93,4 +93,102 @@ const adminListMenu: NavbarItem[] = [
|
||||
icon: "people",
|
||||
link: "/admin/user-access",
|
||||
},
|
||||
];
|
||||
|
||||
const superAdminListMenu: NavbarItem[] = [
|
||||
{
|
||||
label: "Main Dashboard",
|
||||
icon: "home",
|
||||
link: "/admin/dashboard",
|
||||
},
|
||||
{
|
||||
label: "Investasi",
|
||||
icon: "wallet",
|
||||
links: [
|
||||
{ label: "Dashboard", link: "/admin/investment" },
|
||||
{ label: "Publish", link: "/admin/investment/publish/status" },
|
||||
{ label: "Review", link: "/admin/investment/review/status" },
|
||||
{ label: "Reject", link: "/admin/investment/reject/status" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Donasi",
|
||||
icon: "hand-right",
|
||||
links: [
|
||||
{ label: "Dashboard", link: "/admin/donation" },
|
||||
{ label: "Publish", link: "/admin/donation/publish/status" },
|
||||
{ label: "Review", link: "/admin/donation/review/status" },
|
||||
{ label: "Reject", link: "/admin/donation/reject/status" },
|
||||
{ label: "Kategori", link: "/admin/donation/category" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Event",
|
||||
icon: "calendar-clear",
|
||||
links: [
|
||||
{ label: "Dashboard", link: "/admin/event" },
|
||||
{ label: "Publish", link: "/admin/event/publish/status" },
|
||||
{ label: "Review", link: "/admin/event/review/status" },
|
||||
{ label: "Reject", link: "/admin/event/reject/status" },
|
||||
{ label: "Tipe Acara", link: "/admin/event/type-of-event" },
|
||||
{ label: "Riwayat", link: "/admin/event/riwayat/status" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Voting",
|
||||
icon: "accessibility-outline",
|
||||
links: [
|
||||
{ label: "Dashboard", link: "/admin/voting" },
|
||||
{ label: "Publish", link: "/admin/voting/publish/status" },
|
||||
{ label: "Review", link: "/admin/voting/review/status" },
|
||||
{ label: "Reject", link: "/admin/voting/reject/status" },
|
||||
{ label: "Riwayat", link: "/admin/voting/riwayat/status" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Job",
|
||||
icon: "desktop-outline",
|
||||
links: [
|
||||
{ label: "Dashboard", link: "/admin/job" },
|
||||
{ label: "Publish", link: "/admin/job/publish/status" },
|
||||
{ label: "Review", link: "/admin/job/review/status" },
|
||||
{ label: "Reject", link: "/admin/job/reject/status" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Forum",
|
||||
icon: "chatbubble-ellipses-outline",
|
||||
links: [
|
||||
{ label: "Dashboard", link: "/admin/forum" },
|
||||
{ label: "Posting", link: "/admin/forum/posting" },
|
||||
{ label: "Report Posting", link: "/admin/forum/report-posting" },
|
||||
{ label: "Report Comment", link: "/admin/forum/report-comment" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Collaboration",
|
||||
icon: "people",
|
||||
links: [
|
||||
{ label: "Dashboard", link: "/admin/collaboration" },
|
||||
{ label: "Publish", link: "/admin/collaboration/publish" },
|
||||
{ label: "Group", link: "/admin/collaboration/group" },
|
||||
{ label: "Reject", link: "/admin/collaboration/reject" },
|
||||
],
|
||||
},
|
||||
{ label: "Maps", icon: "map", link: "/admin/maps" },
|
||||
{
|
||||
label: "App Information",
|
||||
icon: "information-circle",
|
||||
link: "/admin/app-information",
|
||||
},
|
||||
{
|
||||
label: "User Access",
|
||||
icon: "people",
|
||||
link: "/admin/user-access",
|
||||
},
|
||||
{
|
||||
label: "Super Admin",
|
||||
icon: "globe",
|
||||
link: "/admin/super-admin",
|
||||
},
|
||||
];
|
||||
@@ -1,12 +1,45 @@
|
||||
import { BaseBox, MapCustom, StackCustom, TextCustom } from "@/components";
|
||||
import {
|
||||
BaseBox,
|
||||
MapCustom,
|
||||
StackCustom,
|
||||
TextCustom
|
||||
} from "@/components";
|
||||
|
||||
export default function Portofolio_BusinessLocation() {
|
||||
export default function Portofolio_BusinessLocation({
|
||||
data,
|
||||
imageId,
|
||||
setOpenDrawerLocation,
|
||||
}: {
|
||||
data: any;
|
||||
imageId?: string;
|
||||
setOpenDrawerLocation: (value: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<BaseBox>
|
||||
<BaseBox style={{ height: !data ? 200 : "auto" }}>
|
||||
<StackCustom>
|
||||
<TextCustom bold>Lokasi Bisnis</TextCustom>
|
||||
<MapCustom />
|
||||
{!data ? (
|
||||
<TextCustom
|
||||
style={{ paddingTop: 50 }}
|
||||
align="center"
|
||||
color="gray"
|
||||
size={"small"}
|
||||
bold
|
||||
>
|
||||
Lokasi bisnis belum ditambahkan
|
||||
</TextCustom>
|
||||
) : (
|
||||
<MapCustom
|
||||
latitude={data?.latitude}
|
||||
longitude={data?.longitude}
|
||||
namePin={data?.namePin}
|
||||
imageId={imageId}
|
||||
onPress={() => {
|
||||
setOpenDrawerLocation(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</>
|
||||
|
||||
@@ -41,7 +41,11 @@ export default function Portofolio_Data({
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<Ionicons name="home-outline" size={ICON_SIZE_SMALL} color="white" />
|
||||
<Ionicons
|
||||
name="location-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
),
|
||||
label: data && data?.alamatKantor ? data.alamatKantor : "-",
|
||||
},
|
||||
@@ -89,8 +93,6 @@ export default function Portofolio_Data({
|
||||
// },
|
||||
// ];
|
||||
|
||||
// console.log("List Sub Bidang >>", JSON.stringify(listSubBidang, null, 2));
|
||||
|
||||
return (
|
||||
<>
|
||||
<BaseBox>
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import { Ionicons, FontAwesome5, FontAwesome, Fontisto } from "@expo/vector-icons";
|
||||
import {
|
||||
FontAwesome,
|
||||
Fontisto,
|
||||
Ionicons
|
||||
} from "@expo/vector-icons";
|
||||
|
||||
export const drawerItemsPortofolio = ({ id }: { id: string }): IMenuDrawerItem[] => [
|
||||
export const drawerItemsPortofolio = ({
|
||||
id,
|
||||
maps,
|
||||
}: {
|
||||
id: string;
|
||||
maps: any;
|
||||
}): IMenuDrawerItem[] => [
|
||||
{
|
||||
icon: (
|
||||
<Ionicons
|
||||
@@ -45,18 +55,20 @@ export const drawerItemsPortofolio = ({ id }: { id: string }): IMenuDrawerItem[]
|
||||
color={AccentColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Edit Map",
|
||||
path: `/(application)/maps/${id}/edit`,
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<FontAwesome5
|
||||
name="map-pin"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={AccentColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Custom Pin Map",
|
||||
path: `/(application)/maps/${id}/custom-pin`,
|
||||
label: `${!maps ? "Tambah" : "Edit"} Map`,
|
||||
path: !maps
|
||||
? `/(application)/maps/create?id=${id}`
|
||||
: `/(application)/maps/${maps?.id}/edit`,
|
||||
},
|
||||
// {
|
||||
// icon: (
|
||||
// <FontAwesome5
|
||||
// name="map-pin"
|
||||
// size={ICON_SIZE_MEDIUM}
|
||||
// color={AccentColor.white}
|
||||
// />
|
||||
// ),
|
||||
// label: "Custom Pin Map",
|
||||
// path: `/(application)/maps/${id}/custom-pin`,
|
||||
// },
|
||||
];
|
||||
|
||||
10
service/api-admin/api-admin-main-dashboard.ts
Normal file
10
service/api-admin/api-admin-main-dashboard.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { apiConfig } from "../api-config";
|
||||
|
||||
export const apiAdminMainDashboardGetAll = async () => {
|
||||
try {
|
||||
const response = await apiConfig.get(`/mobile/admin/main-dashboard`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
47
service/api-admin/api-admin-user-access.ts
Normal file
47
service/api-admin/api-admin-user-access.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { apiConfig } from "../api-config";
|
||||
|
||||
export const apiAdminUserAccessGetAll = async ({
|
||||
search,
|
||||
category,
|
||||
}: {
|
||||
search?: string;
|
||||
category: "only-user" | "only-admin" | "all-role";
|
||||
}) => {
|
||||
try {
|
||||
const response = await apiConfig.get(`/mobile/admin/user?category=${category}&search=${search}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const apiAdminUserAccessGetById = async ({ id }: { id: string }) => {
|
||||
try {
|
||||
const response = await apiConfig.get(`/mobile/admin/user/${id}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const apiAdminUserAccessUpdateStatus = async ({
|
||||
id,
|
||||
active,
|
||||
role,
|
||||
}: {
|
||||
id: string;
|
||||
active?: boolean;
|
||||
role?: "user" | "admin" | "super_admin";
|
||||
}) => {
|
||||
try {
|
||||
const response = await apiConfig.put(`/mobile/admin/user/${id}`, {
|
||||
data: {
|
||||
active,
|
||||
role,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
41
service/api-client/api-maps.ts
Normal file
41
service/api-client/api-maps.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { apiConfig } from "../api-config";
|
||||
|
||||
export async function apiMapsCreate({ data }: { data: any }) {
|
||||
try {
|
||||
const response = await apiConfig.post(`/mobile/maps`, {
|
||||
data: data,
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function apiMapsGetAll() {
|
||||
try {
|
||||
const response = await apiConfig.get("/mobile/maps");
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function apiMapsGetOne({ id }: { id: string }) {
|
||||
try {
|
||||
const response = await apiConfig.get(`/mobile/maps/${id}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function apiMapsUpdate({ id, data }: { id: string; data: any }) {
|
||||
try {
|
||||
const response = await apiConfig.put(`/mobile/maps/${id}`, {
|
||||
data: data,
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
37
utils/openInDeviceMaps.ts
Normal file
37
utils/openInDeviceMaps.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Platform, Linking, Alert } from "react-native";
|
||||
|
||||
export const openInDeviceMaps = async ({
|
||||
latitude,
|
||||
longitude,
|
||||
title = "Lokasi",
|
||||
}: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
title?: string;
|
||||
}) => {
|
||||
let url = "";
|
||||
|
||||
if (Platform.OS === "ios") {
|
||||
// Apple Maps
|
||||
url = `maps://?q=${encodeURIComponent(title)}&ll=${latitude},${longitude}`;
|
||||
} else {
|
||||
// Android: Google Maps
|
||||
url = `geo:${latitude},${longitude}?q=${latitude},${longitude}(${encodeURIComponent(
|
||||
title
|
||||
)})`;
|
||||
}
|
||||
|
||||
try {
|
||||
const canOpen = await Linking.canOpenURL(url);
|
||||
if (canOpen) {
|
||||
await Linking.openURL(url);
|
||||
} else {
|
||||
// Fallback ke web
|
||||
const webUrl = `https://www.google.com/maps/search/?api=1&query=${latitude},${longitude}`;
|
||||
await Linking.openURL(webUrl);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Gagal membuka Maps:", error);
|
||||
Alert.alert("Error", "Tidak dapat membuka aplikasi Maps.");
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user