upd: skeleton

Deskripsi:
- diskusi umum
- detail diskusi umum
- list pengumuman
- detail pengumuman
- list kegiatan
- detail kegiatan

No Issues
This commit is contained in:
amel
2025-06-03 17:43:45 +08:00
parent 36294ec4eb
commit 1e6a6d08ae
20 changed files with 541 additions and 293 deletions

View File

@@ -1,9 +1,10 @@
import HeaderRightAnnouncementDetail from "@/components/announcement/headerAnnouncementDetail"; import HeaderRightAnnouncementDetail from "@/components/announcement/headerAnnouncementDetail";
import ButtonBackHeader from "@/components/buttonBackHeader"; import ButtonBackHeader from "@/components/buttonBackHeader";
import Skeleton from "@/components/skeleton";
import Styles from "@/constants/Styles"; import Styles from "@/constants/Styles";
import { apiGetAnnouncementOne } from "@/lib/api"; import { apiGetAnnouncementOne } from "@/lib/api";
import { useAuthSession } from "@/providers/AuthProvider"; import { useAuthSession } from "@/providers/AuthProvider";
import { AntDesign, Entypo, MaterialIcons } from "@expo/vector-icons"; import { Entypo, MaterialIcons } from "@expo/vector-icons";
import { router, Stack, useLocalSearchParams } from "expo-router"; import { router, Stack, useLocalSearchParams } from "expo-router";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Dimensions, SafeAreaView, ScrollView, Text, View } from "react-native"; import { Dimensions, SafeAreaView, ScrollView, Text, View } from "react-native";
@@ -24,22 +25,31 @@ export default function DetailAnnouncement() {
const update = useSelector((state: any) => state.announcementUpdate) const update = useSelector((state: any) => state.announcementUpdate)
const entityUser = useSelector((state: any) => state.user) const entityUser = useSelector((state: any) => state.user)
const contentWidth = Dimensions.get('window').width const contentWidth = Dimensions.get('window').width
const [loading, setLoading] = useState(true)
const arrSkeleton = Array.from({ length: 2 }, (_, index) => index)
async function handleLoad() { async function handleLoad(loading: boolean) {
try { try {
setLoading(loading)
const hasil = await decryptToken(String(token?.current)) const hasil = await decryptToken(String(token?.current))
const response = await apiGetAnnouncementOne({ id: id, user: hasil }) const response = await apiGetAnnouncementOne({ id: id, user: hasil })
setData(response.data) setData(response.data)
setDataMember(response.member) setDataMember(response.member)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} finally {
setLoading(false)
} }
} }
useEffect(() => { useEffect(() => {
handleLoad() handleLoad(false)
}, [update]) }, [update])
useEffect(() => {
handleLoad(true)
}, [])
return ( return (
<SafeAreaView> <SafeAreaView>
<Stack.Screen <Stack.Screen
@@ -53,37 +63,66 @@ export default function DetailAnnouncement() {
<ScrollView> <ScrollView>
<View style={[Styles.p15]}> <View style={[Styles.p15]}>
<View style={[Styles.wrapPaper]}> <View style={[Styles.wrapPaper]}>
<View style={Styles.rowItemsCenter}> {
<MaterialIcons name="campaign" size={30} color="black" style={Styles.mr05} /> loading ?
<Text style={[Styles.textDefaultSemiBold]}>{data?.title}</Text> <View>
</View> <View style={[Styles.rowOnly]}>
<View style={[Styles.mt10]}> <Skeleton width={30} height={30} borderRadius={10} />
<RenderHTML <View style={[{ flex: 1 }, Styles.ph05]}>
contentWidth={contentWidth} <Skeleton width={100} widthType="percent" height={30} borderRadius={10} />
source={{ html: data?.desc }} </View>
/> </View>
</View> <Skeleton width={100} widthType="percent" height={10} borderRadius={10} />
<Skeleton width={100} widthType="percent" height={10} borderRadius={10} />
<Skeleton width={100} widthType="percent" height={10} borderRadius={10} />
</View>
:
<>
<View style={Styles.rowItemsCenter}>
<MaterialIcons name="campaign" size={30} color="black" style={Styles.mr05} />
<Text style={[Styles.textDefaultSemiBold]}>{data?.title}</Text>
</View>
<View style={[Styles.mt10]}>
<RenderHTML
contentWidth={contentWidth}
source={{ html: data?.desc }}
/>
</View>
</>
}
</View> </View>
<View style={[Styles.wrapPaper, Styles.mv15]}> <View style={[Styles.wrapPaper, Styles.mv15]}>
{ {
Object.keys(dataMember).map((v: any, i: any) => { loading ?
return ( arrSkeleton.map((item, index) => {
<View key={i} style={[Styles.mb05]}> return (
<Text style={[Styles.textDefaultSemiBold]}>{dataMember[v]?.[0].group}</Text> <View key={index}>
{ <Skeleton width={30} widthType="percent" height={10} borderRadius={10} />
dataMember[v].map((item: any, x: any) => { <Skeleton width={100} widthType="percent" height={10} borderRadius={10} />
return ( <Skeleton width={100} widthType="percent" height={10} borderRadius={10} />
<View key={x} style={[Styles.rowItemsCenter]}> </View>
<Entypo name="dot-single" size={24} color="black" /> )
<Text style={[Styles.textDefault]} numberOfLines={1} ellipsizeMode='tail'>{item.division}</Text> })
</View> :
) Object.keys(dataMember).map((v: any, i: any) => {
}) return (
} <View key={i} style={[Styles.mb05]}>
<Text style={[Styles.textDefaultSemiBold]}>{dataMember[v]?.[0].group}</Text>
{
dataMember[v].map((item: any, x: any) => {
return (
<View key={x} style={[Styles.rowItemsCenter, Styles.w90]}>
<Entypo name="dot-single" size={24} color="black" />
<Text style={[Styles.textDefault]} numberOfLines={1} ellipsizeMode='tail'>{item.division}</Text>
</View>
)
})
}
</View> </View>
) )
}) })
} }
</View> </View>
</View> </View>

View File

@@ -2,6 +2,7 @@ import HeaderRightAnnouncementList from "@/components/announcement/headerAnnounc
import BorderBottomItem from "@/components/borderBottomItem"; import BorderBottomItem from "@/components/borderBottomItem";
import ButtonBackHeader from "@/components/buttonBackHeader"; import ButtonBackHeader from "@/components/buttonBackHeader";
import InputSearch from "@/components/inputSearch"; import InputSearch from "@/components/inputSearch";
import SkeletonContent from "@/components/skeletonContent";
import { ColorsStatus } from "@/constants/ColorsStatus"; import { ColorsStatus } from "@/constants/ColorsStatus";
import Styles from "@/constants/Styles"; import Styles from "@/constants/Styles";
import { apiGetAnnouncement } from "@/lib/api"; import { apiGetAnnouncement } from "@/lib/api";
@@ -26,20 +27,29 @@ export default function Announcement() {
const [search, setSearch] = useState('') const [search, setSearch] = useState('')
const entityUser = useSelector((state: any) => state.user) const entityUser = useSelector((state: any) => state.user)
const update = useSelector((state: any) => state.announcementUpdate) const update = useSelector((state: any) => state.announcementUpdate)
const [loading, setLoading] = useState(true)
const arrSkeleton = Array.from({ length: 5 }, (_, index) => index)
async function handleLoad() { async function handleLoad(loading: boolean) {
try { try {
setLoading(loading)
const hasil = await decryptToken(String(token?.current)) const hasil = await decryptToken(String(token?.current))
const response = await apiGetAnnouncement({ user: hasil, search: search }) const response = await apiGetAnnouncement({ user: hasil, search: search })
setData(response.data) setData(response.data)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} finally {
setLoading(false)
} }
} }
useEffect(() => { useEffect(() => {
handleLoad() handleLoad(false)
}, [search, update]) }, [update])
useEffect(() => {
handleLoad(true)
}, [search])
return ( return (
<SafeAreaView> <SafeAreaView>
@@ -56,27 +66,34 @@ export default function Announcement() {
<View style={[Styles.p15, Styles.mb100]}> <View style={[Styles.p15, Styles.mb100]}>
<InputSearch onChange={setSearch} /> <InputSearch onChange={setSearch} />
{ {
data.length > 0 loading ?
? arrSkeleton.map((item, index) => {
data.map((item, index) => {
return ( return (
<BorderBottomItem <SkeletonContent key={index} />
key={index}
onPress={() => { router.push(`/announcement/${item.id}`) }}
borderType="bottom"
icon={
<View style={[Styles.iconContent, ColorsStatus.lightGreen]}>
<MaterialIcons name="campaign" size={25} color={'#384288'} />
</View>
}
title={item.title}
desc={item.desc.replace(/<[^>]*>?/gm, '')}
rightTopInfo={item.createdAt}
/>
) )
}) })
: :
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada pengumuman</Text> data.length > 0
?
data.map((item, index) => {
return (
<BorderBottomItem
key={index}
onPress={() => { router.push(`/announcement/${item.id}`) }}
borderType="bottom"
icon={
<View style={[Styles.iconContent, ColorsStatus.lightGreen]}>
<MaterialIcons name="campaign" size={25} color={'#384288'} />
</View>
}
title={item.title}
desc={item.desc.replace(/<[^>]*>?/gm, '')}
rightTopInfo={item.createdAt}
/>
)
})
:
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada pengumuman</Text>
} }
</View> </View>
</ScrollView> </ScrollView>

View File

@@ -4,6 +4,8 @@ import HeaderRightDiscussionGeneralDetail from "@/components/discussion_general/
import ImageUser from "@/components/imageNew"; import ImageUser from "@/components/imageNew";
import { InputForm } from "@/components/inputForm"; import { InputForm } from "@/components/inputForm";
import LabelStatus from "@/components/labelStatus"; import LabelStatus from "@/components/labelStatus";
import Skeleton from "@/components/skeleton";
import SkeletonContent from "@/components/skeletonContent";
import { ColorsStatus } from "@/constants/ColorsStatus"; import { ColorsStatus } from "@/constants/ColorsStatus";
import Styles from "@/constants/Styles"; import Styles from "@/constants/Styles";
import { apiGetDiscussionGeneralOne, apiSendDiscussionGeneralCommentar } from "@/lib/api"; import { apiGetDiscussionGeneralOne, apiSendDiscussionGeneralCommentar } from "@/lib/api";
@@ -41,12 +43,22 @@ export default function DetailDiscussionGeneral() {
const [memberDiscussion, setMemberDiscussion] = useState(false) const [memberDiscussion, setMemberDiscussion] = useState(false)
const [komentar, setKomentar] = useState('') const [komentar, setKomentar] = useState('')
const update = useSelector((state: any) => state.discussionGeneralDetailUpdate) const update = useSelector((state: any) => state.discussionGeneralDetailUpdate)
const [loading, setLoading] = useState(true)
const [loadingKomentar, setLoadingKomentar] = useState(true)
const arrSkeleton = Array.from({ length: 3 }, (_, index) => index)
async function handleLoad(cat: 'detail' | 'komentar' | 'cek-anggota') { async function handleLoad(cat: 'detail' | 'komentar' | 'cek-anggota', loading: boolean) {
try { try {
if (cat == "detail") {
setLoading(loading)
} else if (cat == "komentar") {
setLoadingKomentar(loading)
}
const hasil = await decryptToken(String(token?.current)) const hasil = await decryptToken(String(token?.current))
const response = await apiGetDiscussionGeneralOne({ id: id, user: hasil, cat }) const response = await apiGetDiscussionGeneralOne({ id: id, user: hasil, cat })
if (cat == 'detail') { if (cat == 'detail') {
setData(response.data) setData(response.data)
} else if (cat == 'komentar') { } else if (cat == 'komentar') {
@@ -54,17 +66,27 @@ export default function DetailDiscussionGeneral() {
} else if (cat == 'cek-anggota') { } else if (cat == 'cek-anggota') {
setMemberDiscussion(response.data) setMemberDiscussion(response.data)
} }
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} finally {
setLoading(false)
setLoadingKomentar(false)
} }
} }
useEffect(() => { useEffect(() => {
handleLoad('detail') handleLoad('detail', false)
handleLoad('komentar') handleLoad('komentar', false)
handleLoad('cek-anggota') handleLoad('cek-anggota', false)
}, [update]); }, [update]);
useEffect(() => {
handleLoad('detail', true)
handleLoad('komentar', true)
handleLoad('cek-anggota', true)
}, []);
async function handleKomentar() { async function handleKomentar() {
try { try {
if (komentar != '') { if (komentar != '') {
@@ -72,7 +94,7 @@ export default function DetailDiscussionGeneral() {
const response = await apiSendDiscussionGeneralCommentar({ id: id, data: { desc: komentar, user: hasil } }) const response = await apiSendDiscussionGeneralCommentar({ id: id, data: { desc: komentar, user: hasil } })
if (response.success) { if (response.success) {
setKomentar('') setKomentar('')
handleLoad('komentar') handleLoad('komentar', false)
} }
} }
} catch (error) { } catch (error) {
@@ -93,45 +115,57 @@ export default function DetailDiscussionGeneral() {
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<ScrollView> <ScrollView>
<View style={[Styles.p15, Styles.mb100]}> <View style={[Styles.p15, Styles.mb100]}>
<BorderBottomItem {
borderType="bottom" loading ?
icon={ <SkeletonContent />
<View style={[Styles.iconContent, ColorsStatus.lightGreen]}> :
<MaterialIcons name="chat" size={25} color={'#384288'} /> <BorderBottomItem
</View> borderType="bottom"
} icon={
title={data?.title} <View style={[Styles.iconContent, ColorsStatus.lightGreen]}>
subtitle={ <MaterialIcons name="chat" size={25} color={'#384288'} />
!data?.isActive ? </View>
<LabelStatus category='warning' text='ARSIP' size="small" /> }
: title={data?.title}
<LabelStatus category={data.status == 1 ? 'success' : 'error'} text={data.status == 1 ? 'BUKA' : 'TUTUP'} size="small" /> subtitle={
} !data?.isActive ?
rightTopInfo={data?.createdAt} <LabelStatus category='warning' text='ARSIP' size="small" />
desc={data?.desc} :
leftBottomInfo={ <LabelStatus category={data.status == 1 ? 'success' : 'error'} text={data.status == 1 ? 'BUKA' : 'TUTUP'} size="small" />
<View style={[Styles.rowItemsCenter]}> }
<Ionicons name="chatbox-ellipses-outline" size={18} color="grey" style={Styles.mr05} /> rightTopInfo={data?.createdAt}
<Text style={[Styles.textInformation, Styles.cGray, Styles.mb05]}>{dataKomentar.length} Komentar</Text> desc={data?.desc}
</View> leftBottomInfo={
} <View style={[Styles.rowItemsCenter]}>
/> <Ionicons name="chatbox-ellipses-outline" size={18} color="grey" style={Styles.mr05} />
<Text style={[Styles.textInformation, Styles.cGray, Styles.mb05]}>{dataKomentar.length} Komentar</Text>
</View>
}
/>
}
<View style={[Styles.p15]}> <View style={[Styles.p15]}>
{ {
dataKomentar.map((item, i) => { loadingKomentar ?
return ( arrSkeleton.map((item: any, i: number) => {
<BorderBottomItem return (
key={i} <Skeleton key={i} width={100} widthType="percent" height={40} borderRadius={5} />
borderType="bottom" )
icon={ })
<ImageUser src={`https://wibu-storage.wibudev.com/api/files/${item.img}`} size="xs" /> :
} dataKomentar.map((item, i) => {
title={item.username} return (
rightTopInfo={item.createdAt} <BorderBottomItem
desc={item.comment} key={i}
/> borderType="bottom"
) icon={
}) <ImageUser src={`https://wibu-storage.wibudev.com/api/files/${item.img}`} size="xs" />
}
title={item.username}
rightTopInfo={item.createdAt}
desc={item.comment}
/>
)
})
} }
</View> </View>
</View> </View>

View File

@@ -2,6 +2,7 @@ import BorderBottomItem from "@/components/borderBottomItem";
import ButtonTab from "@/components/buttonTab"; import ButtonTab from "@/components/buttonTab";
import InputSearch from "@/components/inputSearch"; import InputSearch from "@/components/inputSearch";
import LabelStatus from "@/components/labelStatus"; import LabelStatus from "@/components/labelStatus";
import SkeletonContent from "@/components/skeletonContent";
import { ColorsStatus } from "@/constants/ColorsStatus"; import { ColorsStatus } from "@/constants/ColorsStatus";
import Styles from "@/constants/Styles"; import Styles from "@/constants/Styles";
import { apiGetDiscussionGeneral } from "@/lib/api"; import { apiGetDiscussionGeneral } from "@/lib/api";
@@ -30,21 +31,30 @@ export default function Discussion() {
const [nameGroup, setNameGroup] = useState('') const [nameGroup, setNameGroup] = useState('')
const [data, setData] = useState<Props[]>([]) const [data, setData] = useState<Props[]>([])
const update = useSelector((state: any) => state.discussionGeneralDetailUpdate) const update = useSelector((state: any) => state.discussionGeneralDetailUpdate)
const [loading, setLoading] = useState(true)
const arrSkeleton = Array.from({ length: 5 }, (_, index) => index)
async function handleLoad() { async function handleLoad(loading: boolean) {
try { try {
setLoading(loading)
const hasil = await decryptToken(String(token?.current)) const hasil = await decryptToken(String(token?.current))
const response = await apiGetDiscussionGeneral({ user: hasil, active: String(active), search: search, group: String(group) }) const response = await apiGetDiscussionGeneral({ user: hasil, active: String(active), search: search, group: String(group) })
setData(response.data) setData(response.data)
setNameGroup(response.filter.name) setNameGroup(response.filter.name)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} finally {
setLoading(false)
} }
} }
useEffect(() => { useEffect(() => {
handleLoad() handleLoad(true)
}, [active, search, group, update]) }, [active, search, group])
useEffect(() => {
handleLoad(false)
}, [update])
return ( return (
<SafeAreaView> <SafeAreaView>
@@ -75,37 +85,45 @@ export default function Discussion() {
} }
<View> <View>
{ {
data.length > 0 loading ?
? arrSkeleton.map((item: any, i: number) => {
data.map((item: any, i: number) => {
return ( return (
<BorderBottomItem <SkeletonContent key={i} />
key={i}
onPress={() => { router.push(`/discussion/${item.id}`) }}
borderType="bottom"
icon={
<View style={[Styles.iconContent, ColorsStatus.lightGreen]}>
<MaterialIcons name="chat" size={25} color={'#384288'} />
</View>
}
title={item.title}
subtitle={
active != "false" && <LabelStatus category={item.status === 1 ? "success" : "error"} text={item.status === 1 ? "BUKA" : "TUTUP"} size="small" />
}
rightTopInfo={item.createdAt}
desc={item.desc}
leftBottomInfo={
<View style={[Styles.rowItemsCenter]}>
<Ionicons name="chatbox-ellipses-outline" size={18} color="grey" style={Styles.mr05} />
<Text style={[Styles.textInformation, Styles.cGray, Styles.mb05]}>Diskusikan</Text>
</View>
}
rightBottomInfo={`${item.total_komentar} Komentar`}
/>
) )
}) })
: :
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada data</Text> data.length > 0
?
data.map((item: any, i: number) => {
return (
<BorderBottomItem
key={i}
onPress={() => { router.push(`/discussion/${item.id}`) }}
borderType="bottom"
icon={
<View style={[Styles.iconContent, ColorsStatus.lightGreen]}>
<MaterialIcons name="chat" size={25} color={'#384288'} />
</View>
}
title={item.title}
subtitle={
active != "false" && <LabelStatus category={item.status === 1 ? "success" : "error"} text={item.status === 1 ? "BUKA" : "TUTUP"} size="small" />
}
rightTopInfo={item.createdAt}
desc={item.desc}
leftBottomInfo={
<View style={[Styles.rowItemsCenter]}>
<Ionicons name="chatbox-ellipses-outline" size={18} color="grey" style={Styles.mr05} />
<Text style={[Styles.textInformation, Styles.cGray, Styles.mb05]}>Diskusikan</Text>
</View>
}
rightBottomInfo={`${item.total_komentar} Komentar`}
/>
)
})
:
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada data</Text>
} }
</View> </View>
</View> </View>

View File

@@ -4,6 +4,7 @@ import ButtonBackHeader from "@/components/buttonBackHeader";
import DrawerBottom from "@/components/drawerBottom"; import DrawerBottom from "@/components/drawerBottom";
import ImageUser from "@/components/imageNew"; import ImageUser from "@/components/imageNew";
import MenuItemRow from "@/components/menuItemRow"; import MenuItemRow from "@/components/menuItemRow";
import SkeletonTwoItem from "@/components/skeletonTwoItem";
import { ColorsStatus } from "@/constants/ColorsStatus"; import { ColorsStatus } from "@/constants/ColorsStatus";
import Styles from "@/constants/Styles"; import Styles from "@/constants/Styles";
import { apiDeleteMemberDiscussionGeneral, apiGetDiscussionGeneralOne } from "@/lib/api"; import { apiDeleteMemberDiscussionGeneral, apiGetDiscussionGeneralOne } from "@/lib/api";
@@ -28,28 +29,37 @@ export default function MemberDiscussionDetail() {
const [isModal, setModal] = useState(false) const [isModal, setModal] = useState(false)
const [chooseUser, setChooseUser] = useState({ idUser: '', name: '', img: '' }) const [chooseUser, setChooseUser] = useState({ idUser: '', name: '', img: '' })
const update = useSelector((state: any) => state.discussionGeneralDetailUpdate) const update = useSelector((state: any) => state.discussionGeneralDetailUpdate)
const [loading, setLoading] = useState(true)
const arrSkeleton = Array.from({ length: 5 }, (_, index) => index)
async function handleLoad() { async function handleLoad(loading: boolean) {
try { try {
setLoading(loading)
const hasil = await decryptToken(String(token?.current)) const hasil = await decryptToken(String(token?.current))
const response = await apiGetDiscussionGeneralOne({ id: id, user: hasil, cat: 'anggota' }) const response = await apiGetDiscussionGeneralOne({ id: id, user: hasil, cat: 'anggota' })
setData(response.data) setData(response.data)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} finally {
setLoading(false)
} }
} }
useEffect(() => {
handleLoad(false)
}, [update]);
useEffect(() => { useEffect(() => {
handleLoad() handleLoad(true)
}, [update]); }, []);
async function handleDeleteUser() { async function handleDeleteUser() {
try { try {
const hasil = await decryptToken(String(token?.current)) const hasil = await decryptToken(String(token?.current))
await apiDeleteMemberDiscussionGeneral({ user: hasil, idUser: chooseUser.idUser }, id) await apiDeleteMemberDiscussionGeneral({ user: hasil, idUser: chooseUser.idUser }, id)
ToastAndroid.show("Berhasil mengelluarkan anggota dari diskusi", ToastAndroid.SHORT) ToastAndroid.show("Berhasil mengeluarkan anggota dari diskusi", ToastAndroid.SHORT)
handleLoad() handleLoad(false)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} finally { } finally {
@@ -84,22 +94,29 @@ export default function MemberDiscussionDetail() {
/> />
} }
{ {
data.map((item, index) => { loading ?
return ( arrSkeleton.map((item, index) => {
<BorderBottomItem return (
key={index} <SkeletonTwoItem key={index} />
borderType="bottom" )
icon={ })
<ImageUser src={`https://wibu-storage.wibudev.com/api/files/${item.img}`} size="sm" /> :
} data.map((item, index) => {
title={item.name} return (
onPress={() => { <BorderBottomItem
setChooseUser(item) key={index}
setModal(true) borderType="bottom"
}} icon={
/> <ImageUser src={`https://wibu-storage.wibudev.com/api/files/${item.img}`} size="sm" />
) }
}) title={item.name}
onPress={() => {
setChooseUser(item)
setModal(true)
}}
/>
)
})
} }
</View> </View>
</View> </View>

View File

@@ -84,13 +84,15 @@ export default function Index() {
} }
} }
useEffect(() => {
handleLoad(false)
}, [update])
useEffect(() => { useEffect(() => {
handleLoad(true) handleLoad(true)
}, [active, search]) }, [active, search])
useEffect(() => {
handleLoad(false)
}, [update])
return ( return (

View File

@@ -59,15 +59,16 @@ export default function Index() {
} }
} }
useEffect(() => {
handleLoad(true)
}, [active, search, group])
useEffect(() => { useEffect(() => {
handleLoad(false) handleLoad(false)
}, [update]) }, [update])
useEffect(() => {
handleLoad(true)
}, [active, search, group])
function handleChooseData(id: string, name: string, active: boolean, group: string) { function handleChooseData(id: string, name: string, active: boolean, group: string) {
setChooseData({ id, name, active, idGroup: group }) setChooseData({ id, name, active, idGroup: group })
setModal(true) setModal(true)

View File

@@ -78,7 +78,7 @@ export default function DetailProject() {
<Stack.Screen <Stack.Screen
options={{ options={{
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />, headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
headerTitle: loading ? 'Loading...' : data?.title, headerTitle: loading ? 'Loading... ' : data?.title,
headerTitleAlign: 'center', headerTitleAlign: 'center',
headerRight: () => (entityUser.role == "user" || entityUser.role == "coadmin") && !isMember ? null : <HeaderRightProjectDetail id={id} status={data?.status} />, headerRight: () => (entityUser.role == "user" || entityUser.role == "coadmin") && !isMember ? null : <HeaderRightProjectDetail id={id} status={data?.status} />,
}} }}

View File

@@ -4,6 +4,8 @@ import InputSearch from "@/components/inputSearch";
import LabelStatus from "@/components/labelStatus"; import LabelStatus from "@/components/labelStatus";
import PaperGridContent from "@/components/paperGridContent"; import PaperGridContent from "@/components/paperGridContent";
import ProgressBar from "@/components/progressBar"; import ProgressBar from "@/components/progressBar";
import Skeleton from "@/components/skeleton";
import SkeletonTwoItem from "@/components/skeletonTwoItem";
import { ColorsStatus } from "@/constants/ColorsStatus"; import { ColorsStatus } from "@/constants/ColorsStatus";
import Styles from "@/constants/Styles"; import Styles from "@/constants/Styles";
import { apiGetProject } from "@/lib/api"; import { apiGetProject } from "@/lib/api";
@@ -41,9 +43,12 @@ export default function ListProject() {
const [data, setData] = useState<Props[]>([]); const [data, setData] = useState<Props[]>([]);
const [isList, setList] = useState(false); const [isList, setList] = useState(false);
const update = useSelector((state: any) => state.projectUpdate) const update = useSelector((state: any) => state.projectUpdate)
const [loading, setLoading] = useState(true)
const arrSkeleton = Array.from({ length: 3 }, (_, index) => index)
async function handleLoad() { async function handleLoad(loading: boolean) {
try { try {
setLoading(loading)
const hasil = await decryptToken(String(token?.current)); const hasil = await decryptToken(String(token?.current));
const response = await apiGetProject({ const response = await apiGetProject({
user: hasil, user: hasil,
@@ -59,12 +64,19 @@ export default function ListProject() {
} }
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} finally {
setLoading(false)
} }
} }
useEffect(() => { useEffect(() => {
handleLoad(); handleLoad(false);
}, [status, search, group, cat, update.data]); }, [update.data]);
useEffect(() => {
handleLoad(true);
}, [status, search, group, cat]);
return ( return (
<SafeAreaView> <SafeAreaView>
@@ -75,7 +87,7 @@ export default function ListProject() {
active={String(status)} active={String(status)}
value="0" value="0"
onPress={() => { onPress={() => {
router.push( router.replace(
`/project?status=0&group=${group}&search=${search}&cat=${cat}` `/project?status=0&group=${group}&search=${search}&cat=${cat}`
); );
}} }}
@@ -93,7 +105,7 @@ export default function ListProject() {
active={String(status)} active={String(status)}
value="1" value="1"
onPress={() => { onPress={() => {
router.push( router.replace(
`/project?status=1&group=${group}&search=${search}&cat=${cat}` `/project?status=1&group=${group}&search=${search}&cat=${cat}`
); );
}} }}
@@ -111,7 +123,7 @@ export default function ListProject() {
active={String(status)} active={String(status)}
value="2" value="2"
onPress={() => { onPress={() => {
router.push( router.replace(
`/project?status=2&group=${group}&search=${search}&cat=${cat}` `/project?status=2&group=${group}&search=${search}&cat=${cat}`
); );
}} }}
@@ -129,7 +141,7 @@ export default function ListProject() {
active={String(status)} active={String(status)}
value="3" value="3"
onPress={() => { onPress={() => {
router.push( router.replace(
`/project?status=3&group=${group}&search=${search}&cat=${cat}` `/project?status=3&group=${group}&search=${search}&cat=${cat}`
); );
}} }}
@@ -171,77 +183,87 @@ export default function ListProject() {
</Text> </Text>
</View> </View>
{ {
data.length > 0 loading ?
? isList ?
isList ? ( arrSkeleton.map((item, index) => (
<View> <SkeletonTwoItem key={index} />
{data.map((item, index) => { ))
return ( :
<BorderBottomItem arrSkeleton.map((item, index) => (
key={index} <Skeleton key={index} width={100} height={180} widthType="percent" borderRadius={10} />
onPress={() => { router.push(`/project/${item.id}`); }} ))
borderType="bottom" :
icon={ data.length > 0
<View ?
style={[Styles.iconContent, ColorsStatus.lightGreen]} isList ? (
> <View>
<AntDesign {data.map((item, index) => {
name="areachart" return (
size={25} <BorderBottomItem
color={"#384288"} key={index}
onPress={() => { router.push(`/project/${item.id}`); }}
borderType="bottom"
icon={
<View
style={[Styles.iconContent, ColorsStatus.lightGreen]}
>
<AntDesign
name="areachart"
size={25}
color={"#384288"}
/>
</View>
}
title={item.title}
/>
);
})}
</View>
) : (
<View>
{data.map((item, index) => {
return (
<PaperGridContent
key={index}
onPress={() => {
router.push(`/project/${item.id}`);
}}
content="page"
title={item.title}
headerColor="primary"
>
<ProgressBar value={item.progress} category="page" />
<View style={[Styles.rowSpaceBetween]}>
<Text style={[Styles.textDefault, Styles.cGray]}>
{item.createdAt}
</Text>
<LabelStatus
size="default"
category={
item.status === 0 ? 'primary' :
item.status === 1 ? 'warning' :
item.status === 2 ? 'success' :
item.status === 3 ? 'error' :
'primary'
}
text={
item.status === 0 ? 'SEGERA' :
item.status === 1 ? 'DIKERJAKAN' :
item.status === 2 ? 'SELESAI' :
item.status === 3 ? 'DIBATALKAN' :
'SEGERA'
}
/> />
</View> </View>
} </PaperGridContent>
title={item.title} );
/> })}
); </View>
})} )
:
<View style={[Styles.mt15]}>
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada kegiatan</Text>
</View> </View>
) : (
<View>
{data.map((item, index) => {
return (
<PaperGridContent
key={index}
onPress={() => {
router.push(`/project/${item.id}`);
}}
content="page"
title={item.title}
headerColor="primary"
>
<ProgressBar value={item.progress} category="page" />
<View style={[Styles.rowSpaceBetween]}>
<Text style={[Styles.textDefault, Styles.cGray]}>
{item.createdAt}
</Text>
<LabelStatus
size="default"
category={
item.status === 0 ? 'primary' :
item.status === 1 ? 'warning' :
item.status === 2 ? 'success' :
item.status === 3 ? 'error' :
'primary'
}
text={
item.status === 0 ? 'SEGERA' :
item.status === 1 ? 'DIKERJAKAN' :
item.status === 2 ? 'SELESAI' :
item.status === 3 ? 'DIBATALKAN' :
'SEGERA'
}
/>
</View>
</PaperGridContent>
);
})}
</View>
)
:
<View style={[Styles.mt15]}>
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada kegiatan</Text>
</View>
} }
</View> </View>
</ScrollView> </ScrollView>

View File

@@ -27,8 +27,8 @@ export default function BorderBottomItem({ title, subtitle, icon, desc, onPress,
<Pressable style={[borderType == 'bottom' ? Styles.wrapItemBorderBottom : borderType == 'all' ? Styles.wrapItemBorderAll : Styles.wrapItemBorderNone, bgColor && bgColor == 'white' && ColorsStatus.white]} onPress={onPress}> <Pressable style={[borderType == 'bottom' ? Styles.wrapItemBorderBottom : borderType == 'all' ? Styles.wrapItemBorderAll : Styles.wrapItemBorderNone, bgColor && bgColor == 'white' && ColorsStatus.white]} onPress={onPress}>
<View style={[Styles.rowItemsCenter]}> <View style={[Styles.rowItemsCenter]}>
{icon} {icon}
<View style={[Styles.rowSpaceBetween, width ? { width: lebar } : { width: '88%' },]}> <View style={[Styles.rowSpaceBetween, width ? { width: lebar } : { width: '88%' }]}>
<View style={[Styles.ml10, width ? { width: lebar } : { width: '88%' }]}> <View style={[Styles.ml10, width ? { width: lebar } : { width: '70%' }]}>
<Text style={[titleWeight == 'normal' ? Styles.textDefault : Styles.textDefaultSemiBold]} numberOfLines={1} ellipsizeMode='tail'>{title}</Text> <Text style={[titleWeight == 'normal' ? Styles.textDefault : Styles.textDefaultSemiBold]} numberOfLines={1} ellipsizeMode='tail'>{title}</Text>
{ {
subtitle && subtitle &&

View File

@@ -133,7 +133,7 @@ export default function ModalSelect({ open, close, title, category, idParent, on
<ImageWithLabel <ImageWithLabel
key={index} key={index}
label={item.name} label={item.name}
src={item.img} src={`https://wibu-storage.wibudev.com/api/files/${item.img}`}
onClick={() => onChoose(item.idUser, item.name, item.img)} onClick={() => onChoose(item.idUser, item.name, item.img)}
/> />
)) ))

View File

@@ -145,7 +145,7 @@ export default function ModalSelectMultiple({ open, close, title, category, choo
}; };
return ( return (
<DrawerBottom animation="slide" isVisible={open} setVisible={close} title={title} height={75}> <DrawerBottom animation="slide" isVisible={open} setVisible={close} title={title} height={85}>
<ScrollView style={[Styles.mb50]}> <ScrollView style={[Styles.mb50]}>
{ {
category == 'share-division' ? category == 'share-division' ?
@@ -170,8 +170,8 @@ export default function ModalSelectMultiple({ open, close, title, category, choo
: :
data.map((item: any, index: number) => { data.map((item: any, index: number) => {
return ( return (
<> <View key={index}>
<Pressable key={index} style={[Styles.itemSelectModal]} onPress={() => { handleGroupCheck(item.id) }}> <Pressable style={[Styles.itemSelectModal]} onPress={() => { handleGroupCheck(item.id) }}>
<Text style={[Styles.textMediumSemiBold]}>{item.name}</Text> <Text style={[Styles.textMediumSemiBold]}>{item.name}</Text>
{ {
checked[item.id] && checked[item.id].length === item.Division.length checked[item.id] && checked[item.id].length === item.Division.length
@@ -185,7 +185,7 @@ export default function ModalSelectMultiple({ open, close, title, category, choo
item.Division.map((child: any, v: number) => { item.Division.map((child: any, v: number) => {
return ( return (
<Pressable key={v} style={[Styles.itemSelectModal]} onPress={() => { handleCheck(item.id, child.id) }}> <Pressable key={v} style={[Styles.itemSelectModal]} onPress={() => { handleCheck(item.id, child.id) }}>
<Text style={[Styles.ml10, Styles.textMediumNormal]}>{child.name}</Text> <Text style={[Styles.ml10, Styles.textMediumNormal, Styles.w80]} numberOfLines={1} ellipsizeMode="tail" >{child.name}</Text>
{ {
checked[item.id] && checked[item.id].includes(child.id) && <AntDesign name="check" size={20} /> checked[item.id] && checked[item.id].includes(child.id) && <AntDesign name="check" size={20} />
} }
@@ -193,7 +193,7 @@ export default function ModalSelectMultiple({ open, close, title, category, choo
) )
}) })
} }
</> </View>
) )
}) })
} }

View File

@@ -14,6 +14,7 @@ import AlertKonfirmasi from "../alertKonfirmasi";
import BorderBottomItem from "../borderBottomItem"; import BorderBottomItem from "../borderBottomItem";
import DrawerBottom from "../drawerBottom"; import DrawerBottom from "../drawerBottom";
import MenuItemRow from "../menuItemRow"; import MenuItemRow from "../menuItemRow";
import Skeleton from "../skeleton";
type Props = { type Props = {
@@ -32,9 +33,12 @@ export default function SectionFile({ status, member }: { status: number | undef
const update = useSelector((state: any) => state.projectUpdate) const update = useSelector((state: any) => state.projectUpdate)
const [idSelect, setIdSelect] = useState('') const [idSelect, setIdSelect] = useState('')
const dispatch = useDispatch() const dispatch = useDispatch()
const [loading, setLoading] = useState(true)
const arrSkeleton = Array.from({ length: 3 })
async function handleLoad() { async function handleLoad(loading: boolean) {
try { try {
setLoading(loading)
const hasil = await decryptToken(String(token?.current)); const hasil = await decryptToken(String(token?.current));
const response = await apiGetProjectOne({ const response = await apiGetProjectOne({
user: hasil, user: hasil,
@@ -44,13 +48,20 @@ export default function SectionFile({ status, member }: { status: number | undef
setData(response.data); setData(response.data);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} finally {
setLoading(false)
} }
} }
useEffect(() => { useEffect(() => {
handleLoad(); handleLoad(false);
}, [update.file]); }, [update.file]);
useEffect(() => {
handleLoad(true);
}, []);
async function handleDelete() { async function handleDelete() {
try { try {
const hasil = await decryptToken(String(token?.current)); const hasil = await decryptToken(String(token?.current));
@@ -141,21 +152,28 @@ export default function SectionFile({ status, member }: { status: number | undef
<Text style={[Styles.textDefaultSemiBold, Styles.mv05]}>File</Text> <Text style={[Styles.textDefaultSemiBold, Styles.mv05]}>File</Text>
<View style={[Styles.wrapPaper]}> <View style={[Styles.wrapPaper]}>
{ {
data.length > 0 ? loading ?
data.map((item, index) => { arrSkeleton.map((item, index) => {
return ( return (
<BorderBottomItem <Skeleton key={index} width={100} height={40} widthType="percent" borderRadius={10} />
key={index}
borderType="all"
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
title={item.name + '.' + item.extension}
titleWeight="normal"
onPress={() => { setIdSelect(item.id); setModal(true) }}
/>
) )
}) })
: :
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada file</Text> data.length > 0 ?
data.map((item, index) => {
return (
<BorderBottomItem
key={index}
borderType="all"
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
title={item.name + '.' + item.extension}
titleWeight="normal"
onPress={() => { setIdSelect(item.id); setModal(true) }}
/>
)
})
:
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada file</Text>
} }
</View> </View>
</View> </View>

View File

@@ -12,6 +12,7 @@ import BorderBottomItem from "../borderBottomItem";
import DrawerBottom from "../drawerBottom"; import DrawerBottom from "../drawerBottom";
import ImageUser from "../imageNew"; import ImageUser from "../imageNew";
import MenuItemRow from "../menuItemRow"; import MenuItemRow from "../menuItemRow";
import SkeletonTwoItem from "../skeletonTwoItem";
type Props = { type Props = {
id: string; id: string;
@@ -30,14 +31,17 @@ export default function SectionMember({ status }: { status: number | undefined }
const { token, decryptToken } = useAuthSession(); const { token, decryptToken } = useAuthSession();
const { id } = useLocalSearchParams<{ id: string }>(); const { id } = useLocalSearchParams<{ id: string }>();
const [data, setData] = useState<Props[]>([]); const [data, setData] = useState<Props[]>([]);
const [loading, setLoading] = useState(true)
const arrSkeleton = Array.from({ length: 3 })
const [memberChoose, setMemberChoose] = useState({ const [memberChoose, setMemberChoose] = useState({
id: '', id: '',
name: '', name: '',
}) })
async function handleLoad() { async function handleLoad(loading: boolean) {
try { try {
setLoading(loading)
const hasil = await decryptToken(String(token?.current)); const hasil = await decryptToken(String(token?.current));
const response = await apiGetProjectOne({ const response = await apiGetProjectOne({
user: hasil, user: hasil,
@@ -47,13 +51,19 @@ export default function SectionMember({ status }: { status: number | undefined }
setData(response.data); setData(response.data);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} finally {
setLoading(false)
} }
} }
useEffect(() => { useEffect(() => {
handleLoad(); handleLoad(false);
}, [update.member]); }, [update.member]);
useEffect(() => {
handleLoad(true);
}, []);
async function handleDeleteMember() { async function handleDeleteMember() {
try { try {
const hasil = await decryptToken(String(token?.current)); const hasil = await decryptToken(String(token?.current));
@@ -81,28 +91,35 @@ export default function SectionMember({ status }: { status: number | undefined }
<View style={[Styles.wrapPaper]}> <View style={[Styles.wrapPaper]}>
{ {
data.length > 0 loading ?
? arrSkeleton.map((item, index) => {
data.map((item, index) => {
return ( return (
<BorderBottomItem <SkeletonTwoItem key={index} />
key={index} )
borderType="bottom"
icon={<ImageUser src={`https://wibu-storage.wibudev.com/api/files/${item.img}`} />}
title={item.name}
onPress={() => {
if (status == 3) return
setMemberChoose({
id: item.idUser,
name: item.name,
})
setModal(true);
}}
/>
);
}) })
: :
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada anggota</Text> data.length > 0
?
data.map((item, index) => {
return (
<BorderBottomItem
key={index}
borderType="bottom"
icon={<ImageUser src={`https://wibu-storage.wibudev.com/api/files/${item.img}`} />}
title={item.name}
onPress={() => {
if (status == 3) return
setMemberChoose({
id: item.idUser,
name: item.name,
})
setModal(true);
}}
/>
);
})
:
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada anggota</Text>
} }
</View> </View>
</View> </View>

View File

@@ -12,6 +12,7 @@ import DrawerBottom from "../drawerBottom";
import ItemSectionTanggalTugas from "../itemSectionTanggalTugas"; import ItemSectionTanggalTugas from "../itemSectionTanggalTugas";
import MenuItemRow from "../menuItemRow"; import MenuItemRow from "../menuItemRow";
import ModalSelect from "../modalSelect"; import ModalSelect from "../modalSelect";
import SkeletonTask from "../skeletonTask";
type Props = { type Props = {
id: string; id: string;
@@ -32,13 +33,16 @@ export default function SectionTanggalTugasProject({ status, member }: { status:
const { token, decryptToken } = useAuthSession(); const { token, decryptToken } = useAuthSession();
const { id } = useLocalSearchParams<{ id: string }>(); const { id } = useLocalSearchParams<{ id: string }>();
const [data, setData] = useState<Props[]>([]); const [data, setData] = useState<Props[]>([]);
const [loading, setLoading] = useState(true)
const arrSkeleton = Array.from({ length: 5 });
const [tugas, setTugas] = useState({ const [tugas, setTugas] = useState({
id: '', id: '',
status: 0, status: 0,
}) })
async function handleLoad() { async function handleLoad(loading: boolean) {
try { try {
setLoading(loading)
const hasil = await decryptToken(String(token?.current)); const hasil = await decryptToken(String(token?.current));
const response = await apiGetProjectOne({ const response = await apiGetProjectOne({
user: hasil, user: hasil,
@@ -48,13 +52,18 @@ export default function SectionTanggalTugasProject({ status, member }: { status:
setData(response.data); setData(response.data);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} finally {
setLoading(false)
} }
} }
useEffect(() => { useEffect(() => {
handleLoad(); handleLoad(false);
}, [update.task]); }, [update.task]);
useEffect(() => {
handleLoad(true);
}, []);
async function handleUpdate(status: number) { async function handleUpdate(status: number) {
@@ -100,29 +109,36 @@ export default function SectionTanggalTugasProject({ status, member }: { status:
</Text> </Text>
<View style={[Styles.wrapPaper]}> <View style={[Styles.wrapPaper]}>
{ {
data.length > 0 loading ?
? arrSkeleton.map((item, index) => {
data.map((item, index) => {
return ( return (
<ItemSectionTanggalTugas <SkeletonTask key={index} />
key={index} )
done={item.status === 1}
title={item.title}
dateStart={item.dateStart}
dateEnd={item.dateEnd}
onPress={() => {
if (status == 3 || (!member && (entityUser.role == "user" || entityUser.role == "coadmin"))) return
setTugas({
id: item.id,
status: item.status
})
setModal(true)
}}
/>
);
}) })
: :
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada tugas</Text> data.length > 0
?
data.map((item, index) => {
return (
<ItemSectionTanggalTugas
key={index}
done={item.status === 1}
title={item.title}
dateStart={item.dateStart}
dateEnd={item.dateEnd}
onPress={() => {
if (status == 3 || (!member && (entityUser.role == "user" || entityUser.role == "coadmin"))) return
setTugas({
id: item.id,
status: item.status
})
setModal(true)
}}
/>
);
})
:
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada tugas</Text>
} }
</View> </View>
</View> </View>

View File

@@ -3,7 +3,7 @@ import React, { useEffect } from 'react';
import { StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
import Animated, { Easing, useAnimatedStyle, useSharedValue, withRepeat, withTiming } from 'react-native-reanimated'; import Animated, { Easing, useAnimatedStyle, useSharedValue, withRepeat, withTiming } from 'react-native-reanimated';
const Skeleton = ({ width, height, borderRadius = 0, widthType = 'number' }: { width: number, widthType?: 'number' | 'percent', height: number, borderRadius?: number }) => { const Skeleton = ({ width, height, borderRadius = 0, widthType = 'number', style }: { width: number, widthType?: 'number' | 'percent', height: number, borderRadius?: number, style?: any }) => {
const opacity = useSharedValue(0.3); const opacity = useSharedValue(0.3);
useEffect(() => { useEffect(() => {
@@ -25,7 +25,7 @@ const Skeleton = ({ width, height, borderRadius = 0, widthType = 'number' }: { w
style={[ style={[
styles.skeleton, styles.skeleton,
{ width: widthType === 'percent' ? `${width}%` : width, height: height, borderRadius: borderRadius }, { width: widthType === 'percent' ? `${width}%` : width, height: height, borderRadius: borderRadius },
animatedStyle, Styles.mv05 animatedStyle, Styles.mv05, style
]} ]}
/> />
); );

View File

@@ -0,0 +1,19 @@
import Styles from "@/constants/Styles";
import { View } from "react-native";
import Skeleton from "./skeleton";
export default function SkeletonContent() {
return (
<View style={[Styles.borderBottom, Styles.mv05]}>
<View style={[Styles.rowOnly]}>
<Skeleton width={40} height={40} borderRadius={10} />
<View style={[{ flex: 1, justifyContent: 'center' }, Styles.ph05]}>
<Skeleton width={30} widthType="percent" height={10} borderRadius={10} />
<Skeleton width={50} widthType="percent" height={10} borderRadius={10} />
</View>
</View>
<Skeleton width={100} widthType="percent" height={40} borderRadius={5} />
<Skeleton width={100} widthType="percent" height={10} borderRadius={5} />
</View>
)
}

View File

@@ -0,0 +1,25 @@
import Styles from "@/constants/Styles";
import { View } from "react-native";
import Skeleton from "./skeleton";
export default function SkeletonTask() {
return (
<View style={[Styles.borderBottom, Styles.mv05]}>
<View style={[Styles.rowOnly]}>
<Skeleton width={10} height={10} borderRadius={3} style={[Styles.mr05]} />
<Skeleton width={30} widthType="percent" height={10} borderRadius={10} />
</View>
<Skeleton width={100} widthType="percent" height={30} borderRadius={5} />
<View style={[Styles.rowSpaceBetween]}>
<View style={[Styles.w45]}>
<Skeleton width={100} height={10} borderRadius={5} />
<Skeleton width={100} widthType="percent" height={30} borderRadius={5} />
</View>
<View style={[Styles.w45]}>
<Skeleton width={100} height={10} borderRadius={5} />
<Skeleton width={100} widthType="percent" height={30} borderRadius={5} />
</View>
</View>
</View>
)
}

View File

@@ -5,7 +5,7 @@ import Skeleton from "./skeleton";
export default function SkeletonTwoItem() { export default function SkeletonTwoItem() {
return ( return (
<View style={[Styles.rowOnly]}> <View style={[Styles.rowOnly]}>
<Skeleton width={50} height={50} borderRadius={10} /> <Skeleton width={50} height={50} borderRadius={100} />
<View style={[{ flex: 1 }, Styles.ph05]}> <View style={[{ flex: 1 }, Styles.ph05]}>
<Skeleton width={100} widthType="percent" height={50} borderRadius={10} /> <Skeleton width={100} widthType="percent" height={50} borderRadius={10} />
</View> </View>

View File

@@ -182,6 +182,9 @@ const Styles = StyleSheet.create({
w50: { w50: {
width: '50%' width: '50%'
}, },
w45: {
width: '45%'
},
w40: { w40: {
width: '40%' width: '40%'
}, },