- Simpan idGroup user ke Redux saat login agar perbandingan group bisa dilakukan - Filter button persetujuan project: isApprover hanya tampil jika group sama - Filter button persetujuan division/task: isApprover hanya tampil jika group sama - Pass idGroup ke SectionTanggalTugasProject dan SectionTanggalTugasTask dari parent - Samakan warna icon, label, dan nama pada riwayat persetujuan - Ubah bg alasan penolakan dari merah ke netral, label tetap merah - Ekstrak inline styles ModalRiwayatApproval ke approval.styles.ts
124 lines
4.7 KiB
TypeScript
124 lines
4.7 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={[Styles.approvalBadge, { backgroundColor: config.color + '20' }]}>
|
|
<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={[Styles.approvalItem, { borderColor: colors.icon + '30' }]}
|
|
>
|
|
{/* Status + tanggal */}
|
|
<View style={[Styles.rowItemsCenter, Styles.approvalItemHeader]}>
|
|
<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.text} style={Styles.approvalIconMr} />
|
|
<Text style={[Styles.textMediumSemiBold]}>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.text} style={Styles.approvalIconMr} />
|
|
<Text style={[Styles.textMediumSemiBold]}>Disetujui Oleh: </Text>
|
|
<Text style={[Styles.textMediumNormal]}>
|
|
{item.approver?.name ?? '-'}
|
|
</Text>
|
|
</View>
|
|
|
|
{/* Catatan penolakan */}
|
|
{item.note && (
|
|
<View style={[Styles.approvalNoteBox, { backgroundColor: colors.icon + '12' }]}>
|
|
<Text style={[Styles.textSmallSemiBold, Styles.approvalNoteLabel, { color: colors.error }]}>
|
|
Alasan Penolakan
|
|
</Text>
|
|
<Text style={[Styles.textMediumNormal]}>
|
|
{item.note}
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
))
|
|
) : (
|
|
<Text style={[Styles.textDefault, Styles.approvalEmptyText, { color: colors.dimmed }]}>
|
|
Belum ada riwayat persetujuan
|
|
</Text>
|
|
)}
|
|
</ScrollView>
|
|
</DrawerBottom>
|
|
)
|
|
}
|