Merge pull request 'amalia/17-apr-26' (#37) from amalia/17-apr-26 into join
Reviewed-on: #37
This commit is contained in:
@@ -3,6 +3,7 @@ import AppHeader from "@/components/AppHeader";
|
|||||||
import BorderBottomItem from "@/components/borderBottomItem";
|
import BorderBottomItem from "@/components/borderBottomItem";
|
||||||
import Skeleton from "@/components/skeleton";
|
import Skeleton from "@/components/skeleton";
|
||||||
import Text from '@/components/Text';
|
import Text from '@/components/Text';
|
||||||
|
import ErrorView from "@/components/ErrorView";
|
||||||
import { ConstEnv } from "@/constants/ConstEnv";
|
import { ConstEnv } from "@/constants/ConstEnv";
|
||||||
import { isImageFile } from "@/constants/FileExtensions";
|
import { isImageFile } from "@/constants/FileExtensions";
|
||||||
import Styles from "@/constants/Styles";
|
import Styles from "@/constants/Styles";
|
||||||
@@ -65,6 +66,7 @@ export default function DetailAnnouncement() {
|
|||||||
const [loadingOpen, setLoadingOpen] = useState(false)
|
const [loadingOpen, setLoadingOpen] = useState(false)
|
||||||
const [preview, setPreview] = useState(false)
|
const [preview, setPreview] = useState(false)
|
||||||
const [chooseFile, setChooseFile] = useState<FileData>()
|
const [chooseFile, setChooseFile] = useState<FileData>()
|
||||||
|
const [isError, setIsError] = useState(false)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the image preview modal for the selected image file
|
* Opens the image preview modal for the selected image file
|
||||||
@@ -79,6 +81,7 @@ export default function DetailAnnouncement() {
|
|||||||
|
|
||||||
async function handleLoad(loading: boolean) {
|
async function handleLoad(loading: boolean) {
|
||||||
try {
|
try {
|
||||||
|
setIsError(false)
|
||||||
setLoading(loading)
|
setLoading(loading)
|
||||||
const hasil = await decryptToken(String(token?.current))
|
const hasil = await decryptToken(String(token?.current))
|
||||||
const response: ApiResponse = await apiGetAnnouncementOne({ id: id, user: hasil })
|
const response: ApiResponse = await apiGetAnnouncementOne({ id: id, user: hasil })
|
||||||
@@ -87,10 +90,12 @@ export default function DetailAnnouncement() {
|
|||||||
setDataMember(response.member)
|
setDataMember(response.member)
|
||||||
setDataFile(response.file)
|
setDataFile(response.file)
|
||||||
} else {
|
} else {
|
||||||
|
setIsError(true)
|
||||||
Toast.show({ type: 'small', text1: response.message })
|
Toast.show({ type: 'small', text1: response.message })
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
setIsError(true)
|
||||||
const message = error?.response?.data?.message || "Gagal mengambil data"
|
const message = error?.response?.data?.message || "Gagal mengambil data"
|
||||||
|
|
||||||
Toast.show({ type: 'small', text1: message })
|
Toast.show({ type: 'small', text1: message })
|
||||||
@@ -206,6 +211,11 @@ export default function DetailAnnouncement() {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
{isError && !loading ? (
|
||||||
|
<View style={[Styles.mv50]}>
|
||||||
|
<ErrorView />
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
<View style={[Styles.p15, Styles.mb50]}>
|
<View style={[Styles.p15, Styles.mb50]}>
|
||||||
<View style={[Styles.wrapPaper, Styles.borderAll, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
<View style={[Styles.wrapPaper, Styles.borderAll, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||||
{
|
{
|
||||||
@@ -304,6 +314,7 @@ export default function DetailAnnouncement() {
|
|||||||
}
|
}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
)}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<ImageViewing
|
<ImageViewing
|
||||||
|
|||||||
42
components/ErrorView.tsx
Normal file
42
components/ErrorView.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import Text from '@/components/Text';
|
||||||
|
import Styles from '@/constants/Styles';
|
||||||
|
import { useTheme } from '@/providers/ThemeProvider';
|
||||||
|
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||||
|
import React from 'react';
|
||||||
|
import { View } from 'react-native';
|
||||||
|
|
||||||
|
interface ErrorViewProps {
|
||||||
|
title?: string;
|
||||||
|
message?: string;
|
||||||
|
icon?: keyof typeof MaterialCommunityIcons.glyphMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ErrorView component to display error or empty states.
|
||||||
|
* Used when data is not found, deleted, or an error occurs during fetching.
|
||||||
|
*/
|
||||||
|
export default function ErrorView({
|
||||||
|
title = "Terjadi Kesalahan",
|
||||||
|
message = "Data tidak ditemukan atau sudah dihapus.",
|
||||||
|
icon = "alert-circle-outline"
|
||||||
|
}: ErrorViewProps) {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[Styles.flex1, Styles.contentItemCenter, Styles.ph20]}>
|
||||||
|
<View style={[Styles.mb10]}>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name={icon}
|
||||||
|
size={40}
|
||||||
|
color={colors.dimmed}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<Text style={[Styles.textDefaultSemiBold, { color: colors.text, textAlign: 'center' }]}>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
<Text style={[Styles.textMediumNormal, { color: colors.dimmed, textAlign: 'center', marginTop: 4 }]}>
|
||||||
|
{message}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,10 +7,6 @@ import Text from "../Text";
|
|||||||
export default function ReportChartDocument({ data }: { data: { label: string; value: number; }[] }) {
|
export default function ReportChartDocument({ data }: { data: { label: string; value: number; }[] }) {
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
const maxValue = Math.max(...data.map(i => i.value))
|
const maxValue = Math.max(...data.map(i => i.value))
|
||||||
const barData = [
|
|
||||||
{ value: 23, label: 'Gambar', },
|
|
||||||
{ value: 12, label: 'Dokumen' },
|
|
||||||
];
|
|
||||||
const width = Dimensions.get("window").width;
|
const width = Dimensions.get("window").width;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export default function ChartDokumenHome({ refreshing }: { refreshing: boolean }
|
|||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
const [data, setData] = useState<Props>([])
|
const [data, setData] = useState<Props>([])
|
||||||
const [maxValue, setMaxValue] = useState(5)
|
const [maxValue, setMaxValue] = useState(5)
|
||||||
|
const [chartKey, setChartKey] = useState(0)
|
||||||
const barData = [
|
const barData = [
|
||||||
{ value: 23, label: 'Gambar', frontColor: '#fac858' },
|
{ value: 23, label: 'Gambar', frontColor: '#fac858' },
|
||||||
{ value: 12, label: 'Dokumen', frontColor: '#92cc76' },
|
{ value: 12, label: 'Dokumen', frontColor: '#92cc76' },
|
||||||
@@ -32,8 +33,8 @@ export default function ChartDokumenHome({ refreshing }: { refreshing: boolean }
|
|||||||
setLoading(loading)
|
setLoading(loading)
|
||||||
const hasil = await decryptToken(String(token?.current))
|
const hasil = await decryptToken(String(token?.current))
|
||||||
const response = await apiGetDataHome({ cat: "dokumen", user: hasil })
|
const response = await apiGetDataHome({ cat: "dokumen", user: hasil })
|
||||||
const maxValue = response.data.reduce((max: number, obj: { value: number; }) => Math.max(max, obj.value), -Infinity);
|
const maxVal = response.data.reduce((max: number, obj: { value: number; }) => Math.max(max, Number(obj.value)), 0);
|
||||||
const roundUp = Math.ceil(maxValue / 10) * 10
|
const roundUp = maxVal > 0 ? Math.ceil(maxVal / 10) * 10 : 10;
|
||||||
setMaxValue(roundUp)
|
setMaxValue(roundUp)
|
||||||
const convertedArray = response.data.map((item: { color: any; label: any; value: any; }) => ({
|
const convertedArray = response.data.map((item: { color: any; label: any; value: any; }) => ({
|
||||||
frontColor: item.color,
|
frontColor: item.color,
|
||||||
@@ -41,6 +42,7 @@ export default function ChartDokumenHome({ refreshing }: { refreshing: boolean }
|
|||||||
value: Number(item.value)
|
value: Number(item.value)
|
||||||
}));
|
}));
|
||||||
setData(convertedArray)
|
setData(convertedArray)
|
||||||
|
setChartKey((prev: number) => prev + 1)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
@@ -49,8 +51,9 @@ export default function ChartDokumenHome({ refreshing }: { refreshing: boolean }
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (refreshing)
|
if (refreshing) {
|
||||||
handleData(false)
|
handleData(false)
|
||||||
|
}
|
||||||
}, [refreshing]);
|
}, [refreshing]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -64,6 +67,7 @@ export default function ChartDokumenHome({ refreshing }: { refreshing: boolean }
|
|||||||
loading ? <Skeleton width={100} height={200} borderRadius={10} widthType="percent" />
|
loading ? <Skeleton width={100} height={200} borderRadius={10} widthType="percent" />
|
||||||
:
|
:
|
||||||
<BarChart
|
<BarChart
|
||||||
|
key={chartKey}
|
||||||
showFractionalValues={false}
|
showFractionalValues={false}
|
||||||
showYAxisIndices
|
showYAxisIndices
|
||||||
noOfSections={4}
|
noOfSections={4}
|
||||||
|
|||||||
Reference in New Issue
Block a user