upd: diskusi divisi

Deskripsi:
- list diskusi
- search diskusi
- detail diskusi
- kirim komentar
- edit diskusi
- status diskusi
- arsip diskusi
- tambah diskusi
- role akses user diskusi

No Issues
This commit is contained in:
amel
2025-05-22 17:19:35 +08:00
parent 7eaa8cf95b
commit 3f67f65ae5
14 changed files with 643 additions and 317 deletions

View File

@@ -16,9 +16,10 @@ type Props = {
titleWeight?: 'normal' | 'bold'
bgColor?: 'white' | 'transparent'
width?: number
descEllipsize?: boolean
}
export default function BorderBottomItem({ title, subtitle, icon, desc, onPress, rightTopInfo, borderType, leftBottomInfo, rightBottomInfo, titleWeight, bgColor, width }: Props) {
export default function BorderBottomItem({ title, subtitle, icon, desc, onPress, rightTopInfo, borderType, leftBottomInfo, rightBottomInfo, titleWeight, bgColor, width, descEllipsize }: Props) {
const lebarDim = Dimensions.get("window").width;
const lebar = width ? lebarDim * width / 100 : 'auto';
@@ -44,7 +45,7 @@ export default function BorderBottomItem({ title, subtitle, icon, desc, onPress,
</View>
</View>
{desc && <Text style={[Styles.textDefault, Styles.mt05, { textAlign: 'justify' }]} numberOfLines={2} ellipsizeMode='tail'>{desc}</Text>}
{desc && <Text style={[Styles.textDefault, Styles.mt05, { textAlign: 'justify' }]} numberOfLines={descEllipsize == false ? 0 : 2} ellipsizeMode='tail'>{desc}</Text>}
{
(leftBottomInfo || rightBottomInfo) &&
(

View File

@@ -1,8 +1,12 @@
import Styles from "@/constants/Styles"
import { apiArchiveDiscussion, apiOpenCloseDiscussion } from "@/lib/api"
import { setUpdateDiscussion } from "@/lib/discussionUpdate"
import { useAuthSession } from "@/providers/AuthProvider"
import { MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"
import { router } from "expo-router"
import { useState } from "react"
import { ToastAndroid, View } from "react-native"
import { useDispatch, useSelector } from "react-redux"
import AlertKonfirmasi from "../alertKonfirmasi"
import ButtonMenuHeader from "../buttonMenuHeader"
import DrawerBottom from "../drawerBottom"
@@ -10,48 +14,94 @@ import MenuItemRow from "../menuItemRow"
type Props = {
id: string | string[]
status: number | undefined,
isActive: boolean | undefined
}
export default function HeaderRightDiscussionDetail({ id }: Props) {
export default function HeaderRightDiscussionDetail({ id, status, isActive }: Props) {
const [isVisible, setVisible] = useState(false)
const { token, decryptToken } = useAuthSession()
const update = useSelector((state: any) => state.discussionUpdate)
const dispatch = useDispatch()
const handleOpenClose = async () => {
try {
const hasil = await decryptToken(String(token?.current))
const response = await apiOpenCloseDiscussion({ status: Number(status), user: hasil }, String(id))
if (response.success) {
ToastAndroid.show('Berhasil mengubah data', ToastAndroid.SHORT)
dispatch(setUpdateDiscussion({ ...update, data: !update.data }))
setVisible(false)
} else {
ToastAndroid.show(response.message, ToastAndroid.SHORT)
}
} catch (error) {
console.error(error)
ToastAndroid.show('Terjadi kesalahan', ToastAndroid.SHORT)
} finally {
setVisible(false)
}
}
const handleArchive = async () => {
try {
const hasil = await decryptToken(String(token?.current))
const response = await apiArchiveDiscussion({ user: hasil, active: !isActive }, String(id))
if (response.success) {
ToastAndroid.show('Berhasil mengubah data', ToastAndroid.SHORT)
dispatch(setUpdateDiscussion({ ...update, data: !update.data }))
setVisible(false)
} else {
ToastAndroid.show(response.message, ToastAndroid.SHORT)
}
} catch (error) {
console.error(error)
ToastAndroid.show('Terjadi kesalahan', ToastAndroid.SHORT)
} finally {
setVisible(false)
}
}
return (
<>
<ButtonMenuHeader onPress={() => { setVisible(true) }} />
<DrawerBottom animation="slide" isVisible={isVisible} setVisible={setVisible} title="Menu">
<View style={Styles.rowItemsCenter}>
<MenuItemRow
icon={<MaterialCommunityIcons name="pencil-outline" color="black" size={25} />}
title="Edit"
onPress={() => {
setVisible(false)
router.push(`./${id}/edit`)
}}
/>
<MenuItemRow
icon={<MaterialIcons name="close" color="black" size={25} />}
title="Tutup Diskusi"
onPress={() => {
AlertKonfirmasi({
title: 'Konfirmasi',
desc: 'Apakah anda yakin ingin menutup diskusi?',
onPress: () => {
{
isActive &&
<>
<MenuItemRow
icon={<MaterialCommunityIcons name="pencil-outline" color="black" size={25} />}
title="Edit"
onPress={() => {
setVisible(false)
ToastAndroid.show('Berhasil mengubah data', ToastAndroid.SHORT)
}
})
}}
/>
router.push(`./${id}/edit`)
}}
/>
<MenuItemRow
icon={<MaterialIcons name={status == 1 ? 'close' : 'check'} color="black" size={25} />}
title={status == 1 ? 'Tutup Diskusi' : 'Buka Diskusi'}
onPress={() => {
AlertKonfirmasi({
title: 'Konfirmasi',
desc: `Apakah anda yakin ingin ${status == 1 ? 'menutup' : 'membuka'} diskusi?`,
onPress: () => {
handleOpenClose()
}
})
}}
/>
</>
}
<MenuItemRow
icon={<MaterialCommunityIcons name="archive-outline" color="black" size={25} />}
title="Arsipkan"
title={isActive ? 'Arsipkan' : 'Aktifkan Diskusi'}
onPress={() => {
AlertKonfirmasi({
title: 'Konfirmasi',
desc: 'Apakah anda yakin ingin mengarsipkan diskusi?',
desc: isActive ? 'Apakah anda yakin ingin mengarsipkan diskusi?' : 'Apakah anda yakin ingin mengaktifkan diskusi?',
onPress: () => {
setVisible(false)
ToastAndroid.show('Berhasil mengubah data', ToastAndroid.SHORT)
handleArchive()
}
})
}}

View File

@@ -1,18 +1,47 @@
import Styles from "@/constants/Styles"
import { apiGetDivisionOneFeature } from "@/lib/api"
import { useAuthSession } from "@/providers/AuthProvider"
import { AntDesign } from "@expo/vector-icons"
import { router } from "expo-router"
import { useState } from "react"
import { router, useLocalSearchParams } from "expo-router"
import { useEffect, useState } from "react"
import { View } from "react-native"
import { useSelector } from "react-redux"
import ButtonMenuHeader from "../buttonMenuHeader"
import DrawerBottom from "../drawerBottom"
import MenuItemRow from "../menuItemRow"
export default function HeaderRightDiscussionList() {
const [isVisible, setVisible] = useState(false)
const [isAdminDivision, setIsAdminDivision] = useState(false);
const { token, decryptToken } = useAuthSession()
const { id } = useLocalSearchParams<{ id: string }>();
const entityUser = useSelector((state: any) => state.user);
async function handleCheckAdmin() {
try {
const hasil = await decryptToken(String(token?.current));
const response = await apiGetDivisionOneFeature({
id,
user: hasil,
cat: "check-admin",
});
setIsAdminDivision(response.data);
} catch (error) {
console.error(error);
}
}
useEffect(() => {
handleCheckAdmin()
}, [])
return (
<>
<ButtonMenuHeader onPress={() => { setVisible(true) }} />
{
(entityUser.role == "user" || entityUser.role == "coadmin") && !isAdminDivision
? <></> :
<ButtonMenuHeader onPress={() => { setVisible(true) }} />
}
<DrawerBottom animation="slide" isVisible={isVisible} setVisible={setVisible} title="Menu">
<View style={Styles.rowItemsCenter}>
<MenuItemRow

View File

@@ -1,16 +1,17 @@
import Styles from "@/constants/Styles";
import { Ionicons, Feather } from "@expo/vector-icons";
import { Text, View } from "react-native";
import { Feather, Ionicons } from "@expo/vector-icons";
import { Pressable, Text, View } from "react-native";
type Props = {
title: string
user: string
date: string
onPress: () => void
}
export default function DiscussionItem({ title, user, date }: Props) {
export default function DiscussionItem({ title, user, date, onPress }: Props) {
return (
<View style={[Styles.wrapItemDiscussion]}>
<Pressable style={[Styles.wrapItemDiscussion]} onPress={onPress}>
<View style={[Styles.rowItemsCenter, Styles.mb10]}>
<Ionicons name="chatbox-ellipses-outline" size={22} color="black" style={Styles.mr10} />
<Text style={{ fontWeight: 'bold' }} numberOfLines={1} ellipsizeMode="tail">{title}</Text>
@@ -25,6 +26,6 @@ export default function DiscussionItem({ title, user, date }: Props) {
<Text style={[Styles.textInformation]}>{date}</Text>
</View>
</View>
</View>
</Pressable>
)
}

View File

@@ -1,50 +1,65 @@
import Styles from "@/constants/Styles";
import { apiGetDivisionOneFeature } from "@/lib/api";
import { useAuthSession } from "@/providers/AuthProvider";
import { useLocalSearchParams } from "expo-router";
import { router, useLocalSearchParams } from "expo-router";
import { useEffect, useState } from "react";
import { Text, View } from "react-native";
import DiscussionItem from "../discussionItem";
type Props = {
id: string
title: string
desc: string
user: string
date: string
}
id: string;
title: string;
desc: string;
user: string;
date: string;
};
export default function DiscussionDivisionDetail() {
const { token, decryptToken } = useAuthSession()
const { id } = useLocalSearchParams<{ id: string }>()
const [data, setData] = useState<Props[]>([])
const { token, decryptToken } = useAuthSession();
const { id } = useLocalSearchParams<{ id: string }>();
const [data, setData] = useState<Props[]>([]);
async function handleLoad() {
try {
const hasil = await decryptToken(String(token?.current))
const response = await apiGetDivisionOneFeature({ user: hasil, id, cat: 'new-discussion' })
setData(response.data)
} catch (error) {
console.error(error)
}
}
async function handleLoad() {
try {
const hasil = await decryptToken(String(token?.current));
const response = await apiGetDivisionOneFeature({
user: hasil,
id,
cat: "new-discussion",
});
setData(response.data);
} catch (error) {
console.error(error);
}
}
useEffect(() => {
handleLoad()
}, [])
return (
<View style={[Styles.mb15]}>
<Text style={[Styles.textDefaultSemiBold, Styles.mv10]}>Diskusi</Text>
<View style={[Styles.wrapPaper]}>
{
data.length > 0 ?
data.map((item, index) => (
<DiscussionItem key={index} title={item.desc} user={item.user} date={item.date} />
))
:
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada diskusi</Text>
}
</View>
useEffect(() => {
handleLoad();
}, []);
return (
<View style={[Styles.mb15]}>
<Text style={[Styles.textDefaultSemiBold, Styles.mv10]}>Diskusi</Text>
<View style={[Styles.wrapPaper]}>
{data.length > 0 ? (
data.map((item, index) => (
<DiscussionItem
key={index}
title={item.desc}
user={item.user}
date={item.date}
onPress={() => {
router.push(`/division/${id}/discussion/${item.id}`);
}}
/>
))
) : (
<Text
style={[Styles.textDefault, Styles.cGray, { textAlign: "center" }]}
>
Tidak ada diskusi
</Text>
)}
</View>
)
}
</View>
);
}

View File

@@ -4,13 +4,13 @@ import { Text, View } from "react-native";
type Props = {
category: 'error' | 'success' | 'warning' | 'primary'
category: 'error' | 'success' | 'warning' | 'primary' | 'secondary'
text: string
size: 'small' | 'default'
}
export default function LabelStatus({ category, text, size }: Props) {
return (
<View style={[size == "small" ? Styles.labelStatusSmall : Styles.labelStatus, ColorsStatus[category]]}>
<View style={[size == "small" ? Styles.labelStatusSmall : Styles.labelStatus, ColorsStatus[category], Styles.round10]}>
<Text style={[size == "small" ? Styles.textSmallSemiBold : Styles.textMediumSemiBold, Styles.cWhite, { textAlign: 'center' }]}>{text}</Text>
</View>
)