feat: filter approval berdasarkan group dan perbaikan tampilan riwayat
- 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
This commit is contained in:
@@ -26,6 +26,7 @@ type Props = {
|
||||
reason: string
|
||||
status: number
|
||||
isActive: boolean
|
||||
idGroup: string
|
||||
}
|
||||
|
||||
export default function DetailTaskDivision() {
|
||||
@@ -159,7 +160,7 @@ export default function DetailTaskDivision() {
|
||||
}
|
||||
<SectionProgress progress={progress} doneCount={taskStats?.done} totalCount={taskStats?.total} />
|
||||
<SectionReportTask refreshing={refreshing} />
|
||||
<SectionTanggalTugasTask refreshing={refreshing} isMemberDivision={isMemberDivision} isAdminDivision={isAdminDivision} status={data?.status} />
|
||||
<SectionTanggalTugasTask refreshing={refreshing} isMemberDivision={isMemberDivision} isAdminDivision={isAdminDivision} status={data?.status} idGroup={data?.idGroup ?? ''} />
|
||||
<SectionFileTask refreshing={refreshing} isMemberDivision={isMemberDivision} />
|
||||
<SectionLinkTask refreshing={refreshing} isMemberDivision={isMemberDivision} />
|
||||
<SectionMemberTask refreshing={refreshing} isAdminDivision={isAdminDivision} />
|
||||
|
||||
@@ -150,7 +150,7 @@ export default function DetailProject() {
|
||||
}
|
||||
<SectionProgress progress={progress} doneCount={taskStats?.done} totalCount={taskStats?.total} />
|
||||
<SectionReportProject refreshing={refreshing} />
|
||||
<SectionTanggalTugasProject status={data?.status} member={isMember} refreshing={refreshing} />
|
||||
<SectionTanggalTugasProject status={data?.status} member={isMember} refreshing={refreshing} idGroup={data?.idGroup ?? ''} />
|
||||
<SectionFile status={data?.status} member={isMember} refreshing={refreshing} />
|
||||
<SectionLink status={data?.status} member={isMember} refreshing={refreshing} />
|
||||
<SectionMember status={data?.status} refreshing={refreshing} />
|
||||
|
||||
@@ -33,13 +33,7 @@ function ApprovalStatusBadge({ status }: { status: number }) {
|
||||
: { label: 'Menunggu', color: '#FFA94D' }
|
||||
|
||||
return (
|
||||
<View style={{
|
||||
backgroundColor: config.color + '20',
|
||||
borderRadius: 20,
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 3,
|
||||
alignSelf: 'flex-start',
|
||||
}}>
|
||||
<View style={[Styles.approvalBadge, { backgroundColor: config.color + '20' }]}>
|
||||
<Text style={[Styles.textSmallSemiBold, { color: config.color }]}>
|
||||
{config.label}
|
||||
</Text>
|
||||
@@ -79,16 +73,10 @@ export default function ModalRiwayatApproval({ isVisible, setVisible, data, load
|
||||
data.map((item, index) => (
|
||||
<View
|
||||
key={item.id}
|
||||
style={{
|
||||
borderWidth: 1,
|
||||
borderColor: colors.icon + '30',
|
||||
borderRadius: 10,
|
||||
padding: 12,
|
||||
marginBottom: 10,
|
||||
}}
|
||||
style={[Styles.approvalItem, { borderColor: colors.icon + '30' }]}
|
||||
>
|
||||
{/* Status + tanggal */}
|
||||
<View style={[Styles.rowItemsCenter, { justifyContent: 'space-between', marginBottom: 8 }]}>
|
||||
<View style={[Styles.rowItemsCenter, Styles.approvalItemHeader]}>
|
||||
<ApprovalStatusBadge status={item.status} />
|
||||
<Text style={[Styles.textSmallSemiBold, { color: colors.dimmed }]}>
|
||||
{item.createdAt}
|
||||
@@ -97,15 +85,15 @@ export default function ModalRiwayatApproval({ isVisible, setVisible, data, load
|
||||
|
||||
{/* 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>
|
||||
<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.dimmed} style={{ marginRight: 6 }} />
|
||||
<Text style={[Styles.textMediumSemiBold, { color: colors.dimmed }]}>Disetujui Oleh: </Text>
|
||||
<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>
|
||||
@@ -113,16 +101,11 @@ export default function ModalRiwayatApproval({ isVisible, setVisible, data, load
|
||||
|
||||
{/* 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 }]}>
|
||||
<View style={[Styles.approvalNoteBox, { backgroundColor: colors.icon + '12' }]}>
|
||||
<Text style={[Styles.textSmallSemiBold, Styles.approvalNoteLabel, { color: colors.error }]}>
|
||||
Alasan Penolakan
|
||||
</Text>
|
||||
<Text style={[Styles.textMediumNormal, { color: colors.text }]}>
|
||||
<Text style={[Styles.textMediumNormal]}>
|
||||
{item.note}
|
||||
</Text>
|
||||
</View>
|
||||
@@ -130,7 +113,7 @@ export default function ModalRiwayatApproval({ isVisible, setVisible, data, load
|
||||
</View>
|
||||
))
|
||||
) : (
|
||||
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }]}>
|
||||
<Text style={[Styles.textDefault, Styles.approvalEmptyText, { color: colors.dimmed }]}>
|
||||
Belum ada riwayat persetujuan
|
||||
</Text>
|
||||
)}
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function CaraouselHome({ refreshing }: { refreshing: boolean }) {
|
||||
async function handleUser() {
|
||||
const hasil = await decryptToken(String(token?.current))
|
||||
const response = await apiGetProfile({ id: hasil })
|
||||
dispatch(setEntityUser({ role: response.data.idUserRole, admin: false, isApprover: response.data.isApprover ?? false }))
|
||||
dispatch(setEntityUser({ role: response.data.idUserRole, admin: false, isApprover: response.data.isApprover ?? false, idGroup: response.data.idGroup ?? '' }))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -59,7 +59,7 @@ export default function CaraouselHome2({ refreshing }: { refreshing: boolean })
|
||||
// Sync User Role to Redux
|
||||
useEffect(() => {
|
||||
if (profile) {
|
||||
dispatch(setEntityUser({ role: profile.idUserRole, admin: false, isApprover: profile.isApprover ?? false }))
|
||||
dispatch(setEntityUser({ role: profile.idUserRole, admin: false, isApprover: profile.isApprover ?? false, idGroup: profile.idGroup ?? '' }))
|
||||
}
|
||||
}, [profile, dispatch])
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ type ApprovalRecord = {
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export default function SectionTanggalTugasProject({ status, member, refreshing }: { status: number | undefined, member: boolean, refreshing?: boolean }) {
|
||||
export default function SectionTanggalTugasProject({ status, member, refreshing, idGroup }: { status: number | undefined, member: boolean, refreshing?: boolean, idGroup: string }) {
|
||||
const { colors } = useTheme();
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
const dispatch = useDispatch()
|
||||
@@ -61,7 +61,7 @@ export default function SectionTanggalTugasProject({ status, member, refreshing
|
||||
const [tugas, setTugas] = useState({ id: '', status: 0 })
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
|
||||
const isApprover = entityUser.isApprover || ['supadmin', 'developer'].includes(entityUser.role)
|
||||
const isApprover = (entityUser.isApprover && entityUser.idGroup === idGroup) || ['supadmin', 'developer'].includes(entityUser.role)
|
||||
const isAdmin = entityUser.role !== 'user' && entityUser.role !== 'coadmin'
|
||||
|
||||
async function handleLoad(loading: boolean) {
|
||||
|
||||
@@ -38,7 +38,7 @@ type ApprovalRecord = {
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export default function SectionTanggalTugasTask({ refreshing, isMemberDivision, isAdminDivision, status }: { refreshing: boolean, isMemberDivision: boolean, isAdminDivision: boolean, status?: number }) {
|
||||
export default function SectionTanggalTugasTask({ refreshing, isMemberDivision, isAdminDivision, status, idGroup }: { refreshing: boolean, isMemberDivision: boolean, isAdminDivision: boolean, status?: number, idGroup: string }) {
|
||||
const { colors } = useTheme()
|
||||
const dispatch = useDispatch()
|
||||
const entityUser = useSelector((state: any) => state.user);
|
||||
@@ -60,7 +60,7 @@ export default function SectionTanggalTugasTask({ refreshing, isMemberDivision,
|
||||
const [tugas, setTugas] = useState({ id: '', status: 0 })
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
|
||||
const isApprover = entityUser.isApprover || ['supadmin', 'developer'].includes(entityUser.role)
|
||||
const isApprover = (entityUser.isApprover && entityUser.idGroup === idGroup) || ['supadmin', 'developer'].includes(entityUser.role)
|
||||
const isAdmin = entityUser.role !== 'user' && entityUser.role !== 'coadmin'
|
||||
const canTakeAction = isMemberDivision || isAdmin
|
||||
|
||||
|
||||
13
constants/styles/approval.styles.ts
Normal file
13
constants/styles/approval.styles.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { StyleSheet } from "react-native";
|
||||
|
||||
const ApprovalStyles = StyleSheet.create({
|
||||
approvalBadge: { borderRadius: 20, paddingHorizontal: 10, paddingVertical: 3, alignSelf: 'flex-start' },
|
||||
approvalItem: { borderWidth: 1, borderRadius: 10, padding: 12, marginBottom: 10 },
|
||||
approvalItemHeader: { justifyContent: 'space-between', marginBottom: 8 },
|
||||
approvalIconMr: { marginRight: 6 },
|
||||
approvalNoteBox: { borderRadius: 8, padding: 8, marginTop: 4 },
|
||||
approvalNoteLabel: { marginBottom: 2 },
|
||||
approvalEmptyText: { textAlign: 'center' },
|
||||
});
|
||||
|
||||
export default ApprovalStyles;
|
||||
@@ -10,6 +10,7 @@ import ModalStyles from './modal.styles';
|
||||
import HeaderStyles from './header.styles';
|
||||
import ComponentStyles from './component.styles';
|
||||
import NotificationStyles from './notification.styles';
|
||||
import ApprovalStyles from './approval.styles';
|
||||
|
||||
const Styles = StyleSheet.create({
|
||||
...SpacingStyles,
|
||||
@@ -23,6 +24,7 @@ const Styles = StyleSheet.create({
|
||||
...HeaderStyles,
|
||||
...ComponentStyles,
|
||||
...NotificationStyles,
|
||||
...ApprovalStyles,
|
||||
});
|
||||
|
||||
export default Styles;
|
||||
|
||||
Reference in New Issue
Block a user