Compare commits

..

86 Commits

Author SHA1 Message Date
a43ddaa9d6 Invesment
Fix:
- styles/global-styles.ts : tambah alignSelfFlexEnd
- my-holding: basic ui
- portofolio.: basic ui
- transaction: basic ui

## No Issue
2025-07-30 11:15:57 +08:00
927db87749 Component
Add:
- upload button : masih percobaan

Utils:
Add:
- file validasi untuk upload file

Pakage
Add:
- expo-document-picker
- expo-file-system

## No Issue
2025-07-30 10:39:54 +08:00
8a514d2670 Investasi
Add:
- lib/dummy-data/investment : list master
- app/(application)/(user)/investment/(tabs)

Main Layout:
Fix:
- app/(application)/(user)/investment/create.tsx

Component
Add:
- Progress

## No Issue
2025-07-29 17:22:11 +08:00
554428b7b4 Crowd
Add:
-  assets/images/constants/crowd-hipmi.png
-  app/(application)/(user)/crowdfunding/
-  app/(application)/(user)/investment/
-  app/(application)/(user)/donation/

Fix:
-  screens/Home/topFeatureSection.tsx
-  app/(application)/(user)/_layout.tsx

## No Issue
2025-07-29 11:41:41 +08:00
befbd1a47d Voting:
Add:
- ComponentDetailDataSection : Tampilan judul, deskripsi, batas waktu
- BoxDetailHistorySection
- (user)/voting/[id]/history

Fix:
Perbaikan component detail data pada:
- screens/Voting/BoxDetailSection.tsx
-  screens/Voting/BoxDetailPublishSection.tsx
- screens/Voting/BoxDetailContribution.tsx

# No Issue
2025-07-29 10:50:24 +08:00
c9a1ac1db5 Voting
Add:
- BoxDetailContribution
- app/(application)/(user)/voting/[id]/contribution.tsx

Fix:
- app/(application)/(user)/voting/(tabs)/contribution.tsx
- app/(application)/(user)/voting/[id]/[status]/detail.tsx
- app/(application)/(user)/voting/[id]/list-of-contributor.tsx
- app/(application)/(user)/voting/[id]/index.tsx

# No Issue
2025-07-28 17:29:52 +08:00
4bcfcb5f5a Icon:
Add:
- IconArchive

Voting
Add:
- detail voting
- BoxDetailHasilVotingSection
- BoxDetailPublishSection
- BoxDetailSection
- ButtonStatusSection

Fix:
- BoxPublishSection
- ReportListSection: Hapus import useless

# No Issue
2025-07-28 16:43:54 +08:00
f21ff744d3 Component
Add:
- TabsTwoHeaderCustom

Voting
Fix:
- history

# Np Issue
2025-07-28 14:51:41 +08:00
b18044f53f Component
Add:
- Badge
- Container

Voting
Add:
- screens/Voting

Fix:
- (tabs)/ index, contribution, status

# No Issue
2025-07-28 12:24:35 +08:00
20258d1fe5 Component
Icon:
- IconContribution
- IconHistory

Voting
Add:
- voting (tabs)
- (user)/_layout : penambahan layout voting

# No Issue
2025-07-25 16:58:06 +08:00
51d696128e Component:
Add: components/_ShareComponent/DummyLandscapeImage.

Job
Add:
- edit & status per id

- BoxDetailSectio
- ButtonStatusSection

Fix:
- index, status, archive: penyesuaian ui

# No Issue
2025-07-25 15:32:10 +08:00
1b1732c7d8 Job
Add : app/(application)/(user)/job/[id]/

Fix:
-  app/(application)/(user)/job/(tabs)/archive.tsx
-  app/(application)/(user)/job/(tabs)/index.tsx
-  app/(application)/(user)/job/(tabs)/status.tsx
-  app/(application)/(user)/job/create.tsx

Package:
Add: expo-clipboard

# No Issue
2025-07-25 14:19:57 +08:00
7528c449eb Component
Add:
-  components/_ShareComponent/SearchInput.tsx

Job
Add:
-  screens/Job/
-  app/(application)/(user)/job/(tabs)/index.tsx

Forum:
Fix:
- app/(application)/(user)/job/(tabs)/index: Component search input terpusat

# No Issue
2025-07-25 11:19:11 +08:00
ed87d4a3f3 Job:
Add:
- app/(application)/(user)/job/

Event:
Fix:
- app/(application)/(user)/event/(tabs)/_layout.tsx : penggunaan icon terpusat

Collaboration:
Fix:
- app/(application)/(user)/collaboration/(tabs)/_layout.tsx : penggunaan icon terpusat

Home
Fix:
- Penambahan onPres ke job

Component
Add:
- Icon: home, status

# No Issue
2025-07-25 10:53:31 +08:00
603003865b Collaboration
Add:
- (user)/collaboration/[id]/[detail]/

Fix:
- collaboration/(tabs)/group

# No Issue
2025-07-24 17:42:16 +08:00
e02ae8e35d Component
Add :
- CheckboxGroup
- components/_Icon/

Fix:
- CheckboxCustom: penambahan fitur untuk checkbox group

Feature:
Collaboration
Add :
- ProjectMainSelectedSection

Fix :
- detail-participant
- detail-project-main

# No Issue
2025-07-24 16:47:16 +08:00
4f8ae2d7e0 Component
Add :
- Checkbox

Fix :
- Drawer: tinggi bisa auto
- AvataraAndOtherHeaderComponent : View pembungkus di hapus

Feature : Collaboration
Fix:
- detail-participant
- detail-project-main
(fix tampilan)

# No Issue
2025-07-24 15:00:35 +08:00
360ac5807c Component
Add:
- Contoh Flatlist untuk tampilan yang membutuhkan load data

# No Issue
2025-07-24 11:53:47 +08:00
8cb0054580 fix
Feature:
Add :
- Collaboration/BoxDetailSection
- detail-participant
-
/detail-project-main

Fix:
- participant

# No Issue
2025-07-24 10:25:33 +08:00
64d5a4308c Component
Add:
- Divider

Collaboration
Fix:
- index

Add:
- list pf participants

# No Issue
2025-07-23 16:40:04 +08:00
30d61c84aa Feature:
Add:
- Component alert system
- Page collaboration Id

Fix:
- Box base
- Menu drawer dibuatkan interface

# No Issue '
2025-07-23 15:40:53 +08:00
70e324e76e Fix:
- nama page > participant

Component
Fix:
- Base bos > clean code

# No Issue
2025-07-23 12:09:38 +08:00
4474b46ff3 Collacoration
Add :
- collaboration page
- collaboration component

Component
Fix:
- Base box : tambah props background

# No Issue
2025-07-23 11:31:58 +08:00
aa4ea9fb0c deskripsi:
Fix: ProfileSection & /dummy-user ( image )
Event: box publisj

# No Issue"
2025-07-23 10:25:40 +08:00
81d86885f4 Deskripsi
Event:
Add  app/(application)/(user)/event/[id]/history

# No Issue"
2025-07-22 15:20:53 +08:00
814f261881 Deskripsi:
Event:
Add history detail
Fix : - nama variabel pada [id]/

Component
Fix:
- Spacing : tipe data num | str
- ScrollView : tambah background

# No Issue "
2025-07-22 14:56:29 +08:00
7889d25a44 Deskripsi:
Event:
Add  app/(application)/(user)/event/(tabs)/contribution.tsx
     app/(application)/(user)/event/(tabs)/history.tsx

Fix modified:   app/(application)/(user)/event/(tabs)/_layout.tsx
        deleted:    app/(application)/(user)/event/(tabs)/kontribusi.tsx
        deleted:    app/(application)/(user)/event/(tabs)/riwayat.tsx
    modified:   app/(application)/(user)/event/[id]/publish.tsx

# No Issue
2025-07-22 12:14:08 +08:00
ce0e82e627 Deskripsi:
Event:
Add file:
- contibution with id
- list of participan
- publish
- BoxDetailPublishSection
- BoxPublishSection
- menuDrawerPublish

Fix:
Event:
- layout dan index\

# No Issue
2025-07-22 12:06:54 +08:00
08dfd62bfd Deskripsi:
Event:
Add menu drawer draft
Fix event layoutk, index (beranda) , kontribusi

Comp
Add share avatar-username

# No Issue
2025-07-22 10:35:47 +08:00
c8cc0f0232 deskripsi
Fix: page event/id/deteil menjadi event/id/status
Feature:
Add  screens/Event/AlertButtonStatusSection
Add app/(application)/(user)/event/[id]/[status]/

# No Issue"
2025-07-21 17:39:37 +08:00
b844a8151d deskripsi:
Fix event: layout dan event deatil
Feature: Button dot, edit screen

# No Issue
2025-07-21 15:23:18 +08:00
f9e96aa077 Deskripsi:
Fix: event beranda dan status > tampilan
Feature: create event dan halaman detail status
Fix: Basebox: Hide safearea kalau ada tabs
Fix: TextCustom: Tambah size xlarge
Fix: ScrollCustom penambhan props value

# No Issue
2025-07-18 16:30:56 +08:00
b8b1efc71e Deskripsi:
- New component: Datetime custom, datetime android, datetime ios
- Fix: event create input datetime

# No Issue
2025-07-18 11:48:24 +08:00
eaf0ebfb0a feature
Deskripsi:
try component: Date input
2025-07-17 17:21:31 +08:00
e68d366d49 Deskripsi:
Fix: ViewWrapper tinggi footer
Style: Constan value untuk tinggi footer per OS

# No Issue
2025-07-15 15:30:11 +08:00
9999f78ed4 deskripsi:
Feat: event create
Fix: tabs event

# No Issue
2025-07-15 15:21:41 +08:00
2be5afe5ca Fix: register text input 2025-07-15 14:15:33 +08:00
24913a9f97 deskripsi:
Fix: portofolio: alert hapus porto
Fix: profile create hapus text input alamat & edit ganti select jenis kelamin
Fix: menu forum
Fix:  button porto

# No Issue
2025-07-15 14:13:08 +08:00
3376336c55 Deskripsi:
- new comp : Radio
- fix comp : Text area > placeholder diatas
- fix page : report forum sudah pakai radio

# No Issue
2025-07-15 12:01:28 +08:00
a0dad5618a feature
Desskripsi:
- new page: other-report-commentar, other-report-posting, report-commentar, report-posting

# No Issue
2025-07-15 11:24:57 +08:00
cce27c26f6 feature & fix
deskripsi:
feature :
- Forum : beranda, edit, detail, create
# No Issue
2025-07-14 17:37:43 +08:00
3211404397 fix
deskripsi:
- forum & forumku
2025-07-14 16:30:32 +08:00
fbde2fd031 feature & fix
deskripsi:
feature :
- forumku

fix :
- forum index
- Back button : bisa custom icon
- Button custom : di tambah href
# No Issue
2025-07-14 14:12:01 +08:00
ac9dae7c5b feature & fix
deskripsi:
feature:
- floating button
- Forum create

fix:
- Base box
- Avatar : penambahan onPres & href
- Text custom : penambahan warna props green
- Text Area : penambhaan hight
- ViewWrapper : penambahan props floating
# No Issue "
2025-07-14 11:56:44 +08:00
5183769a7c feature & fix
deskripsi:
- new component : Scroll
- fix : notifikasi, use search, portofolio item drawer
- new constats : padding value
- fix component :
Text custom : tambah warna gray di props
Text Input: tambah props container Style

# No Issue
2025-07-11 17:34:04 +08:00
3301cf3d7a fix
deskripsi:
- fix page: user search
- fix global-style
- fix component Text Input dan ViewWrapper ( sekarang bisa menambahkan header )
# No Issue
2025-07-10 17:37:54 +08:00
6913e9e4b5 fix
deskripsi:
- fix page maps
- fix ViewWrapper : props styles di tambah
# No Issue
2025-07-10 16:57:49 +08:00
c5798b3127 feature & fix
deskripsi:
- new component Clickable
- new folder (image) untuk take picture dan imaga preview
- fix klik gambar
# No Issue
2025-07-10 16:47:02 +08:00
2c94638e27 fix
deskripsi:
- fix component : Menu drawer
- fix feature : list menui drawer pada portofolio & profile
# No Issue
2025-07-10 15:21:40 +08:00
b8ed577fea feature & fix
deskripsi:
- new component : Center
- fix component : Button > disable props
- feture : fix portofolio > edit, edit logo, edit sosmed
- fix feature : maps > edit map, custom-pin
- fix featue : profile > update photo
# No Issue
2025-07-10 15:03:52 +08:00
e68a18bb89 feature
deskripsi:
- komponen Map
# No Issue
2025-07-10 14:03:01 +08:00
862af44c03 feature
deskripsi:
- portofolio: detail bisnis, maps, media social
- new component divide
# No Issue
2025-07-10 11:58:23 +08:00
71ea0ca5f2 feature & fix
deskripsi:
- perubahan komponen drawer
- penambahan list page drawer portofolio
- new file portofolio: edit, edit logo, edit sosmed
- new file maps: edit, custom-pin
#No Issue
2025-07-10 10:27:30 +08:00
ee7efaef6a fix & featur
deskripsi:
- fix component stack dan drawer
- new page list path profile
2025-07-09 17:22:43 +08:00
bfb029058c feature
deskripsi:
- new page daftar portofolio
2025-07-09 15:29:04 +08:00
b7e774a556 fix
deskripsi:
- perubahan pada component: ButtonCustom, TextArea, TextInput
- fix style global
- tambhan color pada palet
2025-07-09 15:03:41 +08:00
2901d19db0 fix
deksripsi:
- fix Text input exported
2025-07-09 11:52:38 +08:00
5c4dadbe7c fix
dekripsi:
- fix styles Select
2025-07-09 11:40:22 +08:00
6ac122c631 styles
deskripsi:
- fix styles Text input & Text area
2025-07-09 10:58:47 +08:00
16559b94fe feature & fix
deskripsi:
- fix Text input
- feature Box footer & button center
2025-07-09 10:19:02 +08:00
0698e14d36 fix route
deskripsi:
- perbaiki route tujuan pada create profile
2025-07-08 14:18:50 +08:00
3d9672154c fix folder
deskripsi:
- fix folder profile ke (user)
2025-07-08 12:22:15 +08:00
55b4b1fa8d fix folder
deskripsi:
- pindah folder portofolio ke (user)
2025-07-08 12:11:35 +08:00
b80968999e fix
-deskripsi:
- fix folder: map, marketplace, forum ke (user)
2025-07-08 12:04:51 +08:00
6bac89c536 fix folder
deskripsi:
- pindah folder event ke (user)
2025-07-08 11:58:32 +08:00
b9af7e0ca7 fix folder
deksripsi:
- pindah folder user search & notifikasi ke (user)
2025-07-08 11:47:32 +08:00
8abf23fd13 fix
deskripsi:
- fix ViewWrapper : flexibel terhadap keypad
- saat keypad keluar tidak ada lagi space di atas navighation bar

No Issue
2025-07-08 10:46:45 +08:00
1a16b16f47 feature & fix
deksripsi:
feature:
- Information Box
- Create profile
fix:
component: Alet, Avatar, Select
# No Issue
2025-07-04 17:42:22 +08:00
0b1fd05eec feature
deskripsi:
- folder take-picture
2025-07-04 16:32:44 +08:00
b54693caa7 feature
deskripsi:
- new component camera
2025-07-04 15:36:09 +08:00
17e6208aae feature
deskripsi:
- new component select
- fix styles login dan verifikasi
2025-07-03 17:43:52 +08:00
f5cf9e1549 fix component
- buttom radius valuenya constans
- perbaikan bottom pada auth file
2025-07-03 17:05:53 +08:00
7b58e3315f feature
deskripsi:
- tampilan edit profile
- resourcing stack
- fix text input & buttom
2025-07-03 16:35:39 +08:00
101c9053d8 feature
deskripsi:
- new component stack
2025-07-03 14:54:05 +08:00
4a92385d6d fix nama komponen profile 2025-07-03 14:24:10 +08:00
7e39133c2f feature components
deskripsi:
- BaseBox, TextCustom
- Perbaikan home & profile
2025-07-03 11:29:51 +08:00
e2744f0344 fix styles dan feature component text 2025-07-02 14:55:12 +08:00
9667065bb3 fix folder component 2025-07-02 12:20:42 +08:00
23ae416f42 fix component drawer 2025-07-02 11:56:04 +08:00
8fb37db0db feature
deksripsi:
- merapikan folder profile
- Issu: Drawer
2025-07-02 11:43:22 +08:00
258e20751e feature profile
deskripsi:
- drawer & alert
- screen baru: edit profile, update photo, update background, create portofolio
2025-07-01 17:47:51 +08:00
564ea68d29 feature
deksripsi:
- resourcing drawer dan alert
2025-07-01 11:58:54 +08:00
4701fce07c feature profile
deskripsi
- resaourcing drawer
2025-06-30 17:44:00 +08:00
d58304a146 feature event
deskripsi:
- resourcing component event
2025-06-30 16:41:17 +08:00
5577ef5d1e fix custom tabs
deskripsi:
- penambahan style untuk tabs
2025-06-30 12:09:52 +08:00
c1bee77629 new featur
deskripsi:
- search user
- notifications
2025-06-30 10:39:04 +08:00
232 changed files with 15233 additions and 1361 deletions

View File

@@ -1,6 +1,6 @@
{
"expo": {
"name": "hipmi-mobile",
"name": "HIPMI BADUNG",
"slug": "hipmi-mobile",
"version": "1.0.0",
"orientation": "portrait",
@@ -38,7 +38,16 @@
"resizeMode": "contain",
"backgroundColor": "#ffffff"
}
]
],
[
"expo-camera",
{
"cameraPermission": "Allow $(PRODUCT_NAME) to access your camera",
"microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone",
"recordAudioAndroid": true
}
],
"expo-font"
],
"experiments": {
"typedRoutes": true

View File

@@ -0,0 +1,9 @@
import { LandscapeFrameUploaded, ViewWrapper } from "@/components";
export default function PreviewImage() {
return (
<ViewWrapper>
<LandscapeFrameUploaded />
</ViewWrapper>
);
}

View File

@@ -0,0 +1,171 @@
import {
ButtonCustom,
Spacing,
StackCustom,
ViewWrapper
} from "@/components";
import AntDesign from "@expo/vector-icons/AntDesign";
import FontAwesome6 from "@expo/vector-icons/FontAwesome6";
import { CameraType, CameraView, useCameraPermissions } from "expo-camera";
import { Image } from "expo-image";
import * as ImagePicker from "expo-image-picker";
import { router, useLocalSearchParams } from "expo-router";
import { useRef, useState } from "react";
import { Pressable, StyleSheet, Text, View } from "react-native";
export default function TakePicture() {
const { id } = useLocalSearchParams();
// console.log("Take Picture ID >>", id);
const [permission, requestPermission] = useCameraPermissions();
const ref = useRef<CameraView>(null);
const [uri, setUri] = useState<string | null>(null);
const [facing, setFacing] = useState<CameraType>("back");
if (!permission?.granted) {
return (
<View style={styles.container}>
<Text style={{ textAlign: "center" }}>
We need your permission to use the camera
</Text>
<Pressable onPress={requestPermission}>
<Text style={{ color: "blue", marginTop: 10 }}>Grant permission</Text>
</Pressable>
</View>
);
}
const takePicture = async () => {
const photo = await ref.current?.takePictureAsync();
setUri(photo?.uri || null);
};
const pickImage = async () => {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 1,
});
if (!result.canceled) {
setUri(result.assets[0].uri);
}
};
const toggleFacing = () => {
setFacing((prev) => (prev === "back" ? "front" : "back"));
};
const renderPicture = () => {
return (
<View>
<Image
source={uri ? uri : null}
contentFit="contain"
style={{ width: 340, aspectRatio: 1 }}
/>
<Spacing />
<StackCustom >
<ButtonCustom onPress={() => setUri(null)} title="Foto ulang" />
<ButtonCustom
onPress={() => {
console.log("Upload picture >>", id);
router.back();
}}
title="Upload Foto"
/>
</StackCustom>
</View>
);
};
const renderCameraUI = () => {
return (
<View style={styles.cameraOverlay}>
<View style={styles.shutterContainer}>
<Pressable onPress={toggleFacing}>
<FontAwesome6 name="rotate-left" size={32} color="white" />
</Pressable>
<Pressable onPress={takePicture}>
{({ pressed }) => (
<View
style={[
styles.shutterBtn,
{
opacity: pressed ? 0.5 : 1,
},
]}
>
<View style={styles.shutterBtnInner} />
</View>
)}
</Pressable>
<Pressable onPress={pickImage}>
<AntDesign name="folderopen" size={32} color="white" />
</Pressable>
</View>
</View>
);
};
return (
<>
{uri ? (
<ViewWrapper>
<View style={styles.container}>{renderPicture()}</View>
</ViewWrapper>
) : (
<>
<CameraView
style={styles.camera}
ref={ref}
facing={facing}
responsiveOrientationWhenOrientationLocked
/>
{renderCameraUI()}
</>
)}
</>
);
}
const styles = StyleSheet.create({
container: {
justifyContent: "center",
alignItems: "center",
},
camera: {
flex: 1,
width: "100%",
},
cameraOverlay: {
...StyleSheet.absoluteFillObject,
justifyContent: "flex-end",
padding: 44,
},
shutterContainer: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
shutterBtn: {
backgroundColor: "transparent",
borderWidth: 5,
borderColor: "white",
width: 85,
height: 85,
borderRadius: 45,
alignItems: "center",
justifyContent: "center",
},
shutterBtnInner: {
width: 70,
height: 70,
borderRadius: 50,
backgroundColor: "white",
},
});

