Merge pull request 'amalia/18-mei-26' (#49) from amalia/18-mei-26 into join
Reviewed-on: #49
This commit is contained in:
@@ -26,6 +26,7 @@ type Props = {
|
|||||||
reason: string
|
reason: string
|
||||||
status: number
|
status: number
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
|
idGroup: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DetailTaskDivision() {
|
export default function DetailTaskDivision() {
|
||||||
@@ -159,7 +160,7 @@ export default function DetailTaskDivision() {
|
|||||||
}
|
}
|
||||||
<SectionProgress progress={progress} doneCount={taskStats?.done} totalCount={taskStats?.total} />
|
<SectionProgress progress={progress} doneCount={taskStats?.done} totalCount={taskStats?.total} />
|
||||||
<SectionReportTask refreshing={refreshing} />
|
<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} />
|
<SectionFileTask refreshing={refreshing} isMemberDivision={isMemberDivision} />
|
||||||
<SectionLinkTask refreshing={refreshing} isMemberDivision={isMemberDivision} />
|
<SectionLinkTask refreshing={refreshing} isMemberDivision={isMemberDivision} />
|
||||||
<SectionMemberTask refreshing={refreshing} isAdminDivision={isAdminDivision} />
|
<SectionMemberTask refreshing={refreshing} isAdminDivision={isAdminDivision} />
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ export default function TugasFileScreen() {
|
|||||||
disabled={loadingUpload}
|
disabled={loadingUpload}
|
||||||
/>
|
/>
|
||||||
<ButtonSelect
|
<ButtonSelect
|
||||||
value="Pilih dari File Proyek"
|
value="Pilih dari File Kegiatan ini"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setSelectedProjectFiles([]);
|
setSelectedProjectFiles([]);
|
||||||
setPickerModal(true);
|
setPickerModal(true);
|
||||||
|
|||||||
@@ -9,12 +9,11 @@ import { pushToPage } from "@/lib/pushToPage";
|
|||||||
import { useAuthSession } from "@/providers/AuthProvider";
|
import { useAuthSession } from "@/providers/AuthProvider";
|
||||||
import { useTheme } from "@/providers/ThemeProvider";
|
import { useTheme } from "@/providers/ThemeProvider";
|
||||||
import { Feather } from "@expo/vector-icons";
|
import { Feather } from "@expo/vector-icons";
|
||||||
import { router, Stack } from "expo-router";
|
|
||||||
import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query";
|
import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { useEffect, useMemo } from "react";
|
import { router, Stack } from "expo-router";
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { FlatList, Pressable, RefreshControl, SafeAreaView, View } from "react-native";
|
import { FlatList, Pressable, RefreshControl, SafeAreaView, View } from "react-native";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
id: string
|
id: string
|
||||||
@@ -79,6 +78,15 @@ export default function Notification() {
|
|||||||
}, [data])
|
}, [data])
|
||||||
|
|
||||||
const listData = useMemo<ListRow[]>(() => {
|
const listData = useMemo<ListRow[]>(() => {
|
||||||
|
const BULAN: Record<string, number> = {
|
||||||
|
'JAN': 0, 'FEB': 1, 'MAR': 2, 'APR': 3, 'MEI': 4, 'JUN': 5,
|
||||||
|
'JUL': 6, 'AGU': 7, 'SEP': 8, 'OKT': 9, 'NOV': 10, 'DES': 11,
|
||||||
|
}
|
||||||
|
const parseDate = (str: string) => {
|
||||||
|
const [d, m, y] = str.split(' ')
|
||||||
|
return new Date(Number(y), BULAN[m] ?? 0, Number(d)).getTime()
|
||||||
|
}
|
||||||
|
|
||||||
const groups: Record<string, Props[]> = {}
|
const groups: Record<string, Props[]> = {}
|
||||||
const dateOrder: string[] = []
|
const dateOrder: string[] = []
|
||||||
|
|
||||||
@@ -90,6 +98,8 @@ export default function Notification() {
|
|||||||
groups[item.createdAt].push(item)
|
groups[item.createdAt].push(item)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
dateOrder.sort((a, b) => parseDate(b) - parseDate(a))
|
||||||
|
|
||||||
const result: ListRow[] = []
|
const result: ListRow[] = []
|
||||||
dateOrder.forEach((date) => {
|
dateOrder.forEach((date) => {
|
||||||
result.push({ _type: 'header', date })
|
result.push({ _type: 'header', date })
|
||||||
@@ -137,6 +147,17 @@ export default function Notification() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleMarkOneRead(id: string) {
|
||||||
|
try {
|
||||||
|
const hasil = await decryptToken(String(token?.current))
|
||||||
|
await apiReadOneNotification({ user: hasil, id: id })
|
||||||
|
await queryClient.invalidateQueries({ queryKey: ['notifications'] })
|
||||||
|
dispatch(setUpdateNotification(!updateNotification))
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={[Styles.flex1, { backgroundColor: colors.background }]}>
|
<SafeAreaView style={[Styles.flex1, { backgroundColor: colors.background }]}>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@@ -153,7 +174,7 @@ export default function Notification() {
|
|||||||
disabled={markingAll}
|
disabled={markingAll}
|
||||||
style={{ opacity: markingAll ? 0.5 : 1, padding: 4 }}
|
style={{ opacity: markingAll ? 0.5 : 1, padding: 4 }}
|
||||||
>
|
>
|
||||||
<Feather name="check-square" size={22} color="white" />
|
<Feather name="check-square" size={20} color="white" />
|
||||||
</Pressable>
|
</Pressable>
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
@@ -175,7 +196,8 @@ export default function Notification() {
|
|||||||
onCancel={() => setShowConfirm(false)}
|
onCancel={() => setShowConfirm(false)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View style={[Styles.flex1, Styles.ph15, { paddingTop: 10 }]}>
|
|
||||||
|
<View style={[Styles.flex1, Styles.ph15, Styles.notifContainer]}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
[0, 1, 2, 3, 4].map((_, i) => <SkeletonTwoItem key={i} />)
|
[0, 1, 2, 3, 4].map((_, i) => <SkeletonTwoItem key={i} />)
|
||||||
) : flatData.length === 0 ? (
|
) : flatData.length === 0 ? (
|
||||||
@@ -204,11 +226,11 @@ export default function Notification() {
|
|||||||
renderItem={({ item }) => {
|
renderItem={({ item }) => {
|
||||||
if (item._type === 'header') {
|
if (item._type === 'header') {
|
||||||
return (
|
return (
|
||||||
<View style={[Styles.rowItemsCenter, { marginTop: 16, marginBottom: 8 }]}>
|
<View style={[Styles.rowItemsCenter, Styles.notifHeaderRow]}>
|
||||||
<Text style={{ fontSize: 11, fontWeight: '600', color: colors.dimmed, letterSpacing: 0.6, textTransform: 'uppercase' }}>
|
<Text style={[Styles.notifDateText, { color: colors.dimmed }]}>
|
||||||
{item.date}
|
{item.date}
|
||||||
</Text>
|
</Text>
|
||||||
<View style={{ flex: 1, height: 1, backgroundColor: colors.icon + '20', marginLeft: 8 }} />
|
<View style={[Styles.notifDateSeparator, { backgroundColor: colors.icon + '20' }]} />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -218,37 +240,20 @@ export default function Notification() {
|
|||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={() => handleReadNotification(item.id, item.category, item.idContent)}
|
onPress={() => handleReadNotification(item.id, item.category, item.idContent)}
|
||||||
style={({ pressed }) => [{
|
style={({ pressed }) => [Styles.notifItemRow, {
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
borderRadius: 10,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: colors.icon + '20',
|
borderColor: colors.icon + '20',
|
||||||
backgroundColor: pressed
|
backgroundColor: pressed
|
||||||
? colors.icon + '10'
|
? colors.icon + '10'
|
||||||
: item.isRead
|
: item.isRead
|
||||||
? colors.icon + '10'
|
? colors.icon + '10'
|
||||||
: colors.card,
|
: colors.card,
|
||||||
paddingHorizontal: 12,
|
|
||||||
paddingVertical: 10,
|
|
||||||
marginBottom: 6,
|
|
||||||
}]}
|
}]}
|
||||||
>
|
>
|
||||||
{/* Colored icon */}
|
<View style={[Styles.notifIconContainer, { backgroundColor: color + '20' }]}>
|
||||||
<View style={{
|
|
||||||
width: 42,
|
|
||||||
height: 42,
|
|
||||||
borderRadius: 21,
|
|
||||||
backgroundColor: color + '20',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
flexShrink: 0,
|
|
||||||
}}>
|
|
||||||
<Feather name={icon} size={20} color={color} />
|
<Feather name={icon} size={20} color={color} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Content */}
|
<View style={[Styles.flex1, Styles.notifContent]}>
|
||||||
<View style={[Styles.flex1, { marginLeft: 10 }]}>
|
|
||||||
<View style={[Styles.rowSpaceBetween, Styles.itemsCenter]}>
|
<View style={[Styles.rowSpaceBetween, Styles.itemsCenter]}>
|
||||||
<View style={[Styles.flex1, Styles.mr10]}>
|
<View style={[Styles.flex1, Styles.mr10]}>
|
||||||
<Text
|
<Text
|
||||||
@@ -258,6 +263,20 @@ export default function Notification() {
|
|||||||
{item.title}
|
{item.title}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
{!item.isRead && (
|
||||||
|
<Pressable
|
||||||
|
onPress={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
handleMarkOneRead(item.id)
|
||||||
|
}}
|
||||||
|
hitSlop={8}
|
||||||
|
style={({ pressed }) => ({ opacity: pressed ? 0.5 : 1, flexShrink: 0 })}
|
||||||
|
>
|
||||||
|
<Text style={Styles.notifMarkReadText}>
|
||||||
|
Tandai dibaca
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
<Text
|
<Text
|
||||||
style={[Styles.textMediumNormal, { color: item.isRead ? colors.dimmed : colors.text, opacity: item.isRead ? 0.7 : 1 }]}
|
style={[Styles.textMediumNormal, { color: item.isRead ? colors.dimmed : colors.text, opacity: item.isRead ? 0.7 : 1 }]}
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ export default function DetailProject() {
|
|||||||
}
|
}
|
||||||
<SectionProgress progress={progress} doneCount={taskStats?.done} totalCount={taskStats?.total} />
|
<SectionProgress progress={progress} doneCount={taskStats?.done} totalCount={taskStats?.total} />
|
||||||
<SectionReportProject refreshing={refreshing} />
|
<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} />
|
<SectionFile status={data?.status} member={isMember} refreshing={refreshing} />
|
||||||
<SectionLink status={data?.status} member={isMember} refreshing={refreshing} />
|
<SectionLink status={data?.status} member={isMember} refreshing={refreshing} />
|
||||||
<SectionMember status={data?.status} refreshing={refreshing} />
|
<SectionMember status={data?.status} refreshing={refreshing} />
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ export default function ProjectTugasFileScreen() {
|
|||||||
disabled={loadingUpload}
|
disabled={loadingUpload}
|
||||||
/>
|
/>
|
||||||
<ButtonSelect
|
<ButtonSelect
|
||||||
value="Pilih dari File Proyek"
|
value="Pilih dari File Kegiatan ini"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setSelectedProjectFiles([]);
|
setSelectedProjectFiles([]);
|
||||||
setPickerModal(true);
|
setPickerModal(true);
|
||||||
|
|||||||
@@ -33,13 +33,7 @@ function ApprovalStatusBadge({ status }: { status: number }) {
|
|||||||
: { label: 'Menunggu', color: '#FFA94D' }
|
: { label: 'Menunggu', color: '#FFA94D' }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{
|
<View style={[Styles.approvalBadge, { backgroundColor: config.color + '20' }]}>
|
||||||
backgroundColor: config.color + '20',
|
|
||||||
borderRadius: 20,
|
|
||||||
paddingHorizontal: 10,
|
|
||||||
paddingVertical: 3,
|
|
||||||
alignSelf: 'flex-start',
|
|
||||||
}}>
|
|
||||||
<Text style={[Styles.textSmallSemiBold, { color: config.color }]}>
|
<Text style={[Styles.textSmallSemiBold, { color: config.color }]}>
|
||||||
{config.label}
|
{config.label}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -79,16 +73,10 @@ export default function ModalRiwayatApproval({ isVisible, setVisible, data, load
|
|||||||
data.map((item, index) => (
|
data.map((item, index) => (
|
||||||
<View
|
<View
|
||||||
key={item.id}
|
key={item.id}
|
||||||
style={{
|
style={[Styles.approvalItem, { borderColor: colors.icon + '30' }]}
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: colors.icon + '30',
|
|
||||||
borderRadius: 10,
|
|
||||||
padding: 12,
|
|
||||||
marginBottom: 10,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{/* Status + tanggal */}
|
{/* Status + tanggal */}
|
||||||
<View style={[Styles.rowItemsCenter, { justifyContent: 'space-between', marginBottom: 8 }]}>
|
<View style={[Styles.rowItemsCenter, Styles.approvalItemHeader]}>
|
||||||
<ApprovalStatusBadge status={item.status} />
|
<ApprovalStatusBadge status={item.status} />
|
||||||
<Text style={[Styles.textSmallSemiBold, { color: colors.dimmed }]}>
|
<Text style={[Styles.textSmallSemiBold, { color: colors.dimmed }]}>
|
||||||
{item.createdAt}
|
{item.createdAt}
|
||||||
@@ -97,15 +85,15 @@ export default function ModalRiwayatApproval({ isVisible, setVisible, data, load
|
|||||||
|
|
||||||
{/* Pengaju */}
|
{/* Pengaju */}
|
||||||
<View style={[Styles.rowItemsCenter, Styles.mb05]}>
|
<View style={[Styles.rowItemsCenter, Styles.mb05]}>
|
||||||
<MaterialCommunityIcons name="account-arrow-up-outline" size={15} color={colors.dimmed} style={{ marginRight: 6 }} />
|
<MaterialCommunityIcons name="account-arrow-up-outline" size={15} color={colors.text} style={Styles.approvalIconMr} />
|
||||||
<Text style={[Styles.textMediumSemiBold, { color: colors.dimmed }]}>Diajukan Oleh: </Text>
|
<Text style={[Styles.textMediumSemiBold]}>Diajukan Oleh: </Text>
|
||||||
<Text style={[Styles.textMediumNormal]}>{item.submitter.name}</Text>
|
<Text style={[Styles.textMediumNormal]}>{item.submitter.name}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Approver */}
|
{/* Approver */}
|
||||||
<View style={[Styles.rowItemsCenter, item.note ? Styles.mb05 : {}]}>
|
<View style={[Styles.rowItemsCenter, item.note ? Styles.mb05 : {}]}>
|
||||||
<MaterialCommunityIcons name="account-check-outline" size={15} color={colors.dimmed} style={{ marginRight: 6 }} />
|
<MaterialCommunityIcons name="account-check-outline" size={15} color={colors.text} style={Styles.approvalIconMr} />
|
||||||
<Text style={[Styles.textMediumSemiBold, { color: colors.dimmed }]}>Disetujui Oleh: </Text>
|
<Text style={[Styles.textMediumSemiBold]}>Disetujui Oleh: </Text>
|
||||||
<Text style={[Styles.textMediumNormal]}>
|
<Text style={[Styles.textMediumNormal]}>
|
||||||
{item.approver?.name ?? '-'}
|
{item.approver?.name ?? '-'}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -113,16 +101,11 @@ export default function ModalRiwayatApproval({ isVisible, setVisible, data, load
|
|||||||
|
|
||||||
{/* Catatan penolakan */}
|
{/* Catatan penolakan */}
|
||||||
{item.note && (
|
{item.note && (
|
||||||
<View style={{
|
<View style={[Styles.approvalNoteBox, { backgroundColor: colors.icon + '12' }]}>
|
||||||
backgroundColor: colors.error + '12',
|
<Text style={[Styles.textSmallSemiBold, Styles.approvalNoteLabel, { color: colors.error }]}>
|
||||||
borderRadius: 8,
|
|
||||||
padding: 8,
|
|
||||||
marginTop: 4,
|
|
||||||
}}>
|
|
||||||
<Text style={[Styles.textSmallSemiBold, { color: colors.error, marginBottom: 2 }]}>
|
|
||||||
Alasan Penolakan
|
Alasan Penolakan
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[Styles.textMediumNormal, { color: colors.text }]}>
|
<Text style={[Styles.textMediumNormal]}>
|
||||||
{item.note}
|
{item.note}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -130,7 +113,7 @@ export default function ModalRiwayatApproval({ isVisible, setVisible, data, load
|
|||||||
</View>
|
</View>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }]}>
|
<Text style={[Styles.textDefault, Styles.approvalEmptyText, { color: colors.dimmed }]}>
|
||||||
Belum ada riwayat persetujuan
|
Belum ada riwayat persetujuan
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export default function CaraouselHome({ refreshing }: { refreshing: boolean }) {
|
|||||||
async function handleUser() {
|
async function handleUser() {
|
||||||
const hasil = await decryptToken(String(token?.current))
|
const hasil = await decryptToken(String(token?.current))
|
||||||
const response = await apiGetProfile({ id: hasil })
|
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(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export default function CaraouselHome2({ refreshing }: { refreshing: boolean })
|
|||||||
// Sync User Role to Redux
|
// Sync User Role to Redux
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (profile) {
|
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])
|
}, [profile, dispatch])
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ type ApprovalRecord = {
|
|||||||
createdAt: string
|
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 { colors } = useTheme();
|
||||||
const entityUser = useSelector((state: any) => state.user)
|
const entityUser = useSelector((state: any) => state.user)
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
@@ -61,7 +61,7 @@ export default function SectionTanggalTugasProject({ status, member, refreshing
|
|||||||
const [tugas, setTugas] = useState({ id: '', status: 0 })
|
const [tugas, setTugas] = useState({ id: '', status: 0 })
|
||||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
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 isAdmin = entityUser.role !== 'user' && entityUser.role !== 'coadmin'
|
||||||
|
|
||||||
async function handleLoad(loading: boolean) {
|
async function handleLoad(loading: boolean) {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ type ApprovalRecord = {
|
|||||||
createdAt: string
|
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 { colors } = useTheme()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const entityUser = useSelector((state: any) => state.user);
|
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 [tugas, setTugas] = useState({ id: '', status: 0 })
|
||||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
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 isAdmin = entityUser.role !== 'user' && entityUser.role !== 'coadmin'
|
||||||
const canTakeAction = isMemberDivision || isAdmin
|
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;
|
||||||
@@ -9,6 +9,8 @@ import CardStyles from './card.styles';
|
|||||||
import ModalStyles from './modal.styles';
|
import ModalStyles from './modal.styles';
|
||||||
import HeaderStyles from './header.styles';
|
import HeaderStyles from './header.styles';
|
||||||
import ComponentStyles from './component.styles';
|
import ComponentStyles from './component.styles';
|
||||||
|
import NotificationStyles from './notification.styles';
|
||||||
|
import ApprovalStyles from './approval.styles';
|
||||||
|
|
||||||
const Styles = StyleSheet.create({
|
const Styles = StyleSheet.create({
|
||||||
...SpacingStyles,
|
...SpacingStyles,
|
||||||
@@ -21,6 +23,8 @@ const Styles = StyleSheet.create({
|
|||||||
...ModalStyles,
|
...ModalStyles,
|
||||||
...HeaderStyles,
|
...HeaderStyles,
|
||||||
...ComponentStyles,
|
...ComponentStyles,
|
||||||
|
...NotificationStyles,
|
||||||
|
...ApprovalStyles,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Styles;
|
export default Styles;
|
||||||
|
|||||||
29
constants/styles/notification.styles.ts
Normal file
29
constants/styles/notification.styles.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { StyleSheet } from "react-native";
|
||||||
|
|
||||||
|
const NotificationStyles = StyleSheet.create({
|
||||||
|
notifContainer: { paddingTop: 10 },
|
||||||
|
notifHeaderRow: { marginTop: 16, marginBottom: 8 },
|
||||||
|
notifDateSeparator: { flex: 1, height: 1, marginLeft: 8 },
|
||||||
|
notifDateText: { fontSize: 11, fontWeight: '600', letterSpacing: 0.6, textTransform: 'uppercase' },
|
||||||
|
notifItemRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderRadius: 10,
|
||||||
|
borderWidth: 1,
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
paddingVertical: 10,
|
||||||
|
marginBottom: 6,
|
||||||
|
},
|
||||||
|
notifIconContainer: {
|
||||||
|
width: 42,
|
||||||
|
height: 42,
|
||||||
|
borderRadius: 21,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
flexShrink: 0,
|
||||||
|
},
|
||||||
|
notifContent: { marginLeft: 10 },
|
||||||
|
notifMarkReadText: { fontSize: 11, color: '#3B82F6', fontWeight: '600' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default NotificationStyles;
|
||||||
Reference in New Issue
Block a user