Compare commits

...

10 Commits

Author SHA1 Message Date
72e8d7aabd Admin User Access
Add:
- admin/user-access.tsx

### No Issue
2025-08-07 17:15:02 +08:00
29377c0bb0 Admin App Infromation
Add:
- admin/app-information/sticker/

#### No Issue
2025-08-07 15:44:22 +08:00
88cf3321e1 Admin App Information
Add:
- app-information/information-bank

### No Issue
2025-08-07 14:09:56 +08:00
9ad1ccfd5f Admin App Information
Add:
- app-information/information-bank/

Component
Fix:
- constans-value: Icon size re name

### No Issue
2025-08-07 12:05:16 +08:00
d47fff469b Admin
App-Information
Add:
- screens/Admin/App-Information
- _ShareComponent/Admin
- app/(application)/admin/app-information

### No Issue
2025-08-06 17:35:30 +08:00
bcc0e02581 Admin
Add:
- screens/Admin/
- admin/notification/

Component
Add:
-  components/_ShareComponent/GridSectionView.tsx

### No Issue
2025-08-06 15:02:47 +08:00
caf250c5ca Admin
Add:
- Menambahkan folder : /admin/collaboration , /admin/investmen
- NavbarMenu

### No Issue
2025-08-06 11:53:33 +08:00
c4c16f19c1 Admin
Add:"
- app/(application)/admin/

## NO Issue
2025-08-05 17:40:46 +08:00
48c34aa26c Admin
Add:
- app/(application)/(admin)/

## No Issue
2025-08-05 12:11:25 +08:00
e16b0c2fce Donation
Add:
- [transaction]/success.tsx
- [transaction]/failed.tsx

Fix:
- InformationBox : props type di tambah React.ReactNode
- /donation/create && /donation/create-story : fix route

### No Issue
2025-08-05 11:01:41 +08:00
60 changed files with 2699 additions and 55 deletions

View File

