Compare commits

...

13 Commits

Author SHA1 Message Date
19b02ffc01 upd: easignore 2025-09-11 14:43:10 +08:00
b9b615636b fix: calendar
Deskripsi:
- tinggi scroll pada tambah data
- tinggi scroll pada edit tambah anggota
- checked anggota pada edit tambah anggota
- on press disable saat user telah menjadi anggota pada edit tambah anggota

No Issues
2025-09-11 11:44:34 +08:00
d52453c530 upd: ios 2025-09-09 16:40:26 +08:00
1853cb573c upd : modal filter
Deskripsi:
- update refresh modal filter saat ada perubahan data grup

No Issues
2025-09-09 14:35:45 +08:00
83291676d3 upd : scroll view
Deskripsi:
- horizontal view hide
- vertical view hide
- scroll view height

No Issues
2025-09-09 12:30:40 +08:00
1509d1b702 upd: version app 2025-09-09 12:05:24 +08:00
8d6a0d3981 fixx :api tambah pengumuman 2025-09-09 12:05:04 +08:00
d20307fc0b upd: refresh division detail home
Deskripsi:
- refresh load pada halaman home detail division

No Issues
2025-09-09 11:57:33 +08:00
060f96e7b2 upd : home page
Deskripsi:
- load refresh pada halaman home

No Issues
2025-09-09 11:49:13 +08:00
a15724756e fix: diskusi divisi
Deskripsi:
- multiline input deskripsi pada tambah dan edit diskusi divisi

No Issues
2025-09-09 11:15:49 +08:00
c8de5d185a fix : notification page
Deskripsi:
- load refresh notification page

No Issues
2025-09-09 11:09:19 +08:00
5fa364be24 fix: pencarian
Deskripsi:
- scroll height
- tanpa wrap
- pake scroll map

No Issues
2025-09-09 11:05:37 +08:00
4768007df3 fix:tinggi scroll profile 2025-09-09 10:47:43 +08:00
50 changed files with 366 additions and 211 deletions

View File

@@ -38,6 +38,4 @@ yarn-error.*
app-example
x.ts
x.sh
/android
/ios
x.sh

1
.gitignore vendored
View File

@@ -45,5 +45,4 @@ x.sh
google-services.json
service-account.json
GoogleService-Info.plist.bak

View File

@@ -0,0 +1,4 @@
kotlin version: 2.0.21
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
1. Kotlin compile daemon is ready

View File

