Compare commits

...

7 Commits

Author SHA1 Message Date
0770237fe5 Integrasi API: Admin Job
Add:
-  service/api-admin/api-admin-job.ts

Fix:
 modified:   app/(application)/admin/job/[status]/status.tsx
        modified:   app/(application)/admin/job/index.tsx
        modified:   components/_ShareComponent/SearchInput.tsx

### No issue
2025-10-16 16:44:43 +08:00
6f4dd79568 Integrasi API: Admin Collaboration
Fix: Tampilan yang terintegrasi API
 - app/(application)/(user)/collaboration/[id]/edit.tsx
 - app/(application)/admin/collaboration/[id]/[status].tsx
 - app/(application)/admin/collaboration/[id]/group.tsx
 - app/(application)/admin/collaboration/[id]/reject-input.tsx
 - app/(application)/admin/collaboration/group.tsx
 - app/(application)/admin/collaboration/index.tsx
 - app/(application)/admin/collaboration/publish.tsx
 - app/(application)/admin/collaboration/reject.tsx
 - components/_ShareComponent/Admin/TableValue.tsx
 - screens/Collaboration/BoxPublishSection.tsx
 - service/api-admin/api-admin-collaboration.ts

### No Issue
2025-10-16 14:48:31 +08:00
9faa0b0f64 Collaboration Mobile API
Add:
- /admin/collaboration/[id]/reject-input.tsx
- /api-admin/api-admin-collaboration.ts

Fix:
- Integrasi ke tampilan UI
app/(application)/admin/collaboration/[id]/[status].tsx
app/(application)/admin/collaboration/index.tsx
app/(application)/admin/collaboration/publish.tsx
components/_ShareComponent/Admin/TableTitle.tsx
components/_ShareComponent/Admin/TableValue.tsx

### No Issue
2025-10-15 17:30:25 +08:00
e05a7c8701 Portofolio
Fix:
(application)/(user)/portofolio/[id]/index.
Button hapus hanya muncul jika user login adalah author

### No Issue
2025-10-15 15:45:01 +08:00
f50c5099d8 Integrasi API: App Information & Maps
Add:
- service/api-admin/api-master-admin.ts

Fix:
app/(application)/admin/app-information/business-field/[id]/index.tsx
app/(application)/admin/app-information/business-field/create.tsx
app/(application)/admin/app-information/index.tsx
app/(application)/admin/app-information/information-bank/[id]/index.tsx
app/(application)/admin/app-information/information-bank/create.tsx
app/(application)/admin/maps.tsx
screens/Admin/App-Information/BusinessFieldSection.tsx
screens/Admin/App-Information/InformationBankSection.tsx
screens/Admin/App-Information/StickerSection.tsx
screens/Authentication/LoginView.tsx
service/api-client/api-master.ts

- Perbaikan berupa integrasi API

### No Issue
2025-10-15 15:09:18 +08:00
5f36620988 Integrasi Admin: User Acces & Super Admin
Add:
- admin/super-admin/
- admin/user-access/
- service/api-admin/

Fix:
- (user)/profile/[id]/index: penambahan useData dari useAuthuntuk merestart value masterRole
- integrasi pada tampilan admin

### No Issue
2025-10-14 17:28:40 +08:00
f750d158be Integrasi Map Business
Add:
 components/Map/MapSelected.tsx
        components/_ShareComponent/GridTwoView.tsx
        service/api-client/api-maps.ts
utils/openInDeviceMaps.ts

Fix:
 modified:   app/(application)/(user)/maps/[id]/edit.tsx
        modified:   app/(application)/(user)/maps/create.tsx
        modified:   app/(application)/(user)/maps/index.tsx
        modified:   app/(application)/(user)/portofolio/[id]/index.tsx
        modified:   components/Map/MapCustom.tsx
        modified:   screens/Portofolio/BusinessLocationSection.tsx
        modified:   screens/Portofolio/DataPortofolio.tsx
        modified:   screens/Portofolio/ListPage.tsx

### No issue
2025-10-13 17:46:47 +08:00
55 changed files with 3324 additions and 714 deletions

View File

@@ -19,7 +19,6 @@ import Toast from "react-native-toast-message";
export default function CollaborationEdit() {
const { id } = useLocalSearchParams();
console.log("id :", id);
const [data, setData] = useState<any>();
const [listMaster, setListMaster] = useState<any[]>([]);
const [loadingData, setLoadingData] = useState(false);

View File

@@ -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,
},
});

View File

@@ -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

View File

@@ -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>
</>
);
}

View File

@@ -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,15 +94,21 @@ 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}
/>
<Portofolio_ButtonDelete
id={id as string}
isLoadingDelete={isLoadingDelete}
setIsLoadingDelete={setIsLoadingDelete}
/>
{data?.Profile?.id !== profileId ? null : (
<Portofolio_ButtonDelete
id={id as string}
isLoadingDelete={isLoadingDelete}
setIsLoadingDelete={setIsLoadingDelete}
/>
)}
<Spacing />
</StackCustom>
)}
@@ -110,10 +121,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>
</>
);
}

View File

@@ -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}>

View File

@@ -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",

View File

@@ -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

View File