@@ -477,6 +477,20 @@ export default function UserLayout() {
),
}}
/>
<Stack.Screen
name="donation/[id]/(transaction-flow)/[transaction]/success"
options={{
title: "Donasi Berhasil",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="donation/[id]/(transaction-flow)/[transaction]/failed"
options={{
title: "Donasi Gagal",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== End Donation Section ========= */}

View File

@@ -1,13 +1,14 @@
import {
BadgeCustom,
BaseBox,
DummyLandscapeImage,
Grid,
StackCustom,
TextCustom,
ViewWrapper
BadgeCustom,
BaseBox,
DummyLandscapeImage,
Grid,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { dummyMasterStatusTransaction } from "@/lib/dummy-data/_master/status-transaction";
import { router } from "expo-router";
import { View } from "react-native";
export default function DonationMyDonation() {
@@ -17,6 +18,19 @@ export default function DonationMyDonation() {
);
return dummyMasterStatusTransaction[randomIndex];
});
const handlePress = (value: string) => {
if (value === "menunggu") {
router.push(`/donation/${value}/(transaction-flow)/123/invoice`);
} else if (value === "proses") {
router.push(`/donation/${value}/(transaction-flow)/123/process`);
} else if (value === "berhasil") {
router.push(`/donation/${value}/(transaction-flow)/123/success`);
} else if (value === "gagal") {
router.push(`/donation/${value}/(transaction-flow)/123/failed`);
}
};
return (
<ViewWrapper hideFooter>
{randomStatusData.map((item, index) => (
@@ -24,11 +38,13 @@ export default function DonationMyDonation() {
key={index}
paddingTop={7}
paddingBottom={7}
href={`/investment/${index}`}
onPress={() => {
handlePress(item.value);
}}
>
<Grid>
<Grid.Col span={5}>
<DummyLandscapeImage height={100} />
<DummyLandscapeImage height={100} unClickPath />
</Grid.Col>
<Grid.Col span={1}>
<View />

View File

@@ -0,0 +1,83 @@
import {
BaseBox,
Grid,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { GStyles } from "@/styles/global-styles";
import { FontAwesome6 } from "@expo/vector-icons";
import dayjs from "dayjs";
export default function DonasiFailed() {
return (
<ViewWrapper>
<StackCustom>
<BaseBox>
<StackCustom>
<TextCustom bold align="center">
Transaksi anda gagal karena bukti transfer tidak sesuai dengan
data kami. Jika ini masalah khusus silahkan hubungi pada kontak
whatsapp kami.
</TextCustom>
<FontAwesome6
name="whatsapp"
size={50}
color={MainColor.green}
style={GStyles.alignSelfCenter}
/>
</StackCustom>
</BaseBox>
<BaseBox>
<TextCustom bold align="center" size="large">
Detail Transaksi
</TextCustom>
<Spacing />
<StackCustom>
{listData.map((item, i) => (
<Grid key={i}>
<Grid.Col span={5}>
<TextCustom bold>{item.label}</TextCustom>
</Grid.Col>
<Grid.Col span={7}>
<TextCustom style={{ paddingLeft: 10 }}>
{item.value}
</TextCustom>
</Grid.Col>
</Grid>
))}
</StackCustom>
</BaseBox>
</StackCustom>
</ViewWrapper>
);
}
const listData = [
{
label: "Bank",
value: " BCA",
},
{
label: "Rekening Penerima",
value: "Himpunan Pengusaha Muda Indonesia",
},
{
label: "No Rekening",
value: "2304235678854332",
},
{
label: "Jumlah Donasi",
value: "Rp. 750.000",
},
{
label: "Tanggal",
value: `${dayjs(new Date()).format("DD/MM/YYYY")}`,
},
];

View File

@@ -13,12 +13,12 @@ import { MainColor } from "@/constants/color-palet";
import { router, useLocalSearchParams } from "expo-router";
export default function DonationInvoice() {
const { id } = useLocalSearchParams();
const { id, transaction } = useLocalSearchParams();
return (
<>
<ViewWrapper>
<StackCustom>
<InformationBox text="Mohon transfer ke rekening dibawah" />
<InformationBox text={`Mohon transfer donasi anda ke rekening dibawah dengan Id: ${transaction}`} />
<BaseBox>
<StackCustom gap={"xs"}>
<TextCustom>Nama BANK</TextCustom>

View File

@@ -0,0 +1,83 @@
import {
BaseBox,
Grid,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { GStyles } from "@/styles/global-styles";
import { FontAwesome6 } from "@expo/vector-icons";
import dayjs from "dayjs";
export default function DonationSuccess() {
return (
<ViewWrapper>
<StackCustom>
<BaseBox>
<StackCustom>
<FontAwesome6
name="money-bill-wave"
size={100}
color={MainColor.green}
style={GStyles.alignSelfCenter}
/>
<TextCustom bold align="center">
Terimakasih telah percaya pada kami untuk mengelola dana anda!
Info mengenai update Penggalian Dana ini bisa di lihat di kolom
berita.
</TextCustom>
</StackCustom>
</BaseBox>
<BaseBox>
<TextCustom bold align="center" size="large">
Detail Transaksi
</TextCustom>
<Spacing />
<StackCustom>
{listData.map((item, i) => (
<Grid key={i}>
<Grid.Col span={5}>
<TextCustom bold>{item.label}</TextCustom>
</Grid.Col>
<Grid.Col span={7}>
<TextCustom style={{ paddingLeft: 10 }}>
{item.value}
</TextCustom>
</Grid.Col>
</Grid>
))}
</StackCustom>
</BaseBox>
</StackCustom>
</ViewWrapper>
);
}
const listData = [
{
label: "Bank",
value: " BCA",
},
{
label: "Rekening Penerima",
value: "Himpunan Pengusaha Muda Indonesia",
},
{
label: "No Rekening",
value: "2304235678854332",
},
{
label: "Jumlah Donasi",
value: "Rp. 750.000",
},
{
label: "Tanggal",
value: `${dayjs(new Date()).format("DD/MM/YYYY")}`,
},
];

View File

@@ -57,7 +57,7 @@ export default function DonationCreateStory() {
<Spacing />
<ButtonCustom
onPress={() => {
router.navigate(`/donation/(tabs)/status`);
router.replace(`/donation/(tabs)/status`);
}}
>
Simpan

View File

@@ -66,7 +66,7 @@ export default function DonationCreate() {
<Spacing />
<ButtonCustom
onPress={() => {
router.push("/donation/create-story");
router.replace("/donation/create-story");
}}
>
Selanjutnya

View File

@@ -7,6 +7,8 @@ export default function ApplicationLayout() {
<>
<Stack screenOptions={HeaderStyles}>
<Stack.Screen name="(user)" options={{ headerShown: false }} />
<Stack.Screen name="admin" options={{ headerShown: false }} />
{/* Take Picture */}
<Stack.Screen

View File

@@ -0,0 +1,227 @@
import {
AlertDefaultSystem,
DrawerCustom,
GridComponentView,
MenuDrawerDynamicGrid,
StackCustom,
TextCustom,
} from "@/components";
import DrawerAdmin from "@/components/Drawer/DrawerAdmin";
import NavbarMenu from "@/components/Drawer/NavbarMenu";
import { AccentColor, MainColor } from "@/constants/color-palet";
import { ICON_SIZE_MEDIUM, ICON_SIZE_SMALL, ICON_SIZE_XLARGE } from "@/constants/constans-value";
import { adminListMenu } from "@/screens/Admin/listPageAdmin";
import { GStyles } from "@/styles/global-styles";
import { FontAwesome6, Ionicons } from "@expo/vector-icons";
import { router, Stack } from "expo-router";
import { useState } from "react";
export default function AdminLayout() {
const [openDrawerNavbar, setOpenDrawerNavbar] = useState(false);
const [openDrawerUser, setOpenDrawerUser] = useState(false);
return (
<>
<Stack
screenOptions={{
title: "HIPMI DASHBOARD",
headerStyle: GStyles.headerStyle,
headerTitleStyle: GStyles.headerTitleStyle,
headerTitleAlign: "center",
contentStyle: {
borderBottomColor: AccentColor.blue,
},
headerLeft: () => (
<Ionicons
name="menu"
size={ICON_SIZE_XLARGE}
color={MainColor.white}
onPress={() => setOpenDrawerNavbar(true)}
/>
),
headerRight: () => (
<FontAwesome6
name="circle-user"
size={ICON_SIZE_MEDIUM}
color={MainColor.white}
onPress={() => setOpenDrawerUser(true)}
/>
),
}}
>
<Stack.Screen name="dashboard"
// options={{ title: "Main Dashboard" }}
/>
<Stack.Screen
name="investment/index"
// options={{ title: "Dashboard Investasi" }}
/>
<Stack.Screen
name="investment/publish"
// options={{ title: "Investasi Publish" }}
/>
<Stack.Screen
name="investment/review"
// options={{ title: "Investasi Review" }}
/>
<Stack.Screen
name="investment/reject"
// options={{ title: "Investasi Reject" }}
/>
<Stack.Screen name="maps"
// options={{ title: "Maps" }}
/>
<Stack.Screen name="app-information/index"
// options={{ title: "Information" }}
/>
<Stack.Screen name="job/index"
// options={{ title: "Dashboard Job" }}
/>
<Stack.Screen name="job/publish"
// options={{ title: "Job Publish" }}
/>
<Stack.Screen name="job/review"
// options={{ title: "Job Review" }}
/>
<Stack.Screen name="job/reject"
// options={{ title: "Job Reject" }}
/>
<Stack.Screen
name="collaboration/index"
// options={{ title: "Dashboard Collaboration" }}
/>
<Stack.Screen
name="collaboration/publish"
// options={{ title: "Collaboration Publish" }}
/>
<Stack.Screen
name="collaboration/group"
// options={{ title: "Collaboration Group" }}
/>
<Stack.Screen
name="collaboration/reject"
// options={{ title: "Collaboration Reject" }}
/>
</Stack>
<DrawerAdmin
isVisible={openDrawerNavbar}
onClose={() => setOpenDrawerNavbar(false)}
>
<StackCustom gap={"xs"}>
<Ionicons
name="close"
size={ICON_SIZE_SMALL}
color={MainColor.white}
onPress={() => setOpenDrawerNavbar(false)}
style={{ alignSelf: "flex-end" }}
/>
<NavbarMenu
items={adminListMenu}
onClose={() => setOpenDrawerNavbar(false)}
/>
</StackCustom>
</DrawerAdmin>
<DrawerCustom
isVisible={openDrawerUser}
closeDrawer={() => setOpenDrawerUser(false)}
height={"auto"}
>
<StackCustom>
<GridComponentView
leftIcon={
<Ionicons
name="person"
size={ICON_SIZE_SMALL}
color={MainColor.white}
/>
}
>
<TextCustom>Username</TextCustom>
</GridComponentView>
<GridComponentView
leftIcon={
<Ionicons
name="ribbon-outline"
size={ICON_SIZE_SMALL}
color={MainColor.white}
/>
}
>
<TextCustom>User Role</TextCustom>
</GridComponentView>
<MenuDrawerDynamicGrid
columns={3}
data={[
{
label: "Notifikasi",
value: "notification",
icon: (
<Ionicons
name="notifications"
size={ICON_SIZE_SMALL}
color={MainColor.white}
/>
),
path: "/admin/notification",
},
{
label: "Kembali ke User",
value: "back-to-user",
icon: (
<Ionicons
name="git-compare"
size={ICON_SIZE_SMALL}
color={MainColor.white}
/>
),
path: "" as any,
},
{
label: "Keluar",
value: "logout",
icon: (
<Ionicons
name="log-out"
size={ICON_SIZE_SMALL}
color={MainColor.white}
/>
),
path: "" as any,
color: MainColor.red,
},
]}
onPressItem={(item) => {
if (item.value === "notification") {
router.push("/admin/notification");
setOpenDrawerUser(false);
} else if (item.value === "back-to-user") {
AlertDefaultSystem({
title: "Kembali ke User",
message: "Apakah Anda yakin ingin kembali ke user?",
textLeft: "Batal",
textRight: "Ya",
onPressRight: () => {
router.replace(`/(application)/(user)/profile/${123}`);
},
});
} else if (item.value === "logout") {
AlertDefaultSystem({
title: "Keluar",
message: "Apakah Anda yakin ingin keluar?",
textLeft: "Batal",
textRight: "Keluar",
onPressRight: () => {
router.replace("/");
},
});
}
}}
/>
</StackCustom>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,36 @@
import {
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { router } from "expo-router";
export default function AdminAppInformation_BusinessFieldDetail() {
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => router.back()}
>
Update
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={buttonSubmit}>
<StackCustom>
<AdminBackButtonAntTitle title="Update Bidang Bisnis" />
<TextInputCustom
label="Nama Bidang Bisnis"
placeholder="Masukan Nama Bidang Bisnis"
required
/>
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,36 @@
import {
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { router } from "expo-router";
export default function AdminAppInformation_BusinessFieldCreate() {
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => router.back()}
>
Tambah
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={buttonSubmit}>
<StackCustom>
<AdminBackButtonAntTitle title="Tambah Bidang Bisnis" />
<TextInputCustom
label="Nama Bidang Bisnis"
placeholder="Masukan Nama Bidang Bisnis"
required
/>
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,96 @@
import {
ActionIcon,
ScrollableCustom,
StackCustom,
ViewWrapper,
} from "@/components";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import AdminAppInformation_BusinessFieldSection from "@/screens/Admin/App-Information/BusinessFieldSection";
import AdminAppInformation_Bank from "@/screens/Admin/App-Information/InformationBankSection";
import AdminAppInformation_StickerSection from "@/screens/Admin/App-Information/StickerSection";
import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { useState } from "react";
import { View } from "react-native";
export default function AdminInformation() {
const [activeCategory, setActiveCategory] = useState<string | null>("bank");
const [activePage, setActivePage] = useState<string>("Informasi Bank");
const handlePress = (item: any) => {
setActiveCategory(item.value);
setActivePage(item.label);
// tambahkan logika lain seperti filter dsb.
};
const scrollComponent = (
<StackCustom>
<ScrollableCustom
data={listPage}
onButtonPress={handlePress}
activeId={activeCategory as any}
/>
</StackCustom>
);
const renderContent = () => {
switch (activeCategory) {
case "bank":
return <AdminAppInformation_Bank />;
case "business":
return <AdminAppInformation_BusinessFieldSection />;
case "sticker":
return <AdminAppInformation_StickerSection />;
default:
return <AdminAppInformation_Bank />;
}
};
return (
<>
<ViewWrapper headerComponent={scrollComponent}>
<AdminComp_BoxTitle
title={activePage}
rightComponent={
<View style={{ flexDirection: "row" }}>
<ActionIcon
icon={
<Ionicons name="add" size={ICON_SIZE_SMALL} color="black" />
}
onPress={() => {
if (activeCategory === "bank") {
router.push("/admin/app-information/information-bank/create");
} else if (activeCategory === "business") {
router.push("/admin/app-information/business-field/create");
} else if (activeCategory === "sticker") {
router.push("/admin/app-information/sticker/create");
}
}}
/>
</View>
}
/>
{renderContent()}
</ViewWrapper>
</>
);
}
const listPage = [
{
id: "1",
label: "Informasi Bank",
value: "bank",
},
{
id: "2",
label: "Bidang Bisnis",
value: "business",
},
{
id: "3",
label: "Stiker",
value: "sticker",
},
];

View File

@@ -0,0 +1,50 @@
import {
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { router } from "expo-router";
export default function AdminAppInformation_BankDetail() {
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => router.back()}
>
Update
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={buttonSubmit}>
<StackCustom>
<AdminBackButtonAntTitle title="Update Bank" />
<StackCustom>
<TextInputCustom
label="Nama Bank"
placeholder="Masukan Nama Bank"
required
/>
<TextInputCustom
label="Nama Rekening"
placeholder="Masukan Nama Rekening"
required
/>
<TextInputCustom
label="Nomor Rekening"
placeholder="Masukan Nomor Rekening"
required
/>
</StackCustom>
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,50 @@
import {
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextInputCustom,
ViewWrapper
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { router } from "expo-router";
export default function AdminAppInformation_BankCreate() {
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => router.back()}
>
Tambah
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={buttonSubmit}>
<StackCustom>
<AdminBackButtonAntTitle title="Tambah Daftar Bank" />
<StackCustom>
<TextInputCustom
label="Nama Bank"
placeholder="Masukan Nama Bank"
required
/>
<TextInputCustom
label="Nama Rekening"
placeholder="Masukan Nama Rekening"
required
/>
<TextInputCustom
label="Nomor Rekening"
placeholder="Masukan Nomor Rekening"
required
/>
</StackCustom>
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,80 @@
import {
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
CheckboxCustom,
CheckboxGroup,
LandscapeFrameUploaded,
SelectCustom,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { dummyMasterEmotions } from "@/lib/dummy-data/_master/emotions";
import { router } from "expo-router";
import { useState } from "react";
import { View } from "react-native";
export default function AdminAppInformation_StickerCreate() {
const [value, setValue] = useState<string | null>(null);
const [selectedEmotions, setSelectedEmotions] = useState<string[]>([]);
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom onPress={() => router.back()}>Update</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={buttonSubmit}>
<StackCustom>
<AdminBackButtonAntTitle title="Edit Stiker" />
<StackCustom gap={"xs"}>
<LandscapeFrameUploaded />
<ButtonCenteredOnly icon="upload" onPress={() => {}}>
Upload
</ButtonCenteredOnly>
<Spacing />
<SelectCustom
required
label="Jenis Kelamin"
data={[
{ label: "Laki-laki", value: "laki-laki" },
{ label: "Perempuan", value: "perempuan" },
]}
value={value}
onChange={(value) => setValue(value as string)}
/>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<TextCustom>Pilih Emosi Stiker</TextCustom>
<TextCustom color="red">*</TextCustom>
</View>
<Spacing height={10} />
<CheckboxGroup
value={selectedEmotions}
onChange={(value) => setSelectedEmotions(value as string[])}
>
<StackCustom>
{dummyMasterEmotions.map((e) => (
<CheckboxCustom
size={25}
key={e.id}
label={<TextCustom>{e.label}</TextCustom>}
valueKey={e.id}
/>
))}
</StackCustom>
</CheckboxGroup>
</StackCustom>
</StackCustom>
<Spacing />
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,84 @@
import {
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
CheckboxCustom,
CheckboxGroup,
LandscapeFrameUploaded,
SelectCustom,
Spacing,
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { dummyMasterEmotions } from "@/lib/dummy-data/_master/emotions";
import { router } from "expo-router";
import { useState } from "react";
import { View } from "react-native";
export default function AdminAppInformation_StickerCreate() {
const [value, setValue] = useState<string | null>(null);
const [selectedEmotions, setSelectedEmotions] = useState<string[]>([]);
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => router.back()}
>
Tambah
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={buttonSubmit}>
<StackCustom>
<AdminBackButtonAntTitle title="Tambah Stiker" />
<StackCustom gap={"xs"}>
<LandscapeFrameUploaded />
<ButtonCenteredOnly icon="upload" onPress={() => {}}>
Upload
</ButtonCenteredOnly>
<Spacing />
<SelectCustom
required
label="Jenis Kelamin"
data={[
{ label: "Laki-laki", value: "laki-laki" },
{ label: "Perempuan", value: "perempuan" },
]}
value={value}
onChange={(value) => setValue(value as string)}
/>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<TextCustom>Pilih Emosi Stiker</TextCustom>
<TextCustom color="red">*</TextCustom>
</View>
<Spacing height={10} />
<CheckboxGroup
value={selectedEmotions}
onChange={(value) => setSelectedEmotions(value as string[])}
>
<StackCustom>
{dummyMasterEmotions.map((e) => (
<CheckboxCustom
size={25}
key={e.id}
label={<TextCustom>{e.label}</TextCustom>}
valueKey={e.id}
/>
))}
</StackCustom>
</CheckboxGroup>
</StackCustom>
</StackCustom>
<Spacing/>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,11 @@
import { TextCustom, ViewWrapper } from "@/components";
export default function AdminCollaborationGroup() {
return (
<>
<ViewWrapper>
<TextCustom>Admin Collaboration Group</TextCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,11 @@
import { TextCustom, ViewWrapper } from "@/components";
export default function AdminCollaboration() {
return (
<>
<ViewWrapper>
<TextCustom>Admin Collaboration</TextCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,11 @@
import { TextCustom, ViewWrapper } from "@/components";
export default function AdminCollaborationPublish() {
return (
<>
<ViewWrapper>
<TextCustom>Admin Collaboration Publish</TextCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,11 @@
import { TextCustom, ViewWrapper } from "@/components";
export default function AdminCollaborationReject() {
return (
<>
<ViewWrapper>
<TextCustom>Admin Collaboration Reject</TextCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,40 @@
import {
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
import { MainColor } from "@/constants/color-palet";
import { Ionicons } from "@expo/vector-icons";
export default function AdminDashboard() {
return (
<>
<ViewWrapper>
<StackCustom>
<TextCustom bold size={30}>
Main Dashboard
</TextCustom>
{listData.map((item, i) => (
<AdminComp_BoxDashboard key={i} item={item} />
))}
</StackCustom>
</ViewWrapper>
</>
);
}
const listData = [
{
label: "User",
value: 4,
icon: <Ionicons name="people" size={30} color={MainColor.yellow} />,
},
{
label: "Portofolio",
value: 7,
icon: (
<Ionicons name="id-card-outline" size={30} color={MainColor.yellow} />
),
},
];

View File

@@ -0,0 +1,11 @@
import { TextCustom, ViewWrapper } from "@/components";
export default function AdminInvestment() {
return (
<>
<ViewWrapper>
<TextCustom>Admin Investment</TextCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,11 @@
import { TextCustom, ViewWrapper } from "@/components";
export default function AdminInvestmentPublish() {
return (
<>
<ViewWrapper>
<TextCustom>Admin Investment Publish</TextCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,11 @@
import { TextCustom, ViewWrapper } from "@/components";
export default function AdminInvestmentReject() {
return (
<>
<ViewWrapper>
<TextCustom>Admin Investment Reject</TextCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,11 @@
import { TextCustom, ViewWrapper } from "@/components";
export default function AdminInvestmentReview() {
return (
<>
<ViewWrapper>
<TextCustom>Admin Investment Review</TextCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,11 @@
import { TextCustom, ViewWrapper } from "@/components";
export default function AdminJob() {
return (
<>
<ViewWrapper>
<TextCustom>Admin Job</TextCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,11 @@
import { TextCustom, ViewWrapper } from "@/components";
export default function AdminJobPublish() {
return (
<>
<ViewWrapper>
<TextCustom>Admin Job Publish</TextCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,11 @@
import { TextCustom, ViewWrapper } from "@/components";
export default function AdminJobReject() {
return (
<>
<ViewWrapper>
<TextCustom>Admin Job Reject</TextCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,11 @@
import { TextCustom, ViewWrapper } from "@/components";
export default function AdminJobReview() {
return (
<>
<ViewWrapper>
<TextCustom>Admin Job Review</TextCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,11 @@
import { MapCustom, ViewWrapper } from "@/components";
export default function AdminMaps() {
return (
<>
<ViewWrapper>
<MapCustom height={"100%"} />
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,20 @@
import { BackButton, TextCustom, ViewWrapper } from "@/components";
import { Stack } from "expo-router";
export default function AdminNotification() {
return (
<>
<Stack.Screen
options={{
title: "Admin Notifikasi",
headerLeft: () => <BackButton />,
headerRight: () => <></>,
}}
/>
<ViewWrapper>
<TextCustom>Notification</TextCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,117 @@
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

@@ -14,7 +14,10 @@ export default function RootLayout() {
headerTitleAlign: "center",
}}
>
<Stack.Screen name="index" options={{ title: "" }} />
<Stack.Screen
name="index"
options={{ title: "", headerBackVisible: false }}
/>
<Stack.Screen name="+not-found" options={{ title: "" }} />
<Stack.Screen
name="verification"

View File

@@ -0,0 +1,59 @@
import { MainColor } from "@/constants/color-palet";
import { Href, router } from "expo-router";
import { DimensionValue, TouchableOpacity } from "react-native";
type SizeType = "xs" | "sm" | "md" | "lg" | "xl" | number | string | undefined;
export default function ActionIcon({
href,
onPress,
icon,
size = "md",
}: {
href?: Href;
onPress?: () => void;
icon: React.ReactNode;
size?: SizeType;
}) {
const sizeMap = {
xs: 22,
sm: 26,
md: 30,
lg: 34,
xl: 38,
};
const getSize = (size: SizeType): DimensionValue => {
if (!size) return sizeMap.md; // Default to 'md' if size is undefined
if (typeof size === 'string' && size in sizeMap) {
return sizeMap[size as keyof typeof sizeMap];
}
return size as DimensionValue;
};
const iconSize = getSize(size);
return (
<TouchableOpacity
activeOpacity={0.7}
style={{
backgroundColor: MainColor.yellow,
padding: 5,
borderRadius: 50,
alignItems: "center",
justifyContent: "center",
width: iconSize,
height: iconSize,
}}
onPress={() => {
if (href) {
router.push(href);
} else {
onPress?.();
}
}}
>
{icon}
</TouchableOpacity>
);
}

View File

@@ -3,8 +3,9 @@ import { Ionicons } from "@expo/vector-icons";
import Grid from "../Grid/GridCustom";
import TextCustom from "../Text/TextCustom";
import BaseBox from "./BaseBox";
import React from "react";
export default function InformationBox({ text }: { text: string }) {
export default function InformationBox({ text }: { text: React.ReactNode | string }) {
return (
<>
<BaseBox paddingTop={5} paddingBottom={5}>

View File

@@ -1,4 +1,4 @@
import { AccentColor } from "@/constants/color-palet";
import { AccentColor, MainColor } from "@/constants/color-palet";
import { MaterialIcons } from "@expo/vector-icons"; // Bisa diganti dengan ikon lain
import React, { useContext } from "react";
import { Animated, Text, TouchableOpacity, View } from "react-native";
@@ -17,7 +17,7 @@ const CheckboxGroupContext =
// Tipe props
// Tambahkan prop baru: groupValueKey
interface CheckboxProps {
label?: string;
label?: string | React.ReactNode;
description?: string;
error?: string;
value?: boolean; // controlled value (untuk standalone)
@@ -39,7 +39,7 @@ const CheckboxCustom: React.FC<CheckboxProps> = ({
onChange,
disabled: propDisabled,
size = 20,
color = AccentColor.softblue,
color = MainColor.yellow,
style,
component,
valueKey,

View File

@@ -1,22 +1,28 @@
import { MainColor } from "@/constants/color-palet";
import React from "react";
import { StyleSheet, TextInput, View } from "react-native";
import { StyleProp, StyleSheet, TextInput, View, ViewStyle } from "react-native";
interface CircularInputProps {
value: string | number;
onChange?: (value: string) => void;
value?: string | number
onChange?: (value: string | number) => void;
icon?: React.ReactNode;
style?: StyleProp<ViewStyle>
}
const CircularInput: React.FC<CircularInputProps> = ({ value, onChange }) => {
const CircularInput: React.FC<CircularInputProps> = ({ value, onChange, icon, style }) => {
return (
<View style={styles.circleContainer}>
<TextInput
value={String(value)}
onChangeText={onChange}
style={styles.input}
keyboardType="numeric"
maxLength={2} // Batasan maksimal karakter
/>
<View style={[styles.circleContainer, style]}>
{icon ? (
icon
) : (
<TextInput
value={String(value)}
onChangeText={onChange}
style={styles.input}
keyboardType="numeric"
maxLength={2} // Batasan maksimal karakter
/>
)}
</View>
);
};

View File

@@ -2,8 +2,8 @@ import { AccentColor } from "@/constants/color-palet";
import { View } from "react-native";
export default function Divider({
color = AccentColor.blue,
size = 1,
color = AccentColor.white,
size = 0.5,
marginTop= 12,
marginBottom= 12,
}: {

View File

@@ -0,0 +1,157 @@
import React, { useEffect, useRef } from "react";
import {
Animated,
Dimensions,
InteractionManager,
PanResponder,
StyleSheet,
} from "react-native";
import { AccentColor, MainColor } from "@/constants/color-palet";
import { SafeAreaView } from "react-native-safe-area-context";
// Lebar drawer (bisa di-pass sebagai prop)
const DRAWER_WIDTH = Dimensions.get("window").width * 0.8; // 80% lebar layar
interface DrawerAdminProps {
children?: React.ReactNode;
width?: number; // lebar drawer
isVisible: boolean;
onClose: () => void; // ganti nama dari closeDrawer agar lebih jelas
}
export default function DrawerAdmin({
children,
width = DRAWER_WIDTH,
isVisible,
onClose,
}: DrawerAdminProps) {
const drawerAnim = useRef(new Animated.Value(-width)).current; // mulai dari kiri (tersembunyi)
// Efek untuk handle animasi saat isVisible berubah
useEffect(() => {
if (isVisible) {
Animated.timing(drawerAnim, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}).start();
} else {
Animated.timing(drawerAnim, {
toValue: -width,
duration: 300,
useNativeDriver: true,
}).start();
}
}, [isVisible, width, onClose, drawerAnim]);
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: (_, gestureState) => {
return Math.abs(gestureState.dx) > 10; // deteksi gesek horizontal
},
onPanResponderMove: (_, gestureState) => {
let newAnim = gestureState.dx; // geser ke kanan = dx positif → drawerAnim negatif
newAnim = Math.max(-width, Math.min(0, newAnim)); // batas antara -width dan 0
drawerAnim.setValue(newAnim);
},
onPanResponderRelease: (_, gestureState) => {
if (gestureState.dx > 100) {
// gesek kencang ke kiri → tutup
InteractionManager.runAfterInteractions(() => {
onClose();
});
} else {
// kembali ke posisi terbuka penuh
Animated.spring(drawerAnim, {
toValue: 0,
useNativeDriver: true,
}).start();
}
},
})
).current;
if (!isVisible) return null;
return (
<>
{/* Overlay Gelap */}
<Animated.View
style={[
styles.overlay,
{
opacity: drawerAnim.interpolate({
inputRange: [-width, 0],
outputRange: [0, 0.6],
extrapolate: "clamp",
}),
},
]}
onTouchStart={() => {
InteractionManager.runAfterInteractions(() => {
onClose();
});
}}
/>
{/* Left Drawer */}
<Animated.View
style={[
styles.drawer,
{
width,
transform: [{ translateX: drawerAnim }],
},
]}
{...panResponder.panHandlers}
>
{/* Handle Bar (opsional) */}
<SafeAreaView
edges={["top", "bottom"]}
style={{
flex: 1,
}}
>
{children}
{/* <Spacing/> */}
</SafeAreaView>
</Animated.View>
</>
);
}
const styles = StyleSheet.create({
overlay: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "black",
zIndex: 998,
},
drawer: {
position: "absolute",
top: 0,
bottom: 0,
left: 0,
backgroundColor: AccentColor.darkblue,
// borderRadius: 20, // opsional
shadowColor: "#000",
shadowOffset: { width: 2, height: 0 },
shadowOpacity: 0.2,
shadowRadius: 5,
elevation: 5,
zIndex: 999,
padding: 20,
},
handleBar: {
width: 10,
height: 5,
backgroundColor: MainColor.yellow,
borderRadius: 5,
alignSelf: "flex-start",
marginBottom: 20,
},
});

View File

@@ -7,6 +7,7 @@ import { Href } from "expo-router";
type IMenuDrawerItemProps = {
icon: React.ReactNode;
label: string;
value?: string;
path?: Href;
color?: string;
}

View File

@@ -0,0 +1,276 @@
import { AccentColor, MainColor } from "@/constants/color-palet";
import { Ionicons } from "@expo/vector-icons";
import { router, usePathname } from "expo-router";
import React, { useEffect, useRef, useState } from "react";
import {
Animated,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
export interface NavbarItem {
label: string;
icon?: keyof typeof Ionicons.glyphMap;
color?: string;
link?: string;
links?: {
label: string;
link: string;
}[];
initiallyOpened?: boolean;
}
interface NavbarMenuProps {
items: NavbarItem[];
onClose?: () => void;
}
export default function NavbarMenu({ items, onClose }: NavbarMenuProps) {
const pathname = usePathname();
const [activeLink, setActiveLink] = useState<string | null>(null);
const [openKeys, setOpenKeys] = useState<string[]>([]); // Untuk kontrol dropdown
// Normalisasi path: hapus trailing slash
const normalizePath = (path: string) => path.replace(/\/+$/, "");
const normalizedPathname = pathname ? normalizePath(pathname) : "";
// Set activeLink saat pathname berubah
useEffect(() => {
if (normalizedPathname) {
setActiveLink(normalizedPathname);
}
}, [normalizedPathname]);
// Toggle dropdown
const toggleOpen = (label: string) => {
setOpenKeys((prev) =>
prev.includes(label) ? prev.filter((key) => key !== label) : [label]
);
};
return (
<View
style={{
// flex: 1,
// backgroundColor: MainColor.black,
marginBottom: 20,
}}
>
<ScrollView
contentContainerStyle={{
paddingVertical: 10, // Opsional: tambahkan padding
}}
// showsVerticalScrollIndicator={false} // Opsional: sembunyikan indikator scroll
>
{items.map((item) => (
<MenuItem
key={item.label}
item={item}
onClose={onClose}
activeLink={activeLink}
setActiveLink={setActiveLink}
isOpen={openKeys.includes(item.label)}
toggleOpen={() => toggleOpen(item.label)}
/>
))}
</ScrollView>
</View>
);
}
// Komponen Item Menu
function MenuItem({
item,
onClose,
activeLink,
setActiveLink,
isOpen,
toggleOpen,
}: {
item: NavbarItem;
onClose?: () => void;
activeLink: string | null;
setActiveLink: (link: string | null) => void;
isOpen: boolean;
toggleOpen: () => void;
}) {
const isActive = activeLink === item.link;
const animatedHeight = useRef(new Animated.Value(0)).current;
// Animasi saat isOpen berubah
React.useEffect(() => {
Animated.timing(animatedHeight, {
toValue: isOpen ? (item.links ? item.links.length * 40 : 0) : 0,
duration: 200,
useNativeDriver: false,
}).start();
}, [isOpen, item.links, animatedHeight]);
// Jika ada submenu
if (item.links && item.links.length > 0) {
return (
<View>
{/* Parent Item */}
<TouchableOpacity style={styles.parentItem} onPress={toggleOpen}>
<Ionicons
name={item.icon}
size={16}
color={MainColor.white}
style={styles.icon}
/>
<Text style={styles.parentText}>{item.label}</Text>
<Ionicons
name={isOpen ? "chevron-up" : "chevron-down"}
size={16}
color={MainColor.white}
/>
</TouchableOpacity>
{/* Submenu (Animated) */}
<Animated.View
style={[
styles.submenu,
// {
// backgroundColor: "red",
// },
{
height: animatedHeight,
opacity: animatedHeight.interpolate({
inputRange: [0, item.links.length * 40],
outputRange: [0, 1],
extrapolate: "clamp",
}),
},
]}
>
{item.links.map((subItem, index) => {
const isSubActive = activeLink === subItem.link;
return (
<TouchableOpacity
key={index}
style={[styles.subItem, isSubActive && styles.subItemActive]}
onPress={() => {
setActiveLink(subItem.link);
onClose?.();
router.push(subItem.link as any);
}}
>
<Ionicons
name="radio-button-on-outline"
size={16}
color={isSubActive ? MainColor.yellow : MainColor.white}
style={styles.icon}
/>
<Text
style={[
styles.subText,
isSubActive && { color: MainColor.yellow },
]}
>
{subItem.label}
</Text>
</TouchableOpacity>
);
})}
</Animated.View>
</View>
);
}
// Menu tanpa submenu
return (
<TouchableOpacity
style={[styles.singleItem, isActive && styles.singleItemActive]}
onPress={() => {
setActiveLink(item.link || null);
onClose?.();
router.push(item.link as any);
}}
>
<Ionicons
name={item.icon}
size={16}
color={isActive ? MainColor.yellow : MainColor.white}
style={styles.icon}
/>
<Text
style={[
styles.singleText,
{ color: isActive ? MainColor.yellow : MainColor.white },
]}
>
{item.label}
</Text>
</TouchableOpacity>
);
}
// Styles
const styles = StyleSheet.create({
container: {
marginBottom: 5,
},
parentItem: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 12,
paddingHorizontal: 10,
// backgroundColor: AccentColor.darkblue,
borderRadius: 8,
marginBottom: 5,
justifyContent: "space-between",
},
parentText: {
flex: 1,
fontSize: 16,
fontWeight: "500",
marginLeft: 10,
color: MainColor.white,
},
singleItem: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 12,
paddingHorizontal: 10,
// backgroundColor: AccentColor.darkblue,
borderRadius: 8,
marginBottom: 5,
},
singleItemActive: {
backgroundColor: AccentColor.blue,
},
singleText: {
fontSize: 16,
fontWeight: "500",
marginLeft: 10,
color: MainColor.white,
},
icon: {
width: 24,
textAlign: "center",
paddingRight: 10,
},
submenu: {
overflow: "hidden",
marginLeft: 30,
marginTop: 5,
},
subItem: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 8,
paddingHorizontal: 10,
borderRadius: 6,
marginBottom: 4,
},
subItemActive: {
backgroundColor: AccentColor.blue,
},
subText: {
color: MainColor.white,
fontSize: 16,
fontWeight: "500",
},
});

View File

@@ -0,0 +1,279 @@
import React, { useEffect, useRef, useState } from "react";
import {
Animated,
Dimensions,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
import { AccentColor, MainColor } from "@/constants/color-palet";
import { Ionicons } from "@expo/vector-icons";
import { router, usePathname } from "expo-router";
// Lebar sidebar
const SIDEBAR_WIDTH = Dimensions.get("window").width * 0.8;
interface SidebarItem {
label: string;
icon?: keyof typeof Ionicons.glyphMap;
color?: string;
link?: string;
links?: {
label: string;
link: string;
}[];
initiallyOpened?: boolean;
}
interface SidebarMenuProps {
items: SidebarItem[];
onClose?: () => void;
}
export default function SidebarMenu({ items, onClose }: SidebarMenuProps) {
const pathname = usePathname();
const [activeLink, setActiveLink] = useState<string | null>(null);
const [openKeys, setOpenKeys] = useState<string[]>([]); // Untuk kontrol dropdown
// Normalisasi path: hapus trailing slash
const normalizePath = (path: string) => path.replace(/\/+$/, "");
const normalizedPathname = pathname ? normalizePath(pathname) : "";
// Set activeLink saat pathname berubah
useEffect(() => {
if (normalizedPathname) {
setActiveLink(normalizedPathname);
}
}, [normalizedPathname]);
// Auto-buka dropdown jika submenu aktif
useEffect(() => {
const activeItem = items.find((item) =>
item.links?.some((sub) => sub.link === normalizedPathname)
);
if (activeItem && !openKeys.includes(activeItem.label)) {
setOpenKeys([activeItem.label]);
}
}, [normalizedPathname, items, openKeys]);
// Toggle dropdown
const toggleOpen = (label: string) => {
setOpenKeys((prev) =>
prev.includes(label) ? prev.filter((key) => key !== label) : [label]
);
};
return (
<View style={styles.container}>
{items.map((item) => (
<MenuItem
key={item.label}
item={item}
onClose={onClose}
activeLink={activeLink}
setActiveLink={setActiveLink}
isOpen={openKeys.includes(item.label)}
toggleOpen={() => toggleOpen(item.label)}
/>
))}
</View>
);
}
// Komponen Item Menu
function MenuItem({
item,
onClose,
activeLink,
setActiveLink,
isOpen,
toggleOpen,
}: {
item: SidebarItem;
onClose?: () => void;
activeLink: string | null;
setActiveLink: (link: string | null) => void;
isOpen: boolean;
toggleOpen: () => void;
}) {
const animatedHeight = useRef(new Animated.Value(0)).current;
// Animasi saat isOpen berubah
React.useEffect(() => {
Animated.timing(animatedHeight, {
toValue: isOpen ? (item.links ? item.links.length * 40 : 0) : 0,
duration: 200,
useNativeDriver: false,
}).start();
}, [isOpen, item.links]);
// Cek apakah menu ini aktif
const isActive = activeLink === item.link;
// Cek apakah ada submenu aktif
const hasActiveSubItem = item.links?.some((sub) => sub.link === activeLink);
// Jika ada submenu
if (item.links && item.links.length > 0) {
return (
<View>
{/* Parent Item */}
<TouchableOpacity style={styles.parentItem} onPress={toggleOpen}>
<Ionicons
name={item.icon}
size={20}
color={MainColor.white}
style={styles.icon}
/>
<Text style={styles.parentText}>{item.label}</Text>
<Ionicons
name={isOpen ? "chevron-up" : "chevron-down"}
size={16}
color={MainColor.white}
/>
</TouchableOpacity>
{/* Submenu (Animated) */}
<Animated.View
style={[
styles.submenu,
{
height: animatedHeight,
opacity: animatedHeight.interpolate({
inputRange: [0, item.links.length * 40],
outputRange: [0, 1],
extrapolate: "clamp",
}),
},
]}
>
{item.links.map((subItem, index) => {
const isSubActive = activeLink === subItem.link;
return (
<TouchableOpacity
key={index}
style={[styles.subItem, isSubActive && styles.subItemActive]}
onPress={() => {
setActiveLink(subItem.link);
onClose?.();
router.push(subItem.link as any);
}}
>
<Ionicons
name="caret-forward-sharp"
size={16}
color={isSubActive ? MainColor.yellow : MainColor.white}
style={styles.icon}
/>
<Text
style={[
styles.subText,
isSubActive && { color: MainColor.yellow },
]}
>
{subItem.label}
</Text>
</TouchableOpacity>
);
})}
</Animated.View>
</View>
);
}
// Menu tanpa submenu
return (
<TouchableOpacity
style={[styles.singleItem, isActive && styles.singleItemActive]}
onPress={() => {
setActiveLink(item.link || null);
onClose?.();
router.push(item.link as any);
}}
>
<Ionicons
name={item.icon}
size={20}
color={isActive ? MainColor.yellow : MainColor.white}
style={styles.icon}
/>
<Text
style={[
styles.singleText,
{ color: isActive ? MainColor.yellow : MainColor.white },
]}
>
{item.label}
</Text>
</TouchableOpacity>
);
}
// Styles
const styles = StyleSheet.create({
container: {
marginTop: 20,
},
parentItem: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 12,
paddingHorizontal: 10,
backgroundColor: AccentColor.darkblue,
borderRadius: 8,
marginBottom: 5,
justifyContent: "space-between",
},
parentText: {
flex: 1,
fontSize: 16,
fontWeight: "500",
marginLeft: 10,
color: MainColor.white,
},
singleItem: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 12,
paddingHorizontal: 10,
backgroundColor: AccentColor.darkblue,
borderRadius: 8,
marginBottom: 5,
},
singleItemActive: {
backgroundColor: AccentColor.blue,
},
singleText: {
fontSize: 16,
fontWeight: "500",
marginLeft: 10,
color: MainColor.white,
},
icon: {
width: 24,
textAlign: "center",
paddingRight: 10,
},
submenu: {
overflow: "hidden",
marginLeft: 30,
marginTop: 5,
},
subItem: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 8,
paddingHorizontal: 10,
borderRadius: 6,
marginBottom: 4,
},
subItemActive: {
backgroundColor: AccentColor.blue,
},
subText: {
color: MainColor.white,
fontSize: 14,
},
});

View File

@@ -22,7 +22,7 @@ interface TextCustomProps {
style?: StyleProp<TextStyle>;
bold?: boolean;
semiBold?: boolean;
size?: "default" | "large" | "small" | "xlarge";
size?: "default" | "large" | "small" | "xlarge" | number
color?: "default" | "yellow" | "red" | "gray" | "green" | "black"
align?: TextAlign; // Prop untuk alignment
truncate?: boolean | number;
@@ -54,6 +54,7 @@ const TextCustom: React.FC<TextCustomProps> = ({
if (size === "large") selectedStyles.push(styles.large);
else if (size === "xlarge") selectedStyles.push(styles.xlarge);
else if (size === "small") selectedStyles.push(styles.small);
else if (typeof size === "number") selectedStyles.push({ fontSize: size });
// Color
if (color === "yellow") selectedStyles.push(styles.yellow);
@@ -105,7 +106,7 @@ export const styles = StyleSheet.create({
fontSize: TEXT_SIZE_MEDIUM,
color: MainColor.white,
fontFamily: "Poppins-Regular",
lineHeight: 20,
// lineHeight: 20,
},
bold: {
fontFamily: "Poppins-Bold",

View File

@@ -0,0 +1,13 @@
import ActionIcon from "@/components/ActionIcon/ActionIcon";
import { MainColor } from "@/constants/color-palet";
import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router";
export default function AdminBackButton() {
return (
<ActionIcon
icon={<Ionicons name="arrow-back" size={16} color={MainColor.darkblue} />}
onPress={() => router.back()}
/>
);
}

View File

@@ -0,0 +1,31 @@
import Grid from "@/components/Grid/GridCustom";
import TextCustom from "@/components/Text/TextCustom";
import { View } from "react-native";
import AdminBackButton from "./BackButton";
export default function AdminBackButtonAntTitle({
title,
}: {
title: string;
}) {
return (
<>
<Grid>
<Grid.Col span={2}>
<AdminBackButton />
</Grid.Col>
<Grid.Col
span={8}
style={{ alignItems: "center", justifyContent: "center" }}
>
<TextCustom bold size={"large"} align="center">
{title}
</TextCustom>
</Grid.Col>
<Grid.Col span={2}>
<View />
</Grid.Col>
</Grid>
</>
);
}

View File

@@ -0,0 +1,46 @@
import BaseBox from "@/components/Box/BaseBox";
import CircleContainer from "@/components/Container/CircleContainer";
import Grid from "@/components/Grid/GridCustom";
import StackCustom from "@/components/Stack/StackCustom";
import TextCustom from "@/components/Text/TextCustom";
import { MainColor } from "@/constants/color-palet";
interface BoxDashboardProps {
item: {
label: string;
value: string | number;
icon: React.ReactNode;
};
}
export default function AdminComp_BoxDashboard({ item }: BoxDashboardProps) {
return (
<>
<BaseBox
backgroundColor={MainColor.soft_darkblue}
paddingTop={5}
paddingBottom={5}
>
<Grid containerStyle={{ marginBlock: 0 }}>
<Grid.Col
span={9}
style={{
paddingLeft: 10,
}}
>
<StackCustom gap={"xs"}>
<TextCustom bold>{item.label}</TextCustom>
<TextCustom size={50}>{item.value}</TextCustom>
</StackCustom>
</Grid.Col>
<Grid.Col
span={3}
style={{ alignItems: "flex-start", justifyContent: "center" }}
>
<CircleContainer icon={item.icon} />
</Grid.Col>
</Grid>
</BaseBox>
</>
);
}

View File

@@ -0,0 +1,52 @@
import BaseBox from "@/components/Box/BaseBox";
import Grid from "@/components/Grid/GridCustom";
import TextCustom from "@/components/Text/TextCustom";
import { TEXT_SIZE_LARGE } from "@/constants/constans-value";
export default function AdminComp_BoxTitle({
title,
rightComponent,
}: {
title: string;
rightComponent?: React.ReactNode;
}) {
return (
<>
<BaseBox
style={{ flexDirection: "row", justifyContent: "space-between" }}
paddingTop={5}
paddingBottom={5}
>
{/* <TextCustom
// style={{ alignSelf: "center" }}
bold
size={TEXT_SIZE_LARGE}
>
{title}
</TextCustom>
{rightComponent} */}
<Grid>
<Grid.Col span={6} style={{ justifyContent: "center" }}>
<TextCustom
// style={{ alignSelf: "center" }}
bold
size={TEXT_SIZE_LARGE}
>
{title}
</TextCustom>
</Grid.Col>
<Grid.Col
span={6}
style={{
justifyContent: "center",
alignItems: "flex-end",
}}
>
{rightComponent}
</Grid.Col>
</Grid>
</BaseBox>
</>
);
}

View File

@@ -0,0 +1,25 @@
import Grid from "../Grid/GridCustom";
export default function GridComponentView({
leftIcon,
children,
rightIcon,
}: {
leftIcon?: React.ReactNode;
children: React.ReactNode;
rightIcon?: React.ReactNode;
}) {
return (
<Grid containerStyle={{ marginBottom: 0 }}>
<Grid.Col span={1} style={{ justifyContent: "center" }}>
{leftIcon}
</Grid.Col>
<Grid.Col span={10} style={{ justifyContent: "center" }}>
{children}
</Grid.Col>
<Grid.Col span={1} style={{ justifyContent: "center" }}>
{rightIcon}
</Grid.Col>
</Grid>
);
}

View File

@@ -1,6 +1,8 @@
// Alert
import AlertCustom from "./Alert/AlertCustom";
import AlertDefaultSystem from "./Alert/AlertDefaultSystem";
// ActionIcon
import ActionIcon from "./ActionIcon/ActionIcon";
// Button
import LeftButtonCustom from "./Button/BackButton";
import ButtonCenteredOnly from "./Button/ButtonCenteredOnly";
@@ -55,10 +57,14 @@ import TabBarBackground from "./_ShareComponent/TabBarBackground";
import ViewWrapper from "./_ShareComponent/ViewWrapper";
import SearchInput from "./_ShareComponent/SearchInput";
import DummyLandscapeImage from "./_ShareComponent/DummyLandscapeImage";
import GridComponentView from "./_ShareComponent/GridSectionView";
// Progress
import ProgressCustom from "./Progress/ProgressCustom";
export {
// ActionIcon
ActionIcon,
// Alert
AlertCustom,
AlertDefaultSystem,
// Image
@@ -107,6 +113,7 @@ export {
// ShareComponent
SearchInput,
DummyLandscapeImage,
GridComponentView,
Spacing,
// Stack
StackCustom,

View File

@@ -8,11 +8,13 @@ export {
TEXT_SIZE_MEDIUM,
TEXT_SIZE_LARGE,
TEXT_SIZE_XLARGE,
ICON_SIZE_SMALL,
ICON_SIZE_MEDIUM,
ICON_SIZE_XLARGE as ICON_SIZE_XLARGE,
ICON_SIZE_LARGE as ICON_SIZE_MEDIUM,
ICON_SIZE_MEDIUM as ICON_SIZE_SMALL,
ICON_SIZE_XSMALL as ICON_SIZE_XSMALL,
ICON_SIZE_SMALLL as ICON_SIZE_BUTTON,
DRAWER_HEIGHT,
RADIUS_BUTTON,
ICON_SIZE_BUTTON,
PADDING_EXTRA_SMALL,
PADDING_SMALL,
PADDING_MEDIUM,
@@ -31,9 +33,11 @@ const TEXT_SIZE_LARGE = 16;
const TEXT_SIZE_XLARGE = 18;
// Icon Size
const ICON_SIZE_BUTTON = 18
const ICON_SIZE_SMALL = 20;
const ICON_SIZE_MEDIUM = 24;
const ICON_SIZE_XSMALL = 10
const ICON_SIZE_SMALLL = 14
const ICON_SIZE_MEDIUM = 18;
const ICON_SIZE_LARGE = 22;
const ICON_SIZE_XLARGE = 26;
// Drawer Height
const DRAWER_HEIGHT = 500; // tinggi drawer5

View File

@@ -0,0 +1,123 @@
export const dummyMasterEmotions = [
{
id: 1,
label: "Senang",
value: "senang",
isActive: true,
createdAt: "2025-06-16T09:57:16.726Z",
updatedAt: "2025-06-16T09:57:16.726Z",
},
{
id: 2,
label: "Sedih",
value: "sedih",
isActive: true,
createdAt: "2025-06-16T09:57:16.730Z",
updatedAt: "2025-06-16T09:57:16.730Z",
},
{
id: 15,
label: "Takut",
value: "takut",
isActive: true,
createdAt: "2025-06-16T09:57:16.730Z",
updatedAt: "2025-06-16T09:57:16.730Z",
},
{
id: 6,
label: "Marah",
value: "marah",
isActive: true,
createdAt: "2025-06-16T09:57:16.730Z",
updatedAt: "2025-06-16T09:57:16.730Z",
},
{
id: 8,
label: "Terkejut",
value: "terkejut",
isActive: true,
createdAt: "2025-06-16T09:57:16.730Z",
updatedAt: "2025-06-16T09:57:16.730Z",
},
{
id: 14,
label: "Malas",
value: "malas",
isActive: true,
createdAt: "2025-06-16T09:57:16.730Z",
updatedAt: "2025-06-16T09:57:16.730Z",
},
{
id: 7,
label: "Penasaran",
value: "penasaran",
isActive: true,
createdAt: "2025-06-16T09:57:16.731Z",
updatedAt: "2025-06-16T09:57:16.731Z",
},
{
id: 3,
label: "Cinta",
value: "cinta",
isActive: true,
createdAt: "2025-06-16T09:57:16.731Z",
updatedAt: "2025-06-16T09:57:16.731Z",
},
{
id: 4,
label: "Bangga",
value: "bangga",
isActive: true,
createdAt: "2025-06-16T09:57:16.731Z",
updatedAt: "2025-06-16T09:57:16.731Z",
},
{
id: 5,
label: "Malu",
value: "malu",
isActive: true,
createdAt: "2025-06-16T09:57:16.731Z",
updatedAt: "2025-06-16T09:57:16.731Z",
},
{
id: 11,
label: "Kesal",
value: "kesal",
isActive: true,
createdAt: "2025-06-16T09:57:16.732Z",
updatedAt: "2025-06-16T09:57:16.732Z",
},
{
id: 9,
label: "Bingung",
value: "bingung",
isActive: true,
createdAt: "2025-06-16T09:57:16.732Z",
updatedAt: "2025-06-16T09:57:16.732Z",
},
{
id: 10,
label: "Kaget",
value: "kaget",
isActive: true,
createdAt: "2025-06-16T09:57:16.732Z",
updatedAt: "2025-06-16T09:57:16.732Z",
},
{
id: 12,
label: "Iri",
value: "iri",
isActive: true,
createdAt: "2025-06-16T09:57:16.732Z",
updatedAt: "2025-06-16T09:57:16.732Z",
},
{
id: 13,
label: "Lega",
value: "lega",
isActive: true,
createdAt: "2025-06-16T09:57:16.733Z",
updatedAt: "2025-06-16T09:57:16.733Z",
},
];

View File

@@ -0,0 +1,83 @@
import {
ActionIcon,
Grid,
StackCustom,
TextCustom
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import dummyMasterBidangBisnis from "@/lib/dummy-data/master-bidang-bisnis";
import { FontAwesome5 } from "@expo/vector-icons";
import { router } from "expo-router";
import { useState } from "react";
import { View } from "react-native";
import { Divider, Switch } from "react-native-paper";
export default function AdminAppInformation_BusinessFieldSection() {
const [value, setValue] = useState(false);
const [selectedBusinessField, setSelectedBusinessField] = useState<any>(null);
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>Nama Bidang Bisnis</TextCustom>
</Grid.Col>
</Grid>
<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>
</>
</>
);
}

View File

@@ -0,0 +1,77 @@
import { ActionIcon, Grid, StackCustom, TextCustom } from "@/components";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { dummyMasterBank } from "@/lib/dummy-data/_master/bank";
import { FontAwesome5 } from "@expo/vector-icons";
import { router } from "expo-router";
import { useState } from "react";
import { View } from "react-native";
import { Divider, Switch } from "react-native-paper";
export default function AdminAppInformation_Bank() {
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>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>
</>
</>
);
}

View File

@@ -0,0 +1,109 @@
import { ActionIcon, Grid, StackCustom, TextCustom } from "@/components";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { FontAwesome5 } from "@expo/vector-icons";
import { Image } from "expo-image";
import { router } from "expo-router";
import { useState } from "react";
import { View } from "react-native";
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>
</>
);
}
const listSticker = [
{
id: "1",
label: "Stiker 1",
value: "sticker 1",
path: "https://wibu-storage.wibudev.com/api/files/cmb0dkn5700bjbpnnwcfpzxpz",
},
{
id: "2",
label: "Stiker 2",
value: "sticker 2",
path: "https://wibu-storage.wibudev.com/api/files/cmb0djnya00bhbpnn8b2sfpg3",
},
{
id: "3",
label: "Stiker 3",
value: "sticker 3",
path: "https://wibu-storage.wibudev.com/api/files/cmb0dj0o400bfbpnn6hrfa71y",
},
{
id: "4",
label: "Stiker 4",
value: "sticker 4",
path: "https://wibu-storage.wibudev.com/api/files/cmb0d5rdb00abbpnnrbhxrxjz",
},
{
id: "5",
label: "Stiker 5",
value: "sticker 5",
path: "https://wibu-storage.wibudev.com/api/files/cmb0d4j5q00a9bpnn0qpxa0k4",
},
];

View File

@@ -0,0 +1,96 @@
import { NavbarItem } from "@/components/Drawer/NavbarMenu";
export { adminListMenu }
const adminListMenu: 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" },
{ label: "Review", link: "/admin/investment/review" },
{ label: "Reject", link: "/admin/investment/reject" },
],
},
{
label: "Donasi",
icon: "hand-right",
links: [
{ label: "Dashboard", link: "/admin/donasi" },
{ label: "Publish", link: "/admin/donasi/publish" },
{ label: "Review", link: "/admin/donasi/review" },
{ label: "Reject", link: "/admin/donasi/reject" },
{ label: "Kategori", link: "/admin/donasi/kategori" },
],
},
{
label: "Event",
icon: "calendar-clear",
links: [
{ label: "Dashboard", link: "/admin/event" },
{ label: "Publish", link: "/admin/event/publish" },
{ label: "Review", link: "/admin/event/review" },
{ label: "Reject", link: "/admin/event/reject" },
{ label: "Tipe Acara", link: "/admin/event/tipe-acara" },
{ label: "Riwayat", link: "/admin/event/riwayat" },
],
},
{
label: "Voting",
icon: "accessibility-outline",
links: [
{ label: "Dashboard", link: "/admin/voting" },
{ label: "Publish", link: "/admin/voting/publish" },
{ label: "Review", link: "/admin/voting/review" },
{ label: "Reject", link: "/admin/voting/reject" },
{ label: "Riwayat", link: "/admin/voting/riwayat" },
],
},
{
label: "Job",
icon: "desktop-outline",
links: [
{ label: "Dashboard", link: "/admin/job" },
{ label: "Publish", link: "/admin/job/publish" },
{ label: "Review", link: "/admin/job/review" },
{ label: "Reject", link: "/admin/job/reject" },
],
},
{
label: "Forum",
icon: "chatbubble-ellipses-outline",
links: [
{ label: "Dashboard", link: "/admin/forum" },
{ label: "Posting", link: "/admin/forum/publish" },
{ label: "Report Posting", link: "/admin/forum/review" },
{ label: "Report Comment", link: "/admin/forum/reject" },
],
},
{
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",
},
];

View File

@@ -32,14 +32,15 @@ export default function LoginView() {
// router.navigate("/verification");
// router.navigate(`/(application)/(user)/profile/${id}`);
// router.navigate("/(application)/(user)/home");
router.navigate("/(application)/(user)/home");
// router.navigate(`/(application)/profile/${id}/edit`);
// router.navigate(`/(application)/(user)/portofolio/${id}`)
// router.navigate(`/(application)/(image)/preview-image/${id}`);
// router.replace("/(application)/(user)/event/(tabs)");
// router.replace("/(application)/coba");
// router.navigate("/investment/(tabs)")
router.navigate("/crowdfunding")
// router.navigate("/investment/(tabs)")1
// router.navigate("/crowdfunding")
// router.navigate("/admin/dashboard")
}
return (
@@ -77,9 +78,15 @@ export default function LoginView() {
placeholder="Masukkan nomor"
/>
<Spacing height={20} />
<Spacing />
<ButtonCustom onPress={handleLogin}>Login</ButtonCustom>
<Spacing />
<ButtonCustom onPress={() => router.navigate("/admin/user-access")}>
Admin ( Delete Soon )
</ButtonCustom>
</View>
</ViewWrapper>
);

View File

@@ -52,11 +52,17 @@ export const drawerItemsProfile = ({
label: "Tambah portofolio",
path: `/(application)/portofolio/${id}/create`,
},
// {
// icon: "settings",
// label: "Dashboard Admin",
// path: `/(application)/profile/dashboard-admin`,
// },
{
icon: (
<Ionicons
name="settings"
size={ICON_SIZE_MEDIUM}
color={AccentColor.white}
/>
),
label: "Dashboard Admin",
path: `/(application)/admin/dashboard`,
},
{
icon: (
<Ionicons

View File

@@ -26,9 +26,14 @@ export default function Profile_MenuDrawerSection({
<>
{/* Menu Items */}
<MenuDrawerDynamicGrid
data={drawerItems}
data={drawerItems.map((item) => ({
icon: item.icon,
label: item.label,
path: item.path as any,
color: item.color,
}))}
columns={4} // Ubah ke 2 jika ingin 2 kolom per baris
onPressItem={handlePress}
onPressItem={(item) => handlePress(item as any)}
/>
</>
);

View File

@@ -36,11 +36,11 @@ export const GStyles = StyleSheet.create({
right: 0,
zIndex: 10,
backgroundColor: "transparent",
paddingBlock: PADDING_SMALL,
// paddingBlock: PADDING_EXTRA_SMALL,
paddingInline: PADDING_MEDIUM,
// padding: 16,
// paddingTop: 8,
// paddingBottom: 8,
paddingTop: 8,
paddingBottom: 8,
},
floatingContainer: {
position: "absolute",

View File

@@ -13,5 +13,5 @@
"**/*.tsx",
".expo/types/**/*.ts",
"expo-env.d.ts"
]
, "app/(application)/admin/app-information/business-field/[id]" ]
}