- tambah komponen ModalRiwayatApproval dan ModalTolakApproval - update itemSectionTanggalTugas untuk mendukung status menunggu persetujuan - update sectionTanggalTugas (project) dan sectionTanggalTugasTask (divisi) dengan alur approval lengkap - tambah API approval project task dan division task di lib/api.ts - tambah toggle approver di headerMemberDetail dan tampilkan badge approver di detail member - update carouselHome untuk dispatch isApprover ke Redux - update drawerBottom untuk mendukung scroll pada modal - ganti label 'Belum dimulai' menjadi 'Belum ada tugas yang diselesaikan'
141 lines
5.3 KiB
TypeScript
141 lines
5.3 KiB
TypeScript
import Styles from "@/constants/Styles"
|
|
import { useTheme } from "@/providers/ThemeProvider"
|
|
import { MaterialCommunityIcons } from "@expo/vector-icons"
|
|
import { useRef, useState } from "react"
|
|
import { ScrollView, View } from "react-native"
|
|
import DrawerBottom from "./drawerBottom"
|
|
import Skeleton from "./skeleton"
|
|
import Text from "./Text"
|
|
|
|
type ApprovalRecord = {
|
|
id: string
|
|
status: number // 0=pending, 1=approved, 2=rejected
|
|
note?: string
|
|
submitter: { name: string }
|
|
approver?: { name: string }
|
|
createdAt: string
|
|
}
|
|
|
|
type Props = {
|
|
isVisible: boolean
|
|
setVisible: (value: boolean) => void
|
|
data: ApprovalRecord[]
|
|
loading: boolean
|
|
}
|
|
|
|
function ApprovalStatusBadge({ status }: { status: number }) {
|
|
const { colors } = useTheme()
|
|
const config =
|
|
status === 1
|
|
? { label: 'Disetujui', color: colors.success }
|
|
: status === 2
|
|
? { label: 'Ditolak', color: colors.error }
|
|
: { label: 'Menunggu', color: '#FFA94D' }
|
|
|
|
return (
|
|
<View style={{
|
|
backgroundColor: config.color + '20',
|
|
borderRadius: 20,
|
|
paddingHorizontal: 10,
|
|
paddingVertical: 3,
|
|
alignSelf: 'flex-start',
|
|
}}>
|
|
<Text style={[Styles.textSmallSemiBold, { color: config.color }]}>
|
|
{config.label}
|
|
</Text>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
export default function ModalRiwayatApproval({ isVisible, setVisible, data, loading }: Props) {
|
|
const { colors } = useTheme()
|
|
const arrSkeleton = Array.from({ length: 3 })
|
|
const scrollRef = useRef<ScrollView>(null)
|
|
const [scrollOffset, setScrollOffset] = useState(0)
|
|
|
|
return (
|
|
<DrawerBottom
|
|
isVisible={isVisible}
|
|
setVisible={setVisible}
|
|
title="Riwayat Persetujuan"
|
|
animation="slide"
|
|
height={60}
|
|
scrollOffset={scrollOffset}
|
|
scrollTo={(p) => scrollRef.current?.scrollTo(p)}
|
|
>
|
|
<ScrollView
|
|
ref={scrollRef}
|
|
showsVerticalScrollIndicator={false}
|
|
onScroll={({ nativeEvent }) => setScrollOffset(nativeEvent.contentOffset.y)}
|
|
scrollEventThrottle={16}
|
|
>
|
|
{loading ? (
|
|
arrSkeleton.map((_, i) => (
|
|
<View key={i} style={[Styles.mb10]}>
|
|
<Skeleton width={100} widthType="percent" height={80} borderRadius={10} />
|
|
</View>
|
|
))
|
|
) : data.length > 0 ? (
|
|
data.map((item, index) => (
|
|
<View
|
|
key={item.id}
|
|
style={{
|
|
borderWidth: 1,
|
|
borderColor: colors.icon + '30',
|
|
borderRadius: 10,
|
|
padding: 12,
|
|
marginBottom: 10,
|
|
}}
|
|
>
|
|
{/* Status + tanggal */}
|
|
<View style={[Styles.rowItemsCenter, { justifyContent: 'space-between', marginBottom: 8 }]}>
|
|
<ApprovalStatusBadge status={item.status} />
|
|
<Text style={[Styles.textSmallSemiBold, { color: colors.dimmed }]}>
|
|
{item.createdAt}
|
|
</Text>
|
|
</View>
|
|
|
|
{/* Pengaju */}
|
|
<View style={[Styles.rowItemsCenter, Styles.mb05]}>
|
|
<MaterialCommunityIcons name="account-arrow-up-outline" size={15} color={colors.dimmed} style={{ marginRight: 6 }} />
|
|
<Text style={[Styles.textMediumSemiBold, { color: colors.dimmed }]}>Diajukan Oleh: </Text>
|
|
<Text style={[Styles.textMediumNormal]}>{item.submitter.name}</Text>
|
|
</View>
|
|
|
|
{/* Approver */}
|
|
<View style={[Styles.rowItemsCenter, item.note ? Styles.mb05 : {}]}>
|
|
<MaterialCommunityIcons name="account-check-outline" size={15} color={colors.dimmed} style={{ marginRight: 6 }} />
|
|
<Text style={[Styles.textMediumSemiBold, { color: colors.dimmed }]}>Disetujui Oleh: </Text>
|
|
<Text style={[Styles.textMediumNormal]}>
|
|
{item.approver?.name ?? '-'}
|
|
</Text>
|
|
</View>
|
|
|
|
{/* Catatan penolakan */}
|
|
{item.note && (
|
|
<View style={{
|
|
backgroundColor: colors.error + '12',
|
|
borderRadius: 8,
|
|
padding: 8,
|
|
marginTop: 4,
|
|
}}>
|
|
<Text style={[Styles.textSmallSemiBold, { color: colors.error, marginBottom: 2 }]}>
|
|
Alasan Penolakan
|
|
</Text>
|
|
<Text style={[Styles.textMediumNormal, { color: colors.text }]}>
|
|
{item.note}
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
))
|
|
) : (
|
|
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }]}>
|
|
Belum ada riwayat persetujuan
|
|
</Text>
|
|
)}
|
|
</ScrollView>
|
|
</DrawerBottom>
|
|
)
|
|
}
|