@@ -4,7 +4,7 @@ export default {
expo: {
name: "Desa+",
slug: "mobile-darmasaba",
version: "1.0.2",
version: "1.0.4",
jsEngine: "jsc",
orientation: "portrait",
icon: "./assets/images/logo-icon-small.png",
@@ -18,11 +18,11 @@ export default {
ITSAppUsesNonExemptEncryption: false,
CFBundleDisplayName: "Desa+"
},
googleServicesFile: "./ios/Desa/GoogleService-Info.plist"
googleServicesFile: process.env.IOS_GOOGLE_SERVICES_FILE
},
android: {
package: "mobiledarmasaba.app",
versionCode: 6,
versionCode: 8,
adaptiveIcon: {
foregroundImage: "./assets/images/logo-icon-small.png",
backgroundColor: "#ffffff"
@@ -59,7 +59,7 @@ export default {
"@react-native-firebase/app",
{
ios: {
googleServicesFile: "./ios/Desa/GoogleService-Info.plist"
googleServicesFile: process.env.IOS_GOOGLE_SERVICES_FILE
}
}
]

View File

@@ -8,7 +8,7 @@ import { useAuthSession } from "@/providers/AuthProvider";
import { Entypo, MaterialIcons } from "@expo/vector-icons";
import { router, Stack, useLocalSearchParams } from "expo-router";
import React, { useEffect, useState } from "react";
import { Dimensions, SafeAreaView, ScrollView, View } from "react-native";
import { Dimensions, RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
import RenderHTML from 'react-native-render-html';
import { useSelector } from "react-redux";
@@ -28,6 +28,7 @@ export default function DetailAnnouncement() {
const contentWidth = Dimensions.get('window').width
const [loading, setLoading] = useState(true)
const arrSkeleton = Array.from({ length: 2 }, (_, index) => index)
const [refreshing, setRefreshing] = useState(false)
async function handleLoad(loading: boolean) {
try {
@@ -56,6 +57,13 @@ export default function DetailAnnouncement() {
return htmlRegex.test(text);
};
const handleRefresh = async () => {
setRefreshing(true)
handleLoad(false)
await new Promise(resolve => setTimeout(resolve, 2000));
setRefreshing(false)
};
return (
<SafeAreaView>
<Stack.Screen
@@ -66,7 +74,16 @@ export default function DetailAnnouncement() {
headerRight: () => entityUser.role != 'user' && entityUser.role != 'coadmin' ? <HeaderRightAnnouncementDetail id={id} /> : <></>,
}}
/>
<ScrollView>
<ScrollView
showsVerticalScrollIndicator={false}
style={[Styles.h100]}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={() => handleRefresh()}
/>
}
>
<View style={[Styles.p15]}>
<View style={[Styles.wrapPaper]}>
{

View File

@@ -110,8 +110,11 @@ export default function CreateAnnouncement() {
),
}}
/>
<ScrollView>
<View style={[Styles.p15, Styles.mb100]}>
<ScrollView
showsVerticalScrollIndicator={false}
style={[Styles.h100]}
>
<View style={[Styles.p15]}>
<InputForm
label="Judul"
type="default"

View File

@@ -153,8 +153,11 @@ export default function EditAnnouncement() {
),
}}
/>
<ScrollView>
<View style={[Styles.p15, Styles.mb100]}>
<ScrollView
showsVerticalScrollIndicator={false}
style={[Styles.h100]}
>
<View style={[Styles.p15]}>
<InputForm
label="Judul"
type="default"

View File

@@ -130,7 +130,7 @@ export default function EditBanner() {
category="update" />,
}}
/>
<ScrollView>
<ScrollView showsVerticalScrollIndicator={false} style={[Styles.h100]}>
<View style={[Styles.p15, Styles.mb100]}>
<View style={[Styles.mb15]}>
{selectedImage != undefined ? (

View File

@@ -117,8 +117,8 @@ export default function CreateBanner() {
),
}}
/>
<ScrollView>
<View style={[Styles.p15, Styles.mb100]}>
<ScrollView showsVerticalScrollIndicator={false} style={[Styles.h100]}>
<View style={[Styles.p15]}>
<View style={[Styles.mb15]}>
{selectedImage != undefined ? (
<Pressable onPress={pickImageAsync}>

View File

@@ -139,7 +139,7 @@ export default function DetailDiscussionGeneral() {
}}
/>
<View style={{ flex: 1 }}>
<ScrollView>
<ScrollView showsVerticalScrollIndicator={false}>
<View style={[Styles.p15, Styles.mb100]}>
{
loading ?

View File

@@ -116,7 +116,7 @@ export default function AddMemberDiscussionDetail() {
selectMember.length > 0
?
<View>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]}>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]} showsHorizontalScrollIndicator={false}>
{
selectMember.map((item: any, index: any) => (
<ImageWithLabel
@@ -133,7 +133,9 @@ export default function AddMemberDiscussionDetail() {
:
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
}
<ScrollView>
<ScrollView
showsVerticalScrollIndicator={false}
>
{
data.length > 0 ?

View File

@@ -142,7 +142,7 @@ export default function CreateDiscussionGeneral() {
),
}}
/>
<ScrollView>
<ScrollView showsVerticalScrollIndicator={false} style={[Styles.h100]}>
<View style={[Styles.p15, Styles.mb100]}>
{
(entityUser.role == "supadmin" ||

View File

@@ -119,7 +119,7 @@ export default function EditDiscussionGeneral() {
),
}}
/>
<ScrollView>
<ScrollView showsVerticalScrollIndicator={false} style={[Styles.h100]}>
<View style={[Styles.p15]}>
<InputForm
label="Judul"

View File

@@ -123,7 +123,7 @@ export default function AddMemberCalendarEvent() {
selectMember.length > 0
?
<View>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]}>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]} showsHorizontalScrollIndicator={false}>
{
selectMember.map((item: any, index: any) => (
<ImageWithLabel
@@ -140,7 +140,10 @@ export default function AddMemberCalendarEvent() {
:
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
}
<ScrollView>
<ScrollView
showsVerticalScrollIndicator={false}
style={[Styles.h100]}
>
{
data.length > 0 ?
data.map((item: any, index: any) => {
@@ -151,7 +154,6 @@ export default function AddMemberCalendarEvent() {
style={[Styles.itemSelectModal]}
onPress={() => {
!found && onChoose(item.idUser, item.name, item.img)
onChoose(item.idUser, item.name, item.img)
}}
>
<View style={[Styles.rowItemsCenter]}>
@@ -164,7 +166,7 @@ export default function AddMemberCalendarEvent() {
</View>
</View>
{
selectMember.some((i: any) => i.idUser == item.id) && <AntDesign name="check" size={20} color={'black'} />
selectMember.some((i: any) => i.idUser == item.idUser) && <AntDesign name="check" size={20} color={'black'} />
}
</Pressable>
)

View File

@@ -112,7 +112,7 @@ export default function CreateCalendarAddMember() {
selectMember.length > 0
?
<View>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]}>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]} showsHorizontalScrollIndicator={false}>
{
selectMember.map((item: any, index: any) => (
<ImageWithLabel
@@ -129,7 +129,10 @@ export default function CreateCalendarAddMember() {
:
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
}
<ScrollView>
<ScrollView
showsVerticalScrollIndicator={false}
style={[Styles.h100]}
>
{
data.length > 0 ?

View File

@@ -93,6 +93,7 @@ export default function DiscussionDivisionEdit() {
required
value={data}
onChange={setData}
multiline
/>
</View>
</ScrollView>

View File

@@ -56,7 +56,14 @@ export default function CreateDiscussionDivision() {
/>
<ScrollView>
<View style={[Styles.p15, Styles.mb100]}>
<InputForm label="Diskusi" type="default" placeholder="Hal yang didiskusikan" required onChange={setDesc} />
<InputForm
label="Diskusi"
type="default"
placeholder="Hal yang didiskusikan"
required
onChange={setDesc}
multiline
/>
</View>
</ScrollView>
</SafeAreaView>

View File

@@ -118,7 +118,7 @@ export default function AddMemberTask() {
selectMember.length > 0
?
<View>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]}>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]} showsHorizontalScrollIndicator={false}>
{
selectMember.map((item: any, index: any) => (
<ImageWithLabel
@@ -135,7 +135,9 @@ export default function AddMemberTask() {
:
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
}
<ScrollView>
<ScrollView
showsVerticalScrollIndicator={false}
>
{
data.length > 0 ?

View File

@@ -88,7 +88,7 @@ export default function AddMemberCreateTask() {
selectMember.length > 0
?
<View>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]}>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]} showsHorizontalScrollIndicator={false}>
{
selectMember.map((item: any, index: any) => (
<ImageWithLabel
@@ -105,7 +105,9 @@ export default function AddMemberCreateTask() {
:
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
}
<ScrollView>
<ScrollView
showsVerticalScrollIndicator={false}
>
{
data.length > 0 ?

View File

@@ -106,7 +106,7 @@ export default function ListTask() {
return (
<View style={[Styles.p15, { flex: 1 }]}>
<View>
<ScrollView horizontal style={[Styles.mb10]}>
<ScrollView horizontal style={[Styles.mb10]} showsHorizontalScrollIndicator={false}>
<ButtonTab
active={statusFix}
value="0"

View File

@@ -119,7 +119,7 @@ export default function AddMemberDivision() {
selectMember.length > 0
?
<View>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]}>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]} showsHorizontalScrollIndicator={false}>
{
selectMember.map((item: any, index: any) => (
<ImageWithLabel
@@ -136,7 +136,9 @@ export default function AddMemberDivision() {
:
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
}
<ScrollView>
<ScrollView
showsVerticalScrollIndicator={false}
>
{
data.length > 0 ?

View File

@@ -10,7 +10,7 @@ import { apiGetDivisionOneDetail } from "@/lib/api"
import { useAuthSession } from "@/providers/AuthProvider"
import { router, Stack, useLocalSearchParams } from "expo-router"
import { useEffect, useState } from "react"
import { SafeAreaView, ScrollView, View } from "react-native"
import { RefreshControl, SafeAreaView, ScrollView, View } from "react-native"
type Props = {
id: string,
@@ -26,11 +26,12 @@ export default function DetailDivisionFitur() {
const { id } = useLocalSearchParams<{ id: string }>()
const [data, setData] = useState<Props>()
const [loading, setLoading] = useState(true)
const [refreshing, setRefreshing] = useState(false)
async function handleLoad() {
async function handleLoad(loading: boolean) {
try {
setLoading(true)
setLoading(loading)
const hasil = await decryptToken(String(token?.current))
const response = await apiGetDivisionOneDetail({ user: hasil, id })
setData(response.data.division)
@@ -42,9 +43,16 @@ export default function DetailDivisionFitur() {
}
useEffect(() => {
handleLoad()
handleLoad(true)
}, [])
const handleRefresh = async () => {
setRefreshing(true)
handleLoad(false)
await new Promise(resolve => setTimeout(resolve, 2000));
setRefreshing(false)
};
return (
<SafeAreaView>
<Stack.Screen
@@ -55,13 +63,21 @@ export default function DetailDivisionFitur() {
headerRight: () => <HeaderRightDivisionDetail id={id} />,
}}
/>
<ScrollView>
<CaraouselHome />
<ScrollView
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={handleRefresh}
/>
}
showsVerticalScrollIndicator={false}
>
<CaraouselHome refreshing={refreshing} />
<View style={[Styles.ph15, Styles.mb100]}>
<FiturDivisionDetail />
<TaskDivisionDetail />
<FileDivisionDetail />
<DiscussionDivisionDetail />
<FiturDivisionDetail refreshing={refreshing}/>
<TaskDivisionDetail refreshing={refreshing}/>
<FileDivisionDetail refreshing={refreshing}/>
<DiscussionDivisionDetail refreshing={refreshing}/>
</View>
</ScrollView>
</SafeAreaView>

View File

@@ -81,7 +81,7 @@ export default function CreateDivisionAddMember() {
selectMember.length > 0
?
<View>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]}>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]} showsHorizontalScrollIndicator={false}>
{
selectMember.map((item: any, index: any) => (
<ImageWithLabel
@@ -98,7 +98,9 @@ export default function CreateDivisionAddMember() {
:
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
}
<ScrollView>
<ScrollView
showsVerticalScrollIndicator={false}
>
{
data.length > 0 ?

View File

@@ -13,8 +13,8 @@ import { apiGetProfile } from "@/lib/api";
import { setEntities } from "@/lib/entitiesSlice";
import { useAuthSession } from "@/providers/AuthProvider";
import { Stack } from "expo-router";
import { useEffect } from "react";
import { Platform, SafeAreaView, ScrollView, View } from "react-native";
import { useEffect, useState } from "react";
import { Platform, RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useDispatch, useSelector } from "react-redux";
@@ -24,6 +24,7 @@ export default function Home() {
const dispatch = useDispatch()
const { token, decryptToken, signOut } = useAuthSession()
const insets = useSafeAreaInsets()
const [refreshing, setRefreshing] = useState(false)
useEffect(() => {
handleUserLogin()
@@ -38,6 +39,13 @@ export default function Home() {
});
}
const handleRefresh = async () => {
setRefreshing(true)
handleUserLogin()
await new Promise(resolve => setTimeout(resolve, 2000));
setRefreshing(false)
};
return (
<SafeAreaView>
<Stack.Screen
@@ -52,16 +60,24 @@ export default function Home() {
),
}}
/>
<ScrollView>
<CaraouselHome />
<ScrollView
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={handleRefresh}
/>
}
showsVerticalScrollIndicator={false}
>
<CaraouselHome refreshing={refreshing}/>
<View style={[Styles.ph15, Styles.mb100]}>
<FiturHome />
<ProjectHome />
<DivisionHome />
<ChartProgresHome />
<ChartDokumenHome />
<EventHome />
<DisccussionHome />
<ProjectHome refreshing={refreshing}/>
<DivisionHome refreshing={refreshing}/>
<ChartProgresHome refreshing={refreshing}/>
<ChartDokumenHome refreshing={refreshing}/>
<EventHome refreshing={refreshing}/>
<DisccussionHome refreshing={refreshing}/>
</View>
</ScrollView>
</SafeAreaView>

View File

@@ -231,7 +231,7 @@ export default function CreateMember() {
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
keyboardVerticalOffset={headerHeight}
>
<ScrollView>
<ScrollView showsVerticalScrollIndicator={false}>
<View style={[Styles.p15]}>
<View style={{ justifyContent: "center", alignItems: "center" }}>
{selectedImage != undefined ? (

View File

@@ -264,8 +264,8 @@ export default function EditMember() {
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
keyboardVerticalOffset={headerHeight}
>
<ScrollView>
<View style={[Styles.p15, Styles.mb100]}>
<ScrollView showsVerticalScrollIndicator={false} style={[Styles.h100]}>
<View style={[Styles.p15]}>
<View style={{ justifyContent: "center", alignItems: "center" }}>
{
errorImg ?

View File

@@ -9,7 +9,7 @@ import { pushToPage } from "@/lib/pushToPage";
import { useAuthSession } from "@/providers/AuthProvider";
import { Feather } from "@expo/vector-icons";
import { useEffect, useState } from "react";
import { SafeAreaView, View, VirtualizedList } from "react-native";
import { RefreshControl, SafeAreaView, View, VirtualizedList } from "react-native";
import { useDispatch, useSelector } from "react-redux";
type Props = {
@@ -31,6 +31,7 @@ export default function Notification() {
const arrSkeleton = Array.from({ length: 5 }, (_, index) => index)
const dispatch = useDispatch()
const updateNotification = useSelector((state: any) => state.notificationUpdate)
const [refreshing, setRefreshing] = useState(false)
async function handleLoad(loading: boolean, thisPage: number) {
try {
@@ -88,33 +89,12 @@ export default function Notification() {
}
}
// function pushToPage(category: string, idContent: string) {
// const cat = category.split('/')
// if (cat.length > 1) {
// if (cat[2] == 'calendar') {
// router.push(`/division/${cat[1]}/calendar/${idContent}`)
// } else if (cat[2] == 'discussion') {
// router.push(`/division/${cat[1]}/discussion/${idContent}`)
// } else if (cat[2] == 'document') {
// router.push(`/division/${cat[1]}/document/${idContent}`)
// } else if (cat[2] == 'task') {
// router.push(`/division/${cat[1]}/task/${idContent}`)
// }
// } else {
// if (cat[0] == 'announcement') {
// router.push(`/announcement/${idContent}`)
// } else if (cat[0] == 'discussion-general') {
// router.push(`/discussion/${idContent}`)
// } else if (cat[0] == 'division') {
// router.push(`/division/${idContent}`)
// } else if (cat[0] == 'member') {
// router.push(`/member/${idContent}`)
// } else if (cat[0] == 'project') {
// router.push(`/project/${idContent}`)
// }
// }
// }
const handleRefresh = async () => {
setRefreshing(true)
handleLoad(false, 1)
await new Promise(resolve => setTimeout(resolve, 2000));
setRefreshing(false)
};
return (
<SafeAreaView>
@@ -156,6 +136,12 @@ export default function Notification() {
onEndReached={loadMoreData}
onEndReachedThreshold={0.5}
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={handleRefresh}
/>
}
/>
:
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada data</Text>

View File

@@ -38,7 +38,7 @@ export default function Profile() {
/>
}}
/>
<ScrollView>
<ScrollView style={[Styles.h100]}>
<View style={{ flexDirection: 'column' }}>
<View style={[Styles.wrapHeadViewMember]}>
<Image

View File

@@ -117,7 +117,7 @@ export default function AddMemberProject() {
selectMember.length > 0
?
<View>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]}>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]} showsHorizontalScrollIndicator={false}>
{
selectMember.map((item: any, index: any) => (
<ImageWithLabel
@@ -134,35 +134,43 @@ export default function AddMemberProject() {
:
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
}
<ScrollView>
<ScrollView
showsVerticalScrollIndicator={false}
style={[Styles.h100]}
>
{
data.length > 0 ?
data.map((item: any, index: any) => {
const found = dataOld.some((i: any) => i.idUser == item.id)
return (
<Pressable
key={index}
style={[Styles.itemSelectModal]}
onPress={() => {
!found && onChoose(item.id, item.name, item.img)
}}
>
<View style={[Styles.rowItemsCenter, Styles.w80]}>
<ImageUser src={`${ConstEnv.url_storage}/files/${item.img}`} border />
<View style={[Styles.ml10]}>
<Text style={[Styles.textDefault]} numberOfLines={1}>{item.name}</Text>
<View style={[Styles.mb100]}>
{
data.map((item: any, index: any) => {
const found = dataOld.some((i: any) => i.idUser == item.id)
return (
<Pressable
key={index}
style={[Styles.itemSelectModal]}
onPress={() => {
!found && onChoose(item.id, item.name, item.img)
}}
>
<View style={[Styles.rowItemsCenter, Styles.w80]}>
<ImageUser src={`${ConstEnv.url_storage}/files/${item.img}`} border />
<View style={[Styles.ml10]}>
<Text style={[Styles.textDefault]} numberOfLines={1}>{item.name}</Text>
{
found && <Text style={[Styles.textInformation, Styles.cGray]}>sudah menjadi anggota</Text>
}
</View>
</View>
{
found && <Text style={[Styles.textInformation, Styles.cGray]}>sudah menjadi anggota</Text>
selectMember.some((i: any) => i.idUser == item.id) && <AntDesign name="check" size={20} color={'black'} />
}
</View>
</View>
{
selectMember.some((i: any) => i.idUser == item.id) && <AntDesign name="check" size={20} color={'black'} />
}
</Pressable>
)
}
)
</Pressable>
)
}
)
}
</View>
:
<Text style={[Styles.textDefault, { textAlign: 'center' }]}>Tidak ada data</Text>
}

View File

@@ -88,8 +88,11 @@ export default function ProjectCancel() {
),
}}
/>
<ScrollView>
<View style={[Styles.p15, Styles.mb100]}>
<ScrollView
showsVerticalScrollIndicator={false}
style={[Styles.h100]}
>
<View style={[Styles.p15]}>
<InputForm
label="Alasan Pembatalan"
type="default"
@@ -99,6 +102,7 @@ export default function ProjectCancel() {
error={error}
errorText="Alasan pembatalan harus diisi"
onChange={(val) => onValidation(val)}
multiline
/>
</View>
</ScrollView>

View File

@@ -107,8 +107,11 @@ export default function ReportProject() {
),
}}
/>
<ScrollView>
<View style={[Styles.p15, Styles.mb100]}>
<ScrollView
showsVerticalScrollIndicator={false}
style={[Styles.h100]}
>
<View style={[Styles.p15]}>
<InputForm
label="Laporan Kegiatan"
type="default"

View File

@@ -205,8 +205,11 @@ export default function CreateProject() {
),
}}
/>
<ScrollView>
<View style={[Styles.p15, Styles.mb100]}>
<ScrollView
showsVerticalScrollIndicator={false}
style={[Styles.h100]}
>
<View style={[Styles.p15]}>
{
(entityUser.role == "supadmin" || entityUser.role == "developer")
&&

View File

@@ -95,7 +95,7 @@ export default function AddMemberCreateProject() {
selectMember.length > 0
?
<View>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]}>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]} showsHorizontalScrollIndicator={false}>
{
selectMember.map((item: any, index: any) => (
<ImageWithLabel
@@ -112,7 +112,9 @@ export default function AddMemberCreateProject() {
:
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
}
<ScrollView>
<ScrollView
showsVerticalScrollIndicator={false}
>
{
data.length > 0 ?

View File

@@ -120,7 +120,7 @@ export default function ListProject() {
return (
<View style={[Styles.p15, { flex: 1 }]}>
<View>
<ScrollView horizontal style={[Styles.mb10]}>
<ScrollView horizontal style={[Styles.mb10]} showsHorizontalScrollIndicator={false}>
<ButtonTab
active={statusFix}
value="0"

View File

@@ -1,5 +1,6 @@
import BorderBottomItem from "@/components/borderBottomItem";
import ButtonBackHeader from "@/components/buttonBackHeader";
import ImageUser from "@/components/imageNew";
import InputSearch from "@/components/inputSearch";
import Text from '@/components/Text';
import { ColorsStatus } from "@/constants/ColorsStatus";
@@ -9,8 +10,8 @@ import { apiGetSearch } from "@/lib/api";
import { useAuthSession } from "@/providers/AuthProvider";
import { AntDesign, MaterialIcons } from "@expo/vector-icons";
import { router, Stack } from "expo-router";
import { useState } from "react";
import { FlatList, Image, SafeAreaView, View } from "react-native";
import React, { useState } from "react";
import { RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
import Toast from "react-native-toast-message";
type PropsUser = {
@@ -40,10 +41,13 @@ export default function Search() {
const [dataUser, setDataUser] = useState<PropsUser[]>([])
const [dataDivisi, setDataDivisi] = useState<PropDivisi[]>([])
const [dataProject, setDataProject] = useState<PropProject[]>([])
const [refreshing, setRefreshing] = useState(false)
const [search, setSearch] = useState('')
async function handleSearch(cari: string) {
try {
if (cari.length > 3) {
setSearch(cari)
if (cari.length >= 3) {
const user = await decryptToken(String(token?.current))
const hasil = await apiGetSearch({ text: cari, user: user })
if (hasil.success) {
@@ -65,6 +69,14 @@ export default function Search() {
}
const handleRefresh = async () => {
setRefreshing(true)
handleSearch(search)
await new Promise(resolve => setTimeout(resolve, 2000));
setRefreshing(false)
};
return (
<>
<SafeAreaView>
@@ -80,41 +92,44 @@ export default function Search() {
{
dataProject.length + dataDivisi.length + dataUser.length > 0
?
<View style={[Styles.wrapPaper, Styles.mb100]}>
<ScrollView
style={[Styles.h100]}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={handleRefresh}
/>
}
>
{
dataUser.length > 0 &&
<View style={[Styles.mb05]}>
<View style={[Styles.mv05, Styles.p10]}>
<Text>ANGGOTA</Text>
<FlatList
data={dataUser}
keyExtractor={(item) => String(item.id)}
renderItem={({ item }) => (
{
dataUser.map((item, index) => (
<BorderBottomItem
key={index}
borderType="bottom"
icon={<Image
source={{ uri: `${ConstEnv.url_storage}/files/${item.img}` }}
style={[Styles.userProfileSmall]}
/>}
icon={<ImageUser src={`${ConstEnv.url_storage}/files/${item.img}`} />}
title={item.name}
subtitle={`${item.group}-${item.position}`}
onPress={() => {
router.push(`/member/${item.id}`)
}}
/>
)}
/>
))
}
</View>
}
{
dataDivisi.length > 0 &&
<View style={[Styles.mb05]}>
<View style={[Styles.mv05, Styles.p10]}>
<Text>DIVISI</Text>
<FlatList
data={dataDivisi}
keyExtractor={(item) => String(item.id)}
renderItem={({ item }) => (
{
dataDivisi.map((item, index) => (
<BorderBottomItem
key={index}
borderType="bottom"
icon={
<View style={[Styles.iconContent, ColorsStatus.primary]}>
@@ -127,21 +142,20 @@ export default function Search() {
router.push(`/division/${item.id}`)
}}
/>
)}
/>
))
}
</View>
}
{
dataProject.length > 0 &&
<View style={[Styles.mb10]}>
<View style={[Styles.mv05, Styles.p10]}>
<Text>KEGIATAN</Text>
<FlatList
data={dataProject}
keyExtractor={(item) => String(item.id)}
renderItem={({ item }) => (
{
dataProject.map((item, index) => (
<BorderBottomItem
key={index}
borderType="bottom"
icon={
<View style={[Styles.iconContent, ColorsStatus.primary]}>
@@ -154,13 +168,11 @@ export default function Search() {
router.push(`/project/${item.id}`)
}}
/>
)}
/>
))
}
</View>
}
</View>
</ScrollView>
:
<View style={[Styles.contentItemCenter, Styles.mt10]}>
<Text style={[Styles.textInformation, Styles.cGray]}>Tidak ada data</Text>

View File

@@ -16,15 +16,15 @@ type Props = {
date: string;
};
export default function DiscussionDivisionDetail() {
export default function DiscussionDivisionDetail({ refreshing }: { refreshing: boolean }) {
const { token, decryptToken } = useAuthSession();
const { id } = useLocalSearchParams<{ id: string }>();
const [data, setData] = useState<Props[]>([]);
const [loading, setLoading] = useState(true)
async function handleLoad() {
async function handleLoad(loading: boolean) {
try {
setLoading(true)
setLoading(loading)
const hasil = await decryptToken(String(token?.current));
const response = await apiGetDivisionOneFeature({
user: hasil,
@@ -40,8 +40,15 @@ export default function DiscussionDivisionDetail() {
}
useEffect(() => {
handleLoad();
}, []);
if (refreshing)
handleLoad(false)
}, [refreshing])
useEffect(() => {
handleLoad(true)
}, [])
return (
<View style={[Styles.mb15]}>
<Text style={[Styles.textDefaultSemiBold, Styles.mv10]}>Diskusi</Text>

View File

@@ -24,7 +24,7 @@ type Props = {
idStorage: string
}
export default function FileDivisionDetail() {
export default function FileDivisionDetail({ refreshing }: { refreshing: boolean }) {
const ref = React.useRef<ICarouselInstance>(null);
const width = Dimensions.get("window").width;
const [data, setData] = useState<Props[]>([])
@@ -33,9 +33,9 @@ export default function FileDivisionDetail() {
const [loading, setLoading] = useState(true)
const [loadingOpen, setLoadingOpen] = useState(false)
async function handleLoad() {
async function handleLoad(loading: boolean) {
try {
setLoading(true)
setLoading(loading)
const hasil = await decryptToken(String(token?.current))
const response = await apiGetDivisionOneFeature({ user: hasil, id, cat: 'new-file' })
setData(response.data)
@@ -47,7 +47,12 @@ export default function FileDivisionDetail() {
}
useEffect(() => {
handleLoad()
if (refreshing)
handleLoad(false)
}, [refreshing])
useEffect(() => {
handleLoad(true)
}, [])

View File

@@ -16,7 +16,7 @@ type Props = {
kalender: number
}
export default function FiturDivisionDetail() {
export default function FiturDivisionDetail({ refreshing }: { refreshing: boolean }) {
const { token, decryptToken } = useAuthSession()
const { id } = useLocalSearchParams<{ id: string }>()
const [data, setData] = useState<Props>({
@@ -36,7 +36,10 @@ export default function FiturDivisionDetail() {
}
}
useEffect(() => {
if (refreshing)
handleLoad()
}, [refreshing])
useEffect(() => {
handleLoad()

View File

@@ -18,7 +18,7 @@ type Props = {
projectTitle: string
}
export default function TaskDivisionDetail() {
export default function TaskDivisionDetail({ refreshing }: { refreshing: boolean }) {
const { token, decryptToken } = useAuthSession()
const { id } = useLocalSearchParams<{ id: string }>()
const [data, setData] = useState<Props[]>([])
@@ -26,9 +26,9 @@ export default function TaskDivisionDetail() {
const width = Dimensions.get("window").width;
const [loading, setLoading] = useState(true)
async function handleLoad() {
async function handleLoad(loading: boolean) {
try {
setLoading(true)
setLoading(loading)
const hasil = await decryptToken(String(token?.current))
const response = await apiGetDivisionOneFeature({ user: hasil, id, cat: 'today-task' })
setData(response.data)
@@ -40,7 +40,12 @@ export default function TaskDivisionDetail() {
}
useEffect(() => {
handleLoad()
if (refreshing)
handleLoad(false)
}, [refreshing])
useEffect(() => {
handleLoad(true)
}, [])
return (

View File

@@ -11,7 +11,7 @@ import Carousel, { ICarouselInstance } from "react-native-reanimated-carousel";
import { useDispatch, useSelector } from "react-redux";
import Text from "../Text";
export default function CaraouselHome() {
export default function CaraouselHome({ refreshing }: { refreshing: boolean }) {
const { decryptToken, token } = useAuthSession()
const ref = React.useRef<ICarouselInstance>(null);
const width = Dimensions.get("window").width;
@@ -37,6 +37,11 @@ export default function CaraouselHome() {
dispatch(setEntityUser({ role: response.data.idUserRole, admin: false }))
}
useEffect(() => {
if (refreshing)
handleBannerView()
}, [refreshing]);
useEffect(() => {
handleBannerView()
}, [dispatch]);

View File

@@ -13,7 +13,7 @@ type Props = {
frontColor: string;
}[]
export default function ChartDokumenHome() {
export default function ChartDokumenHome({ refreshing }: { refreshing: boolean }) {
const [loading, setLoading] = useState(true)
const { decryptToken, token } = useAuthSession()
const [data, setData] = useState<Props>([])
@@ -25,9 +25,9 @@ export default function ChartDokumenHome() {
const width = Dimensions.get("window").width;
async function handleData() {
async function handleData(loading: boolean) {
try {
setLoading(true)
setLoading(loading)
const hasil = await decryptToken(String(token?.current))
const response = await apiGetDataHome({ cat: "dokumen", user: hasil })
const maxValue = response.data.reduce((max: number, obj: { value: number; }) => Math.max(max, obj.value), -Infinity);
@@ -47,7 +47,12 @@ export default function ChartDokumenHome() {
}
useEffect(() => {
handleData()
if (refreshing)
handleData(false)
}, [refreshing]);
useEffect(() => {
handleData(true)
}, []);
return (

View File

@@ -13,14 +13,14 @@ type Props = {
color: string;
}[]
export default function ChartProgresHome() {
export default function ChartProgresHome({ refreshing }: { refreshing: boolean }) {
const { decryptToken, token } = useAuthSession()
const [data, setData] = useState<Props>([])
const [loading, setLoading] = useState(true)
async function handleData() {
async function handleData(loading: boolean) {
try {
setLoading(true)
setLoading(loading)
const hasil = await decryptToken(String(token?.current))
const response = await apiGetDataHome({ cat: "progress", user: hasil })
const convertedArray = response.data.map((item: { color: any; text: any; value: any; }) => ({
@@ -37,7 +37,12 @@ export default function ChartProgresHome() {
}
useEffect(() => {
handleData()
if (refreshing)
handleData(false)
}, [refreshing]);
useEffect(() => {
handleData(true)
}, []);
return (

View File

@@ -17,15 +17,15 @@ type Props = {
user: string
}
export default function DisccussionHome() {
export default function DisccussionHome({ refreshing }: { refreshing: boolean }) {
const { decryptToken, token } = useAuthSession()
const [data, setData] = useState<Props[]>([])
const [loading, setLoading] = useState(true)
async function handleData() {
async function handleData(loading: boolean) {
try {
setLoading(true)
setLoading(loading)
const hasil = await decryptToken(String(token?.current))
const response = await apiGetDataHome({ cat: "discussion", user: hasil })
setData(response.data)
@@ -37,7 +37,12 @@ export default function DisccussionHome() {
}
useEffect(() => {
handleData()
if (refreshing)
handleData(false)
}, [refreshing]);
useEffect(() => {
handleData(true)
}, []);
return (

View File

@@ -15,7 +15,7 @@ type Props = {
jumlah: number
}
export default function DivisionHome() {
export default function DivisionHome({ refreshing }: { refreshing: boolean }) {
const { decryptToken, token } = useAuthSession()
const ref = React.useRef<ICarouselInstance>(null)
const width = Dimensions.get("window").width
@@ -23,9 +23,9 @@ export default function DivisionHome() {
const [loading, setLoading] = useState(true)
const arrSkeleton = Array.from({ length: 2 }, (_, index) => index)
async function handleData() {
async function handleData(loading: boolean) {
try {
setLoading(true)
setLoading(loading)
const hasil = await decryptToken(String(token?.current))
const response = await apiGetDataHome({ cat: "division", user: hasil })
setData(response.data)
@@ -37,14 +37,19 @@ export default function DivisionHome() {
}
useEffect(() => {
handleData()
if (refreshing)
handleData(false)
}, [refreshing]);
useEffect(() => {
handleData(true)
}, []);
return (
<View style={[Styles.mb15]}>
<Text style={[Styles.textDefaultSemiBold, Styles.mv10]}>Divisi Teraktif</Text>
<Text style={[Styles.textDefaultSemiBold, Styles.mb10]}>Divisi Teraktif</Text>
{
loading ?
arrSkeleton.map((item, index) => (

View File

@@ -22,14 +22,14 @@ type Props = {
user_name: string
}
export default function EventHome() {
export default function EventHome({ refreshing }: { refreshing: boolean }) {
const { decryptToken, token } = useAuthSession()
const [data, setData] = useState<Props[]>([])
const [loading, setLoading] = useState(true)
async function handleData() {
async function handleData(loading: boolean) {
try {
setLoading(true)
setLoading(loading)
const hasil = await decryptToken(String(token?.current))
const response = await apiGetDataHome({ cat: "event", user: hasil })
setData(response.data)
@@ -41,7 +41,12 @@ export default function EventHome() {
}
useEffect(() => {
handleData()
if (refreshing)
handleData(false)
}, [refreshing]);
useEffect(() => {
handleData(true)
}, []);
return (

View File

@@ -20,16 +20,16 @@ type Props = {
createdAt: string
}
export default function ProjectHome() {
export default function ProjectHome({ refreshing }: { refreshing: boolean }) {
const { decryptToken, token } = useAuthSession()
const ref = React.useRef<ICarouselInstance>(null);
const width = Dimensions.get("window").width;
const [data, setData] = useState<Props[]>([])
const [loading, setLoading] = useState(true)
async function handleData() {
async function handleData(loading: boolean) {
try {
setLoading(true)
setLoading(loading)
const hasil = await decryptToken(String(token?.current))
const response = await apiGetDataHome({ cat: "kegiatan", user: hasil })
setData(response.data)
@@ -41,12 +41,17 @@ export default function ProjectHome() {
}
useEffect(() => {
handleData()
if (refreshing)
handleData(false)
}, [refreshing]);
useEffect(() => {
handleData(true)
}, []);
return (
<View style={[Styles.mb15]}>
<Text style={[Styles.textDefaultSemiBold, Styles.mv10]}>Kegiatan Terupdate</Text>
<Text style={[Styles.textDefaultSemiBold, Styles.mb10]}>Kegiatan Terupdate</Text>
{
loading ? (<Skeleton width={100} height={150} borderRadius={10} widthType="percent" />)
:

View File

@@ -33,6 +33,7 @@ export default function ModalFilter({ open, close, page, category }: Props) {
const { token, decryptToken } = useAuthSession()
const dispatch = useDispatch()
const entities = useSelector((state: any) => state.filterGroup)
const update = useSelector((state: any) => state.groupUpdate)
const [chooseGroup, setChooseGroup] = useState('')
async function handleLoad() {
@@ -42,10 +43,8 @@ export default function ModalFilter({ open, close, page, category }: Props) {
}
useEffect(() => {
if (entities.length == 0) {
handleLoad()
}
}, [dispatch]);
}, [dispatch, update]);

View File

@@ -135,7 +135,7 @@ export default function ModalSelect({ open, close, title, category, idParent, on
selectMember.length > 0
?
<View>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]}>
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]} showsHorizontalScrollIndicator={false}>
{
selectMember.map((item: any, index: any) => (
<ImageWithLabel
@@ -154,7 +154,7 @@ export default function ModalSelect({ open, close, title, category, idParent, on
}
</>
}
<ScrollView>
<ScrollView showsVerticalScrollIndicator={false}>
<View>
{
category != 'status-task' ?

File diff suppressed because one or more lines are too long

View File

@@ -234,7 +234,7 @@ export const apiGetDivisionGroup = async ({ user }: { user: string }) => {
};
export const apiCreateAnnouncement = async ({ data }: { data: { title: string, desc: string, user: string, groups: any[] } }) => {
const response = await api.post(`/mobile/announcement/`, data)
const response = await api.post(`/mobile/announcement`, data)
return response.data;
};