Files
mobile-darmasaba/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx

334 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import AppHeader from "@/components/AppHeader"
import DrawerBottom from "@/components/drawerBottom"
import HeaderRightCalendarDetail from "@/components/calendar/headerCalendarDetail"
import ImageUser from "@/components/imageNew"
import MenuItemRow from "@/components/menuItemRow"
import ModalConfirmation from "@/components/ModalConfirmation"
import Text from "@/components/Text"
import { ConstEnv } from "@/constants/ConstEnv"
import Styles from "@/constants/Styles"
import { apiDeleteCalendarMember, apiGetCalendarOne, apiGetDivisionOneFeature } from "@/lib/api"
import { setUpdateCalendar } from "@/lib/calendarUpdate"
import { useAuthSession } from "@/providers/AuthProvider"
import { useTheme } from "@/providers/ThemeProvider"
import { MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"
import Clipboard from "@react-native-clipboard/clipboard"
import { router, Stack, useLocalSearchParams } from "expo-router"
import { useEffect, useState } from "react"
import { Pressable, RefreshControl, SafeAreaView, ScrollView, View } from "react-native"
import Toast from "react-native-toast-message"
import { useDispatch, useSelector } from "react-redux"
type Props = {
id: string;
timeStart: string;
timeEnd: string;
dateStart: string;
dateEnd: string;
idCalendar: string;
status: number;
title: string;
desc: string;
linkMeet: string;
repeatEventTyper: string;
repeatValue: number;
}
type PropsMember = {
id: string;
idUser: string;
name: string;
img: string;
email: string
}
export default function DetailEventCalendar() {
const { colors } = useTheme()
const { id, detail } = useLocalSearchParams<{ id: string, detail: string }>();
const [data, setData] = useState<Props>()
const [member, setMember] = useState<PropsMember[]>([])
const { token, decryptToken } = useAuthSession();
const [memberChoose, setMemberChoose] = useState({ id: '', name: '' })
const [isModalMember, setModalMember] = useState(false)
const update = useSelector((state: any) => state.calendarUpdate)
const dispatch = useDispatch()
const entityUser = useSelector((state: any) => state.user);
const [isMemberDivision, setIsMemberDivision] = useState(false);
const [showDeleteModal, setShowDeleteModal] = useState(false)
const [loading, setLoading] = useState(true)
const [refreshing, setRefreshing] = useState(false)
async function handleCheckMember() {
try {
const hasil = await decryptToken(String(token?.current));
const response = await apiGetDivisionOneFeature({
id,
user: hasil,
cat: "check-member",
});
setIsMemberDivision(response.data);
} catch (error) {
console.error(error);
}
}
async function handleLoad(loading: boolean) {
try {
setLoading(loading)
const hasil = await decryptToken(String(token?.current));
const response = await apiGetCalendarOne({
user: hasil,
id: detail,
cat: 'data',
});
if (response.success) {
setData(response.data);
} else {
router.replace(`/division/${id}/calendar/`)
}
} catch (error) {
console.error(error);
} finally {
setLoading(false)
}
}
async function handleLoadMember() {
try {
const hasil = await decryptToken(String(token?.current));
const response = await apiGetCalendarOne({
user: hasil,
id: detail,
cat: 'member',
});
setMember(response.data);
} catch (error) {
console.error(error);
}
}
useEffect(() => {
handleLoad(true);
handleCheckMember()
}, []);
useEffect(() => {
handleLoadMember();
}, [update.member]);
const handleCopy = (text: string) => {
Clipboard.setString(text);
Toast.show({ type: 'small', text1: 'Berhasil menyalin link', })
};
async function handleDeleteUser() {
try {
const hasil = await decryptToken(String(token?.current));
const response = await apiDeleteCalendarMember({
user: hasil,
idUser: memberChoose.id,
}, String(data?.idCalendar));
if (response.success) {
dispatch(setUpdateCalendar({ ...update, member: !update.member }));
}
Toast.show({ type: 'small', text1: response.message, })
} catch (error: any) {
console.error(error);
const message = error?.response?.data?.message || "Gagal menghapus anggota"
Toast.show({ type: 'small', text1: message })
} finally {
setModalMember(false)
}
}
const handleRefresh = async () => {
setRefreshing(true)
handleLoad(false)
handleLoadMember()
await new Promise(resolve => setTimeout(resolve, 2000));
setRefreshing(false)
};
const canManage = !((entityUser.role === "user" || entityUser.role === "coadmin") && !isMemberDivision)
const repeatLabel: Record<string, string> = {
once: 'Acara 1 Kali',
daily: 'Setiap Hari',
weekly: 'Mingguan',
monthly: 'Bulanan',
yearly: 'Tahunan',
}
function InfoRow({ icon, label, value, onCopy }: { icon: string, label: string, value?: string, onCopy?: () => void }) {
return (
<View style={[Styles.sectionActionRow, { paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: colors.icon + '14' }]}>
<View style={[Styles.sectionIconBox, { backgroundColor: colors.dimmed + '18' }]}>
<MaterialCommunityIcons name={icon as any} size={18} color={colors.dimmed} />
</View>
<View style={Styles.flex1}>
<Text style={[Styles.textSmallSemiBold, { color: colors.dimmed, marginBottom: 2 }]}>{label}</Text>
{loading
? <View style={{ height: 13, borderRadius: 6, backgroundColor: colors.icon + '20', width: '70%' }} />
: <Text style={[Styles.textDefault, { color: colors.text }]}>{value || '-'}</Text>
}
</View>
{onCopy && !loading && value && (
<Pressable onPress={onCopy} style={{ padding: 4 }}>
<MaterialCommunityIcons name="content-copy" size={16} color={colors.dimmed} />
</Pressable>
)}
</View>
)
}
return (
<SafeAreaView style={[Styles.flex1, { backgroundColor: colors.background }]}>
<Stack.Screen
options={{
header: () => (
<AppHeader
title="Detail Acara"
showBack={true}
onPressLeft={() => router.back()}
right={
(entityUser.role === "user" || entityUser.role === "coadmin") && !isMemberDivision
? <></> : <HeaderRightCalendarDetail id={String(data?.idCalendar)} idReminder={String(detail)} />
}
/>
)
}}
/>
<ScrollView
showsVerticalScrollIndicator={false}
style={Styles.h100}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} tintColor={colors.icon} />}
>
<View style={[Styles.p15, Styles.mb100]}>
{/* Info acara */}
<View style={[Styles.wrapPaper, Styles.sectionCard, Styles.noShadow, Styles.mb15,
{ backgroundColor: colors.card, borderColor: colors.icon + '18', padding: 0, overflow: 'hidden' }]}>
<View style={{ padding: 16, borderBottomWidth: 1, borderBottomColor: colors.icon + '14' }}>
<View style={Styles.sectionActionRow}>
<View style={[Styles.sectionIconBox, { backgroundColor: colors.dimmed + '18' }]}>
<MaterialCommunityIcons name="calendar-text" size={18} color={colors.dimmed} />
</View>
<Text style={[Styles.textDefaultSemiBold, Styles.flex1, { color: colors.text }]}>Detail Acara</Text>
</View>
</View>
<View style={{ paddingHorizontal: 16 }}>
<InfoRow icon="format-title" label="Judul" value={data?.title} />
<InfoRow icon="calendar-month-outline" label="Tanggal" value={data?.dateStart} />
<InfoRow icon="clock-outline" label="Waktu" value={data ? `${data.timeStart} ${data.timeEnd}` : undefined} />
<InfoRow icon="repeat" label="Pengulangan" value={data?.repeatEventTyper ? repeatLabel[data.repeatEventTyper] : undefined} />
<InfoRow icon="link-variant" label="Link Meet" value={data?.linkMeet} onCopy={data?.linkMeet ? () => handleCopy(data.linkMeet) : undefined} />
<View style={[Styles.sectionActionRow, { paddingVertical: 10, alignItems: 'flex-start' }]}>
<View style={[Styles.sectionIconBox, { backgroundColor: colors.dimmed + '18' }]}>
<MaterialCommunityIcons name="card-text-outline" size={18} color={colors.dimmed} />
</View>
<View style={Styles.flex1}>
<Text style={[Styles.textSmallSemiBold, { color: colors.dimmed, marginBottom: 2 }]}>Deskripsi</Text>
{loading
? <View style={{ height: 13, borderRadius: 6, backgroundColor: colors.icon + '20', width: '80%' }} />
: <Text style={[Styles.textDefault, { color: colors.text, lineHeight: 22 }]}>{data?.desc || '-'}</Text>
}
</View>
</View>
</View>
</View>
{/* Daftar anggota */}
<View style={[Styles.wrapPaper, Styles.sectionCard, Styles.noShadow,
{ backgroundColor: colors.card, borderColor: colors.icon + '18', padding: 0, overflow: 'hidden' }]}>
<View style={[Styles.sectionActionRow, { padding: 16, borderBottomWidth: 1, borderBottomColor: colors.icon + '14' }]}>
<View style={[Styles.sectionIconBox, { backgroundColor: colors.dimmed + '18' }]}>
<MaterialIcons name="people" size={18} color={colors.dimmed} />
</View>
<Text style={[Styles.textDefaultSemiBold, Styles.flex1, { color: colors.text }]}>Anggota</Text>
<Text style={[Styles.textMediumNormal, { color: colors.dimmed }]}>{member.length} anggota</Text>
</View>
{member.length === 0
? (
<View style={[Styles.contentItemCenter, { paddingVertical: 40 }]}>
<MaterialIcons name="people-outline" size={34} color={colors.icon + '50'} />
<Text style={[Styles.textMediumNormal, Styles.mt10, { color: colors.dimmed }]}>Belum ada anggota</Text>
</View>
)
: member.map((item, index) => (
<Pressable
key={index}
onPress={() => {
if (!canManage) return
setMemberChoose({ id: item.idUser, name: item.name })
setModalMember(true)
}}
style={({ pressed }) => [
Styles.rowItemsCenter, Styles.ph15,
{
paddingVertical: 12, gap: 14,
borderBottomWidth: index < member.length - 1 ? 1 : 0,
borderBottomColor: colors.icon + '14',
backgroundColor: pressed && canManage ? colors.icon + '0E' : 'transparent',
},
]}
>
<ImageUser src={`${ConstEnv.url_storage}/files/${item.img}`} size="xs" />
<View style={Styles.flex1}>
<Text style={[Styles.textDefault, { color: colors.text }]} numberOfLines={1}>{item.name}</Text>
<Text style={[Styles.textMediumNormal, { color: colors.dimmed }]} numberOfLines={1}>{item.email}</Text>
</View>
{canManage && <MaterialCommunityIcons name="chevron-right" size={18} color={colors.icon + '60'} />}
</Pressable>
))
}
</View>
</View>
</ScrollView>
<DrawerBottom animation="slide" isVisible={isModalMember} setVisible={setModalMember} title={memberChoose.name}>
<View style={Styles.rowItemsCenter}>
<MenuItemRow
icon={<MaterialCommunityIcons name="account-eye" color={colors.text} size={25} />}
title="Lihat Profil"
onPress={() => {
setModalMember(false)
router.push(`/member/${memberChoose.id}`)
}}
/>
<MenuItemRow
icon={<MaterialCommunityIcons name="account-remove" color={colors.text} size={25} />}
title="Keluarkan"
onPress={() => {
setModalMember(false)
setTimeout(() => {
setShowDeleteModal(true)
}, 600)
}}
/>
</View>
</DrawerBottom>
<ModalConfirmation
visible={showDeleteModal}
title="Konfirmasi"
message="Apakah Anda yakin ingin mengeluarkan anggota?"
onConfirm={() => {
setShowDeleteModal(false)
handleDeleteUser()
}}
onCancel={() => setShowDeleteModal(false)}
confirmText="Keluar"
cancelText="Batal"
/>
</SafeAreaView>
)
}