@@ -1,18 +1,89 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { router } from "expo-router";
import { MainColor } from "@/constants/color-palet";
import {
apiAdminMasterBusinessFieldById,
apiAdminMasterBusinessFieldUpdate,
} from "@/service/api-admin/api-master-admin";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
import { Switch } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function AdminAppInformation_BusinessFieldDetail() {
const { id } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
const [isLoading, setIsLoading] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadDetail();
}, [id])
);
const onLoadDetail = async () => {
try {
const response = await apiAdminMasterBusinessFieldById({
id: id as string,
});
setData(response.data);
} catch (error) {
console.log("[ERROR]", error);
setData(null);
}
};
const handlerSubmit = async () => {
if (!data.name) {
Toast.show({
type: "error",
text1: "Lengkapi Data",
});
return;
}
try {
setIsLoading(true);
const response = await apiAdminMasterBusinessFieldUpdate({
id: id as string,
data: data,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Gagal update data",
});
return;
}
Toast.show({
type: "success",
text1: "Data berhasil di update",
});
router.back();
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => router.back()}
disabled={!data?.name}
isLoading={isLoading}
onPress={() => handlerSubmit()}
>
Update
</ButtonCustom>
@@ -28,6 +99,15 @@ export default function AdminAppInformation_BusinessFieldDetail() {
label="Nama Bidang Bisnis"
placeholder="Masukan Nama Bidang Bisnis"
required
value={data?.name}
onChangeText={(value) => setData({ ...data, name: value })}
/>
<TextCustom>Status Aktivasi</TextCustom>
<Switch
color={MainColor.yellow}
value={data?.active}
onValueChange={(value) => setData({ ...data, active: value })}
/>
</StackCustom>
</ViewWrapper>

View File

@@ -6,13 +6,52 @@ import {
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { apiAdminMasterBusinessFieldCreate } from "@/service/api-admin/api-master-admin";
import { router } from "expo-router";
import { useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminAppInformation_BusinessFieldCreate() {
const [data, setData] = useState<any>({
name: "",
});
const [isLoading, setIsLoading] = useState(false);
const handlerSubmit = async () => {
if (!data.name) {
Toast.show({
type: "error",
text1: "Lengkapi Data",
});
return;
}
try {
setIsLoading(true);
const response = await apiAdminMasterBusinessFieldCreate({ data: data });
if (response.success) {
Toast.show({
type: "success",
text1: "Data berhasil di tambah",
});
router.back();
}
} catch (error) {
console.log("[ERROR]", error);
Toast.show({
type: "error",
text1: "Gagal tambah data",
});
} finally {
setIsLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => router.back()}
onPress={() => handlerSubmit()}
isLoading={isLoading}
>
Tambah
</ButtonCustom>
@@ -28,6 +67,8 @@ export default function AdminAppInformation_BusinessFieldCreate() {
label="Nama Bidang Bisnis"
placeholder="Masukan Nama Bidang Bisnis"
required
value={data.name}
onChangeText={(value) => setData({ ...data, name: value })}
/>
</StackCustom>
</ViewWrapper>

View File

@@ -1,8 +1,4 @@
import {
ScrollableCustom,
StackCustom,
ViewWrapper
} from "@/components";
import { ScrollableCustom, StackCustom, ViewWrapper } from "@/components";
import AdminActionIconPlus from "@/components/_ShareComponent/Admin/ActionIconPlus";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminAppInformation_BusinessFieldSection from "@/screens/Admin/App-Information/BusinessFieldSection";
@@ -10,6 +6,7 @@ import AdminAppInformation_Bank from "@/screens/Admin/App-Information/Informatio
import AdminAppInformation_StickerSection from "@/screens/Admin/App-Information/StickerSection";
import { router } from "expo-router";
import { useState } from "react";
import { Alert } from "react-native";
export default function AdminInformation() {
const [activeCategory, setActiveCategory] = useState<string | null>("bank");
@@ -57,7 +54,8 @@ export default function AdminInformation() {
} else if (activeCategory === "business") {
router.push("/admin/app-information/business-field/create");
} else if (activeCategory === "sticker") {
router.push("/admin/app-information/sticker/create");
Alert.alert("Coming Soon", "Next Update");
// router.push("/admin/app-information/sticker/create");
}
}}
/>

View File

@@ -1,18 +1,90 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextInputCustom,
ViewWrapper,
BoxButtonOnFooter,
ButtonCustom,
CenterCustom,
Grid,
StackCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { router } from "expo-router";
import { MainColor } from "@/constants/color-palet";
import {
apiAdminMasterBankById,
apiAdminMasterBankUpdate,
} from "@/service/api-admin/api-master-admin";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
import { Switch } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function AdminAppInformation_BankDetail() {
const { id } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
const [isLoading, setLoading] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadList();
}, [id])
);
const onLoadList = async () => {
try {
const response = await apiAdminMasterBankById({ id: id as string });
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const handlerUpdate = async () => {
if (!data.namaBank || !data.namaAkun || !data.norek) {
Toast.show({
type: "error",
text1: "Lengkapi Data",
});
return;
}
try {
setLoading(true);
const response = await apiAdminMasterBankUpdate({
id: id as string,
data: data,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Gagal update data",
});
} else {
Toast.show({
type: "success",
text1: "Success",
text2: "Data berhasil di update",
});
router.back();
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => router.back()}
disabled={!data?.namaBank || !data?.namaAkun || !data?.norek}
isLoading={isLoading}
onPress={() => handlerUpdate()}
>
Update
</ButtonCustom>
@@ -25,22 +97,46 @@ export default function AdminAppInformation_BankDetail() {
<AdminBackButtonAntTitle title="Update Bank" />
<StackCustom>
<TextInputCustom
label="Nama Bank"
placeholder="Masukan Nama Bank"
required
/>
<Grid>
<Grid.Col span={6}>
<TextInputCustom
label="Nama Bank"
placeholder="Masukan Nama Bank"
required
value={data?.namaBank}
onChangeText={(value) =>
setData({ ...data, namaBank: value })
}
/>
</Grid.Col>
<Grid.Col span={6} style={{ alignItems: "center" }}>
<TextCustom>Status Aktivasi</TextCustom>
<CenterCustom>
<Switch
onValueChange={(value) =>
setData({ ...data, isActive: value })
}
color={MainColor.yellow}
value={data?.isActive}
/>
</CenterCustom>
</Grid.Col>
</Grid>
<TextInputCustom
label="Nama Rekening"
placeholder="Masukan Nama Rekening"
required
value={data?.namaAkun}
onChangeText={(value) => setData({ ...data, namaAkun: value })}
/>
<TextInputCustom
label="Nomor Rekening"
placeholder="Masukan Nomor Rekening"
required
value={data?.norek}
onChangeText={(value) => setData({ ...data, norek: value })}
/>
</StackCustom>
</StackCustom>

View File

@@ -1,19 +1,51 @@
import {
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextInputCustom,
ViewWrapper
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { apiAdminMasterBankCreate } from "@/service/api-admin/api-master-admin";
import { router } from "expo-router";
import { useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminAppInformation_BankCreate() {
const [data, setData] = useState<any>({
namaBank: "",
namaAkun: "",
norek: "",
});
const [isLoading, setLoading] = useState(false);
const handlerSubmit = async () => {
try {
setLoading(true);
const response = await apiAdminMasterBankCreate({ data: data });
if (response.success) {
Toast.show({
type: "success",
text1: "Data berhasil di tambah",
});
router.back();
}
} catch (error) {
console.log("[ERROR]", error);
Toast.show({
type: "error",
text1: "Gagal tambah data",
});
} finally {
setLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => router.back()}
>
<ButtonCustom isLoading={isLoading} onPress={handlerSubmit}>
Tambah
</ButtonCustom>
</BoxButtonOnFooter>
@@ -29,18 +61,25 @@ export default function AdminAppInformation_BankCreate() {
label="Nama Bank"
placeholder="Masukan Nama Bank"
required
value={data.namaBank}
onChangeText={(value) => setData({ ...data, namaBank: value })}
/>
<TextInputCustom
label="Nama Rekening"
placeholder="Masukan Nama Rekening"
required
value={data.namaAkun}
onChangeText={(value) => setData({ ...data, namaAkun: value })}
/>
<TextInputCustom
keyboardType="numeric"
label="Nomor Rekening"
placeholder="Masukan Nomor Rekening"
required
value={data.norek}
onChangeText={(value) => setData({ ...data, norek: value })}
/>
</StackCustom>
</StackCustom>

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BaseBox,
BoxButtonOnFooter,
@@ -8,18 +9,45 @@ import {
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
import { MainColor } from "@/constants/color-palet";
import { useLocalSearchParams } from "expo-router";
import { apiAdminCollaborationGetById } from "@/service/api-admin/api-admin-collaboration";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminCollaborationPublish() {
const { id, status } = useLocalSearchParams();
console.log("params:", id, status);
const bottomFooter = (
const [data, setData] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
handlerLoadData();
}, [status])
);
const handlerLoadData = async () => {
try {
const response = await apiAdminCollaborationGetById({
id: id as string,
category: status as any,
});
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const bottomFooter = status === "publish" && (
<BoxButtonOnFooter>
<ButtonCustom
backgroundColor={MainColor.red}
textColor="white"
onPress={() => {}}
onPress={() => {
router.push(`/admin/collaboration/${id}/reject-input`);
}}
>
Reject
</ButtonCustom>
@@ -29,12 +57,12 @@ export default function AdminCollaborationPublish() {
return (
<>
<ViewWrapper
headerComponent={<AdminBackButtonAntTitle title={`Detail ${status}`} />}
headerComponent={<AdminBackButtonAntTitle title={`Detail`} />}
footerComponent={bottomFooter}
>
<BaseBox>
<StackCustom>
{listData.map((item, i) => (
{listData(data)?.map((item, i) => (
<Grid key={i}>
<Grid.Col
span={4}
@@ -49,41 +77,49 @@ export default function AdminCollaborationPublish() {
))}
</StackCustom>
</BaseBox>
{data?.report && (
<BaseBox>
<GridTwoView
spanLeft={4}
spanRight={8}
leftIcon={<TextCustom bold>Catatan report</TextCustom>}
rightIcon={<TextCustom>{data?.report}</TextCustom>}
/>
</BaseBox>
)}
</ViewWrapper>
</>
);
}
const listData = [
const listData = (data: any) => [
{
label: "Username",
value: "Bagas Banuna",
value: (data && data?.Author?.username) || "-",
},
{
label: "Judul Proyek",
value:
"Judul Proyek: Lorem ipsum dolor sit amet consectetur adipisicing elit.",
value: (data && data?.title) || "-",
},
{
label: "Industri",
value: "Kesehatan",
value: (data && data?.ProjectCollaborationMaster_Industri?.name) || "-",
},
{
label: "Jumlah Partisipan ",
value: "0",
value: (data && data?.ProjectCollaboration_Partisipasi.length) || "0",
},
{
label: "Lokasi",
value: "Kuta Selatan, Bali",
value: (data && data?.lokasi) || "-",
},
{
label: "Tujuan Proyek",
value:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
value: (data && data?.purpose) || "-",
},
{
label: "Keuntungan",
value:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
value: (data && data?.benefit) || "-",
},
];

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BaseBox,
Grid,
@@ -6,23 +7,45 @@ import {
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { useLocalSearchParams } from "expo-router";
import { apiAdminCollaborationGetById } from "@/service/api-admin/api-admin-collaboration";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminCollaborationGroup() {
const { id } = useLocalSearchParams();
console.log("params:", id);
const [data, setData] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
const response = await apiAdminCollaborationGetById({
id: id as string,
category: "group",
});
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
return (
<>
<ViewWrapper
headerComponent={
<AdminBackButtonAntTitle title={`Detail Group ${id}`} />
}
headerComponent={<AdminBackButtonAntTitle title={`Detail Group`} />}
>
<StackCustom gap={"xs"}>
<BaseBox>
<StackCustom>
{listData.map((item, i) => (
<Grid key={i}>
{listData(data).map((item: any, index: number) => (
<Grid key={index}>
<Grid.Col
span={4}
style={{ justifyContent: "center", paddingRight: 10 }}
@@ -40,21 +63,36 @@ export default function AdminCollaborationGroup() {
<StackCustom>
<TextCustom align="center">Anggota</TextCustom>
{Array.from({ length: 10 }).map((_, i) => (
<Grid key={i}>
<Grid.Col
span={4}
style={{ justifyContent: "center", paddingRight: 10 }}
>
<TextCustom bold>Username</TextCustom>
</Grid.Col>
<Grid.Col span={8} style={{ justifyContent: "center" }}>
<TextCustom>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
</TextCustom>
</Grid.Col>
</Grid>
))}
{data?.ProjectCollaboration_AnggotaRoomChat?.map(
(item: any, index: number) => (
<StackCustom key={index} gap={0}>
<Grid>
<Grid.Col
span={4}
style={{ justifyContent: "center", paddingRight: 10 }}
>
<TextCustom bold>Nama</TextCustom>
</Grid.Col>
<Grid.Col span={8} style={{ justifyContent: "center" }}>
<TextCustom>
{item?.User?.Profile?.name || "-"}
</TextCustom>
</Grid.Col>
</Grid>
<Grid>
<Grid.Col
span={4}
style={{ justifyContent: "center", paddingRight: 10 }}
>
<TextCustom bold>Username</TextCustom>
</Grid.Col>
<Grid.Col span={8} style={{ justifyContent: "center" }}>
<TextCustom>{item?.User?.username || "-"}</TextCustom>
</Grid.Col>
</Grid>
</StackCustom>
)
)}
</StackCustom>
</BaseBox>
</StackCustom>
@@ -63,35 +101,35 @@ export default function AdminCollaborationGroup() {
);
}
const listData = [
const listData = (data: any) => [
{
label: "Admin Group",
value: "Bagas Banuna",
value: data?.ProjectCollaboration?.Author?.username || "-",
},
{
label: "Nama Group",
value: "Lorem ipsum dolor sit amet consectetur adipisicing elit.",
value: data?.name || "-",
},
{
label: "Industri",
value: "Kesehatan",
value:
data?.ProjectCollaboration?.ProjectCollaborationMaster_Industri?.name ||
"-",
},
{
label: "Jumlah Partisipan ",
value: "0",
value: data?.ProjectCollaboration_AnggotaRoomChat?.length || "-",
},
{
label: "Lokasi",
value: "Kuta Selatan, Bali",
value: data?.ProjectCollaboration?.lokasi || "-",
},
{
label: "Tujuan Proyek",
value:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
value: data?.ProjectCollaboration?.purpose || "-",
},
{
label: "Keuntungan",
value:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
value: data?.ProjectCollaboration?.benefit || "-",
},
];

View File

@@ -0,0 +1,83 @@
import {
AlertDefaultSystem,
BoxButtonOnFooter,
TextAreaCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import { apiAdminCollaborationReject } from "@/service/api-admin/api-admin-collaboration";
import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminCollaborationRejectInput() {
const { id } = useLocalSearchParams();
const [value, setValue] = useState("");
const handlerReject = async () => {
console.log("value:", value);
// router.replace(`/admin/job/reject/status`);
try {
const response = await apiAdminCollaborationReject({
id: id as string,
data: value,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Report gagal",
});
}
Toast.show({
type: "success",
text1: "Report berhasil",
});
router.back();
} catch (error) {
console.log("[ERROR]", error);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<AdminButtonReject
title="Reject"
onReject={() =>
AlertDefaultSystem({
title: "Reject",
message: "Apakah anda yakin ingin menolak data ini?",
textLeft: "Batal",
textRight: "Ya",
onPressRight: () => {
handlerReject();
},
})
}
/>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper
footerComponent={buttonSubmit}
headerComponent={
<AdminBackButtonAntTitle title="Penolakan Collaboration" />
}
>
<TextAreaCustom
value={value}
onChangeText={setValue}
placeholder="Masukan alasan"
required
showCount
maxLength={1000}
/>
</ViewWrapper>
</>
);
}

View File

@@ -1,67 +1,101 @@
import {
ActionIcon,
BaseBox,
Spacing,
LoaderCustom,
StackCustom,
TextCustom,
ViewWrapper,
ViewWrapper
} from "@/components";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
import { Octicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { View } from "react-native";
import { Divider } from "react-native-paper";
export default function AdminCollaborationGroup() {
const [list, setList] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState(false);
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [])
);
const handlerLoadList = async () => {
try {
setLoadList(true);
const response = await apiAdminCollaboration({
category: "group",
});
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
return (
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
<StackCustom gap={"xs"}>
<StackCustom>
<AdminComp_BoxTitle title="Group" />
<BaseBox>
<>
<AdminTitleTable
title1="Aksi"
title2="Admin Group"
title3="Nama Group"
title2="Jumlah peserta"
title3="Nama group"
/>
<Spacing height={10} />
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
{loadList ? (
<LoaderCustom />
) : _.isEmpty(list) ? (
<TextCustom align="center" color="gray">
Belum ada data
</TextCustom>
) : (
list?.map((item: any, index: number) => (
<View key={index}>
<AdminTableValue
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/collaboration/${item.id}/group`);
}}
/>
}
onPress={() => {
router.push(`/admin/collaboration/${index}/group`);
}}
value2={
<TextCustom truncate={1}>
{item?.ProjectCollaboration_AnggotaRoomChat?.length ||
"-"}
</TextCustom>
}
value3={
<TextCustom truncate={2}>{item?.name || "-"}</TextCustom>
}
/>
}
value2={
<TextCustom truncate={1}>Username username </TextCustom>
}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae
mollitia.
</TextCustom>
}
/>
))}
</BaseBox>
</View>
))
)}
</>
</StackCustom>
</ViewWrapper>
</>

View File

@@ -2,14 +2,41 @@ import { StackCustom, ViewWrapper } from "@/components";
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { MainColor } from "@/constants/color-palet";
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
import { Entypo, FontAwesome } from "@expo/vector-icons";
import { useFocusEffect } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminCollaboration() {
const [list, setList] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [])
);
const handlerLoadList = async () => {
try {
const response = await apiAdminCollaboration({
category: "dashboard",
});
console.log("[RESPONSE]", JSON.stringify(response, null, 2));
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
return (
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
<StackCustom gap={"xs"}>
{listData.map((item, i) => (
{listData(list as any).map((item, i) => (
<AdminComp_BoxDashboard key={i} item={item} />
))}
</StackCustom>
@@ -18,20 +45,23 @@ export default function AdminCollaboration() {
);
}
const listData = [
{
label: "Publish",
value: 4,
icon: <Entypo name="publish" size={25} color={MainColor.green} />,
},
{
label: "Group",
value: 7,
icon: <FontAwesome name="group" size={25} color={MainColor.yellow} />,
},
{
label: "Reject",
value: 7,
icon: <FontAwesome name="warning" size={25} color={MainColor.red} />,
},
];
const listData = (list: any) => {
console.log("[LIST masuk]", JSON.stringify(list, null, 2));
return [
{
label: "Publish",
value: (list && list?.publish) || "0",
icon: <Entypo name="publish" size={25} color={MainColor.green} />,
},
{
label: "Group",
value: (list && list?.group) || "0",
icon: <FontAwesome name="group" size={25} color={MainColor.yellow} />,
},
{
label: "Reject",
value: (list && list?.reject) || "0",
icon: <FontAwesome name="warning" size={25} color={MainColor.red} />,
},
];
};

View File

@@ -1,7 +1,6 @@
import {
ActionIcon,
BaseBox,
Spacing,
LoaderCustom,
StackCustom,
TextCustom,
ViewWrapper,
@@ -11,60 +10,95 @@ import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
import { Octicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { View } from "react-native";
import { Divider } from "react-native-paper";
export default function AdminCollaborationPublish() {
const [list, setList] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState(false);
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [])
);
const handlerLoadList = async () => {
try {
setLoadList(true);
const response = await apiAdminCollaboration({
category: "publish",
});
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
return (
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
<StackCustom gap={"xs"}>
<StackCustom>
<AdminComp_BoxTitle title="Publish" />
<BaseBox>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Proyek"
/>
<Spacing height={10} />
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/collaboration/${index}/publish`);
}}
/>
}
value2={
<TextCustom truncate={1}>Username username </TextCustom>
}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae
mollitia.
</TextCustom>
}
/>
))}
</BaseBox>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Proyek"
/>
{/* <Spacing height={10} /> */}
<Divider />
{loadList ? (
<LoaderCustom />
) : _.isEmpty(list) ? (
<TextCustom align="center" color="gray">
Belum ada data
</TextCustom>
) : (
list?.map((item: any, index: number) => (
<View key={index}>
<AdminTableValue
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/collaboration/${item?.id}/publish`);
}}
/>
}
value2={
<TextCustom align="center" truncate={1}>
{item?.Author?.username || "-"}{" "}
</TextCustom>
}
value3={
<TextCustom align="center" truncate={2}>
{item?.title || "-"}
</TextCustom>
}
/>
</View>
))
)}
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -1,67 +1,99 @@
import {
ActionIcon,
BaseBox,
Spacing,
LoaderCustom,
StackCustom,
TextCustom,
ViewWrapper,
ViewWrapper
} from "@/components";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
import { Octicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { View } from "react-native";
import { Divider } from "react-native-paper";
export default function AdminCollaborationReject() {
const [list, setList] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState(false);
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [])
);
const handlerLoadList = async () => {
try {
setLoadList(true);
const response = await apiAdminCollaboration({
category: "reject",
});
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
return (
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
<StackCustom gap={"xs"}>
<StackCustom>
<AdminComp_BoxTitle title="Reject" />
<BaseBox>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Proyek"
/>
<Spacing height={10} />
<Divider />
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Proyek"
/>
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/collaboration/${index}/reject`);
}}
/>
}
value2={
<TextCustom truncate={1}>Username username </TextCustom>
}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae
mollitia.
</TextCustom>
}
/>
))}
</BaseBox>
{loadList ? (
<LoaderCustom />
) : _.isEmpty(list) ? (
<TextCustom>Belum ada data</TextCustom>
) : (
list?.map((item: any) => (
<View key={item.id}>
<AdminTableValue
key={item?.id}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/collaboration/${item?.id}/reject`);
}}
/>
}
value2={
<TextCustom align="center" truncate={1}>
{item?.Author?.username || "-"}
</TextCustom>
}
value3={
<TextCustom align="center" truncate={2}>
{item?.title || "-"}
</TextCustom>
}
/>
</View>
))
)}
</StackCustom>
</ViewWrapper>
</>

View File

@@ -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} />
),

View File

@@ -1,8 +1,9 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
BaseBox,
LoaderCustom,
SearchInput,
Spacing,
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
@@ -11,17 +12,52 @@ import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminJob } from "@/service/api-admin/api-admin-job";
import { Octicons } from "@expo/vector-icons";
import { router, useLocalSearchParams } from "expo-router";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
export default function AdminJobStatus() {
const { status } = useLocalSearchParams();
console.log("[STATUS]", status);
const [list, setList] = useState<any | null>(null);
const [loadList, setLoadList] = useState(false);
const [search, setSearch] = useState("");
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [status, search])
);
const handlerLoadList = async () => {
try {
setLoadList(true);
const response = await apiAdminJob({
category: status as "publish" | "review" | "reject",
search,
});
console.log("[RESPONSE >>]", JSON.stringify(response, null, 2));
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
const rightComponent = (
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari"
onChangeText={setSearch}
value={search}
/>
);
return (
@@ -32,44 +68,53 @@ export default function AdminJobStatus() {
rightComponent={rightComponent}
/>
<BaseBox>
<StackCustom>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Pekerjaan"
/>
<Spacing />
{/* <Spacing /> */}
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/job/${index}/${status}`);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae mollitia.
</TextCustom>
}
/>
))}
</BaseBox>
{loadList ? (
<LoaderCustom />
) : _.isEmpty(list) ? (
<TextCustom align="center" color="gray">
Tidak ada data
</TextCustom>
) : (
list?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/job/${item.id}/${status}`);
}}
/>
}
value2={
<TextCustom align="center" truncate={1}>
{item?.Author?.username || "-"}
</TextCustom>
}
value3={
<TextCustom truncate={2} align="center">
{item?.title || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Spacing, StackCustom, ViewWrapper } from "@/components";
import {
IconPublish,
@@ -7,15 +8,45 @@ import {
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { MainColor } from "@/constants/color-palet";
import { apiAdminJob } from "@/service/api-admin/api-admin-job";
import { useFocusEffect } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminJob() {
const [data, setData] = useState<any | null>(null);
const [loadList, setLoadList] = useState(false);
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [])
);
const handlerLoadList = async () => {
try {
setLoadList(true);
const response = await apiAdminJob({
category: "dashboard",
});
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
return (
<>
<ViewWrapper>
<AdminTitlePage title="Job Vacancy" />
<Spacing />
<StackCustom gap={"xs"}>
{listData.map((item, i) => (
{listData(data).map((item: any, i: number) => (
<AdminComp_BoxDashboard key={i} item={item} />
))}
</StackCustom>
@@ -24,20 +55,20 @@ export default function AdminJob() {
);
}
const listData = [
const listData = (data: any) => [
{
label: "Publish",
value: 4,
value: (data && data?.publish) || 0,
icon: <IconPublish size={25} color={MainColor.green} />,
},
{
label: "Review",
value: 7,
value: (data && data?.review) || 0,
icon: <IconReview size={25} color={MainColor.orange} />,
},
{
label: "Reject",
value: 5,
value: (data && data?.reject) || 0,
icon: <IconReject size={25} color={MainColor.red} />,
},
];

View File

@@ -1,11 +1,224 @@
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, { 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 AdminMaps() {
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>
<MapCustom height={"100%"} />
<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>
<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>
</>
);
}

View 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}`) || "-",
},
];

View 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>
</>
);
}

View File

@@ -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>
</>
);
}

View 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}`) || "-",
},
];

View 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>
</>
);
}

View File

@@ -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) {

View File

@@ -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>
);

View 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;

View File

@@ -13,14 +13,32 @@ export default function AdminTitleTable({
return (
<>
<Grid>
<Grid.Col span={3} style={{ alignItems: "center", justifyContent: "center" }}>
<TextCustom bold align="center">{title1}</TextCustom>
<Grid.Col
span={3}
style={{
alignItems: "center",
justifyContent: "center",
paddingLeft: 5,
paddingRight: 5,
}}
>
<TextCustom truncate bold align="center">
{title1}
</TextCustom>
</Grid.Col>
<Grid.Col span={3} style={{ alignItems: "center", justifyContent: "center" }}>
<TextCustom bold align="center">{title2}</TextCustom>
<Grid.Col
span={3}
style={{ alignItems: "center", justifyContent: "center", paddingLeft: 5, paddingRight: 5 }}
>
<TextCustom truncate bold align="center">
{title2}
</TextCustom>
</Grid.Col>
<Grid.Col span={6} style={{ alignItems: "center", justifyContent: "center" }}>
<TextCustom bold align="center">{title3}</TextCustom>
<Grid.Col
span={6}
style={{ alignItems: "center", justifyContent: "center", paddingLeft: 5, paddingRight: 5 }}
>
<TextCustom truncate bold align="center">{title3}</TextCustom>
</Grid.Col>
</Grid>
</>

View File

@@ -7,10 +7,12 @@ export default function AdminTableValue({
value1,
value2,
value3,
bottomLine = false,
}: {
value1: React.ReactNode;
value2: React.ReactNode;
value3: React.ReactNode;
bottomLine?: boolean;
}) {
return (
<>
@@ -21,8 +23,8 @@ export default function AdminTableValue({
style={{
alignItems: "center",
justifyContent: "center",
paddingLeft: 5,
paddingRight: 5,
paddingLeft: 10,
paddingRight: 10,
}}
>
{value1}
@@ -32,8 +34,8 @@ export default function AdminTableValue({
style={{
alignItems: "center",
justifyContent: "center",
paddingLeft: 5,
paddingRight: 5,
paddingLeft: 10,
paddingRight: 10,
}}
>
{value2}
@@ -42,14 +44,15 @@ export default function AdminTableValue({
span={6}
style={{
justifyContent: "center",
paddingLeft: 5,
paddingRight: 5,
alignItems: "center",
paddingLeft: 10,
paddingRight: 10,
}}
>
{value3}
</Grid.Col>
</Grid>
<Divider />
{bottomLine && <Divider />}
</View>
</>
);

View 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>
);
}

View File

@@ -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>

View File

@@ -2,7 +2,7 @@ import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import TextInputCustom from "../TextInput/TextInputCustom";
import { Ionicons } from "@expo/vector-icons";
import { StyleProp, ViewStyle, TextStyle } from "react-native";
import { StyleProp, ViewStyle, TextStyle, StyleSheet } from "react-native";
interface SearchInputProps {
placeholder?: string;
@@ -12,6 +12,8 @@ interface SearchInputProps {
containerStyle?: StyleProp<ViewStyle>;
style?: StyleProp<TextStyle>;
onChangeText?: (value: string) => void;
value?: string;
disabled?: boolean;
}
export default function SearchInput({
placeholder,
@@ -21,6 +23,8 @@ export default function SearchInput({
containerStyle,
style,
onChangeText,
value,
disabled,
...props
}: SearchInputProps) {
return (
@@ -29,14 +33,21 @@ export default function SearchInput({
<Ionicons
name="search-outline"
size={ICON_SIZE_SMALL}
color={MainColor.placeholder}
color={disabled ? MainColor.white_gray : MainColor.placeholder}
/>
}
value={value}
onChangeText={onChangeText}
placeholder={placeholder}
borderRadius={50}
containerStyle={[containerStyle, { marginBottom: 0 }]}
containerStyle={[disabled ? styleses.disabled : styleses.containerStyle]}
disabled={disabled}
{...props}
/>
);
}
const styleses = StyleSheet.create({
containerStyle: { width: "100%", marginBottom: 0 },
disabled: { width: "100%", marginBottom: 0, color: MainColor.white_gray },
});

View File

@@ -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}`,
},

View File

@@ -1,29 +1,57 @@
import {
ActionIcon,
BadgeCustom,
CenterCustom,
Grid,
LoaderCustom,
StackCustom,
TextCustom
TextCustom,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { AccentColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import dummyMasterBidangBisnis from "@/lib/dummy-data/master-bidang-bisnis";
import { apiAdminMasterBusinessField } from "@/service/api-admin/api-master-admin";
import { FontAwesome5 } from "@expo/vector-icons";
import { router } from "expo-router";
import { useState } from "react";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { View } from "react-native";
import { Divider, Switch } from "react-native-paper";
import { Divider } from "react-native-paper";
export default function AdminAppInformation_BusinessFieldSection() {
const [value, setValue] = useState(false);
const [selectedBusinessField, setSelectedBusinessField] = useState<any>(null);
const [listData, setListData] = useState<any[] | null>(null);
const [loadData, setLoadData] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadList();
}, [])
);
const onLoadList = async () => {
try {
setLoadData(true);
const response = await apiAdminMasterBusinessField();
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR LIST BUSINESS FIELD]", error);
setListData([]);
} finally {
setLoadData(false);
}
};
return (
<>
<>
<StackCustom>
<Grid>
<Grid.Col span={3} style={{ alignItems: "center" }}>
<Grid.Col span={2} style={{ alignItems: "center" }}>
<TextCustom bold>Aksi</TextCustom>
</Grid.Col>
<Grid.Col span={3} style={{ alignItems: "center" }}>
<Grid.Col span={4} style={{ alignItems: "center" }}>
<TextCustom bold>Status</TextCustom>
</Grid.Col>
<Grid.Col span={6}>
@@ -33,51 +61,54 @@ export default function AdminAppInformation_BusinessFieldSection() {
<Divider />
<StackCustom>
{dummyMasterBidangBisnis.map((e, i) => (
<View key={i}>
<Grid>
<Grid.Col span={3} style={{ alignItems: "center" }}>
<ActionIcon
icon={
<FontAwesome5
name="edit"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/app-information/business-field/${i}`);
}}
/>
</Grid.Col>
<Grid.Col
span={3}
style={{ alignItems: "center", justifyContent: "center" }}
>
<Switch
value={i === selectedBusinessField}
onValueChange={() => {
setValue(!value);
setSelectedBusinessField(i);
}}
theme={{
colors: {
primary: MainColor.yellow,
},
}}
/>
</Grid.Col>
<Grid.Col span={6} style={{ justifyContent: "center" }}>
<TextCustom>{e.name}</TextCustom>
</Grid.Col>
</Grid>
<Divider />
</View>
))}
</StackCustom>
</>
{loadData ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center">Tidak ada data</TextCustom>
) : (
<StackCustom>
{listData?.map((item: any, index: number) => (
<View key={index}>
<Grid>
<Grid.Col span={2} style={{ alignItems: "center" }}>
<ActionIcon
icon={
<FontAwesome5
name="edit"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(
`/admin/app-information/business-field/${item.id}`
);
}}
/>
</Grid.Col>
<Grid.Col
span={4}
style={{ alignItems: "center", justifyContent: "center" }}
>
<CenterCustom>
<BadgeCustom
color={
item.active ? AccentColor.blue : AccentColor.blackgray
}
>
{item.active ? "Aktif" : "Tidak Aktif"}
</BadgeCustom>
</CenterCustom>
</Grid.Col>
<Grid.Col span={6} style={{ justifyContent: "center" }}>
<TextCustom>{item.name}</TextCustom>
</Grid.Col>
</Grid>
</View>
))}
</StackCustom>
)}
</StackCustom>
</>
);
}

View File

@@ -1,77 +1,119 @@
import { ActionIcon, Grid, StackCustom, TextCustom } from "@/components";
import { MainColor } from "@/constants/color-palet";
import {
ActionIcon,
BadgeCustom,
CenterCustom,
Grid,
LoaderCustom,
StackCustom,
TextCustom,
} from "@/components";
import { AccentColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { dummyMasterBank } from "@/lib/dummy-data/_master/bank";
import { apiAdminMasterBank } from "@/service/api-admin/api-master-admin";
import { FontAwesome5 } from "@expo/vector-icons";
import { router } from "expo-router";
import { useState } from "react";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { View } from "react-native";
import { Divider, Switch } from "react-native-paper";
import { Divider } from "react-native-paper";
export default function AdminAppInformation_Bank() {
const [value, setValue] = useState(false);
const [listData, setListData] = useState<any | null>(null);
const [loadData, setLoadData] = useState(false);
useFocusEffect(
useCallback(() => {
loadMasterBank();
}, [])
);
const loadMasterBank = async () => {
try {
setLoadData(true);
const response = await apiAdminMasterBank();
setListData(response.data);
} catch (error) {
console.log("[ERROR LIST BANK]", error);
setListData([]);
} finally {
setLoadData(false);
}
};
return (
<>
<>
<StackCustom>
<Grid>
<Grid.Col span={3} style={{ alignItems: "center" }}>
<TextCustom bold>Aksi</TextCustom>
<Grid.Col span={3}>
<TextCustom bold align="center">
Aksi
</TextCustom>
</Grid.Col>
<Grid.Col span={3} style={{ alignItems: "center" }}>
<TextCustom bold>Status</TextCustom>
<Grid.Col span={3}>
<TextCustom bold align="center">
Status
</TextCustom>
</Grid.Col>
<Grid.Col span={6}>
<TextCustom bold>Nama Bank</TextCustom>
<TextCustom bold align="center">
Nama Bank
</TextCustom>
</Grid.Col>
</Grid>
<Divider />
<StackCustom>
{dummyMasterBank.map((e, i) => (
<View key={i}>
<Grid>
<Grid.Col span={3} style={{ alignItems: "center" }}>
<ActionIcon
icon={
<FontAwesome5
name="edit"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(
`/admin/app-information/information-bank/${i}`
);
}}
/>
</Grid.Col>
<Grid.Col
span={3}
style={{ alignItems: "center", justifyContent: "center" }}
>
<Switch
value={value}
onValueChange={() => {
setValue(!value);
}}
theme={{
colors: {
primary: MainColor.yellow,
},
}}
/>
</Grid.Col>
<Grid.Col span={6} style={{ justifyContent: "center" }}>
<TextCustom>{e.code}</TextCustom>
</Grid.Col>
</Grid>
<Divider />
</View>
))}
</StackCustom>
</>
{loadData ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center">Tidak ada data</TextCustom>
) : (
<StackCustom>
{listData?.map((item: any, index: number) => (
<View key={index}>
<Grid>
<Grid.Col span={3} style={{ alignItems: "center" }}>
<ActionIcon
icon={
<FontAwesome5
name="edit"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(
`/admin/app-information/information-bank/${item.id}`
);
}}
/>
</Grid.Col>
<Grid.Col
span={3}
style={{ alignItems: "center", justifyContent: "center" }}
>
<CenterCustom>
<BadgeCustom
color={
item.isActive
? AccentColor.blue
: AccentColor.blackgray
}
>
{item.isActive ? "Aktif" : "Tidak Aktif"}
</BadgeCustom>
</CenterCustom>
</Grid.Col>
<Grid.Col span={6} style={{ justifyContent: "center" }}>
<TextCustom align="center">{item.namaBank}</TextCustom>
</Grid.Col>
</Grid>
</View>
))}
</StackCustom>
)}
</StackCustom>
</>
);
}

View File

@@ -10,69 +10,77 @@ import { Divider, Switch } from "react-native-paper";
export default function AdminAppInformation_StickerSection() {
const [value, setValue] = useState(false);
return (
<>
<Grid>
<Grid.Col span={3} style={{ alignItems: "center" }}>
<TextCustom bold>Aksi</TextCustom>
</Grid.Col>
<Grid.Col span={3} style={{ alignItems: "center" }}>
<TextCustom bold>Status</TextCustom>
</Grid.Col>
<Grid.Col span={6}>
<TextCustom bold align="center" >Stiker</TextCustom>
</Grid.Col>
</Grid>
<Divider />
<StackCustom>
{listSticker.map((e, i) => (
<View key={i}>
<Grid>
<Grid.Col
span={3}
style={{ alignItems: "center", justifyContent: "center" }}
>
<ActionIcon
icon={
<FontAwesome5
name="edit"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/app-information/sticker/${i}`);
}}
/>
</Grid.Col>
<Grid.Col
span={3}
style={{ alignItems: "center", justifyContent: "center" }}
>
<Switch
value={value}
onValueChange={() => {
setValue(!value);
}}
theme={{
colors: {
primary: MainColor.yellow,
},
}}
/>
</Grid.Col>
<Grid.Col span={6} style={{ justifyContent: "center", alignItems: "center" }}>
<Image source={e.path} style={{ width: 100, height: 100 }} />
</Grid.Col>
</Grid>
<Divider />
</View>
))}
</StackCustom>
<TextCustom bold align="center">
Cooming Soon, Next Update !!
</TextCustom>
</>
);
// return (
// <>
// <Grid>
// <Grid.Col span={3} style={{ alignItems: "center" }}>
// <TextCustom bold>Aksi</TextCustom>
// </Grid.Col>
// <Grid.Col span={3} style={{ alignItems: "center" }}>
// <TextCustom bold>Status</TextCustom>
// </Grid.Col>
// <Grid.Col span={6}>
// <TextCustom bold align="center" >Stiker</TextCustom>
// </Grid.Col>
// </Grid>
// <Divider />
// <StackCustom>
// {listSticker.map((e, i) => (
// <View key={i}>
// <Grid>
// <Grid.Col
// span={3}
// style={{ alignItems: "center", justifyContent: "center" }}
// >
// <ActionIcon
// icon={
// <FontAwesome5
// name="edit"
// size={ICON_SIZE_BUTTON}
// color="black"
// />
// }
// onPress={() => {
// router.push(`/admin/app-information/sticker/${i}`);
// }}
// />
// </Grid.Col>
// <Grid.Col
// span={3}
// style={{ alignItems: "center", justifyContent: "center" }}
// >
// <Switch
// value={value}
// onValueChange={() => {
// setValue(!value);
// }}
// theme={{
// colors: {
// primary: MainColor.yellow,
// },
// }}
// />
// </Grid.Col>
// <Grid.Col span={6} style={{ justifyContent: "center", alignItems: "center" }}>
// <Image source={e.path} style={{ width: 100, height: 100 }} />
// </Grid.Col>
// </Grid>
// <Divider />
// </View>
// ))}
// </StackCustom>
// </>
// );
}
const listSticker = [

View File

@@ -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",
},
];

View File

@@ -93,15 +93,15 @@ export default function LoginView() {
}
}
if (token && !isUserActive) {
if (token && token !== "" && !isUserActive) {
return <Redirect href={"/(application)/(user)/waiting-room"} />;
}
if (token && !isAdmin) {
if (token && token !== "" && !isAdmin) {
return <Redirect href={"/(application)/(user)/home"} />;
}
if (token && isAdmin) {
if (token && token !== "" && isAdmin) {
return <Redirect href={"/(application)/admin/dashboard"} />;
}

View File

@@ -15,23 +15,24 @@ function Collaboration_BoxPublishSection({
data: any;
rightComponentAvatar?: React.ReactNode;
}) {
return (
<>
<BoxWithHeaderSection href={href}>
<StackCustom gap={0}>
<AvatarUsernameAndOtherComponent
avatarHref={`/profile/${data?.Author?.id}`}
avatarHref={`/profile/${data?.Author?.Profile?.id}`}
name={data?.Author?.username || "Username"}
rightComponent={rightComponentAvatar}
avatar={data?.Author?.Profile?.imageId}
withBottomLine
/>
<StackCustom>
<StackCustom style={{paddingBlock: 10}}>
<TextCustom truncate size="large" bold align="center">
{data?.title || "-"}
</TextCustom>
<TextCustom truncate={2}>{data?.purpose || "-"}</TextCustom>
{/* <TextCustom truncate={2}>{data?.purpose || "-"}</TextCustom> */}
{/* <TextCustom bold size="small" >
2 Partisipan
</TextCustom> */}

View File

@@ -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>
</>

View File

@@ -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>

View File

@@ -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`,
// },
];

View File

@@ -0,0 +1,50 @@
import { apiConfig } from "../api-config";
export async function apiAdminCollaboration({
category,
}: {
category: "dashboard" | "publish" | "reject" | "group";
}) {
try {
const response = await apiConfig.get(
`/mobile/admin/collaboration?category=${category}`
);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminCollaborationGetById({
id,
category,
}: {
id: string;
category: "publish" | "reject" | "group";
}) {
try {
const response = await apiConfig.get(
`/mobile/admin/collaboration/${id}?category=${category}`
);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminCollaborationReject({
id,
data,
}: {
id: string;
data: any;
}) {
try {
const response = await apiConfig.put(`/mobile/admin/collaboration/${id}`, {
data: data,
});
return response.data;
} catch (error) {
throw error;
}
}

View File

@@ -0,0 +1,18 @@
import { apiConfig } from "../api-config";
export async function apiAdminJob({
category,
search,
}: {
category: "dashboard" | "publish" | "review" | "reject";
search?: string;
}) {
try {
const response = await apiConfig.get(
`/mobile/admin/job?category=${category}&search=${search}`
);
return response.data;
} catch (error) {
throw error;
}
}

View 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);
}
};

View 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);
}
};

View File

@@ -0,0 +1,102 @@
import { apiConfig } from "../api-config";
// ================== START MASTER BANK ================== //
export async function apiAdminMasterBank() {
try {
const response = await apiConfig.get(`/mobile/admin/master/bank`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminMasterBankById({ id }: { id: string }) {
try {
const response = await apiConfig.get(`/mobile/admin/master/bank/${id}`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminMasterBankUpdate({
id,
data,
}: {
id: string;
data: any;
}) {
try {
const response = await apiConfig.put(`/mobile/admin/master/bank/${id}`, {
data: data,
});
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminMasterBankCreate({ data }: { data: any }) {
try {
const response = await apiConfig.post(`/mobile/admin/master/bank`, {
data: data,
});
return response.data;
} catch (error) {
throw error;
}
}
// ================== END MASTER BANK ================== //
// ================== START BUSINNES FIELD ================== //
export async function apiAdminMasterBusinessField() {
try {
const response = await apiConfig.get(`/mobile/admin/master/business-field`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminMasterBusinessFieldById({ id }: { id: string }) {
try {
const response = await apiConfig.get(`/mobile/admin/master/business-field/${id}`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminMasterBusinessFieldUpdate({
id,
data,
}: {
id: string;
data: any;
}) {
try {
const response = await apiConfig.put(`/mobile/admin/master/business-field/${id}`, {
data: data,
});
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminMasterBusinessFieldCreate({ data }: { data: any }) {
try {
const response = await apiConfig.post(`/mobile/admin/master/business-field`, {
data: data,
});
return response.data;
} catch (error) {
throw error;
}
}
// ================== END BUSINNES FIELD ================== //

View 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;
}
}

View File

@@ -127,6 +127,10 @@ export async function apiMasterInvestment({
}
}
// ================== END MASTER INVESTMENT ================== //
// ================== START MASTER BANK ================== //
export async function apiMasterBank() {
try {
const response = await apiConfig.get(`/mobile/master/bank`);
@@ -136,6 +140,8 @@ export async function apiMasterBank() {
}
}
// ================== END MASTER BANK ================== //
export async function apiMasterDonation({
category,
}: {

37
utils/openInDeviceMaps.ts Normal file
View 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.");
}
};