View File

@@ -0,0 +1,355 @@
import { BackButton } from "@/components";
import LeftButtonCustom from "@/components/Button/BackButton";
import { MainColor } from "@/constants/color-palet";
import { HeaderStyles } from "@/styles/header-styles";
import { Ionicons } from "@expo/vector-icons";
import { router, Stack } from "expo-router";
export default function UserLayout() {
return (
<>
<Stack screenOptions={HeaderStyles}>
<Stack.Screen
name="home"
options={{
title: "HIPMI",
headerLeft: () => (
<Ionicons
name="search"
size={20}
color={MainColor.yellow}
onPress={() => router.push("/user-search")}
/>
),
headerRight: () => (
<Ionicons
name="notifications"
size={20}
color={MainColor.yellow}
onPress={() => router.push("/notifications")}
/>
),
}}
/>
{/* ========== Profile Section ========= */}
<Stack.Screen
name="profile"
options={{
headerShown: false,
}}
/>
{/* ========== Portofolio Section ========= */}
<Stack.Screen
name="portofolio"
options={{
headerShown: false,
}}
/>
{/* ========== User Search Section ========= */}
<Stack.Screen
name="user-search/index"
options={{
title: "Pencarian Pengguna",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== Notification Section ========= */}
<Stack.Screen
name="notifications/index"
options={{
title: "Notifikasi",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== Event Section ========= */}
<Stack.Screen
name="event/(tabs)"
options={{
title: "Event",
headerLeft: () => (
<LeftButtonCustom path="/(application)/(user)/home" />
),
}}
/>
<Stack.Screen
name="event/create"
options={{
title: "Tambah Event",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="event/detail/[id]"
options={{
title: "Event Detail",
headerLeft: () => <LeftButtonCustom />,
}}
/>
<Stack.Screen
name="event/[id]/edit"
options={{
title: "Edit Event",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="event/[id]/list-of-participants"
options={{
title: "Daftar peserta",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== End Event Section ========= */}
{/* ========== Collaboration Section ========= */}
<Stack.Screen
name="collaboration/(tabs)"
options={{
title: "Collaboration",
headerLeft: () => <BackButton path="/home" />,
}}
/>
<Stack.Screen
name="collaboration/create"
options={{
title: "Tambah Proyek",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="collaboration/[id]/list-of-participants"
options={{
title: "Daftar Partisipan",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="collaboration/[id]/detail-participant"
options={{
title: "Partisipasi Proyek",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="collaboration/[id]/edit"
options={{
title: "Edit Proyek",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== End Collaboration Section ========= */}
{/* ========== Voting Section ========= */}
<Stack.Screen
name="voting/create"
options={{
title: "Tambah Voting",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="voting/(tabs)"
options={{
title: "Voting",
headerLeft: () => <BackButton path="/home" />,
}}
/>
<Stack.Screen
name="voting/[id]/edit"
options={{
title: "Edit Voting",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="voting/[id]/list-of-contributor"
options={{
title: "Daftar Kontributor",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== End Voting Section ========= */}
{/* ========== Crowdfunding Section ========= */}
<Stack.Screen
name="crowdfunding/index"
options={{
title: "Crowdfunding",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== End Crowdfunding Section ========= */}
{/* ========== Investment Section ========= */}
<Stack.Screen
name="investment/(tabs)"
options={{
title: "Investasi",
headerLeft: () => <BackButton path="/crowdfunding" />,
}}
/>
<Stack.Screen
name="investment/create"
options={{
title: "Tambah Investasi",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== End Investment Section ========= */}
{/* ========== Donation Section ========= */}
<Stack.Screen
name="donation/create"
options={{
title: "Tambah Donasi",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== End Donation Section ========= */}
{/* ========== Job Section ========= */}
<Stack.Screen
name="job/create"
options={{
title: "Tambah Job",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="job/(tabs)"
options={{
title: "Job Vacancy",
headerLeft: () => <BackButton path="/home" />,
}}
/>
<Stack.Screen
name="job/[id]/index"
options={{
title: "Detail Job",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="job/[id]/edit"
options={{
title: "Edit Job",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== End Job Section ========= */}
{/* ========== Forum Section ========= */}
<Stack.Screen
name="forum/create"
options={{
title: "Tambah Diskusi",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="forum/[id]/edit"
options={{
title: "Edit Diskusi",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="forum/[id]/forumku"
options={{
title: "Forumku",
headerLeft: () => <BackButton icon={"close"} />,
}}
/>
<Stack.Screen
name="forum/[id]/index"
options={{
title: "Detail",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="forum/[id]/report-commentar"
options={{
title: "Laporkan Komentar",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="forum/[id]/other-report-commentar"
options={{
title: "Laporkan Komentar",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="forum/[id]/report-posting"
options={{
title: "Laporkan Diskusi",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="forum/[id]/other-report-posting"
options={{
title: "Laporkan Diskusi",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== Maps Section ========= */}
<Stack.Screen
name="maps/index"
options={{
title: "Maps",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="maps/create"
options={{
title: "Tambah Maps",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="maps/[id]/edit"
options={{
title: "Edit Maps",
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="maps/[id]/custom-pin"
options={{
title: "Custom Pin Maps",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== Marketplace Section ========= */}
<Stack.Screen
name="marketplace/index"
options={{
title: "Market Place",
headerLeft: () => <BackButton />,
}}
/>
</Stack>
</>
);
}

View File

@@ -0,0 +1,36 @@
import { IconHome } from "@/components/_Icon";
import { TabsStyles } from "@/styles/tabs-styles";
import { Ionicons } from "@expo/vector-icons";
import { Tabs } from "expo-router";
export default function CollaborationTabsLayout() {
return (
<Tabs screenOptions={TabsStyles}>
<Tabs.Screen
name="index"
options={{
title: "Beranda",
tabBarIcon: ({ color }) => <IconHome color={color} />,
}}
/>
<Tabs.Screen
name="participant"
options={{
title: "Partisipan",
tabBarIcon: ({ color }) => (
<Ionicons size={20} name="people" color={color} />
),
}}
/>
<Tabs.Screen
name="group"
options={{
title: "Grup",
tabBarIcon: ({ color }) => (
<Ionicons size={20} name="chatbox-ellipses" color={color} />
),
}}
/>
</Tabs>
);
}

View File

@@ -0,0 +1,68 @@
import { BaseBox, Grid, TextCustom } from "@/components";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { MainColor } from "@/constants/color-palet";
import { Feather } from "@expo/vector-icons";
export default function CollaborationGroup() {
return (
<ViewWrapper hideFooter>
{Array.from({ length: 10 }).map((_, index) => (
<BaseBox
key={index}
paddingBlock={5}
href={`/collaboration/${index}/${generateProjectName()}/room-chat`}
>
<Grid>
<Grid.Col span={10}>
<TextCustom bold>{generateProjectName()}</TextCustom>
<TextCustom size="small">2 Anggota</TextCustom>
</Grid.Col>
<Grid.Col
span={2}
style={{ alignItems: "flex-end", justifyContent: "center" }}
>
<Feather name="chevron-right" size={20} color={MainColor.white} />
</Grid.Col>
</Grid>
</BaseBox>
))}
</ViewWrapper>
);
}
function generateProjectName() {
const adjectives = [
"Blue",
"Dark",
"Bright",
"Quantum",
"Silent",
"Cyber",
"Epic",
"Golden",
"Shadow",
"Rapid",
];
const nouns = [
"Spark",
"Core",
"Orbit",
"Nest",
"Drive",
"Nova",
"Cloud",
"Blade",
"Matrix",
"Link",
];
const randomAdjective =
adjectives[Math.floor(Math.random() * adjectives.length)];
const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
return randomAdjective + randomNoun;
}

View File

@@ -0,0 +1,28 @@
import { FloatingButton, ViewWrapper } from "@/components";
import Collaboration_BoxPublishSection from "@/screens/Collaboration/BoxPublishSection";
import { router } from "expo-router";
export default function CollaborationBeranda() {
return (
<>
<ViewWrapper
hideFooter
floatingButton={
<FloatingButton
onPress={() => {
router.push("/collaboration/create");
}}
/>
}
>
{Array.from({ length: 10 }).map((_, index) => (
<Collaboration_BoxPublishSection
key={index}
id={index.toString()}
href={`/collaboration/${index}`}
/>
))}
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,77 @@
import { ButtonCustom, Spacing } from "@/components";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { AccentColor, MainColor } from "@/constants/color-palet";
import Collaboration_BoxPublishSection from "@/screens/Collaboration/BoxPublishSection";
import { useState } from "react";
import { View } from "react-native";
export default function CollaborationParticipans() {
const [activeCategory, setActiveCategory] = useState<string | null>(
"participant"
);
const handlePress = (item: any) => {
setActiveCategory(item);
// tambahkan logika lain seperti filter dsb.
};
const headerComponent = (
<View
style={{
flexDirection: "row",
alignItems: "center",
padding: 5,
backgroundColor: MainColor.soft_darkblue,
borderRadius: 50,
width: "100%",
}}
>
<ButtonCustom
backgroundColor={
activeCategory === "participant" ? MainColor.yellow : AccentColor.blue
}
textColor={
activeCategory === "participant" ? MainColor.black : MainColor.white
}
style={{ width: "49%" }}
onPress={() => handlePress("participant")}
>
Partisipasi Proyek
</ButtonCustom>
<Spacing width={"2%"} />
<ButtonCustom
backgroundColor={
activeCategory === "main" ? MainColor.yellow : AccentColor.blue
}
textColor={
activeCategory === "main" ? MainColor.black : MainColor.white
}
style={{ width: "49%" }}
onPress={() => handlePress("main")}
>
Proyek Saya
</ButtonCustom>
</View>
);
return (
<ViewWrapper hideFooter headerComponent={headerComponent}>
{Array.from({ length: 10 }).map((_, index) => (
<Collaboration_BoxPublishSection
key={index.toString()}
id={index.toString()}
username={` ${
activeCategory === "participant"
? "Partisipasi Proyek"
: "Proyek Saya"
}`}
href={
activeCategory === "participant"
? `/collaboration/${index}/detail-participant`
: `/collaboration/${index}/detail-project-main`
}
/>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,73 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import {
AvatarUsernameAndOtherComponent,
BackButton,
BoxWithHeaderSection,
Grid,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { Stack, useLocalSearchParams } from "expo-router";
export default function CollaborationRoomInfo() {
const { id, detail } = useLocalSearchParams();
return (
<>
<Stack.Screen
options={{
title: `Info`,
headerLeft: () => <BackButton />,
}}
/>
<ViewWrapper>
<BoxWithHeaderSection>
<StackCustom>
{listData.map((item, index) => (
<Grid key={index}>
<Grid.Col span={4}>
<TextCustom bold>{item.title}</TextCustom>
</Grid.Col>
<Grid.Col span={8}>
<TextCustom>{item.value}</TextCustom>
</Grid.Col>
</Grid>
))}
</StackCustom>
</BoxWithHeaderSection>
<BoxWithHeaderSection>
{Array.from({ length: 10 }).map((_, index) => (
<AvatarUsernameAndOtherComponent key={index} avatarHref={`/profile/${index}`} />
))}
</BoxWithHeaderSection>
</ViewWrapper>
</>
);
}
const listData = [
{
title: "Judul Proyek",
value: "Judul Proyek",
},
{
title: "Industri",
value: "Pilihan Industri",
},
{
title: "Deskripsi",
value: "Deskripsi Proyek",
},
{
title: "Tujuan Proyek",
value:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
},
{
title: "Keuntungan Proyek",
value:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
},
];

View File

@@ -0,0 +1,192 @@
import {
BackButton,
BoxButtonOnFooter,
Grid,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import { AccentColor, MainColor } from "@/constants/color-palet";
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import { Feather } from "@expo/vector-icons";
import { router, Stack, useLocalSearchParams } from "expo-router";
import { StyleSheet, TouchableOpacity, View } from "react-native";
export default function CollaborationRoomChat() {
const { id, detail } = useLocalSearchParams();
const inputChat = () => {
return (
<>
<BoxButtonOnFooter>
{/* <View style={{flexDirection: 'row', alignItems: 'center'}}>
<TextInputCustom placeholder="Ketik pesan..." />
<TouchableOpacity
activeOpacity={0.5}
onPress={() => console.log("Send")}
style={{
backgroundColor: AccentColor.blue,
padding: 10,
borderRadius: 50,
}}
>
<Feather name="send" size={30} color={MainColor.white} />
</TouchableOpacity>
</View> */}
<Grid>
<Grid.Col span={9}>
<TextInputCustom placeholder="Ketik pesan..." />
</Grid.Col>
<Grid.Col span={1}>
<View />
</Grid.Col>
<Grid.Col span={2} style={{ alignItems: "center" }}>
<TouchableOpacity
activeOpacity={0.5}
onPress={() => console.log("Send")}
style={{
backgroundColor: AccentColor.blue,
padding: 10,
borderRadius: 50,
}}
>
<Feather
name="send"
size={30}
color={MainColor.white}
/>
</TouchableOpacity>
</Grid.Col>
</Grid>
</BoxButtonOnFooter>
</>
);
};
return (
<>
<Stack.Screen
options={{
title: `Proyek ${detail}`,
headerLeft: () => <BackButton />,
headerRight: () => (
<Feather
name="info"
size={ICON_SIZE_SMALL}
color={MainColor.yellow}
onPress={() => router.push(`/collaboration/${id}/${detail}/info`)}
/>
),
}}
/>
<ViewWrapper footerComponent={inputChat()}>
{dummyData.map((item, index) => (
<View
key={index}
style={[
styles.messageRow,
item.role === 1 ? styles.rightAlign : styles.leftAlign,
]}
>
<View
style={[
styles.bubble,
item.role === 1 ? styles.bubbleRight : styles.bubbleLeft,
]}
>
<TextCustom style={styles.sender}>{item.nama}</TextCustom>
<TextCustom style={styles.message}>{item.chat}</TextCustom>
<TextCustom style={styles.time}>
{new Date(item.time).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}
</TextCustom>
</View>
</View>
))}
{/* <TextInputCustom placeholder="Ketik pesan..." />
<Spacing/> */}
</ViewWrapper>
</>
);
}
const dummyData = [
{
nama: "Dina",
role: 1,
chat: "Hai! Kamu udah lihat dokumen proyek yang baru?",
time: "2025-07-24T09:01:15Z",
},
{
nama: "Rafi",
role: 2,
chat: "Halo! Iya, aku baru aja baca. Kayaknya kita harus revisi bagian akhir deh.",
time: "2025-07-24T09:02:03Z",
},
{
nama: "Dina",
role: 1,
chat: "Setuju. Aku juga kurang sreg sama penutupnya.",
time: "2025-07-24T09:02:45Z",
},
{
nama: "Rafi",
role: 2,
chat: "Oke, aku coba edit malam ini ya. Nanti aku share ulang versinya.",
time: "2025-07-24T09:03:10Z",
},
{
nama: "Dina",
role: 1,
chat: "Siap, makasih ya. Jangan begadang!",
time: "2025-07-24T09:03:30Z",
},
];
const styles = StyleSheet.create({
container: {
paddingVertical: 10,
paddingHorizontal: 12,
},
messageRow: {
flexDirection: "row",
marginBottom: 12,
},
rightAlign: {
justifyContent: "flex-end",
},
leftAlign: {
justifyContent: "flex-start",
},
bubble: {
maxWidth: "75%",
padding: 10,
borderRadius: 12,
},
bubbleRight: {
backgroundColor: "#DCF8C6", // hijau muda
borderTopRightRadius: 0,
},
bubbleLeft: {
backgroundColor: "#F0F0F0", // abu-abu terang
borderTopLeftRadius: 0,
},
sender: {
fontSize: 12,
fontWeight: "bold",
marginBottom: 2,
color: "#555",
},
message: {
fontSize: 15,
color: "#000",
},
time: {
fontSize: 10,
color: "#888",
textAlign: "right",
marginTop: 4,
},
});

View File

@@ -0,0 +1,62 @@
import {
AvatarUsernameAndOtherComponent,
BaseBox,
DrawerCustom,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
import { MaterialIcons } from "@expo/vector-icons";
import { useLocalSearchParams } from "expo-router";
import { useState } from "react";
export default function CollaborationDetailParticipant() {
const { id } = useLocalSearchParams();
const [openDrawerParticipant, setOpenDrawerParticipant] = useState(false);
return (
<>
<ViewWrapper>
<Collaboration_BoxDetailSection id={id as string} />
<BaseBox style={{ height: 500 }}>
<TextCustom align="center" bold size="large">
Partisipan
</TextCustom>
{Array.from({ length: 5 }).map((_, index) => (
<AvatarUsernameAndOtherComponent
key={index}
avatarHref={`/profile/${index}`}
rightComponent={
<MaterialIcons
name="notes"
size={ICON_SIZE_SMALL}
color="white"
onPress={() => setOpenDrawerParticipant(true)}
/>
}
/>
))}
</BaseBox>
</ViewWrapper>
<DrawerCustom
isVisible={openDrawerParticipant}
closeDrawer={() => setOpenDrawerParticipant(false)}
height={"auto"}
>
<StackCustom>
<TextCustom bold>Deskripsi Diri</TextCustom>
<TextCustom>
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Commodi,
itaque adipisci. Voluptas, sed quod! Ad facere labore voluptates,
neque quidem aut reprehenderit ducimus mollitia quisquam temporibus!
Temporibus iusto soluta necessitatibus.
</TextCustom>
</StackCustom>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,106 @@
import {
AlertDefaultSystem,
BackButton,
ButtonCustom,
DotButton,
DrawerCustom,
MenuDrawerDynamicGrid,
Spacing,
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
import { IconEdit } from "@/components/_Icon";
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
import Collaboration_MainParticipanSelectedSection from "@/screens/Collaboration/ProjectMainSelectedSection";
import { router, Stack, useLocalSearchParams } from "expo-router";
import { useState } from "react";
export default function CollaborationDetailProjectMain() {
const { id } = useLocalSearchParams();
const [openDrawer, setOpenDrawer] = useState(false);
const [openDrawerParticipant, setOpenDrawerParticipant] = useState(false);
const [selected, setSelected] = useState<(string | number)[]>([]);
const handleEdit = () => {
console.log("Edit collaboration");
router.push("/(application)/(user)/collaboration/(id)/edit");
};
return (
<>
<Stack.Screen
options={{
title: "Proyek Saya",
headerLeft: () => <BackButton />,
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
}}
/>
<ViewWrapper>
<Collaboration_BoxDetailSection id={id as string} />
<Collaboration_MainParticipanSelectedSection
selected={selected}
setSelected={setSelected}
setOpenDrawerParticipant={setOpenDrawerParticipant}
/>
<ButtonCustom
onPress={() => {
AlertDefaultSystem({
title: "Buat Grup",
message:
"Apakah anda yakin ingin membuat grup untuk proyek ini ?",
textLeft: "Tidak",
textRight: "Ya",
onPressLeft: () => {},
onPressRight: () => {
router.navigate(
"/(application)/(user)/collaboration/(tabs)/group"
);
console.log("selected :", selected);
},
});
}}
>
Buat Grup
</ButtonCustom>
<Spacing />
</ViewWrapper>
<DrawerCustom
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
height={"auto"}
>
<MenuDrawerDynamicGrid
data={[
{
label: "Edit",
path: "/(application)/(user)/collaboration/(tabs)/group",
icon: <IconEdit />,
},
]}
onPressItem={(item) => {
handleEdit();
}}
/>
</DrawerCustom>
<DrawerCustom
isVisible={openDrawerParticipant}
closeDrawer={() => setOpenDrawerParticipant(false)}
height={"auto"}
>
<StackCustom>
<TextCustom bold>Deskripsi Diri</TextCustom>
<TextCustom>
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Commodi,
itaque adipisci. Voluptas, sed quod! Ad facere labore voluptates,
neque quidem aut reprehenderit ducimus mollitia quisquam temporibus!
Temporibus iusto soluta necessitatibus.
</TextCustom>
</StackCustom>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,53 @@
import {
ButtonCustom,
SelectCustom,
StackCustom,
TextAreaCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import { router } from "expo-router";
export default function CollaborationEdit() {
return (
<ViewWrapper>
<StackCustom gap={"xs"}>
<TextInputCustom label="Judul" placeholder="Masukan judul" required />
<TextInputCustom label="Lokasi" placeholder="Masukan lokasi" required />
<SelectCustom
label="Pilih Industri"
data={[
{ label: "Industri 1", value: "industri-1" },
{ label: "Industri 2", value: "industri-2" },
{ label: "Industri 3", value: "industri-3" },
]}
onChange={(value) => console.log(value)}
/>
<TextAreaCustom
required
label="Tujuan Proyek"
placeholder="Masukan tujuan proyek"
showCount
maxLength={1000}
/>
<TextAreaCustom
required
label="Keuntungan Proyek"
placeholder="Masukan keuntungan proyek"
showCount
maxLength={1000}
/>
<ButtonCustom
title="Update"
onPress={() => {
console.log("Update proyek");
router.back();
}}
/>
</StackCustom>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,91 @@
import {
AlertDefaultSystem,
BackButton,
ButtonCustom,
DotButton,
DrawerCustom,
MenuDrawerDynamicGrid,
TextAreaCustom,
ViewWrapper,
} from "@/components";
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
import { Ionicons } from "@expo/vector-icons";
import { router, Stack, useLocalSearchParams } from "expo-router";
import { useState } from "react";
export default function CollaborationDetail() {
const { id } = useLocalSearchParams();
const [openDrawerPartisipasi, setOpenDrawerPartisipasi] = useState(false);
const [openDrawerMenu, setOpenDrawerMenu] = useState(false);
return (
<>
<Stack.Screen
options={{
title: "Detail Proyek",
headerLeft: () => <BackButton />,
headerRight: () => (
<DotButton onPress={() => setOpenDrawerMenu(true)} />
),
}}
/>
<ViewWrapper>
<Collaboration_BoxDetailSection id={id as string} />
<ButtonCustom onPress={() => setOpenDrawerPartisipasi(true)}>
Partisipasi
</ButtonCustom>
</ViewWrapper>
{/* Drawer Partisipasi */}
<DrawerCustom
isVisible={openDrawerPartisipasi}
closeDrawer={() => setOpenDrawerPartisipasi(false)}
height={300}
>
<TextAreaCustom
label="Dekripsi diri"
placeholder="Masukan dekripsi diri"
required
showCount
maxLength={500}
/>
<ButtonCustom
style={{ alignSelf: "flex-end" }}
onPress={() => {
AlertDefaultSystem({
title: "Simpan data deskripsi",
message: "Apakah anda sudah yakin ingin menyimpan data ini ?",
textLeft: "Batal",
textRight: "Simpan",
onPressRight: () => router.replace(`/collaboration/(tabs)/group`),
});
}}
>
Simpan
</ButtonCustom>
</DrawerCustom>
{/* Drawer Menu */}
<DrawerCustom
isVisible={openDrawerMenu}
closeDrawer={() => setOpenDrawerMenu(false)}
height={250}
>
<MenuDrawerDynamicGrid
data={[
{
icon: <Ionicons name="people" size={24} color="white" />,
label: "Daftar Partisipan",
path: `/collaboration/${id}/list-of-participants`,
},
]}
onPressItem={(item) => {
router.push(item.path as any);
setOpenDrawerMenu(false);
}}
/>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,82 @@
import {
AvatarUsernameAndOtherComponent,
BaseBox,
DrawerCustom,
Spacing,
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
import { Feather } from "@expo/vector-icons";
import { useLocalSearchParams } from "expo-router";
import { useState } from "react";
import { ScrollView } from "react-native";
export default function CollaborationListOfParticipants() {
const { id } = useLocalSearchParams();
const [openDrawer, setOpenDrawer] = useState(false);
return (
<>
<ViewWrapper>
{Array.from({ length: 10 }).map((_, index) => (
<BaseBox key={index} paddingBlock={5}>
<AvatarUsernameAndOtherComponent
avatarHref={`/profile/${id}`}
rightComponent={
<Feather
name="chevron-right"
size={24}
color="white"
onPress={() => setOpenDrawer(true)}
/>
}
/>
</BaseBox>
))}
</ViewWrapper>
{/* Drawer */}
<DrawerCustom
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
>
<StackCustom>
<TextCustom bold>Deskripsi diri</TextCustom>
<BaseBox>
<ScrollView style={{ height: "80%" }}>
<TextCustom>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.Lorem
ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.Lorem
ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.Lorem
ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut iqua.Lorem ipsum dolor sit amet,
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua.
</TextCustom>
</ScrollView>
</BaseBox>
<Spacing />
</StackCustom>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,53 @@
import {
ButtonCustom,
SelectCustom,
StackCustom,
TextAreaCustom,
TextInputCustom,
ViewWrapper
} from "@/components";
import { router } from "expo-router";
export default function CollaborationCreate() {
return (
<ViewWrapper>
<StackCustom gap={"xs"}>
<TextInputCustom label="Judul" placeholder="Masukan judul" required />
<TextInputCustom label="Lokasi" placeholder="Masukan lokasi" required />
<SelectCustom
label="Pilih Industri"
data={[
{ label: "Industri 1", value: "industri-1" },
{ label: "Industri 2", value: "industri-2" },
{ label: "Industri 3", value: "industri-3" },
]}
onChange={(value) => console.log(value)}
/>
<TextAreaCustom
required
label="Tujuan Proyek"
placeholder="Masukan tujuan proyek"
showCount
maxLength={1000}
/>
<TextAreaCustom
required
label="Keuntungan Proyek"
placeholder="Masukan keuntungan proyek"
showCount
maxLength={1000}
/>
<ButtonCustom
title="Simpan"
onPress={() => {
console.log("Simpan proyek");
router.back();
}}
/>
</StackCustom>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,68 @@
import {
BaseBox,
Grid,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import { Feather } from "@expo/vector-icons";
import { Image } from "expo-image";
export default function Crowdfunding() {
const listPage = [
{
title: "Investasi",
desc: "Buat investasi dan jual beli saham lebih mudah dengan pengguna lain.",
path: "investment/(tabs)",
},
{
title: "Donasi",
desc: "Berbagi info untuk berdonasi lebih luas dan lebih efisien.",
path: "donation/create",
},
];
return (
<ViewWrapper>
<StackCustom>
<Image
source={require("@/assets/images/constants/crowd-hipmi.png")}
contentFit="cover"
transition={1000}
style={{
width: "100%",
height: 200,
borderRadius: 10,
}}
/>
{listPage.map((item, index) => (
<BaseBox key={index} paddingTop={10} paddingBottom={10} href={item.path as any} marginBottom={0}>
<Grid>
<Grid.Col span={10}>
<StackCustom gap={"xs"}>
<TextCustom bold size="large">
{item.title}
</TextCustom>
<TextCustom>{item.desc}</TextCustom>
</StackCustom>
</Grid.Col>
<Grid.Col
span={2}
style={{ alignItems: "flex-end", justifyContent: "center" }}
>
<Feather
name="chevron-right"
size={ICON_SIZE_SMALL}
color={MainColor.white}
/>
</Grid.Col>
</Grid>
</BaseBox>
))}
</StackCustom>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,11 @@
import { TextCustom, ViewWrapper } from "@/components";
export default function DonationCreate() {
return (
<ViewWrapper>
<TextCustom bold size="large">
Coming Soon !
</TextCustom>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,43 @@
import {
IconContribution,
IconHistory,
IconHome,
IconStatus,
} from "@/components/_Icon";
import { TabsStyles } from "@/styles/tabs-styles";
import { Tabs } from "expo-router";
export default function EventTabsLayout() {
return (
<Tabs screenOptions={TabsStyles}>
<Tabs.Screen
name="index"
options={{
title: "Beranda",
tabBarIcon: ({ color }) => <IconHome color={color} />,
}}
/>
<Tabs.Screen
name="status"
options={{
title: "Status",
tabBarIcon: ({ color }) => <IconStatus color={color} />,
}}
/>
<Tabs.Screen
name="contribution"
options={{
title: "Kontribusi",
tabBarIcon: ({ color }) => <IconContribution color={color} />,
}}
/>
<Tabs.Screen
name="history"
options={{
title: "Riwayat",
tabBarIcon: ({ color }) => <IconHistory color={color} />,
}}
/>
</Tabs>
);
}

View File

@@ -0,0 +1,43 @@
import {
AvatarCustom,
AvatarUsernameAndOtherComponent,
BoxWithHeaderSection,
Grid,
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
import React from "react";
export default function EventContribution() {
return (
<ViewWrapper hideFooter>
{Array.from({ length: 10 }).map((_, index) => (
<BoxWithHeaderSection key={index} href={`/event/${index}/contribution`}>
<StackCustom>
<AvatarUsernameAndOtherComponent
avatarHref={`/profile/${index}`}
rightComponent={
<TextCustom truncate>
{new Date().toLocaleDateString()}
</TextCustom>
}
/>
<TextCustom bold align="center" size="xlarge">
Judul Event Disini
</TextCustom>
<Grid>
{Array.from({ length: 4 }).map((_, index2) => (
<Grid.Col span={3} key={index2}>
<AvatarCustom size="sm" href={`/profile/${index2}`} />
</Grid.Col>
))}
</Grid>
</StackCustom>
</BoxWithHeaderSection>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,68 @@
import { ButtonCustom, Spacing, TextCustom } from "@/components";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { AccentColor, MainColor } from "@/constants/color-palet";
import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection";
import { useState } from "react";
import { View } from "react-native";
export default function EventHistory() {
const [activeCategory, setActiveCategory] = useState<string | null>("all");
const handlePress = (item: any) => {
setActiveCategory(item);
// tambahkan logika lain seperti filter dsb.
};
const headerComponent = (
<View
style={{
flexDirection: "row",
alignItems: "center",
padding: 5,
backgroundColor: MainColor.soft_darkblue,
borderRadius: 50,
width: "100%",
}}
>
<ButtonCustom
backgroundColor={
activeCategory === "all" ? MainColor.yellow : AccentColor.blue
}
textColor={activeCategory === "all" ? MainColor.black : MainColor.white}
style={{ width: "49%" }}
onPress={() => handlePress("all")}
>
Semua Riwayat
</ButtonCustom>
<Spacing width={"2%"} />
<ButtonCustom
backgroundColor={
activeCategory === "main" ? MainColor.yellow : AccentColor.blue
}
textColor={
activeCategory === "main" ? MainColor.black : MainColor.white
}
style={{ width: "49%" }}
onPress={() => handlePress("main")}
>
Riwayat Saya
</ButtonCustom>
</View>
);
return (
<ViewWrapper headerComponent={headerComponent} hideFooter>
{Array.from({ length: 10 }).map((_, index) => (
<Event_BoxPublishSection
key={index.toString()}
id={index.toString()}
username={`Riwayat ${activeCategory === "main" ? "Saya" : "Semua"}`}
rightComponentAvatar={
<TextCustom>{new Date().toLocaleDateString()}</TextCustom>
}
href={`/event/${index}/history`}
/>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,23 @@
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import FloatingButton from "@/components/Button/FloatingButton";
import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection";
import { router } from "expo-router";
export default function EventBeranda() {
return (
<ViewWrapper
hideFooter
floatingButton={
<FloatingButton onPress={() => router.push("/event/create")} />
}
>
{Array.from({ length: 10 }).map((_, index) => (
<Event_BoxPublishSection
key={index}
id={index.toString()}
href={`/event/${index}/publish`}
/>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,63 @@
import {
BoxWithHeaderSection,
Grid,
ScrollableCustom,
StackCustom,
TextCustom
} from "@/components";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { masterStatus } from "@/lib/dummy-data/_master/status";
import { useState } from "react";
export default function EventStatus() {
const id = "test-id-event";
const [activeCategory, setActiveCategory] = useState<string | null>(
"publish"
);
const handlePress = (item: any) => {
setActiveCategory(item.value);
// tambahkan logika lain seperti filter dsb.
};
const scrollComponent = (
<ScrollableCustom
data={masterStatus.map((e, i) => ({
id: i,
label: e.label,
value: e.value,
}))}
onButtonPress={handlePress}
activeId={activeCategory as any}
/>
);
return (
<ViewWrapper headerComponent={scrollComponent}>
<BoxWithHeaderSection href={`/event/${id}/${activeCategory}/detail-event`}>
<StackCustom gap={"xs"}>
<Grid>
<Grid.Col span={8}>
<TextCustom truncate bold>
Lorem ipsum,{" "}
<TextCustom color="green">{activeCategory}</TextCustom> dolor
sit amet consectetur adipisicing elit.
</TextCustom>
</Grid.Col>
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
<TextCustom>{new Date().toLocaleDateString()}</TextCustom>
</Grid.Col>
</Grid>
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur
eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora,
atque. Aperiam minima asperiores dicta perferendis quis adipisci,
dolore optio porro!
</TextCustom>
</StackCustom>
</BoxWithHeaderSection>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,117 @@
import {
BaseBox,
DotButton,
DrawerCustom,
Grid,
MenuDrawerDynamicGrid,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { IMenuDrawerItem } from "@/components/_Interface/types";
import LeftButtonCustom from "@/components/Button/BackButton";
import Event_AlertButtonStatusSection from "@/screens/Event/AlertButtonStatusSection";
import Event_ButtonStatusSection from "@/screens/Event/ButtonStatusSection";
import { menuDrawerDraftEvent } from "@/screens/Event/menuDrawerDraft";
import { router, Stack, useLocalSearchParams } from "expo-router";
import { useState } from "react";
export default function EventDetailStatus() {
const { id, status } = useLocalSearchParams();
const [openDrawer, setOpenDrawer] = useState(false);
const [openAlert, setOpenAlert] = useState(false);
const [openDeleteAlert, setOpenDeleteAlert] = useState(false);
const handlePress = (item: IMenuDrawerItem) => {
console.log("PATH >> ", item.path);
router.navigate(item.path as any);
setOpenDrawer(false);
};
return (
<>
<Stack.Screen
options={{
title: `Detail ${status === "publish" ? "" : status}`,
headerLeft: () => <LeftButtonCustom />,
headerRight: () =>
status === "draft" ? (
<DotButton onPress={() => setOpenDrawer(true)} />
) : null,
}}
/>
<ViewWrapper>
<BaseBox>
<StackCustom>
<TextCustom bold align="center" size="xlarge">
Judul event {status}
</TextCustom>
{listData.map((item, index) => (
<Grid key={index}>
<Grid.Col span={4}>
<TextCustom bold>{item.title}</TextCustom>
</Grid.Col>
<Grid.Col span={8}>
<TextCustom>{item.value}</TextCustom>
</Grid.Col>
</Grid>
))}
</StackCustom>
</BaseBox>
<Event_ButtonStatusSection
status={status as string}
onOpenAlert={setOpenAlert}
onOpenDeleteAlert={setOpenDeleteAlert}
/>
<Spacing />
</ViewWrapper>
<DrawerCustom
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
height={250}
>
<MenuDrawerDynamicGrid
data={menuDrawerDraftEvent({ id: id as string }) as any}
columns={4}
onPressItem={handlePress as any}
/>
</DrawerCustom>
<Event_AlertButtonStatusSection
id={id as string}
status={status as string}
openAlert={openAlert}
setOpenAlert={setOpenAlert}
openDeleteAlert={openDeleteAlert}
setOpenDeleteAlert={setOpenDeleteAlert}
/>
</>
);
}
const listData = [
{
title: "Lokasi",
value:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora, atque. Aperiam minima asperiores dicta perferendis quis adipisci, dolore optio porro!",
},
{
title: "Tipe Acara",
value: "Workshop",
},
{
title: "Tanggal Mulai",
value: "Senin, 18 Juli 2025, 10:00 WIB",
},
{
title: "Tanggal Berakhir",
value: "Selasa, 19 Juli 2025, 12:00 WIB",
},
{
title: "Deskripsi",
value:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora, atque. Aperiam minima asperiores dicta perferendis quis adipisci, dolore optio porro!",
},
];

View File

@@ -0,0 +1,51 @@
import {
DotButton,
DrawerCustom,
MenuDrawerDynamicGrid,
ViewWrapper,
Spacing,
} from "@/components";
import { IMenuDrawerItem } from "@/components/_Interface/types";
import LeftButtonCustom from "@/components/Button/BackButton";
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish";
import { router, Stack, useLocalSearchParams } from "expo-router";
import { useState } from "react";
export default function EventDetailContribution() {
const { id } = useLocalSearchParams();
const [openDrawer, setOpenDrawer] = useState(false);
const handlePress = (item: IMenuDrawerItem) => {
console.log("PATH ", item.path);
router.navigate(item.path as any);
setOpenDrawer(false);
};
return (
<>
<Stack.Screen
options={{
title: `Detail kontribusi`,
headerLeft: () => <LeftButtonCustom />,
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
}}
/>
<ViewWrapper>
<Event_BoxDetailPublishSection />
<Spacing />
</ViewWrapper>
<DrawerCustom
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
height={250}
>
<MenuDrawerDynamicGrid
data={menuDrawerPublishEvent({ id: id as string })}
columns={4}
onPressItem={handlePress}
/>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,107 @@
import {
ButtonCustom,
SelectCustom,
StackCustom,
TextAreaCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
import { masterTypeEvent } from "@/lib/dummy-data/event/master-type-event";
import { DateTimePickerEvent } from "@react-native-community/datetimepicker";
import { router } from "expo-router";
import React, { useState } from "react";
import { Platform } from "react-native";
export default function EventEdit() {
const [selectedDate, setSelectedDate] = useState<
Date | DateTimePickerEvent | null
>(null);
const [selectedEndDate, setSelectedEndDate] = useState<
Date | DateTimePickerEvent | null
>(null);
const handlerSubmit = () => {
try {
if (selectedDate) {
console.log("Tanggal yang dipilih:", selectedDate);
console.log(`ISO Format ${Platform.OS}:`, selectedDate.toString());
// Kirim ke API atau proses lanjutan
} else {
console.log("Tanggal belum dipilih");
}
if (selectedEndDate) {
console.log("Tanggal yang dipilih:", selectedEndDate);
console.log(`ISO Format ${Platform.OS}:`, selectedEndDate.toString());
// Kirim ke API atau proses lanjutan
} else {
console.log("Tanggal berakhir belum dipilih");
}
console.log("Data berhasil terupdate");
router.back()
} catch (error) {
console.log(error);
}
};
const buttonSubmit = (
<ButtonCustom title="Update" onPress={handlerSubmit} />
);
return (
<>
<ViewWrapper>
<StackCustom gap={"xs"}>
<TextInputCustom
placeholder="Masukkan nama event"
label="Nama Event"
required
/>
<SelectCustom
label="Tipe Event"
placeholder="Pilih tipe event"
data={masterTypeEvent}
onChange={(value) => console.log(value)}
/>
<TextInputCustom
label="Lokasi"
placeholder="Masukkan lokasi event"
required
/>
<DateTimePickerCustom
label="Tanggal & Waktu Mulai"
required
onChange={(date: Date) => {
setSelectedDate(date as any);
}}
value={selectedDate as any}
minimumDate={new Date(Date.now())}
/>
<DateTimePickerCustom
label="Tanggal & Waktu Berakhir"
required
onChange={(date: Date) => {
setSelectedEndDate(date as any);
}}
value={selectedEndDate as any}
/>
<TextAreaCustom
label="Deskripsi"
placeholder="Masukkan deskripsi event"
required
showCount
maxLength={100}
/>
{buttonSubmit}
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,51 @@
import {
DotButton,
DrawerCustom,
MenuDrawerDynamicGrid,
ViewWrapper,
Spacing,
} from "@/components";
import { IMenuDrawerItem } from "@/components/_Interface/types";
import LeftButtonCustom from "@/components/Button/BackButton";
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish";
import { router, Stack, useLocalSearchParams } from "expo-router";
import { useState } from "react";
export default function EventDetailHistory() {
const { id } = useLocalSearchParams();
const [openDrawer, setOpenDrawer] = useState(false);
const handlePress = (item: IMenuDrawerItem) => {
console.log("PATH ", item.path);
router.navigate(item.path as any);
setOpenDrawer(false);
};
return (
<>
<Stack.Screen
options={{
title: `Detail riwayat`,
headerLeft: () => <LeftButtonCustom />,
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
}}
/>
<ViewWrapper>
<Event_BoxDetailPublishSection />
<Spacing />
</ViewWrapper>
<DrawerCustom
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
height={250}
>
<MenuDrawerDynamicGrid
data={menuDrawerPublishEvent({ id: id as string })}
columns={4}
onPressItem={handlePress}
/>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,17 @@
import {
AvatarUsernameAndOtherComponent,
BaseBox,
ViewWrapper,
} from "@/components";
export default function EventListOfParticipants() {
return (
<ViewWrapper>
{Array.from({ length: 10 }).map((_, index) => (
<BaseBox key={index} paddingBlock={0}>
<AvatarUsernameAndOtherComponent avatarHref={`/profile/${index}`} />
</BaseBox>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,64 @@
import {
ButtonCustom,
DotButton,
DrawerCustom,
MenuDrawerDynamicGrid,
Spacing,
ViewWrapper
} from "@/components";
import { IMenuDrawerItem } from "@/components/_Interface/types";
import LeftButtonCustom from "@/components/Button/BackButton";
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish";
import { router, Stack, useLocalSearchParams } from "expo-router";
import { useState } from "react";
import { Alert } from "react-native";
export default function EventDetailPublish() {
const { id } = useLocalSearchParams();
const [openDrawer, setOpenDrawer] = useState(false);
const handlePress = (item: IMenuDrawerItem) => {
console.log("PATH ", item.path);
router.navigate(item.path as any);
setOpenDrawer(false);
};
const footerButton = (
<ButtonCustom
backgroundColor="green"
textColor="white"
onPress={() => Alert.alert("Anda berhasil join event ini")}
>
Join
</ButtonCustom>
);
return (
<>
<Stack.Screen
options={{
title: `Event publish`,
headerLeft: () => <LeftButtonCustom />,
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
}}
/>
<ViewWrapper>
<Event_BoxDetailPublishSection footerButton={footerButton} />
<Spacing />
</ViewWrapper>
<DrawerCustom
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
height={250}
>
<MenuDrawerDynamicGrid
data={menuDrawerPublishEvent({ id: id as string })}
columns={4}
onPressItem={handlePress}
/>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,109 @@
import {
ButtonCustom,
SelectCustom,
StackCustom,
TextAreaCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
import { masterTypeEvent } from "@/lib/dummy-data/event/master-type-event";
import { DateTimePickerEvent } from "@react-native-community/datetimepicker";
import { router } from "expo-router";
import React, { useState } from "react";
import { Platform } from "react-native";
export default function EventCreate() {
const [selectedDate, setSelectedDate] = useState<
Date | DateTimePickerEvent | null
>(null);
const [selectedEndDate, setSelectedEndDate] = useState<
Date | DateTimePickerEvent | null
>(null);
const handlerSubmit = () => {
try {
if (selectedDate) {
console.log("Tanggal yang dipilih:", selectedDate);
console.log(`ISO Format ${Platform.OS}:`, selectedDate.toString());
// Kirim ke API atau proses lanjutan
} else {
console.log("Tanggal belum dipilih");
}
if (selectedEndDate) {
console.log("Tanggal yang dipilih:", selectedEndDate);
console.log(`ISO Format ${Platform.OS}:`, selectedEndDate.toString());
// Kirim ke API atau proses lanjutan
} else {
console.log("Tanggal berakhir belum dipilih");
}
console.log("Data berhasil disimpan");
router.navigate("/event/status");
} catch (error) {
console.log(error);
}
};
const buttonSubmit = (
<ButtonCustom title="Simpan" onPress={handlerSubmit} />
// <BoxButtonOnFooter>
// </BoxButtonOnFooter>
);
return (
<>
<ViewWrapper>
<StackCustom gap={"xs"}>
<TextInputCustom
placeholder="Masukkan nama event"
label="Nama Event"
required
/>
<SelectCustom
label="Tipe Event"
placeholder="Pilih tipe event"
data={masterTypeEvent}
onChange={(value) => console.log(value)}
/>
<TextInputCustom
label="Lokasi"
placeholder="Masukkan lokasi event"
required
/>
<DateTimePickerCustom
label="Tanggal & Waktu Mulai"
required
onChange={(date: Date) => {
setSelectedDate(date as any);
}}
value={selectedDate as any}
minimumDate={new Date(Date.now())}
/>
<DateTimePickerCustom
label="Tanggal & Waktu Berakhir"
required
onChange={(date: Date) => {
setSelectedEndDate(date as any);
}}
value={selectedEndDate as any}
/>
<TextAreaCustom
label="Deskripsi"
placeholder="Masukkan deskripsi event"
required
showCount
maxLength={100}
/>
{buttonSubmit}
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,14 @@
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { GStyles } from "@/styles/global-styles";
import { useLocalSearchParams } from "expo-router";
import { Text } from "react-native";
export default function DetailEvent() {
const { id } = useLocalSearchParams();
console.log("id event >", id);
return (
<ViewWrapper>
<Text style={GStyles.textLabel}>Detail Event {id}</Text>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,37 @@
import {
BoxButtonOnFooter,
ButtonCustom,
TextAreaCustom,
ViewWrapper,
} from "@/components";
import { router } from "expo-router";
import { useState } from "react";
export default function ForumEdit() {
const [text, setText] = useState("");
const buttonFooter = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => {
console.log("Posting", text);
router.back();
}}
>
Update
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<ViewWrapper footerComponent={buttonFooter}>
<TextAreaCustom
placeholder="Ketik diskusi anda..."
maxLength={1000}
showCount
value={text}
onChangeText={setText}
/>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,113 @@
import {
AlertCustom,
AvatarCustom,
ButtonCustom,
CenterCustom,
DrawerCustom,
Grid,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
import { listDummyDiscussionForum } from "@/screens/Forum/list-data-dummy";
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
import { useLocalSearchParams } from "expo-router";
import { useState } from "react";
export default function Forumku() {
const { id } = useLocalSearchParams();
const [openDrawer, setOpenDrawer] = useState(false);
const [status, setStatus] = useState("");
const [alertStatus, setAlertStatus] = useState(false);
const [deleteAlert, setDeleteAlert] = useState(false);
return (
<>
<ViewWrapper>
<StackCustom>
<CenterCustom>
<AvatarCustom
href={`/(application)/(image)/preview-image/${id}`}
size="xl"
/>
</CenterCustom>
<Grid>
<Grid.Col span={6}>
<TextCustom bold truncate>
@bagas_banuna
</TextCustom>
<TextCustom>1 postingan</TextCustom>
</Grid.Col>
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
<ButtonCustom href={`/profile/${id}`}>
Kunjungi Profile
</ButtonCustom>
</Grid.Col>
</Grid>
{listDummyDiscussionForum.map((e, i) => (
<Forum_BoxDetailSection
key={i}
data={e}
setOpenDrawer={setOpenDrawer}
setStatus={setStatus}
isTruncate={true}
href={`/forum/${id}`}
/>
))}
</StackCustom>
</ViewWrapper>
{/* Drawer Komponen Eksternal */}
<DrawerCustom
height={350}
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
>
<Forum_MenuDrawerBerandaSection
id={id as string}
status={status}
setIsDrawerOpen={() => {
setOpenDrawer(false);
}}
setShowDeleteAlert={setDeleteAlert}
setShowAlertStatus={setAlertStatus}
/>
</DrawerCustom>
{/* Alert Komponen Eksternal */}
<AlertCustom
isVisible={alertStatus}
onLeftPress={() => setAlertStatus(false)}
onRightPress={() => {
setOpenDrawer(false);
setAlertStatus(false);
console.log("Ubah status forum");
}}
title="Ubah Status Forum"
message="Apakah Anda yakin ingin mengubah status forum ini?"
textLeft="Batal"
textRight="Ubah"
colorRight={MainColor.green}
/>
{/* Alert Delete */}
<AlertCustom
isVisible={deleteAlert}
onLeftPress={() => setDeleteAlert(false)}
onRightPress={() => {
setOpenDrawer(false);
setDeleteAlert(false);
console.log("Hapus forum");
}}
title="Hapus Forum"
message="Apakah Anda yakin ingin menghapus forum ini?"
textLeft="Batal"
textRight="Hapus"
colorRight={MainColor.red}
/>
</>
);
}

View File

@@ -0,0 +1,176 @@
import {
AlertCustom,
ButtonCustom,
DrawerCustom,
Spacing,
TextAreaCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import Forum_CommentarBoxSection from "@/screens/Forum/CommentarBoxSection";
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
import { listDummyCommentarForum } from "@/screens/Forum/list-data-dummy";
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
import Forum_MenuDrawerCommentar from "@/screens/Forum/MenuDrawerSection.tsx/MenuCommentar";
import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react";
export default function ForumDetail() {
const { id } = useLocalSearchParams();
console.log(id);
const [openDrawer, setOpenDrawer] = useState(false);
const [status, setStatus] = useState("");
const [alertStatus, setAlertStatus] = useState(false);
const [deleteAlert, setDeleteAlert] = useState(false);
const [text, setText] = useState("");
// Comentar
const [openDrawerCommentar, setOpenDrawerCommentar] = useState(false);
const [alertDeleteCommentar, setAlertDeleteCommentar] = useState(false);
const dataDummy = {
name: "Bagas",
status: "Open",
date: "14/07/2025",
deskripsi:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
jumlahBalas: 2,
};
return (
<>
<ViewWrapper>
{/* <StackCustom>
</StackCustom> */}
<Forum_BoxDetailSection
data={dataDummy}
setOpenDrawer={setOpenDrawer}
setStatus={setStatus}
/>
<TextAreaCustom
placeholder="Ketik diskusi anda..."
maxLength={1000}
showCount
value={text}
onChangeText={setText}
style={{
marginBottom: 0,
}}
/>
<ButtonCustom
style={{
alignSelf: "flex-end",
}}
onPress={() => {
console.log("Posting", text);
router.back();
}}
>
Balas
</ButtonCustom>
<Spacing height={40} />
{listDummyCommentarForum.map((e, i) => (
<Forum_CommentarBoxSection
key={i}
data={e}
setOpenDrawer={setOpenDrawerCommentar}
/>
))}
</ViewWrapper>
<DrawerCustom
height={350}
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
>
<Forum_MenuDrawerBerandaSection
id={id as string}
status={status}
setIsDrawerOpen={() => {
setOpenDrawer(false);
}}
setShowDeleteAlert={setDeleteAlert}
setShowAlertStatus={setAlertStatus}
/>
</DrawerCustom>
{/* Alert Status */}
<AlertCustom
isVisible={alertStatus}
title="Ubah Status Forum"
message="Apakah Anda yakin ingin mengubah status forum ini?"
onLeftPress={() => {
setOpenDrawer(false);
setAlertStatus(false);
console.log("Batal");
}}
onRightPress={() => {
setOpenDrawer(false);
setAlertStatus(false);
console.log("Ubah status forum");
}}
textLeft="Batal"
textRight="Ubah"
colorRight={MainColor.green}
/>
{/* Alert Delete */}
<AlertCustom
isVisible={deleteAlert}
title="Hapus Forum"
message="Apakah Anda yakin ingin menghapus forum ini?"
onLeftPress={() => {
setOpenDrawer(false);
setDeleteAlert(false);
console.log("Batal");
}}
onRightPress={() => {
setOpenDrawer(false);
setDeleteAlert(false);
console.log("Hapus forum");
}}
textLeft="Batal"
textRight="Hapus"
colorRight={MainColor.red}
/>
{/* Commentar */}
<DrawerCustom
height={350}
isVisible={openDrawerCommentar}
closeDrawer={() => setOpenDrawerCommentar(false)}
>
<Forum_MenuDrawerCommentar
id={id as string}
setIsDrawerOpen={() => {
setOpenDrawerCommentar(false);
}}
setShowDeleteAlert={setAlertDeleteCommentar}
/>
</DrawerCustom>
{/* Alert Delete Commentar */}
<AlertCustom
isVisible={alertDeleteCommentar}
title="Hapus Komentar"
message="Apakah Anda yakin ingin menghapus komentar ini?"
onLeftPress={() => {
setOpenDrawerCommentar(false);
setAlertDeleteCommentar(false);
console.log("Batal");
}}
onRightPress={() => {
setOpenDrawerCommentar(false);
setAlertDeleteCommentar(false);
console.log("Hapus commentar");
}}
textLeft="Batal"
textRight="Hapus"
colorRight={MainColor.red}
/>
</>
);
}

View File

@@ -0,0 +1,32 @@
import {
BoxButtonOnFooter,
ButtonCustom,
TextAreaCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { router } from "expo-router";
export default function ForumOtherReportCommentar() {
const handleSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
backgroundColor={MainColor.red}
textColor={MainColor.white}
onPress={() => {
console.log("Report lainnya");
router.back();
}}
>
Report
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={handleSubmit}>
<TextAreaCustom placeholder="Laporkan Komentar" />
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,32 @@
import {
BoxButtonOnFooter,
ButtonCustom,
TextAreaCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { router } from "expo-router";
export default function ForumOtherReportPosting() {
const handleSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
backgroundColor={MainColor.red}
textColor={MainColor.white}
onPress={() => {
console.log("Report lainnya");
router.back();
}}
>
Report
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={handleSubmit}>
<TextAreaCustom placeholder="Laporkan Diskusi" />
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,42 @@
import {
ButtonCustom,
Spacing,
StackCustom,
ViewWrapper
} from "@/components";
import { AccentColor, MainColor } from "@/constants/color-palet";
import Forum_ReportListSection from "@/screens/Forum/ReportListSection";
import { router } from "expo-router";
export default function ForumReportCommentar() {
return (
<>
<ViewWrapper>
<StackCustom>
<Forum_ReportListSection />
<ButtonCustom
backgroundColor={MainColor.red}
textColor={MainColor.white}
onPress={() => {
console.log("Report");
router.back();
}}
>
Report
</ButtonCustom>
<ButtonCustom
backgroundColor={AccentColor.blue}
textColor={MainColor.white}
onPress={() => {
console.log("Lainnya");
router.replace("/forum/[id]/other-report-commentar");
}}
>
Lainnya
</ButtonCustom>
<Spacing/>
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,37 @@
import { ViewWrapper, StackCustom, ButtonCustom, Spacing } from "@/components";
import { MainColor, AccentColor } from "@/constants/color-palet";
import Forum_ReportListSection from "@/screens/Forum/ReportListSection";
import { router } from "expo-router";
export default function ForumReportPosting() {
return (
<>
<ViewWrapper>
<StackCustom>
<Forum_ReportListSection />
<ButtonCustom
backgroundColor={MainColor.red}
textColor={MainColor.white}
onPress={() => {
console.log("Report");
router.back();
}}
>
Report
</ButtonCustom>
<ButtonCustom
backgroundColor={AccentColor.blue}
textColor={MainColor.white}
onPress={() => {
console.log("Lainnya");
router.replace("/forum/[id]/other-report-posting");
}}
>
Lainnya
</ButtonCustom>
<Spacing />
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,37 @@
import {
BoxButtonOnFooter,
ButtonCustom,
TextAreaCustom,
ViewWrapper,
} from "@/components";
import { router } from "expo-router";
import { useState } from "react";
export default function ForumCreate() {
const [text, setText] = useState("");
const buttonFooter = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => {
console.log("Posting", text);
router.back();
}}
>
Posting
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<ViewWrapper footerComponent={buttonFooter}>
<TextAreaCustom
placeholder="Ketik diskusi anda..."
maxLength={1000}
showCount
value={text}
onChangeText={setText}
/>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,113 @@
import {
AlertCustom,
AvatarCustom,
BackButton,
DrawerCustom,
SearchInput,
ViewWrapper,
} from "@/components";
import FloatingButton from "@/components/Button/FloatingButton";
import { MainColor } from "@/constants/color-palet";
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
import { listDummyDiscussionForum } from "@/screens/Forum/list-data-dummy";
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
import { router, Stack } from "expo-router";
import { useState } from "react";
export default function Forum() {
const id = "test-id-forum";
const [openDrawer, setOpenDrawer] = useState(false);
const [status, setStatus] = useState("");
const [alertStatus, setAlertStatus] = useState(false);
const [deleteAlert, setDeleteAlert] = useState(false);
return (
<>
<Stack.Screen
options={{
title: "Forum",
headerLeft: () => <BackButton />,
headerRight: () => <AvatarCustom href={`/forum/${id}/forumku`} />,
}}
/>
<ViewWrapper
headerComponent={<SearchInput placeholder="Cari topik diskusi" />}
floatingButton={
<FloatingButton
onPress={() =>
router.navigate("/(application)/(user)/forum/create")
}
/>
}
>
{listDummyDiscussionForum.map((e, i) => (
<Forum_BoxDetailSection
key={i}
data={e}
setOpenDrawer={setOpenDrawer}
setStatus={setStatus}
isTruncate={true}
href={`/forum/${id}`}
/>
))}
</ViewWrapper>
<DrawerCustom
height={350}
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
>
<Forum_MenuDrawerBerandaSection
id={id}
status={status}
setIsDrawerOpen={() => {
setOpenDrawer(false);
}}
setShowDeleteAlert={setDeleteAlert}
setShowAlertStatus={setAlertStatus}
/>
</DrawerCustom>
{/* Alert Status */}
<AlertCustom
isVisible={alertStatus}
title="Ubah Status Forum"
message="Apakah Anda yakin ingin mengubah status forum ini?"
onLeftPress={() => {
setOpenDrawer(false);
setAlertStatus(false);
console.log("Batal");
}}
onRightPress={() => {
setOpenDrawer(false);
setAlertStatus(false);
console.log("Ubah status forum");
}}
textLeft="Batal"
textRight="Ubah"
colorRight={MainColor.green}
/>
{/* Alert Delete */}
<AlertCustom
isVisible={deleteAlert}
title="Hapus Forum"
message="Apakah Anda yakin ingin menghapus forum ini?"
onLeftPress={() => {
setOpenDrawer(false);
setDeleteAlert(false);
console.log("Batal");
}}
onRightPress={() => {
setOpenDrawer(false);
setDeleteAlert(false);
console.log("Hapus forum");
}}
textLeft="Batal"
textRight="Hapus"
colorRight={MainColor.red}
/>
</>
);
}

View File

@@ -1,9 +1,9 @@
import NewHomeView from "@/screens/Home/UiHome";
import UiHome from "@/screens/Home/UiHome";
export default function Application() {
return (
<>
<NewHomeView />
<UiHome />
</>
);
}

View File

@@ -0,0 +1,59 @@
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import { TabsStyles } from "@/styles/tabs-styles";
import { Feather, FontAwesome6, Ionicons } from "@expo/vector-icons";
import { Tabs } from "expo-router";
export default function InvestmentTabsLayout() {
return (
<Tabs screenOptions={TabsStyles}>
<Tabs.Screen
name="index"
options={{
title: "Bursa",
tabBarIcon: ({ color }) => (
<Ionicons
name="bar-chart-outline"
color={color}
size={ICON_SIZE_SMALL}
/>
),
}}
/>
<Tabs.Screen
name="portofolio"
options={{
title: "Portofolio",
tabBarIcon: ({ color }) => (
<Feather name="pie-chart" color={color} size={ICON_SIZE_SMALL} />
),
}}
/>
<Tabs.Screen
name="my-holding"
options={{
title: "Saham Saya",
tabBarIcon: ({ color }) => (
<FontAwesome6
name="hand-holding-dollar"
color={color}
size={ICON_SIZE_SMALL}
/>
),
}}
/>
<Tabs.Screen
name="transaction"
options={{
title: "Transaksi",
tabBarIcon: ({ color }) => (
<FontAwesome6
name="money-bill-transfer"
color={color}
size={ICON_SIZE_SMALL}
/>
),
}}
/>
</Tabs>
);
}

View File

@@ -0,0 +1,81 @@
import {
BaseBox,
FloatingButton,
Grid,
ProgressCustom,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import DUMMY_IMAGE from "@/constants/dummy-image-value";
import dayjs from "dayjs";
import { Image } from "expo-image";
import { router } from "expo-router";
import { View } from "react-native";
export default function InvestmentBursa() {
return (
<ViewWrapper
hideFooter
floatingButton={
<FloatingButton onPress={() => router.push("/investment/create")} />
}
>
{Array.from({ length: 10 }).map((_, index) => (
<BaseBox key={index} paddingTop={7} paddingBottom={7}>
<Grid>
<Grid.Col span={5}>
<Image
source={DUMMY_IMAGE.background}
style={{ width: "auto", height: 100, borderRadius: 10 }}
/>
</Grid.Col>
<Grid.Col span={1}>
<View />
</Grid.Col>
<Grid.Col span={6}>
<StackCustom>
<TextCustom truncate={2}>
Title here : Lorem ipsum dolor sit amet consectetur
adipisicing elit. Omnis, exercitationem, sequi enim quod
distinctio maiores laudantium amet, quidem atque repellat sit
vitae qui aliquam est veritatis laborum eum voluptatum totam!
</TextCustom>
<ProgressCustom value={index % 5 * 20} size="lg" />
<TextCustom>
Sisa waktu: {dayjs().diff(dayjs(), "day")} hari
</TextCustom>
</StackCustom>
</Grid.Col>
</Grid>
</BaseBox>
))}
</ViewWrapper>
);
}
// <View style={{ padding: 20, gap: 16 }}>
// <TextCustom>Progress 70%</TextCustom>
// <ProgressCustom value={70} color="primary" size="md" />
// <TextCustom>Success Progress</TextCustom>
// <ProgressCustom value={40} color="success" size="lg" />
// <TextCustom>Warning Progress (small)</TextCustom>
// <ProgressCustom value={90} color="warning" size="sm" />
// <TextCustom>Error Indeterminate</TextCustom>
// <ProgressCustom value={null} color="error" size="md" />
// <TextCustom>Custom Radius</TextCustom>
// <ProgressCustom value={60} color="info" size="xl" radius={4} />
// <ProgressCustom value={70} color="primary" size="lg" />
// <ProgressCustom value={45} color="success" size="md" label="Halfway!" />
// <ProgressCustom value={90} color="warning" size="lg" showLabel={false} />
// <ProgressCustom value={null} color="error" size="sm" label="Loading..." />
// </View>;

View File

@@ -0,0 +1,49 @@
import {
BaseBox,
Grid,
ProgressCustom,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { View } from "react-native";
export default function InvestmentMyHolding() {
return (
<ViewWrapper hideFooter>
{Array.from({ length: 10 }).map((_, index) => (
<BaseBox key={index} paddingTop={7} paddingBottom={7}>
<Grid>
<Grid.Col span={6}>
<StackCustom gap={"xs"}>
<TextCustom truncate={2}>
Title here : Lorem ipsum dolor sit amet consectetur
adipisicing elit. Omnis, exercitationem, sequi enim quod
distinctio maiores laudantium amet, quidem atque repellat sit
vitae qui aliquam est veritatis laborum eum voluptatum totam!
</TextCustom>
<Spacing height={5} />
<TextCustom size="small">Rp. 7.500.000</TextCustom>
<TextCustom size="small">300 Lembar</TextCustom>
</StackCustom>
</Grid.Col>
<Grid.Col span={1}>
<View />
</Grid.Col>
<Grid.Col
span={5}
style={{
justifyContent: "center",
alignItems: "center",
}}
>
<ProgressCustom value={(index % 5) * 20} size="lg" />
</Grid.Col>
</Grid>
</BaseBox>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,70 @@
import {
BaseBox,
Grid,
ScrollableCustom,
Spacing,
TextCustom,
ViewWrapper
} from "@/components";
import DUMMY_IMAGE from "@/constants/dummy-image-value";
import { masterStatus } from "@/lib/dummy-data/_master/status";
import { Image } from "expo-image";
import { useState } from "react";
import { View } from "react-native";
export default function InvestmentPortofolio() {
const [activeCategory, setActiveCategory] = useState<string | null>(
"publish"
);
const handlePress = (item: any) => {
setActiveCategory(item.value);
// tambahkan logika lain seperti filter dsb.
};
const scrollComponent = (
<ScrollableCustom
data={masterStatus.map((e, i) => ({
id: i,
label: e.label,
value: e.value,
}))}
onButtonPress={handlePress}
activeId={activeCategory as any}
/>
);
return (
<ViewWrapper headerComponent={scrollComponent} hideFooter>
{Array.from({ length: 10 }).map((_, index) => (
<BaseBox key={index} paddingTop={7} paddingBottom={7}>
<Grid>
<Grid.Col span={6}>
<TextCustom truncate={2}>
Title here : {activeCategory} Lorem ipsum dolor sit amet consectetur adipisicing
elit. Omnis, exercitationem, sequi enim quod distinctio maiores
laudantium amet, quidem atque repellat sit vitae qui aliquam est
veritatis laborum eum voluptatum totam!
</TextCustom>
<Spacing />
<TextCustom bold size="small">
Target Dana:
</TextCustom>
<TextCustom>Rp. {index + 1 % 3/4 * 1004000}</TextCustom>
</Grid.Col>
<Grid.Col span={1}>
<View />
</Grid.Col>
<Grid.Col span={5}>
<Image
source={DUMMY_IMAGE.background}
style={{ width: "auto", height: 100, borderRadius: 10 }}
/>
</Grid.Col>
</Grid>
</BaseBox>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,53 @@
import {
BadgeCustom,
BaseBox,
Grid,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { GStyles } from "@/styles/global-styles";
import dayjs from "dayjs";
import { View } from "react-native";
export default function InvestmentTransaction() {
return (
<ViewWrapper hideFooter>
{Array.from({ length: 10 }).map((_, i) => (
<BaseBox key={i} paddingTop={7} paddingBottom={7}>
<Grid>
<Grid.Col span={6}>
<StackCustom gap={"xs"}>
<TextCustom truncate>
Title Investment: Lorem ipsum dolor sit amet consectetur
adipisicing elit. Am culpa excepturi deleniti soluta animi
porro amet ducimus.
</TextCustom>
<TextCustom color="gray" size="small">
{dayjs().format("DD/MM/YYYY")}
</TextCustom>
</StackCustom>
</Grid.Col>
<Grid.Col span={1}>
<View />
</Grid.Col>
<Grid.Col span={5} style={{ alignItems: "flex-end" }}>
<StackCustom gap={"xs"}>
<TextCustom bold truncate>
Rp. 7.500.000
</TextCustom>
<BadgeCustom
variant="light"
color="success"
style={GStyles.alignSelfFlexEnd}
>
Berhasil
</BadgeCustom>
</StackCustom>
</Grid.Col>
</Grid>
</BaseBox>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,197 @@
import {
BaseBox,
ButtonCenteredOnly,
ButtonCustom,
CenterCustom,
InformationBox,
LandscapeFrameUploaded,
SelectCustom,
Spacing,
StackCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import dummyPembagianDeviden from "@/lib/dummy-data/investment/pembagian-deviden";
import dummyListPencarianInvestor from "@/lib/dummy-data/investment/pencarian-investor";
import dummyPeriodeDeviden from "@/lib/dummy-data/investment/periode-deviden";
import { FontAwesome5 } from "@expo/vector-icons";
import { router } from "expo-router";
import { useState } from "react";
export default function InvestmentCreate() {
const [data, setData] = useState({
title: "",
targetDana: 0,
hargaPerLembar: 0,
totalLembar: 0,
rasioKeuntungan: 0,
pencarianInvestor: "",
periodeDeviden: "",
pembagianDeviden: "",
});
// const [coba, setCoba] = useState("");
return (
<ViewWrapper>
<StackCustom gap={"xs"}>
{/* <View style={GStyles.inputContainerInput}>
<TextInput
style={{
...GStyles.inputText,
}}
onChangeText={(value) => setCoba(value)}
value={coba}
keyboardType="decimal-pad"
/>
</View> */}
<InformationBox text="Gambar investasi bisa berupa ilustrasi, poster atau foto terkait investasi." />
<LandscapeFrameUploaded />
<ButtonCenteredOnly
icon="upload"
onPress={() => router.push("/take-picture/1")}
>
Upload
</ButtonCenteredOnly>
<Spacing />
<InformationBox text="File prospektus wajib untuk diupload, agar calon investor paham dengan prospek investasi yang akan anda jalankan kedepannya." />
<BaseBox>
<CenterCustom>
<FontAwesome5
name="file-pdf"
size={30}
color={MainColor.disabled}
/>
</CenterCustom>
</BaseBox>
<ButtonCenteredOnly
icon="upload"
onPress={() => router.push("/take-picture/1")}
>
Upload File
</ButtonCenteredOnly>
<Spacing />
<TextInputCustom
required
placeholder="Judul"
label="Judul"
value={data.title}
onChangeText={(value) => setData({ ...data, title: value })}
/>
<TextInputCustom
required
iconLeft="Rp."
placeholder="0"
label="Target Dana"
keyboardType="numeric"
onChangeText={(value) =>
setData({ ...data, targetDana: Number(value) })
}
value={data.targetDana === 0 ? "" : data.targetDana.toString()}
/>
<TextInputCustom
required
iconLeft="Rp."
placeholder="0"
label="Target Dana"
keyboardType="numeric"
onChangeText={(value) =>
setData({ ...data, targetDana: Number(value) })
}
value={data.targetDana === 0 ? "" : data.targetDana.toString()}
/>
<TextInputCustom
required
iconLeft="Rp."
placeholder="0"
label="Harga Per Lembar"
keyboardType="numeric"
onChangeText={(value) =>
setData({ ...data, targetDana: Number(value) })
}
value={data.targetDana === 0 ? "" : data.targetDana.toString()}
/>
<TextInputCustom
required
placeholder="0"
label="Total Lembar"
keyboardType="numeric"
onChangeText={(value) =>
setData({ ...data, totalLembar: Number(value) })
}
value={data.totalLembar === 0 ? "" : data.totalLembar.toString()}
/>
<TextInputCustom
required
iconRight="%"
label="Rasio Keuntungan / ROI %"
placeholder="0"
keyboardType="numeric"
onChangeText={(value) =>
setData({ ...data, rasioKeuntungan: Number(value) })
}
value={
data.rasioKeuntungan === 0 ? "" : data.rasioKeuntungan.toString()
}
/>
<SelectCustom
required
placeholder="Pilih batas waktu"
label="Pencarian Investor"
data={dummyListPencarianInvestor.map((item) => ({
label: item.name + `${" "}hari`,
value: item.id,
}))}
onChange={(value) =>
setData({ ...data, pencarianInvestor: value as any })
}
value={data.pencarianInvestor}
/>
<SelectCustom
required
placeholder="Pilih batas waktu"
label="Pilih Periode Deviden"
data={dummyPeriodeDeviden.map((item) => ({
label: item.name,
value: item.id,
}))}
onChange={(value) =>
setData({ ...data, periodeDeviden: value as any })
}
value={data.periodeDeviden}
/>
<SelectCustom
required
placeholder="Pilih batas waktu"
label="Pilih Pembagian Deviden"
data={dummyPembagianDeviden.map((item) => ({
label: item.name + `${" "}bulan`,
value: item.id,
}))}
onChange={(value) =>
setData({ ...data, pembagianDeviden: value as any })
}
value={data.pembagianDeviden}
/>
<Spacing />
<ButtonCustom onPress={() => router.replace("/investment/portofolio")}>
Simpan
</ButtonCustom>
</StackCustom>
<Spacing height={50} />
</ViewWrapper>
);
}

View File

@@ -0,0 +1,34 @@
import { IconHome, IconStatus } from "@/components/_Icon";
import { TabsStyles } from "@/styles/tabs-styles";
import { Ionicons } from "@expo/vector-icons";
import { Tabs } from "expo-router";
export default function JobTabsLayout() {
return (
<Tabs screenOptions={TabsStyles}>
<Tabs.Screen
name="index"
options={{
title: "Beranda",
tabBarIcon: ({ color }) => <IconHome color={color} />,
}}
/>
<Tabs.Screen
name="status"
options={{
title: "Status",
tabBarIcon: ({ color }) => <IconStatus color={color} />,
}}
/>
<Tabs.Screen
name="archive"
options={{
title: "Arsip",
tabBarIcon: ({ color }) => (
<Ionicons size={20} name="archive" color={color} />
),
}}
/>
</Tabs>
);
}

View File

@@ -0,0 +1,16 @@
import { BaseBox, TextCustom, ViewWrapper } from "@/components";
import { jobDataDummy } from "@/screens/Job/listDataDummy";
export default function JobArchive() {
return (
<ViewWrapper hideFooter>
{jobDataDummy.map((e, i) => (
<BaseBox key={i} paddingTop={20} paddingBottom={20}>
<TextCustom align="center" bold truncate size="large">
{e.posisi}
</TextCustom>
</BaseBox>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,34 @@
import {
AvatarUsernameAndOtherComponent,
BoxWithHeaderSection,
FloatingButton,
SearchInput,
Spacing,
TextCustom,
ViewWrapper
} from "@/components";
import { jobDataDummy } from "@/screens/Job/listDataDummy";
import { router } from "expo-router";
export default function JobBeranda() {
return (
<ViewWrapper
hideFooter
floatingButton={
<FloatingButton onPress={() => router.push("/job/create")} />
}
headerComponent={<SearchInput placeholder="Cari pekerjaan" />}
>
{jobDataDummy.map((item, index) => (
<BoxWithHeaderSection key={index} onPress={() => router.push(`/job/${item.id}`)}>
<AvatarUsernameAndOtherComponent avatarHref={`/profile/${item.id}`} />
<Spacing />
<TextCustom truncate={2} align="center" bold size="large">
{item.posisi}
</TextCustom>
<Spacing />
</BoxWithHeaderSection>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,50 @@
import {
BaseBox,
ScrollableCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { masterStatus } from "@/lib/dummy-data/_master/status";
import { jobDataDummy } from "@/screens/Job/listDataDummy";
import { useState } from "react";
export default function JobStatus() {
const [activeCategory, setActiveCategory] = useState<string | null>(
"publish"
);
const handlePress = (item: any) => {
setActiveCategory(item.value);
// tambahkan logika lain seperti filter dsb.
};
const scrollComponent = (
<ScrollableCustom
data={masterStatus.map((e, i) => ({
id: i,
label: e.label,
value: e.value,
}))}
onButtonPress={handlePress}
activeId={activeCategory as any}
/>
);
return (
<ViewWrapper headerComponent={scrollComponent} hideFooter>
{jobDataDummy.map((e, i) => (
<BaseBox
key={i}
paddingTop={20}
paddingBottom={20}
href={`/job/${e.id}/${activeCategory}/detail`}
// onPress={() => console.log("pressed")}
>
<TextCustom align="center" bold truncate size="large">
{e.posisi} {activeCategory?.toUpperCase()}
</TextCustom>
</BaseBox>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,65 @@
import {
BackButton,
DotButton,
DrawerCustom,
MenuDrawerDynamicGrid,
Spacing,
ViewWrapper,
} from "@/components";
import { IconEdit } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types";
import Job_BoxDetailSection from "@/screens/Job/BoxDetailSection";
import Job_ButtonStatusSection from "@/screens/Job/ButtonStatusSection";
import { jobDataDummy } from "@/screens/Job/listDataDummy";
import { router, Stack, useLocalSearchParams } from "expo-router";
import { useState } from "react";
export default function JobDetailStatus() {
const { id, status } = useLocalSearchParams();
const [openDrawer, setOpenDrawer] = useState(false);
const jobDetail = jobDataDummy.find((e) => e.id === Number(id));
const handlePress = (item: IMenuDrawerItem) => {
console.log("PATH >> ", item.path);
router.navigate(item.path as any);
setOpenDrawer(false);
};
return (
<>
<Stack.Screen
options={{
title: `Detail`,
headerLeft: () => <BackButton />,
headerRight: () =>
status === "draft" ? (
<DotButton onPress={() => setOpenDrawer(true)} />
) : null,
}}
/>
<ViewWrapper>
<Job_BoxDetailSection data={jobDetail} />
<Job_ButtonStatusSection status={status as string} />
<Spacing />
</ViewWrapper>
<DrawerCustom
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
height={"auto"}
>
<MenuDrawerDynamicGrid
data={[
{
icon: <IconEdit />,
label: "Edit",
path: `/job/${id}/edit`,
},
]}
columns={4}
onPressItem={handlePress as any}
/>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,67 @@
import {
ButtonCenteredOnly,
ButtonCustom,
InformationBox,
LandscapeFrameUploaded,
Spacing,
StackCustom,
TextAreaCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import { router } from "expo-router";
export default function JobEdit() {
const buttonSubmit = () => {
return (
<>
<ButtonCustom onPress={() => router.back()}>Update</ButtonCustom>
<Spacing />
</>
);
};
return (
<ViewWrapper>
<StackCustom gap={"xs"}>
<InformationBox text="Poster atau gambar lowongan kerja bersifat opsional, tidak wajib untuk dimasukkan dan upload lah gambar yang sesuai dengan deskripsi lowongan kerja." />
<LandscapeFrameUploaded />
<ButtonCenteredOnly
onPress={() => {
router.push("/(application)/(image)/take-picture/123");
}}
icon="upload"
>
Upload
</ButtonCenteredOnly>
<Spacing />
<TextInputCustom
label="Judul Lowongan"
placeholder="Masukan Judul Lowongan Kerja"
required
/>
<TextAreaCustom
label="Syarat & Kualifikasi"
placeholder="Masukan Syarat & Kualifikasi Lowongan Kerja"
required
showCount
maxLength={1000}
/>
<TextAreaCustom
label="Deskripsi Lowongan"
placeholder="Masukan Deskripsi Lowongan Kerja"
required
showCount
maxLength={1000}
/>
{buttonSubmit()}
</StackCustom>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,79 @@
import {
ButtonCustom,
Spacing,
ViewWrapper
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import Job_BoxDetailSection from "@/screens/Job/BoxDetailSection";
import { jobDataDummy } from "@/screens/Job/listDataDummy";
import { Ionicons } from "@expo/vector-icons";
import * as Clipboard from "expo-clipboard";
import { useLocalSearchParams } from "expo-router";
import { Alert, Linking } from "react-native";
export default function JobDetail() {
const { id } = useLocalSearchParams();
const jobDetail = jobDataDummy.find((e) => e.id === Number(id));
const OpenLinkButton = () => {
const jobUrl =
"https://stg-hipmi.wibudev.com/job-vacancy/cm6ijt9w8005zucv4twsct657";
const openInBrowser = async () => {
const supported = await Linking.canOpenURL(jobUrl);
if (supported) {
await Linking.openURL(jobUrl);
} else {
Alert.alert("Gagal membuka link", "Browser tidak tersedia.");
}
};
return (
<ButtonCustom
iconLeft={
<Ionicons name="globe" size={ICON_SIZE_SMALL} color="white" />
}
onPress={openInBrowser}
backgroundColor="green"
textColor="white"
>
Buka Lowongan di Browser
</ButtonCustom>
);
};
const CopyLinkButton = () => {
const jobUrl =
"https://stg-hipmi.wibudev.com/job-vacancy/cm6ijt9w8005zucv4twsct657";
const copyToClipboard = async () => {
await Clipboard.setStringAsync(jobUrl);
Alert.alert(
"Link disalin",
"Tautan lowongan telah disalin ke clipboard."
);
};
return (
<ButtonCustom
iconLeft={<Ionicons name="copy" size={ICON_SIZE_SMALL} color="white" />}
onPress={copyToClipboard}
backgroundColor={MainColor.orange}
textColor="white"
>
Salin Link
</ButtonCustom>
);
};
return (
<ViewWrapper>
<Job_BoxDetailSection data={jobDetail}/>
<OpenLinkButton />
<Spacing />
<CopyLinkButton />
</ViewWrapper>
);
}

View File

@@ -0,0 +1,73 @@
import {
ButtonCenteredOnly,
ButtonCustom,
InformationBox,
LandscapeFrameUploaded,
Spacing,
StackCustom,
TextAreaCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import { router } from "expo-router";
export default function JobCreate() {
const buttonSubmit = () => {
return (
<>
<ButtonCustom
onPress={() =>
router.replace("/(application)/(user)/job/(tabs)/status")
}
>
Simpan
</ButtonCustom>
<Spacing />
</>
);
};
return (
<ViewWrapper>
<StackCustom gap={"xs"}>
<InformationBox text="Poster atau gambar lowongan kerja bersifat opsional, tidak wajib untuk dimasukkan dan upload lah gambar yang sesuai dengan deskripsi lowongan kerja." />
<LandscapeFrameUploaded />
<ButtonCenteredOnly
onPress={() => {
router.push("/(application)/(image)/take-picture/123");
}}
icon="upload"
>
Upload
</ButtonCenteredOnly>
<Spacing />
<TextInputCustom
label="Judul Lowongan"
placeholder="Masukan Judul Lowongan Kerja"
required
/>
<TextAreaCustom
label="Syarat & Kualifikasi"
placeholder="Masukan Syarat & Kualifikasi Lowongan Kerja"
required
showCount
maxLength={1000}
/>
<TextAreaCustom
label="Deskripsi Lowongan"
placeholder="Masukan Deskripsi Lowongan Kerja"
required
showCount
maxLength={1000}
/>
{buttonSubmit()}
</StackCustom>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,55 @@
import {
AvatarCustom,
BaseBox,
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
InformationBox,
MapCustom,
Spacing,
StackCustom,
ViewWrapper,
} from "@/components";
import CenterCustom from "@/components/Center/CenterCustom";
import { router, useLocalSearchParams } from "expo-router";
export default function MapsCustomPin() {
const { id } = useLocalSearchParams();
const buttonFooter = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => {
console.log(`Simpan maps ${id}`);
router.back();
}}
>
Simpan
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={buttonFooter}>
<StackCustom>
<InformationBox text="Pin map akan secara otomatis menampilkan logo pada porotofolio ini, jika anda ingin melakukan custom silahkan upload logo pin baru anda." />
<CenterCustom>
<AvatarCustom size="xl" />
</CenterCustom>
<ButtonCenteredOnly
onPress={() => console.log("Upload")}
icon="upload"
>
Upload
</ButtonCenteredOnly>
<Spacing />
<BaseBox>
<MapCustom />
</BaseBox>
<Spacing />
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,59 @@
import {
BaseBox,
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
InformationBox,
LandscapeFrameUploaded,
MapCustom,
Spacing,
TextInputCustom,
ViewWrapper
} from "@/components";
import { router, useLocalSearchParams } from "expo-router";
export default function MapsEdit() {
const { id } = useLocalSearchParams();
const buttonFooter = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => {
console.log(`Simpan maps ${id}`);
router.back()
}}
>
Simpan
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<ViewWrapper footerComponent={buttonFooter}>
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
<BaseBox>
<MapCustom />
</BaseBox>
<TextInputCustom
required
label="Nama Pin"
placeholder="Masukkan nama pin maps"
/>
<Spacing />
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
<LandscapeFrameUploaded />
<ButtonCenteredOnly
icon="upload"
onPress={() => {
console.log("Upload foto ");
router.navigate(`/take-picture/${id}`);
}}
>
Upload
</ButtonCenteredOnly>
<Spacing height={50} />
</ViewWrapper>
);
}

View File

@@ -0,0 +1,59 @@
import {
BaseBox,
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
InformationBox,
LandscapeFrameUploaded,
Spacing,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import { router, useLocalSearchParams } from "expo-router";
export default function MapsCreate() {
const { id } = useLocalSearchParams();
const buttonFooter = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => {
console.log(`Simpan maps ${id}`);
router.replace(`/portofolio/${id}`);
}}
>
Simpan
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<ViewWrapper footerComponent={buttonFooter}>
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
<BaseBox style={{ height: 400 }}>
<TextCustom>Maps Her</TextCustom>
</BaseBox>
<TextInputCustom
required
label="Nama Pin"
placeholder="Masukkan nama pin maps"
/>
<Spacing height={50} />
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
<LandscapeFrameUploaded />
<ButtonCenteredOnly
icon="upload"
onPress={() => {
console.log("Upload foto ");
router.navigate(`/take-picture/${id}`);
}}
>
Upload
</ButtonCenteredOnly>
<Spacing height={50} />
</ViewWrapper>
);
}

View File

@@ -0,0 +1,9 @@
import { MapCustom, ViewWrapper } from "@/components";
export default function Maps() {
return (
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
<MapCustom height={"100%"} />
</ViewWrapper>
);
}

View File

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

View File

@@ -0,0 +1,111 @@
import {
BaseBox,
Grid,
ScrollableCustom,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { useState } from "react";
import { View } from "react-native";
const categories = [
{ value: "all", label: "Semua" },
{ value: "event", label: "Event" },
{ value: "job", label: "Job" },
{ value: "voting", label: "Voting" },
{ value: "donasi", label: "Donasi" },
{ value: "investasi", label: "Investasi" },
{ value: "forum", label: "Forum" },
{ value: "collaboration", label: "Collaboration" },
];
const selectedCategory = (value: string) => {
const category = categories.find((c) => c.value === value);
return category?.label;
};
const BoxNotification = ({
index,
activeCategory,
}: {
index: number;
activeCategory: string | null;
}) => {
return (
<>
<BaseBox
onPress={() =>
console.log(
"Notification >",
selectedCategory(activeCategory as string)
)
}
>
<StackCustom>
<TextCustom bold>
# {selectedCategory(activeCategory as string)}
</TextCustom>
<View
style={{
borderBottomColor: MainColor.white_gray,
borderBottomWidth: 1,
}}
/>
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sint odio
unde quidem voluptate quam culpa sequi molestias ipsa corrupti id,
soluta, nostrum adipisci similique, et illo asperiores deleniti eum
labore.
</TextCustom>
<Grid>
<Grid.Col span={6}>
<TextCustom size="small" color="gray">
{index + 1} Agustus 2025
</TextCustom>
</Grid.Col>
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
<TextCustom size="small" color="gray">
Belum lihat
</TextCustom>
</Grid.Col>
</Grid>
</StackCustom>
</BaseBox>
</>
);
};
export default function Notifications() {
const [activeCategory, setActiveCategory] = useState<string | null>("all");
const handlePress = (item: any) => {
setActiveCategory(item.value);
// tambahkan logika lain seperti filter dsb.
};
return (
<ViewWrapper
headerComponent={
<ScrollableCustom
data={categories.map((e, i) => ({
id: i,
label: e.label,
value: e.value,
}))}
onButtonPress={handlePress}
activeId={activeCategory as string}
/>
}
>
{Array.from({ length: 20 }).map((e, i) => (
<View key={i}>
<BoxNotification index={i} activeCategory={activeCategory as any} />
</View>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,191 @@
import {
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
Grid,
InformationBox,
LandscapeFrameUploaded,
SelectCustom,
Spacing,
StackCustom,
TextAreaCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import dummyMasterBidangBisnis from "@/lib/dummy-data/master-bidang-bisnis";
import dummyMasterSubBidangBisnis from "@/lib/dummy-data/master-sub-bidang-bisnis";
import { Ionicons } from "@expo/vector-icons";
import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";
import PhoneInput, { ICountry } from "react-native-international-phone-number";
export default function PortofolioCreate() {
const { id } = useLocalSearchParams();
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
const [inputValue, setInputValue] = useState<string>("");
const [data, setData] = useState({
name: "",
bidang_usaha: "",
sub_bidang_usaha: "",
alamat: "",
nomor_telepon: "",
deskripsi: "",
});
function handleInputValue(phoneNumber: string) {
setInputValue(phoneNumber);
}
function handleSelectedCountry(country: ICountry) {
setSelectedCountry(country);
}
function handleSave() {
console.log("Selanjutnya");
router.replace(`/maps/create`);
}
const buttonSave = (
<BoxButtonOnFooter>
<ButtonCustom onPress={handleSave}>Selanjutnya</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<ViewWrapper footerComponent={buttonSave}>
{/* <TextCustom>Portofolio Create {id}</TextCustom> */}
<StackCustom gap={"xs"}>
<InformationBox text="Lengkapi data bisnis anda." />
<TextInputCustom
required
label="Nama Bisnis"
placeholder="Masukkan nama bisnis"
/>
<SelectCustom
label="Bidang Usaha"
required
data={dummyMasterBidangBisnis.map((item) => ({
label: item.name,
value: item.id,
}))}
value={data.bidang_usaha}
onChange={(value) => {
setData({ ...(data as any), bidang_usaha: value });
}}
/>
<Grid>
<Grid.Col span={10}>
<SelectCustom
// disabled
label="Sub Bidang Usaha"
required
data={dummyMasterSubBidangBisnis.map((item) => ({
label: item.name,
value: item.id,
}))}
value={data.sub_bidang_usaha}
onChange={(value) => {
setData({ ...(data as any), sub_bidang_usaha: value });
}}
/>
</Grid.Col>
<Grid.Col
span={2}
style={{ alignItems: "center", justifyContent: "center" }}
>
<TouchableOpacity onPress={() => console.log("delete")}>
<Ionicons name="trash" size={24} color={MainColor.red} />
</TouchableOpacity>
</Grid.Col>
</Grid>
<ButtonCenteredOnly onPress={() => console.log("add")}>
Tambah Pilihan
</ButtonCenteredOnly>
<Spacing />
<View>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<TextCustom semiBold style={{ color: MainColor.white_gray }}>
Nomor Telepon
</TextCustom>
<Text style={{ color: "red" }}> *</Text>
</View>
<Spacing height={5} />
<PhoneInput
value={inputValue}
onChangePhoneNumber={handleInputValue}
selectedCountry={selectedCountry}
onChangeSelectedCountry={handleSelectedCountry}
defaultCountry="ID"
placeholder="xxx-xxx-xxx"
/>
</View>
<Spacing />
<TextInputCustom
required
label="Alamat Bisnis"
placeholder="Masukkan alamat bisnis"
/>
<TextAreaCustom
label="Deskripsi Bisnis"
placeholder="Masukkan deskripsi bisnis"
value={data.deskripsi}
onChangeText={(value: any) => setData({ ...data, deskripsi: value })}
autosize
minRows={2}
maxRows={5}
required
showCount
maxLength={100}
/>
<Spacing />
{/* Logo */}
<InformationBox text="Upload logo bisnis anda untuk di tampilaka pada portofolio." />
<LandscapeFrameUploaded />
<ButtonCenteredOnly
icon="upload"
onPress={() => {
console.log("Upload logo >>", id);
router.navigate(`/(application)/(image)/take-picture/${id}`);
}}
>
Upload
</ButtonCenteredOnly>
<Spacing height={40} />
{/* Social Media */}
<InformationBox text="Isi hanya pada sosial media yang anda miliki." />
<TextInputCustom
label="Tiktok"
placeholder="Masukkan username tiktok"
/>
<TextInputCustom
label="Facebook"
placeholder="Masukkan username facebook"
/>
<TextInputCustom
label="Instagram"
placeholder="Masukkan username instagram"
/>
<TextInputCustom
label="Twitter"
placeholder="Masukkan username twitter"
/>
<TextInputCustom
label="Youtube"
placeholder="Masukkan username youtube"
/>
<Spacing />
</StackCustom>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,48 @@
import {
AvatarCustom,
BaseBox,
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
ViewWrapper,
} from "@/components";
import { router, useLocalSearchParams } from "expo-router";
export default function PortofolioEditLogo() {
const { id } = useLocalSearchParams();
const buttonFooter = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => {
console.log("Simpan logo ");
router.back();
}}
>
Simpan
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={buttonFooter}>
<BaseBox
style={{
alignItems: "center",
justifyContent: "center",
height: 250,
}}
>
<AvatarCustom size="xl" />
</BaseBox>
<ButtonCenteredOnly
icon="upload"
onPress={() => router.navigate(`/take-picture/${id}`)}
>
Update
</ButtonCenteredOnly>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,36 @@
import {
BoxButtonOnFooter,
ButtonCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import { useLocalSearchParams, router } from "expo-router";
export default function PortofolioEditSocialMedia() {
const { id } = useLocalSearchParams();
const buttonFooter = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => {
console.log(`Simpan sosmed ${id}`);
router.back();
}}
>
Simpan
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={buttonFooter}>
<TextInputCustom label="Tiktok" placeholder="Masukkan tiktok" />
<TextInputCustom label="Instagram" placeholder="Masukkan instagram" />
<TextInputCustom label="Facebook" placeholder="Masukkan facebook" />
<TextInputCustom label="Twitter" placeholder="Masukkan twitter" />
<TextInputCustom label="Youtube" placeholder="Masukkan youtube" />
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,151 @@
import {
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
Grid,
SelectCustom,
Spacing,
StackCustom,
TextAreaCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import dummyMasterBidangBisnis from "@/lib/dummy-data/master-bidang-bisnis";
import dummyMasterSubBidangBisnis from "@/lib/dummy-data/master-sub-bidang-bisnis";
import { Ionicons } from "@expo/vector-icons";
import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";
import PhoneInput, { ICountry } from "react-native-international-phone-number";
export default function PortofolioEdit() {
const { id } = useLocalSearchParams();
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
const [inputValue, setInputValue] = useState<string>("");
const [data, setData] = useState({
name: "",
bidang_usaha: "",
sub_bidang_usaha: "",
alamat: "",
nomor_telepon: "",
deskripsi: "",
});
function handleInputValue(phoneNumber: string) {
setInputValue(phoneNumber);
}
function handleSelectedCountry(country: ICountry) {
setSelectedCountry(country);
}
function handleSave() {
console.log(`Update portofolio berhasil ${id}`);
router.back();
}
const buttonUpdate = (
<BoxButtonOnFooter>
<ButtonCustom onPress={handleSave}>Simpan</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={buttonUpdate}>
<StackCustom gap={"xs"}>
<TextInputCustom
required
label="Nama Bisnis"
placeholder="Masukkan nama bisnis"
/>
<SelectCustom
label="Bidang Usaha"
required
data={dummyMasterBidangBisnis.map((item) => ({
label: item.name,
value: item.id,
}))}
value={data.bidang_usaha}
onChange={(value) => {
setData({ ...(data as any), bidang_usaha: value });
}}
/>
<Grid>
<Grid.Col span={10}>
<SelectCustom
// disabled
label="Sub Bidang Usaha"
required
data={dummyMasterSubBidangBisnis.map((item) => ({
label: item.name,
value: item.id,
}))}
value={data.sub_bidang_usaha}
onChange={(value) => {
setData({ ...(data as any), sub_bidang_usaha: value });
}}
/>
</Grid.Col>
<Grid.Col
span={2}
style={{ alignItems: "center", justifyContent: "center" }}
>
<TouchableOpacity onPress={() => console.log("delete")}>
<Ionicons name="trash" size={24} color={MainColor.red} />
</TouchableOpacity>
</Grid.Col>
</Grid>
<ButtonCenteredOnly onPress={() => console.log("add")}>
Tambah Pilihan
</ButtonCenteredOnly>
<Spacing />
<View>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<TextCustom semiBold style={{ color: MainColor.white_gray }}>
Nomor Telepon
</TextCustom>
<Text style={{ color: "red" }}> *</Text>
</View>
<Spacing height={5} />
<PhoneInput
value={inputValue}
onChangePhoneNumber={handleInputValue}
selectedCountry={selectedCountry}
onChangeSelectedCountry={handleSelectedCountry}
defaultCountry="ID"
placeholder="xxx-xxx-xxx"
/>
</View>
<Spacing />
<TextInputCustom
required
label="Alamat Bisnis"
placeholder="Masukkan alamat bisnis"
/>
<TextAreaCustom
label="Deskripsi Bisnis"
placeholder="Masukkan deskripsi bisnis"
value={data.deskripsi}
onChangeText={(value: any) =>
setData({ ...data, deskripsi: value })
}
autosize
minRows={2}
maxRows={5}
required
showCount
maxLength={100}
/>
<Spacing />
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,78 @@
import { AlertCustom, DrawerCustom } from "@/components";
import LeftButtonCustom from "@/components/Button/BackButton";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { MainColor } from "@/constants/color-palet";
import { drawerItemsPortofolio } from "@/screens/Portofolio/ListPage";
import Portofolio_MenuDrawerSection from "@/screens/Portofolio/MenuDrawer";
import PorfofolioSection from "@/screens/Portofolio/PorfofolioSection";
import { GStyles } from "@/styles/global-styles";
import { Ionicons } from "@expo/vector-icons";
import { Stack, useLocalSearchParams, router } from "expo-router";
import { useState } from "react";
import { TouchableOpacity } from "react-native";
export default function Portofolio() {
const { id } = useLocalSearchParams();
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [deleteAlert, setDeleteAlert] = useState(false);
const openDrawer = () => {
setIsDrawerOpen(true);
};
const closeDrawer = () => {
setIsDrawerOpen(false);
};
return (
<>
<ViewWrapper>
{/* Header */}
<Stack.Screen
options={{
title: "Portofolio",
headerLeft: () => <LeftButtonCustom />,
headerRight: () => (
<TouchableOpacity onPress={openDrawer}>
<Ionicons
name="ellipsis-vertical"
size={20}
color={MainColor.yellow}
/>
</TouchableOpacity>
),
headerStyle: GStyles.headerStyle,
headerTitleStyle: GStyles.headerTitleStyle,
}}
/>
<PorfofolioSection setShowDeleteAlert={setDeleteAlert} />
</ViewWrapper>
{/* Drawer Komponen Eksternal */}
<DrawerCustom
isVisible={isDrawerOpen}
closeDrawer={closeDrawer}
height={350}
>
<Portofolio_MenuDrawerSection
drawerItems={drawerItemsPortofolio({ id: id as string })}
setIsDrawerOpen={setIsDrawerOpen}
/>
</DrawerCustom>
{/* Alert Delete */}
<AlertCustom
isVisible={deleteAlert}
onLeftPress={() => setDeleteAlert(false)}
onRightPress={() => {
setDeleteAlert(false);
console.log("Hapus portofolio");
router.back();
}}
title="Hapus Portofolio"
message="Apakah Anda yakin ingin menghapus portofolio ini?"
textLeft="Batal"
textRight="Hapus"
colorRight={MainColor.red}
/>
</>
);
}

View File

@@ -0,0 +1,47 @@
import { BaseBox, Grid, TextCustom, ViewWrapper } from "@/components";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import { Ionicons } from "@expo/vector-icons";
import { router, useLocalSearchParams } from "expo-router";
export default function ListPortofolio() {
const { id } = useLocalSearchParams();
return (
<ViewWrapper>
{Array.from({ length: 10 }).map((_, index) => (
<BaseBox
key={index}
style={{ backgroundColor: MainColor.darkblue }}
onPress={() => {
console.log("press to Portofolio");
router.push(`/portofolio/${id}`);
}}
>
<Grid>
<Grid.Col
span={10}
style={{ justifyContent: "center", backgroundColor: "" }}
>
<TextCustom bold size="large" truncate={1}>
Nama usaha portofolio
</TextCustom>
<TextCustom size="small" color="yellow">
#id-porofolio12345
</TextCustom>
</Grid.Col>
<Grid.Col
span={2}
style={{ alignItems: "flex-end", justifyContent: "center" }}
>
<Ionicons
name="caret-forward"
size={ICON_SIZE_SMALL}
color="white"
/>
</Grid.Col>
</Grid>
</BaseBox>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,32 @@
import LeftButtonCustom from "@/components/Button/BackButton";
import { HeaderStyles } from "@/styles/header-styles";
import { Stack } from "expo-router";
export default function PortofolioLayout() {
return (
<>
<Stack
screenOptions={{
...HeaderStyles,
headerLeft: () => <LeftButtonCustom />,
}}
>
{/* <Stack.Screen name="[id]/index" options={{ title: "Portofolio" }} /> */}
<Stack.Screen
name="[id]/create"
options={{ title: "Tambah Portofolio" }}
/>
<Stack.Screen
name="[id]/list"
options={{ title: "Daftar Portofolio" }}
/>
<Stack.Screen name="[id]/edit" options={{ title: "Edit Portofolio" }} />
<Stack.Screen name="[id]/edit-logo" options={{ title: "Edit Logo " }} />
<Stack.Screen
name="[id]/edit-social-media"
options={{ title: "Edit Social Media" }}
/>
</Stack>
</>
);
}

View File

@@ -0,0 +1,110 @@
import {
AvatarCustom,
ButtonCenteredOnly,
ButtonCustom,
SelectCustom,
Spacing,
StackCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import BoxButtonOnFooter from "@/components/Box/BoxButtonOnFooter";
import InformationBox from "@/components/Box/InformationBox";
import LandscapeFrameUploaded from "@/components/Image/LandscapeFrameUploaded";
import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react";
import { View } from "react-native";
export default function CreateProfile() {
const { id } = useLocalSearchParams();
const [data, setData] = useState({
name: "",
email: "",
address: "",
gender: "",
});
const handlerSave = () => {
console.log("data create profile >>", data);
router.back();
};
const footerComponent = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={handlerSave}
// disabled={!data.name || !data.email || !data.address || !data.gender}
>
Simpan
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<ViewWrapper footerComponent={footerComponent}>
<StackCustom>
<InformationBox text="Upload foto profile anda." />
<View style={{ alignItems: "center" }}>
<AvatarCustom size="xl" />
<Spacing />
<ButtonCenteredOnly
icon="upload"
onPress={() => router.navigate(`/take-picture/${id}`)}
>
Upload
</ButtonCenteredOnly>
</View>
<Spacing />
<View>
<InformationBox text="Upload foto latar belakang anda." />
<LandscapeFrameUploaded />
<Spacing />
<ButtonCenteredOnly
icon="upload"
onPress={() => router.navigate(`/take-picture/${id}`)}
>
Upload
</ButtonCenteredOnly>
</View>
<Spacing />
<TextInputCustom
required
label="Nama"
placeholder="Masukkan nama"
value={data.name}
onChangeText={(text) => setData({ ...data, name: text })}
/>
<TextInputCustom
keyboardType="email-address"
required
label="Email"
placeholder="Masukkan email"
value={data.email}
onChangeText={(text) => setData({ ...data, email: text })}
/>
<TextInputCustom
required
label="Alamat"
placeholder="Masukkan alamat"
value={data.address}
onChangeText={(text) => setData({ ...data, address: text })}
/>
<SelectCustom
label="Jenis Kelamin"
placeholder="Pilih jenis kelamin"
data={[
{ label: "Laki-laki", value: "laki-laki" },
{ label: "Perempuan", value: "perempuan" },
]}
value={data.gender}
required
onChange={(value) => setData({ ...(data as any), gender: value })}
/>
<Spacing />
</StackCustom>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,108 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import {
ButtonCustom,
SelectCustom,
StackCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import BoxButtonOnFooter from "@/components/Box/BoxButtonOnFooter";
import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react";
import { StyleSheet } from "react-native";
export default function ProfileEdit() {
const { id } = useLocalSearchParams();
const [data, setData] = useState({
nama: "Bagas Banuna",
email: "bagasbanuna@gmail.com",
alamat: "Jember",
selectedValue: "",
});
const options = [
{ label: "Laki-laki", value: "laki-laki" },
{ label: "Perempuan", value: "perempuan" },
];
const handleSave = () => {
console.log({
nama: data.nama,
email: data.email,
alamat: data.alamat,
selectedValue: data.selectedValue,
});
router.back();
};
return (
<ViewWrapper
footerComponent={
<BoxButtonOnFooter>
<ButtonCustom
// disabled={
// !data.nama || !data.email || !data.alamat || !data.selectedValue
// }
onPress={handleSave}
>
Simpan
</ButtonCustom>
</BoxButtonOnFooter>
}
>
<StackCustom gap={"xs"}>
<TextInputCustom
label="Nama"
placeholder="Nama"
value={data.nama}
onChangeText={(text) => {
setData({ ...data, nama: text });
}}
required
/>
<TextInputCustom
label="Email"
placeholder="Email"
value={data.email}
onChangeText={(text) => {
setData({ ...data, email: text });
}}
required
/>
<TextInputCustom
label="Alamat"
placeholder="Alamat"
value={data.alamat}
onChangeText={(text) => {
setData({ ...data, alamat: text });
}}
required
/>
<SelectCustom
required
label="Jenis Kelamin"
placeholder="Pilih jenis kelamin"
data={options}
value={data.selectedValue}
onChange={(value) => {
setData({ ...(data as any), selectedValue: value });
}}
/>
</StackCustom>
</ViewWrapper>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
padding: 20,
},
result: {
marginTop: 20,
fontSize: 16,
fontWeight: "bold",
},
});

View File

@@ -0,0 +1,83 @@
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import AlertCustom from "@/components/Alert/AlertCustom";
import LeftButtonCustom from "@/components/Button/BackButton";
import DrawerCustom from "@/components/Drawer/DrawerCustom";
import { MainColor } from "@/constants/color-palet";
import { drawerItemsProfile } from "@/screens/Profile/ListPage";
import Profile_MenuDrawerSection from "@/screens/Profile/menuDrawerSection";
import ProfileSection from "@/screens/Profile/ProfileSection";
import { GStyles } from "@/styles/global-styles";
import { Ionicons } from "@expo/vector-icons";
import { router, Stack, useLocalSearchParams } from "expo-router";
import React, { useState } from "react";
import { TouchableOpacity } from "react-native";
export default function Profile() {
const { id } = useLocalSearchParams();
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [showLogoutAlert, setShowLogoutAlert] = useState(false);
const openDrawer = () => {
setIsDrawerOpen(true);
};
const closeDrawer = () => {
setIsDrawerOpen(false);
};
const handleLogout = () => {
console.log("User logout");
router.replace("/");
setShowLogoutAlert(false);
};
return (
<>
<ViewWrapper>
{/* Header */}
<Stack.Screen
options={{
title: "Profile",
headerLeft: () => <LeftButtonCustom />,
headerRight: () => (
<TouchableOpacity onPress={openDrawer}>
<Ionicons
name="ellipsis-vertical"
size={20}
color={MainColor.yellow}
/>
</TouchableOpacity>
),
headerStyle: GStyles.headerStyle,
headerTitleStyle: GStyles.headerTitleStyle,
}}
/>
<ProfileSection />
</ViewWrapper>
{/* Drawer Komponen Eksternal */}
<DrawerCustom
height={350}
isVisible={isDrawerOpen}
closeDrawer={closeDrawer}
>
<Profile_MenuDrawerSection
drawerItems={drawerItemsProfile({ id: id as string })}
setShowLogoutAlert={setShowLogoutAlert}
setIsDrawerOpen={setIsDrawerOpen}
/>
</DrawerCustom>
{/* Alert Komponen Eksternal */}
<AlertCustom
isVisible={showLogoutAlert}
onLeftPress={() => setShowLogoutAlert(false)}
onRightPress={handleLogout}
title="Apakah anda yakin ingin keluar?"
textLeft="Batal"
textRight="Keluar"
colorRight={MainColor.red}
/>
</>
);
}

View File

@@ -0,0 +1,190 @@
// COMPONENT : Jika ingin uoload gambar dan video gunakan component ini
import {
ButtonCustom,
Spacing,
StackCustom,
ViewWrapper
} from "@/components";
import AntDesign from "@expo/vector-icons/AntDesign";
import Feather from "@expo/vector-icons/Feather";
import FontAwesome6 from "@expo/vector-icons/FontAwesome6";
import {
CameraMode,
CameraType,
CameraView,
useCameraPermissions,
} from "expo-camera";
import { Image } from "expo-image";
import { router } from "expo-router";
import { useRef, useState } from "react";
import { Button, Pressable, StyleSheet, Text, View } from "react-native";
export default function TakePictureProfile2() {
const [permission, requestPermission] = useCameraPermissions();
const ref = useRef<CameraView>(null);
const [uri, setUri] = useState<string | null>(null);
const [mode, setMode] = useState<CameraMode>("picture");
const [facing, setFacing] = useState<CameraType>("back");
const [recording, setRecording] = useState(false);
if (!permission?.granted) {
return (
<View style={styles.container}>
<Text style={{ textAlign: "center" }}>
We need your permission to use the camera
</Text>
<Button onPress={requestPermission} title="Grant permission" />
</View>
);
}
const takePicture = async () => {
const photo = await ref.current?.takePictureAsync();
setUri(photo?.uri || null);
};
const recordVideo = async () => {
if (recording) {
setRecording(false);
ref.current?.stopRecording();
return;
}
setRecording(true);
const video = await ref.current?.recordAsync();
console.log({ video });
};
const toggleMode = () => {
setMode((prev) => (prev === "picture" ? "video" : "picture"));
};
const toggleFacing = () => {
setFacing((prev) => (prev === "back" ? "front" : "back"));
};
const renderPicture = () => {
console.log("renderPicture", uri);
return (
<View>
<Image
source={uri ? uri : null}
contentFit="contain"
style={{ width: 340, aspectRatio: 1 }}
/>
<Spacing />
<StackCustom>
<ButtonCustom onPress={() => setUri(null)} title="Foto ulang" />
<ButtonCustom
onPress={() => {
console.log("Update foto");
router.back();
}}
title="Update Foto"
/>
</StackCustom>
</View>
);
};
const renderCameraUI = () => {
return (
<View style={styles.cameraOverlay}>
<View style={styles.shutterContainer}>
<Pressable onPress={toggleMode}>
{mode === "picture" ? (
<AntDesign name="picture" size={32} color="white" />
) : (
<Feather name="video" size={32} color="white" />
)}
</Pressable>
<Pressable onPress={mode === "picture" ? takePicture : recordVideo}>
{({ pressed }) => (
<View
style={[
styles.shutterBtn,
{
opacity: pressed ? 0.5 : 1,
},
]}
>
<View
style={[
styles.shutterBtnInner,
{
backgroundColor: mode === "picture" ? "white" : "red",
},
]}
/>
</View>
)}
</Pressable>
<Pressable onPress={toggleFacing}>
<FontAwesome6 name="rotate-left" size={32} color="white" />
</Pressable>
</View>
</View>
);
};
return (
<>
{uri ? (
<ViewWrapper>
<View style={styles.container}>{renderPicture()}</View>
</ViewWrapper>
) : (
<>
<CameraView
style={styles.camera}
ref={ref}
mode={mode}
facing={facing}
mute={false}
responsiveOrientationWhenOrientationLocked
/>
{renderCameraUI()}
</>
)}
</>
);
}
const styles = StyleSheet.create({
container: {
justifyContent: "center",
alignItems: "center",
},
camera: {
flex: 1,
width: "100%",
},
cameraOverlay: {
...StyleSheet.absoluteFillObject,
justifyContent: "flex-end",
padding: 44,
},
shutterContainer: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
shutterBtn: {
backgroundColor: "transparent",
borderWidth: 5,
borderColor: "white",
width: 85,
height: 85,
borderRadius: 45,
alignItems: "center",
justifyContent: "center",
},
shutterBtnInner: {
width: 70,
height: 70,
borderRadius: 50,
},
});

View File

@@ -0,0 +1,47 @@
import {
BaseBox,
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
} from "@/components";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import DUMMY_IMAGE from "@/constants/dummy-image-value";
import { router, useLocalSearchParams } from "expo-router";
import { Image } from "react-native";
export default function UpdateBackgroundProfile() {
const { id } = useLocalSearchParams();
const buttonFooter = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => {
console.log("Simpan foto background >>", id);
router.back();
}}
>
Simpan
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<ViewWrapper footerComponent={buttonFooter}>
<BaseBox
style={{ alignItems: "center", justifyContent: "center", height: 250 }}
>
<Image
source={DUMMY_IMAGE.background}
resizeMode="cover"
style={{ width: "100%", height: "100%", borderRadius: 10 }}
/>
</BaseBox>
<ButtonCenteredOnly
icon="upload"
onPress={() => router.navigate(`/(application)/take-picture/${id}`)}
>
Update
</ButtonCenteredOnly>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,47 @@
import {
AvatarCustom,
BaseBox,
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
} from "@/components";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { router, useLocalSearchParams } from "expo-router";
export default function UpdatePhotoProfile() {
const { id } = useLocalSearchParams();
const buttonFooter = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => {
console.log("Simpan foto profile >>", id);
router.back();
}}
>
Simpan
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<ViewWrapper footerComponent={buttonFooter}>
<BaseBox
style={{ alignItems: "center", justifyContent: "center", height: 250 }}
>
<AvatarCustom size="xl" />
</BaseBox>
<ButtonCenteredOnly
icon="upload"
onPress={() => {
console.log("Update photo >>", id);
router.navigate(`/(application)/take-picture/${id}`);
}}
>
Update
</ButtonCenteredOnly>
{/* <Spacing />
<ButtonCustom>Test</ButtonCustom> */}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,34 @@
import { BackButton } from "@/components";
import { GStyles } from "@/styles/global-styles";
import { Stack } from "expo-router";
export default function ProfileLayout() {
return (
<>
<Stack
screenOptions={{
headerStyle: GStyles.headerStyle,
headerTitleStyle: GStyles.headerTitleStyle,
headerTitleAlign: "center",
headerBackButtonDisplayMode: "minimal",
headerLeft: () => <BackButton />,
}}
>
{/* <Stack.Screen name="[id]/index" options={{ headerShown: false }} /> */}
<Stack.Screen name="[id]/edit" options={{ title: "Edit Profile" }} />
<Stack.Screen
name="[id]/update-photo"
options={{ title: "Update Foto" }}
/>
<Stack.Screen
name="[id]/update-background"
options={{ title: "Update Latar Belakang" }}
/>
<Stack.Screen
name="[id]/create"
options={{ title: "Buat Profile" }}
/>
</Stack>
</>
);
}

View File

@@ -0,0 +1,100 @@
import {
AvatarCustom,
ClickableCustom,
Grid,
Spacing,
StackCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router";
export default function UserSearch() {
function generateRandomPhoneNumber(index: number) {
let prefix;
// Menentukan prefix berdasarkan index genap atau ganjil
if (index % 2 === 0) {
const evenPrefixes = ["6288", "6289", "6281"];
prefix = evenPrefixes[Math.floor(Math.random() * evenPrefixes.length)];
} else {
const oddPrefixes = ["6285", "6283"];
prefix = oddPrefixes[Math.floor(Math.random() * oddPrefixes.length)];
}
// Menghitung panjang sisa nomor acak (antara 10 - 12 digit)
const remainingLength = Math.floor(Math.random() * 3) + 10; // 10, 11, atau 12
// Membuat sisa nomor acak
let randomNumber = "";
for (let i = 0; i < remainingLength; i++) {
randomNumber += Math.floor(Math.random() * 10); // Digit acak antara 0-9
}
// Menggabungkan prefix dan sisa nomor
return prefix + randomNumber;
}
return (
<>
<ViewWrapper
headerComponent={
<TextInputCustom
iconLeft={
<Ionicons
name="search"
size={ICON_SIZE_SMALL}
color={MainColor.placeholder}
/>
}
placeholder="Cari Pengguna"
borderRadius={50}
containerStyle={{ marginBottom: 0 }}
/>
}
>
<StackCustom>
{Array.from({ length: 20 }).map((e, index) => {
return (
<Grid key={index}>
<Grid.Col span={2}>
<AvatarCustom href={`/profile/${index}`}/>
</Grid.Col>
<Grid.Col span={9}>
<TextCustom size="large">Nama user {index}</TextCustom>
<TextCustom size="small">
+{generateRandomPhoneNumber(index)}
</TextCustom>
</Grid.Col>
<Grid.Col
span={1}
style={{
justifyContent: "center",
alignItems: "flex-end",
}}
>
<ClickableCustom
onPress={() => {
console.log("Ke Profile");
router.push(`/profile/${index}`);
}}
>
<Ionicons
name="chevron-forward"
size={ICON_SIZE_SMALL}
color={MainColor.white}
/>
</ClickableCustom>
</Grid.Col>
</Grid>
);
})}
</StackCustom>
<Spacing height={50} />
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,43 @@
import {
IconContribution,
IconHistory,
IconHome,
IconStatus,
} from "@/components/_Icon";
import { TabsStyles } from "@/styles/tabs-styles";
import { Tabs } from "expo-router";
export default function VotingTabsLayout() {
return (
<Tabs screenOptions={TabsStyles}>
<Tabs.Screen
name="index"
options={{
title: "Beranda",
tabBarIcon: ({ color }) => <IconHome color={color} />,
}}
/>
<Tabs.Screen
name="status"
options={{
title: "Status",
tabBarIcon: ({ color }) => <IconStatus color={color} />,
}}
/>
<Tabs.Screen
name="contribution"
options={{
title: "Kontribusi",
tabBarIcon: ({ color }) => <IconContribution color={color} />,
}}
/>
<Tabs.Screen
name="history"
options={{
title: "Riwayat",
tabBarIcon: ({ color }) => <IconHistory color={color} />,
}}
/>
</Tabs>
);
}

View File

@@ -0,0 +1,17 @@
import {
ViewWrapper
} from "@/components";
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
export default function VotingContribution() {
return (
<ViewWrapper hideFooter>
{Array.from({ length: 5 }).map((_, index) => (
<Voting_BoxPublishSection
key={index}
href={`/voting/${index}/contribution`}
/>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,37 @@
import { ViewWrapper } from "@/components";
import TabsTwoHeaderCustom from "@/components/_ShareComponent/TabsTwoHeaderCustom";
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
import { useState } from "react";
export default function VotingHistory() {
const [activeCategory, setActiveCategory] = useState<string | null>("all");
const handlePress = (item: any) => {
setActiveCategory(item);
// tambahkan logika lain seperti filter dsb.
};
return (
<ViewWrapper
hideFooter
headerComponent={
<TabsTwoHeaderCustom
leftValue="all"
rightValue="main"
leftText="Semua Riwayat"
rightText="Riwayat Saya"
activeCategory={activeCategory}
handlePress={handlePress}
/>
}
>
{Array.from({ length: 10 }).map((_, index) => (
<Voting_BoxPublishSection
key={index}
id={activeCategory as any}
href={`/voting/${index}/history`}
/>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,23 @@
import {
FloatingButton,
SearchInput,
ViewWrapper
} from "@/components";
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
import { router } from "expo-router";
export default function VotingBeranda() {
return (
<ViewWrapper
hideFooter
floatingButton={
<FloatingButton onPress={() => router.push("/voting/create")} />
}
headerComponent={<SearchInput placeholder="Cari voting" />}
>
{Array.from({ length: 5 }).map((_, index) => (
<Voting_BoxPublishSection key={index} href={`/voting/${index}`} />
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,60 @@
import {
BadgeCustom,
BaseBox,
ScrollableCustom,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { masterStatus } from "@/lib/dummy-data/_master/status";
import dayjs from "dayjs";
import { useState } from "react";
export default function VotingStatus() {
const [activeCategory, setActiveCategory] = useState<string | null>(
"publish"
);
const handlePress = (item: any) => {
setActiveCategory(item.value);
// tambahkan logika lain seperti filter dsb.
};
const scrollComponent = (
<ScrollableCustom
data={masterStatus.map((e, i) => ({
id: i,
label: e.label,
value: e.value,
}))}
onButtonPress={handlePress}
activeId={activeCategory as any}
/>
);
return (
<ViewWrapper headerComponent={scrollComponent} hideFooter>
{Array.from({ length: 10 }).map((_, i) => (
<BaseBox
key={i}
paddingTop={20}
paddingBottom={20}
href={`/voting/${i}/${activeCategory}/detail`}
>
<StackCustom>
<TextCustom align="center" bold truncate size="large">
Lorem ipsum dolor sit {activeCategory}
</TextCustom>
<BadgeCustom
style={{ width: "70%", alignSelf: "center" }}
variant="light"
>
{dayjs().format("DD/MM/YYYY")} -{" "}
{dayjs().add(1, "day").format("DD/MM/YYYY")}
</BadgeCustom>
</StackCustom>
</BaseBox>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,108 @@
import {
AlertDefaultSystem,
BackButton,
DotButton,
DrawerCustom,
MenuDrawerDynamicGrid,
Spacing,
ViewWrapper,
} from "@/components";
import { IconArchive, IconContribution, IconEdit } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types";
import { Voting_BoxDetailSection } from "@/screens/Voting/BoxDetailSection";
import Voting_ButtonStatusSection from "@/screens/Voting/ButtonStatusSection";
import { router, Stack, useLocalSearchParams } from "expo-router";
import { useState } from "react";
export default function VotingDetailStatus() {
const { id, status } = useLocalSearchParams();
const [openDrawerDraft, setOpenDrawerDraft] = useState(false);
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
const handlePressDraft = (item: IMenuDrawerItem) => {
console.log("PATH >> ", item.path);
router.navigate(item.path as any);
setOpenDrawerDraft(false);
};
const handlePressPublish = (item: IMenuDrawerItem) => {
if (item.path === "") {
AlertDefaultSystem({
title: "Update Arsip",
message: "Apakah Anda yakin ingin mengarsipkan voting ini?",
textLeft: "Batal",
textRight: "Ya",
onPressRight: () => {
console.log("Hapus");
router.back();
},
});
}
router.navigate(item.path as any);
setOpenDrawerPublish(false);
};
return (
<>
<Stack.Screen
options={{
title: `Detail ${status}`,
headerLeft: () => <BackButton />,
headerRight: () =>
status === "draft" ? (
<DotButton onPress={() => setOpenDrawerDraft(true)} />
) : status === "publish" ? (
<DotButton onPress={() => setOpenDrawerPublish(true)} />
) : null,
}}
/>
<ViewWrapper>
<Voting_BoxDetailSection />
<Voting_ButtonStatusSection status={status as string} />
<Spacing />
</ViewWrapper>
{/* ========= Draft Drawer ========= */}
<DrawerCustom
isVisible={openDrawerDraft}
closeDrawer={() => setOpenDrawerDraft(false)}
height={"auto"}
>
<MenuDrawerDynamicGrid
data={[
{
icon: <IconEdit />,
label: "Edit",
path: `/voting/${id}/edit`,
},
]}
columns={4}
onPressItem={handlePressDraft as any}
/>
</DrawerCustom>
{/* ========= Publish Drawer ========= */}
<DrawerCustom
isVisible={openDrawerPublish}
closeDrawer={() => setOpenDrawerPublish(false)}
height={"auto"}
>
<MenuDrawerDynamicGrid
data={[
{
icon: <IconContribution />,
label: "Daftar Kontributor",
path: `/voting/${id}/list-of-contributor`,
},
{
icon: <IconArchive />,
label: "Update Arsip",
path: "" as any,
},
]}
onPressItem={handlePressPublish as any}
/>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,65 @@
import {
AvatarUsernameAndOtherComponent,
BackButton,
DotButton,
DrawerCustom,
MenuDrawerDynamicGrid,
Spacing,
ViewWrapper,
} from "@/components";
import { IconContribution } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types";
import { Voting_BoxDetailContributionSection } from "@/screens/Voting/BoxDetailContribution";
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
import { router, Stack, useLocalSearchParams } from "expo-router";
import { useState } from "react";
export default function VotingDetailContribution() {
const { id } = useLocalSearchParams();
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
const handlePressPublish = (item: IMenuDrawerItem) => {
router.navigate(item.path as any);
setOpenDrawerPublish(false);
};
return (
<>
<Stack.Screen
options={{
title: "Detail Kontribusi",
headerLeft: () => <BackButton />,
headerRight: () => (
<DotButton onPress={() => setOpenDrawerPublish(true)} />
),
}}
/>
<ViewWrapper>
<Voting_BoxDetailContributionSection
headerAvatar={<AvatarUsernameAndOtherComponent />}
/>
<Voting_BoxDetailHasilVotingSection />
<Spacing />
</ViewWrapper>
{/* ========= Publish Drawer ========= */}
<DrawerCustom
isVisible={openDrawerPublish}
closeDrawer={() => setOpenDrawerPublish(false)}
height={"auto"}
>
<MenuDrawerDynamicGrid
data={[
{
icon: <IconContribution />,
label: "Daftar Kontributor",
path: `/voting/${id}/list-of-contributor`,
},
]}
onPressItem={handlePressPublish as any}
/>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,78 @@
import {
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
Grid,
Spacing,
StackCustom,
TextAreaCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
import { MainColor } from "@/constants/color-palet";
import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { TouchableOpacity } from "react-native";
export default function VotingEdit() {
const buttonSubmit = () => {
return (
<>
<BoxButtonOnFooter>
<ButtonCustom
onPress={() =>
router.back()
}
>
Update
</ButtonCustom>
</BoxButtonOnFooter>
</>
);
};
return (
<ViewWrapper footerComponent={buttonSubmit()}>
<StackCustom gap={"xs"}>
<TextInputCustom
label="Judul Voting"
placeholder="MasukanJudul Voting"
required
/>
<TextAreaCustom
label="Deskripsi"
placeholder="Masukan Deskripsi"
required
showCount
maxLength={1000}
/>
<DateTimePickerCustom label="Mulai Voting" required />
<DateTimePickerCustom label="Voting Berakhir" required />
<Grid>
<Grid.Col span={10}>
<TextInputCustom
label="Pilihan"
placeholder="Masukan Pilihan"
required
/>
</Grid.Col>
<Grid.Col
span={2}
style={{ alignItems: "center", justifyContent: "center" }}
>
<TouchableOpacity onPress={() => console.log("delete")}>
<Ionicons name="trash" size={24} color={MainColor.red} />
</TouchableOpacity>
</Grid.Col>
</Grid>
<ButtonCenteredOnly onPress={() => console.log("add")}>
Tambah Pilihan
</ButtonCenteredOnly>
<Spacing />
</StackCustom>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,64 @@
import {
AvatarUsernameAndOtherComponent,
BackButton,
DotButton,
DrawerCustom,
MenuDrawerDynamicGrid,
Spacing,
ViewWrapper,
} from "@/components";
import { IconContribution } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types";
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
import { Voting_BoxDetailHistorySection } from "@/screens/Voting/BoxDetailHistorySection";
import { router, Stack, useLocalSearchParams } from "expo-router";
import { useState } from "react";
export default function VotingDetailHistory() {
const { id } = useLocalSearchParams();
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
const handlePressPublish = (item: IMenuDrawerItem) => {
router.navigate(item.path as any);
setOpenDrawerPublish(false);
};
return (
<>
<Stack.Screen
options={{
title: "Riwayat Voting",
headerLeft: () => <BackButton />,
headerRight: () => (
<DotButton onPress={() => setOpenDrawerPublish(true)} />
),
}}
/>
<ViewWrapper>
<Voting_BoxDetailHistorySection
headerAvatar={<AvatarUsernameAndOtherComponent />}
/>
<Voting_BoxDetailHasilVotingSection />
<Spacing />
</ViewWrapper>
{/* ========= Publish Drawer ========= */}
<DrawerCustom
isVisible={openDrawerPublish}
closeDrawer={() => setOpenDrawerPublish(false)}
height={"auto"}
>
<MenuDrawerDynamicGrid
data={[
{
icon: <IconContribution />,
label: "Daftar Kontributor",
path: `/voting/${id}/list-of-contributor`,
},
]}
onPressItem={handlePressPublish as any}
/>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,87 @@
import {
AlertDefaultSystem,
AvatarUsernameAndOtherComponent,
BackButton,
DotButton,
DrawerCustom,
InformationBox,
MenuDrawerDynamicGrid,
StackCustom,
ViewWrapper,
} from "@/components";
import { IconArchive, IconContribution } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types";
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
import { Voting_BoxDetailPublishSection } from "@/screens/Voting/BoxDetailPublishSection";
import { router, Stack, useLocalSearchParams } from "expo-router";
import React, { useState } from "react";
export default function VotingDetail() {
const { id } = useLocalSearchParams();
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
const handlePressPublish = (item: IMenuDrawerItem) => {
if (item.path === "") {
AlertDefaultSystem({
title: "Update Arsip",
message: "Apakah Anda yakin ingin mengarsipkan voting ini?",
textLeft: "Batal",
textRight: "Ya",
onPressRight: () => {
console.log("Hapus");
router.back();
},
});
}
router.navigate(item.path as any);
setOpenDrawerPublish(false);
};
return (
<>
<Stack.Screen
options={{
title: `Detail Voting`,
headerLeft: () => <BackButton />,
headerRight: () => (
<DotButton onPress={() => setOpenDrawerPublish(true)} />
),
}}
/>
<ViewWrapper>
<StackCustom>
<InformationBox text="Untuk sementara voting ini belum di buka. Voting akan dimulai sesuai dengan tanggal awal pemilihan, dan akan ditutup sesuai dengan tanggal akhir pemilihan." />
<Voting_BoxDetailPublishSection
headerAvatar={<AvatarUsernameAndOtherComponent />}
/>
<Voting_BoxDetailHasilVotingSection />
</StackCustom>
</ViewWrapper>
{/* ========= Publish Drawer ========= */}
<DrawerCustom
isVisible={openDrawerPublish}
closeDrawer={() => setOpenDrawerPublish(false)}
height={"auto"}
>
<MenuDrawerDynamicGrid
data={[
{
icon: <IconContribution />,
label: "Daftar Kontributor",
path: `/voting/${id}/list-of-contributor`,
},
{
icon: <IconArchive />,
label: "Update Arsip",
path: "" as any,
},
]}
onPressItem={handlePressPublish as any}
/>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,26 @@
import {
AvatarUsernameAndOtherComponent,
BadgeCustom,
BaseBox,
ViewWrapper,
} from "@/components";
export default function Voting_ListOfContributor() {
return (
<ViewWrapper>
{Array.from({ length: 10 }).map((_, index) => (
<BaseBox paddingTop={5} paddingBottom={5} key={index.toString()}>
<AvatarUsernameAndOtherComponent
rightComponent={
<BadgeCustom
style={{alignSelf: "flex-end" }}
>
Pilihan {index + 1}
</BadgeCustom>
}
/>
</BaseBox>
))}
</ViewWrapper>
);
}

View File

@@ -0,0 +1,78 @@
import {
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
Grid,
Spacing,
StackCustom,
TextAreaCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
import { MainColor } from "@/constants/color-palet";
import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { TouchableOpacity } from "react-native";
export default function VotingCreate() {
const buttonSubmit = () => {
return (
<>
<BoxButtonOnFooter>
<ButtonCustom
onPress={() =>
router.replace("/(application)/(user)/voting/(tabs)/status")
}
>
Simpan
</ButtonCustom>
</BoxButtonOnFooter>
</>
);
};
return (
<ViewWrapper footerComponent={buttonSubmit()}>
<StackCustom gap={"xs"}>
<TextInputCustom
label="Judul Voting"
placeholder="MasukanJudul Voting"
required
/>
<TextAreaCustom
label="Deskripsi"
placeholder="Masukan Deskripsi"
required
showCount
maxLength={1000}
/>
<DateTimePickerCustom label="Mulai Voting" required />
<DateTimePickerCustom label="Voting Berakhir" required />
<Grid>
<Grid.Col span={10}>
<TextInputCustom
label="Pilihan"
placeholder="Masukan Pilihan"
required
/>
</Grid.Col>
<Grid.Col
span={2}
style={{ alignItems: "center", justifyContent: "center" }}
>
<TouchableOpacity onPress={() => console.log("delete")}>
<Ionicons name="trash" size={24} color={MainColor.red} />
</TouchableOpacity>
</Grid.Col>
</Grid>
<ButtonCenteredOnly onPress={() => console.log("add")}>
Tambah Pilihan
</ButtonCenteredOnly>
<Spacing />
</StackCustom>
</ViewWrapper>
);
}

View File

@@ -1,104 +1,28 @@
import { AccentColor, MainColor } from "@/constants/color-palet";
import { Ionicons } from "@expo/vector-icons";
import { router, Stack } from "expo-router";
import { BackButton } from "@/components";
import { HeaderStyles } from "@/styles/header-styles";
import { Stack } from "expo-router";
export default function ApplicationLayout() {
return (
<>
<Stack
screenOptions={{
headerStyle: { backgroundColor: MainColor.darkblue },
headerTitleStyle: { color: MainColor.yellow, fontWeight: "bold" },
headerTitleAlign: "center",
contentStyle: {
borderBottomColor: AccentColor.blue,
borderBottomWidth: 2,
},
headerLargeStyle: {
backgroundColor: MainColor.darkblue,
},
}}
>
<Stack screenOptions={HeaderStyles}>
<Stack.Screen name="(user)" options={{ headerShown: false }} />
{/* Take Picture */}
<Stack.Screen
name="home"
name="(image)/take-picture/[id]/index"
options={{
title: "HIPMI",
headerLeft: () => (
<Ionicons
name="search"
size={20}
color={MainColor.yellow}
onPress={() => router.back()}
/>
),
headerRight: () => (
<Ionicons
name="notifications"
size={20}
color={MainColor.yellow}
onPress={() => router.back()}
/>
),
title: "Ambil Gambar",
headerLeft: () => <BackButton />,
}}
/>
{/* Preview Image */}
<Stack.Screen
name="forum/index"
name="(image)/preview-image/[id]/index"
options={{
title: "Forum",
headerLeft: () => (
<Ionicons
name="arrow-back"
size={20}
color={MainColor.yellow}
onPress={() => router.back()}
/>
),
}}
/>
<Stack.Screen
name="maps/index"
options={{
title: "Maps",
headerLeft: () => (
<Ionicons
name="arrow-back"
size={20}
color={MainColor.yellow}
onPress={() => router.back()}
/>
),
}}
/>
<Stack.Screen
name="market-place/index"
options={{
title: "Market Place",
headerLeft: () => (
<Ionicons
name="arrow-back"
size={20}
color={MainColor.yellow}
onPress={() => router.back()}
/>
),
}}
/>
<Stack.Screen
name="profile/index"
options={{
title: "Profile",
headerLeft: () => (
<Ionicons
name="arrow-back"
size={20}
color={MainColor.yellow}
onPress={() => router.back()}
/>
),
title: "Preview Gambar",
headerLeft: () => <BackButton />,
}}
/>
</Stack>

View File

@@ -0,0 +1,258 @@
// File: src/screens/EventDetailScreen.tsx
import LeftButtonCustom from "@/components/Button/BackButton";
import { MainColor } from "@/constants/color-palet";
import React, { useState, useEffect } from "react";
import {
FlatList,
View,
Text,
Image,
StyleSheet,
ActivityIndicator,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
// === TYPES ===
type Participant = {
id: number;
name: string;
avatar: string;
};
type EventDetail = {
id: number;
title: string;
description: string;
date: string;
location: string;
organizer: string;
};
// === KOMPONEN UTAMA ===
const EventDetailScreen: React.FC = () => {
const [participants, setParticipants] = useState<Participant[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [loadingMore, setLoadingMore] = useState<boolean>(false);
// Data event
const event: EventDetail = {
id: 1,
title: "Workshop React Native & Expo",
description:
"Pelatihan intensif pengembangan aplikasi mobile menggunakan React Native, Expo, dan TypeScript. Cocok untuk developer tingkat menengah.",
date: "Sabtu, 5 April 2025 | 09:00 - 16:00",
location: "Gedung Teknologi, Jakarta Selatan",
organizer: "DevCommunity Indonesia",
};
// Simulasi API: generate data dummy
const generateParticipants = (
startId: number,
count: number
): Participant[] => {
return Array.from({ length: count }, (_, i) => {
const id = startId + i;
return {
id,
name: `Peserta ${id}`,
avatar: `https://i.pravatar.cc/150?img=${(id % 70) + 1}`, // 70 gambar unik
};
});
};
// Load data awal
useEffect(() => {
const loadInitial = () => {
setTimeout(() => {
const initialData = generateParticipants(1, 20); // 20 peserta pertama
setParticipants(initialData);
setLoading(false);
}, 800);
};
loadInitial();
}, []);
// Load lebih banyak peserta saat scroll ke bawah
const loadMore = () => {
if (loadingMore || participants.length >= 200) return; // Batas 200 peserta
setLoadingMore(true);
setTimeout(() => {
const nextId = participants.length + 1;
const newData = generateParticipants(nextId, 10); // Tambah 10 peserta
setParticipants((prev) => [...prev, ...newData]);
setLoadingMore(false);
}, 1000);
};
// Render footer: loading indicator
const renderFooter = () => {
if (!loadingMore) return null;
return (
<View style={styles.footer}>
<ActivityIndicator size="small" color="#007AFF" />
<Text style={styles.loadingText}> Memuat peserta berikutnya...</Text>
</View>
);
};
// Render header: detail event + info jumlah peserta
const renderHeader = () => (
<>
<View style={styles.headerContainer}>
<LeftButtonCustom path={"/"} />
<Text style={styles.title}>{event.title}</Text>
<Text style={styles.info}>📅 {event.date}</Text>
<Text style={styles.info}>📍 {event.location}</Text>
<Text style={styles.info}>👤 {event.organizer}</Text>
<Text style={styles.description}>{event.description}</Text>
<View style={styles.divider} />
<Text style={styles.sectionTitle}>
Daftar Peserta ({participants.length})
</Text>
</View>
{/* Sub-header tambahan jika perlu */}
{participants.length === 0 ? (
<Text style={styles.empty}>Belum ada peserta yang terdaftar.</Text>
) : null}
</>
);
// Loading awal
if (loading) {
return (
<SafeAreaView style={styles.container}>
<ActivityIndicator size="large" color="#007AFF" style={styles.loader} />
</SafeAreaView>
);
}
return (
<>
<FlatList
data={participants}
keyExtractor={(item) => item.id.toString()}
ListHeaderComponent={renderHeader}
ListFooterComponent={renderFooter}
onEndReached={loadMore}
onEndReachedThreshold={0.5}
renderItem={({ item }) => (
<View style={styles.participantItem}>
<Image source={{ uri: item.avatar }} style={styles.avatar} />
<Text style={styles.participantName}>{item.name}</Text>
</View>
)}
initialNumToRender={10}
maxToRenderPerBatch={5}
windowSize={7}
showsVerticalScrollIndicator={false}
ListEmptyComponent={
<Text style={styles.empty}>Tidak ada peserta.</Text>
}
/>
<SafeAreaView
edges={["bottom"]}
style={{ backgroundColor: MainColor.darkblue }}
/>
</>
);
};
export default EventDetailScreen;
// === STYLES ===
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#f8f9fa",
},
loader: {
marginTop: 20,
},
headerContainer: {
padding: 16,
backgroundColor: "#ffffff",
borderBottomWidth: 1,
borderBottomColor: "#e0e0e0",
},
title: {
fontSize: 24,
fontWeight: "bold",
color: "#1d1d1d",
marginBottom: 8,
},
info: {
fontSize: 16,
color: "#444",
marginBottom: 4,
},
description: {
fontSize: 14,
color: "#666",
lineHeight: 20,
marginTop: 12,
},
divider: {
height: 1,
backgroundColor: "#e0e0e0",
marginVertical: 16,
},
sectionTitle: {
fontSize: 18,
fontWeight: "600",
color: "#1d1d1d",
marginBottom: 12,
},
participantItem: {
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: "#fff",
borderBottomWidth: 1,
borderBottomColor: "#f0f0f0",
},
avatar: {
width: 40,
height: 40,
borderRadius: 20,
marginRight: 12,
},
participantName: {
fontSize: 16,
color: "#333",
},
footer: {
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
padding: 16,
backgroundColor: "#f8f9fa",
},
loadingText: {
color: "#555",
fontSize: 14,
},
empty: {
textAlign: "center",
color: "#999",
fontStyle: "italic",
padding: 16,
backgroundColor: "#fff",
fontSize: 14,
},
subHeader: {
paddingHorizontal: 16,
paddingVertical: 8,
backgroundColor: "#f1f1f1",
fontSize: 14,
color: "#666",
fontStyle: "italic",
},
});

View File

@@ -9,7 +9,11 @@ import {
ScrollView,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { router, Stack } from "expo-router";
import EventDetailScreen from "./double-scroll";
import LeftButtonCustom from "@/components/Button/BackButton";
import CustomUploadButton from "./upload-button";
import { SafeAreaView } from "react-native-safe-area-context";
const { width } = Dimensions.get("window");
@@ -17,7 +21,9 @@ const { width } = Dimensions.get("window");
const HomeScreen = () => (
<View style={styles.screen}>
<Text style={styles.screenTitle}>Selamat Datang!</Text>
<Text style={styles.screenText}>Ini adalah halaman utama aplikasi Anda</Text>
<Text style={styles.screenText}>
Ini adalah halaman utama aplikasi Anda
</Text>
</View>
);
const SearchScreen = () => (
@@ -66,9 +72,7 @@ const CustomTab = ({ icon, label, isActive, onPress }: any) => (
// Main Custom Tab Navigator
const CustomTabNavigator = () => {
const [activeTab, setActiveTab] = React.useState(
'home'
);
const [activeTab, setActiveTab] = React.useState("home");
const [showHome, setShowHome] = React.useState(true);
const tabs = [
@@ -86,7 +90,7 @@ const CustomTabNavigator = () => {
activeIcon: "notifications",
label: "Forum",
component: NotificationScreen,
path: "/(application)/(home-tabs)/forum",
path: "/forum",
},
{
id: "profile",
@@ -94,55 +98,87 @@ const CustomTabNavigator = () => {
activeIcon: "person",
label: "Katalog",
component: ProfileScreen,
path: "/(application)/(home-tabs)/katalog",
path: "/profile",
},
];
// Function untuk handle tab press
const handleTabPress = (tabId: string) => {
setActiveTab(tabId);
// setShowHome(false); // Hide home when any tab is pressed
setShowHome(false); // Hide home when any tab is pressed
};
// Determine which component to show
const getActiveComponent = () => {
if (showHome || activeTab === "home") {
return HomeScreen;
}
const selectedTab = tabs.find((tab) => tab.id === activeTab);
return selectedTab ? selectedTab.component : HomeScreen;
// const selectedTab = tabs.find((tab) => tab.id === activeTab);
// return selectedTab ? selectedTab.component : HomeScreen;
return HomeScreen;
};
const ActiveComponent = getActiveComponent();
return (
<View style={styles.container}>
{/* Content Area */}
<ScrollView>
<View style={styles.content}>
<ActiveComponent />
</View>
</ScrollView>
const handleImageUpload = (file: any) => {
console.log("Gambar dipilih:", file);
// Upload ke server
};
{/* Custom Tab Bar */}
<View style={styles.tabBar}>
<View style={styles.tabContainer}>
{tabs.map((e) => (
<CustomTab
key={e.id}
icon={activeTab === e.id ? e.activeIcon : e.icon}
label={e.label}
isActive={activeTab === e.id && !showHome}
onPress={() => {
// handleTabPress(e.id);
router.push(e.path as any);
}}
/>
))}
</View>
</View>
</View>
const handlePdfOrPngUpload = (file: any) => {
console.log("PDF atau PNG dipilih:", file);
};
return (
<>
<SafeAreaView edges={["bottom"]} style={styles.container}>
<Stack.Screen
options={{
title: "Custom Tab Navigator",
}}
/>
<EventDetailScreen />
<CustomUploadButton
allowedExtensions={["jpeg", "png"]}
buttonTitle="Unggah Gambar (JPEG/PNG)"
onFileSelected={handleImageUpload}
/>
{/* Hanya PDF atau PNG */}
<CustomUploadButton
allowedExtensions={["pdf"]}
buttonTitle="Unggah PDF atau PNG"
onFileSelected={handlePdfOrPngUpload}
/>
</SafeAreaView>
</>
// <View style={styles.container}>
// {/* Content Area */}
// <ScrollView>
// <View style={styles.content}>
// <ActiveComponent />
// </View>
// </ScrollView>
// {/* Custom Tab Bar */}
// <View style={styles.tabBar}>
// <View style={styles.tabContainer}>
// {tabs.map((e) => (
// <CustomTab
// key={e.id}
// icon={activeTab === e.id ? e.activeIcon : e.icon}
// label={e.label}
// isActive={activeTab === e.id && !showHome}
// onPress={() => {
// handleTabPress(e.id);
// router.push(e.path as any);
// }}
// />
// ))}
// </View>
// </View>
// </View>
);
};

View File

@@ -0,0 +1,99 @@
// components/CustomUploadButton.tsx
import React from 'react';
import { Button, Alert, View, StyleSheet } from 'react-native';
import * as DocumentPicker from 'expo-document-picker';
import { isValidFileType, getMimeType } from '../../../utils/fileValidation';
interface UploadButtonProps {
allowedExtensions: string[];
buttonTitle?: string;
onFileSelected?: (file: {
uri: string;
name: string;
size: number | null;
mimeType: string;
}) => void;
}
const CustomUploadButton: React.FC<UploadButtonProps> = ({
allowedExtensions,
buttonTitle = 'Pilih File',
onFileSelected,
}) => {
const handlePickFile = async () => {
try {
// Coba filter dengan MIME type jika memungkinkan
const typeFilter = getMimeTypeFilter(allowedExtensions);
const result = await DocumentPicker.getDocumentAsync({
type: typeFilter, // Ini membantu memfilter di UI pemilih
copyToCacheDirectory: true,
});
if (result.canceled) {
Alert.alert('Dibatalkan', 'Tidak ada file yang dipilih.');
return;
}
const file = result.assets[0];
const { uri, name, size } = file;
// Validasi ekstensi secara manual (cadangan jika MIME tidak akurat)
if (!isValidFileType(name, allowedExtensions)) {
Alert.alert(
'Format Tidak Didukung',
`Hanya file dengan ekstensi berikut yang diperbolehkan: ${allowedExtensions.join(', ')}`
);
return;
}
const mimeType = getMimeType(name);
// Kirim data file ke komponen induk
if (onFileSelected) {
onFileSelected({ uri, name, size: size || null, mimeType });
}
Alert.alert('Berhasil', `File ${name} berhasil dipilih!`);
} catch (error) {
console.error('Error picking file:', error);
Alert.alert('Error', 'Terjadi kesalahan saat memilih file.');
}
};
return (
<View style={styles.container}>
<Button title={buttonTitle} onPress={handlePickFile} />
</View>
);
};
// Fungsi bantu untuk menghasilkan MIME type filter dari ekstensi
const getMimeTypeFilter = (extensions: string[]): string => {
const mimeTypes: string[] = [];
extensions.forEach((ext) => {
switch (ext.toLowerCase()) {
case 'jpg':
case 'jpeg':
if (!mimeTypes.includes('image/jpeg')) mimeTypes.push('image/jpeg');
break;
case 'png':
if (!mimeTypes.includes('image/png')) mimeTypes.push('image/png');
break;
case 'pdf':
if (!mimeTypes.includes('application/pdf')) mimeTypes.push('application/pdf');
break;
default:
mimeTypes.push('*/*'); // fallback
}
});
return mimeTypes.length > 0 ? mimeTypes.join(',') : '*/*';
};
const styles = StyleSheet.create({
container: {
marginVertical: 10,
},
});
export default CustomUploadButton;

View File

@@ -1,9 +0,0 @@
import { Text, View } from "react-native";
export default function Event() {
return (
<View>
<Text>Event</Text>
</View>
)
}

View File

@@ -1,11 +0,0 @@
import { Text, View } from "react-native";
export default function Forum() {
return (
<>
<View>
<Text>Forum</Text>
</View>
</>
);
}

View File

@@ -1,9 +0,0 @@
import { Text, View } from "react-native";
export default function Maps() {
return (
<View>
<Text>Maps</Text>
</View>
)
}

View File

@@ -1,9 +0,0 @@
import { Text, View } from "react-native";
export default function MarketPlace() {
return (
<View>
<Text>Market Place</Text>
</View>
);
}

View File

@@ -1,9 +0,0 @@
import { Text, View } from "react-native";
export default function Profile() {
return (
<View>
<Text>Profile</Text>
</View>
);
}

View File

@@ -1,9 +1,16 @@
import { Text, View } from "react-native";
import { StackCustom, TextCustom, ViewWrapper } from "@/components";
export default function NotFoundScreen() {
return (
<View>
<Text>Not Found</Text>
</View>
)
<ViewWrapper>
<StackCustom align="center" gap={0} style={{justifyContent: "center", alignItems: "center", flex: 1}}>
<TextCustom size="large" bold style={{fontSize: 100}}>
404
</TextCustom>
<TextCustom size="large" bold>
Sorry, File Not Found
</TextCustom>
</StackCustom>
</ViewWrapper>
);
}

View File

@@ -1,34 +1,32 @@
import { MainColor } from "@/constants/color-palet";
import { Stack } from "expo-router";
import "react-native-gesture-handler";
import { SafeAreaProvider } from "react-native-safe-area-context";
export default function RootLayout() {
return (
<SafeAreaProvider
style={{
backgroundColor: MainColor.darkblue,
}}
>
<Stack
screenOptions={{
headerStyle: { backgroundColor: MainColor.darkblue },
headerTitleStyle: { color: MainColor.yellow, fontWeight: "bold" },
headerTitleAlign: "center",
// contentStyle: {
// borderBottomColor: AccentColor.blue,
// borderBottomWidth: 2,
// },
// headerLargeStyle: {
// backgroundColor: MainColor.darkblue,
// },
// headerShadowVisible: false,
}}
>
<Stack.Screen name="index" options={{ headerShown: false }} />
<Stack.Screen name="verification" options={{ headerShown: false }} />
<Stack.Screen name="register" options={{ headerShown: false }} />
<Stack.Screen name="(application)" options={{ headerShown: false }} />
</Stack>
</SafeAreaProvider>
<>
<SafeAreaProvider>
<Stack
screenOptions={{
headerStyle: { backgroundColor: MainColor.darkblue },
headerTitleStyle: { color: MainColor.yellow, fontWeight: "bold" },
headerTitleAlign: "center",
}}
>
<Stack.Screen name="index" options={{ title: "" }} />
<Stack.Screen name="+not-found" options={{ title: "" }} />
<Stack.Screen
name="verification"
options={{ title: "", headerBackVisible: false }}
/>
<Stack.Screen
name="register"
options={{ title: "", headerBackVisible: false }}
/>
<Stack.Screen name="(application)" options={{ headerShown: false }} />
</Stack>
</SafeAreaProvider>
</>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

Some files were not shown because too many files have changed in this diff Show More