From 10212aa5de95a9b260970325f6ba66c1d93081b2 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Sat, 14 Feb 2026 10:58:53 +0800 Subject: [PATCH 1/8] upd: redesign --- .../(fitur-division)/task/update/[detail].tsx | 13 +++++---- app/(application)/project/update/[detail].tsx | 12 ++++---- assets/images/bgproject-dark.png | Bin 0 -> 10490 bytes assets/images/bgproject-light.png | Bin 0 -> 10382 bytes components/home/projectHome.tsx | 4 +-- components/paperGridContent.tsx | 27 ++++++++++++++---- declarations.d.ts | 24 ++++++++++++++++ 7 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 assets/images/bgproject-dark.png create mode 100644 assets/images/bgproject-light.png create mode 100644 declarations.d.ts diff --git a/app/(application)/division/[id]/(fitur-division)/task/update/[detail].tsx b/app/(application)/division/[id]/(fitur-division)/task/update/[detail].tsx index 1511f0c..475e7b0 100644 --- a/app/(application)/division/[id]/(fitur-division)/task/update/[detail].tsx +++ b/app/(application)/division/[id]/(fitur-division)/task/update/[detail].tsx @@ -1,15 +1,16 @@ import AppHeader from "@/components/AppHeader"; import ButtonSaveHeader from "@/components/buttonSaveHeader"; +import ButtonSelect from "@/components/buttonSelect"; import { InputForm } from "@/components/inputForm"; import ModalAddDetailTugasTask from "@/components/task/modalAddDetailTugasTask"; import Text from "@/components/Text"; import Styles from "@/constants/Styles"; -import { useTheme } from "@/providers/ThemeProvider"; import { apiEditTaskTugas, apiGetTaskTugas } from "@/lib/api"; import { formatDateOnly } from "@/lib/fun_formatDateOnly"; import { getDatesInRange } from "@/lib/fun_getDatesInRange"; import { setUpdateTask } from "@/lib/taskUpdate"; import { useAuthSession } from "@/providers/AuthProvider"; +import { useTheme } from "@/providers/ThemeProvider"; import { useHeaderHeight } from '@react-navigation/elements'; import { router, Stack, useLocalSearchParams } from "expo-router"; import 'intl'; @@ -19,7 +20,6 @@ import { useEffect, useState } from "react"; import { KeyboardAvoidingView, Platform, - Pressable, SafeAreaView, ScrollView, View @@ -263,7 +263,7 @@ export default function UpdateProjectTaskDivision() { Tanggal Mulai * - + {from} @@ -271,7 +271,7 @@ export default function UpdateProjectTaskDivision() { Tanggal Berakhir * - + {to} @@ -281,13 +281,14 @@ export default function UpdateProjectTaskDivision() { Tanggal tidak boleh kosong )} - { setModalDetail(true) }} > Detail - + */} + { setModalDetail(true) }} /> Tanggal Mulai * - + {from} Tanggal Berakhir * - + {to} @@ -249,13 +250,14 @@ export default function UpdateProjectTask() { { (error.endDate || error.startDate) && Tanggal tidak boleh kosong } - { setModalDetail(true) }} > Detail - + */} + { setModalDetail(true) }} /> ^H6z^?6b)^$xCFBnQmLQ{&f+8EC3%+ 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 literal 0 HcmV?d00001 diff --git a/assets/images/bgproject-light.png b/assets/images/bgproject-light.png new file mode 100644 index 0000000000000000000000000000000000000000..90aa1e02053d542cdedfc6dfe59309a095eeb5e0 GIT binary patch literal 10382 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 spgsg0aPurp{57CpcQ(T(OMun?1G$bnJC - - {title} - + { + headerColor == 'warning' ? ( + + {title} + + ) : ( + + {title} + + ) + } Date: Sat, 14 Feb 2026 14:01:41 +0800 Subject: [PATCH 2/8] upd: modal konfirmasi Deskripsi: - menerapkan semua modal baru pada semua fitur No Issues'' --- app/(application)/banner/index.tsx | 24 ++++-- app/(application)/discussion/[id].tsx | 32 +++++--- app/(application)/discussion/member/[id].tsx | 28 +++++-- .../calendar/[detail]/index.tsx | 28 +++++-- .../discussion/[detail]/index.tsx | 28 +++++-- .../[id]/(fitur-division)/document/index.tsx | 25 ++++-- app/(application)/division/[id]/info.tsx | 25 ++++-- app/(application)/division/create.tsx | 19 +++-- app/(application)/group/index.tsx | 24 ++++-- app/(application)/position/index.tsx | 24 ++++-- app/(application)/profile.tsx | 23 ++++-- components/ModalConfirmation.tsx | 82 +++++++++++++++++++ .../announcement/headerAnnouncementDetail.tsx | 27 ++++-- components/buttonSaveHeader.tsx | 39 +++++---- components/calendar/headerCalendarDetail.tsx | 27 ++++-- .../discussion/headerDiscussionDetail.tsx | 47 +++++++---- .../headerDiscussionDetail.tsx | 58 +++++++++---- components/division/headerDivisionInfo.tsx | 24 ++++-- .../document/menuBottomSelectDocument.tsx | 28 ++++--- components/member/headerMemberDetail.tsx | 26 ++++-- components/project/headerProjectDetail.tsx | 26 ++++-- components/project/sectionFile.tsx | 28 +++++-- components/project/sectionLink.tsx | 28 +++++-- components/project/sectionMember.tsx | 26 ++++-- components/project/sectionTanggalTugas.tsx | 25 ++++-- components/task/headerTaskDetail.tsx | 25 ++++-- components/task/sectionFileTask.tsx | 31 +++++-- components/task/sectionLinkTask.tsx | 25 ++++-- components/task/sectionMemberTask.tsx | 28 +++++-- components/task/sectionTanggalTugasTask.tsx | 28 +++++-- constants/Styles.ts | 55 ++++++++++++- 31 files changed, 718 insertions(+), 245 deletions(-) create mode 100644 components/ModalConfirmation.tsx diff --git a/app/(application)/banner/index.tsx b/app/(application)/banner/index.tsx index a25e786..f388242 100644 --- a/app/(application)/banner/index.tsx +++ b/app/(application)/banner/index.tsx @@ -1,9 +1,9 @@ -import AlertKonfirmasi from "@/components/alertKonfirmasi" import AppHeader from "@/components/AppHeader" import HeaderRightBannerList from "@/components/banner/headerBannerList" import BorderBottomItem from "@/components/borderBottomItem" import DrawerBottom from "@/components/drawerBottom" import MenuItemRow from "@/components/menuItemRow" +import ModalConfirmation from "@/components/ModalConfirmation" import ModalLoading from "@/components/modalLoading" import Text from "@/components/Text" import { ConstEnv } from "@/constants/ConstEnv" @@ -42,6 +42,7 @@ export default function BannerList() { const [refreshing, setRefreshing] = useState(false) const [loadingOpen, setLoadingOpen] = useState(false) const [viewImg, setViewImg] = useState(false) + const [showDeleteModal, setShowDeleteModal] = useState(false) const handleDeleteEntity = async () => { try { @@ -196,11 +197,9 @@ export default function BannerList() { title="Hapus" onPress={() => { setModal(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah anda yakin ingin menghapus data?', - onPress: () => { handleDeleteEntity() } - }) + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> @@ -213,6 +212,19 @@ export default function BannerList() { onRequestClose={() => setViewImg(false)} doubleTapToZoomEnabled /> + + { + setShowDeleteModal(false) + handleDeleteEntity() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + /> ) } \ No newline at end of file diff --git a/app/(application)/discussion/[id].tsx b/app/(application)/discussion/[id].tsx index 8eff8d7..09d3351 100644 --- a/app/(application)/discussion/[id].tsx +++ b/app/(application)/discussion/[id].tsx @@ -1,4 +1,3 @@ -import AlertKonfirmasi from "@/components/alertKonfirmasi"; import AppHeader from "@/components/AppHeader"; import BorderBottomItem from "@/components/borderBottomItem"; import BorderBottomItem2 from "@/components/borderBottomItem2"; @@ -8,10 +7,10 @@ import ImageUser from "@/components/imageNew"; import { InputForm } from "@/components/inputForm"; import LabelStatus from "@/components/labelStatus"; import MenuItemRow from "@/components/menuItemRow"; +import ModalConfirmation from "@/components/ModalConfirmation"; import Skeleton from "@/components/skeleton"; import SkeletonContent from "@/components/skeletonContent"; import Text from '@/components/Text'; -import { ColorsStatus } from "@/constants/ColorsStatus"; import { ConstEnv } from "@/constants/ConstEnv"; import { regexOnlySpacesOrEnter } from "@/constants/OnlySpaceOrEnter"; import Styles from "@/constants/Styles"; @@ -81,6 +80,7 @@ export default function DetailDiscussionGeneral() { comment: '' }) const [viewEdit, setViewEdit] = useState(false) + const [showDeleteModal, setShowDeleteModal] = useState(false) useEffect(() => { const onValueChange = reference.on('value', snapshot => { @@ -370,7 +370,7 @@ export default function DetailDiscussionGeneral() { Platform.OS == 'android' && Styles.mb12, ]} > - + } /> @@ -396,7 +396,7 @@ export default function DetailDiscussionGeneral() { Platform.OS == 'android' && Styles.mb12, ]} > - + } /> @@ -425,17 +425,27 @@ export default function DetailDiscussionGeneral() { icon={} title="Hapus" onPress={() => { - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah anda yakin ingin menghapus komentar?', - onPress: () => { - handleDeleteKomentar() - } - }) + setVisible(false) + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> + + { + setShowDeleteModal(false) + handleDeleteKomentar() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + /> ) } \ No newline at end of file diff --git a/app/(application)/discussion/member/[id].tsx b/app/(application)/discussion/member/[id].tsx index ee3ce94..76f1113 100644 --- a/app/(application)/discussion/member/[id].tsx +++ b/app/(application)/discussion/member/[id].tsx @@ -1,9 +1,9 @@ -import AlertKonfirmasi from "@/components/alertKonfirmasi"; import AppHeader from "@/components/AppHeader"; import BorderBottomItem from "@/components/borderBottomItem"; import DrawerBottom from "@/components/drawerBottom"; import ImageUser from "@/components/imageNew"; import MenuItemRow from "@/components/menuItemRow"; +import ModalConfirmation from "@/components/ModalConfirmation"; import SkeletonTwoItem from "@/components/skeletonTwoItem"; import Text from '@/components/Text'; import { ColorsStatus } from "@/constants/ColorsStatus"; @@ -36,6 +36,8 @@ export default function MemberDiscussionDetail() { const update = useSelector((state: any) => state.discussionGeneralDetailUpdate) const [loading, setLoading] = useState(true) const arrSkeleton = Array.from({ length: 5 }, (_, index) => index) + const [showDeleteModal, setShowDeleteModal] = useState(false) + async function handleLoad(loading: boolean) { try { @@ -151,20 +153,28 @@ export default function MemberDiscussionDetail() { title="Keluarkan" onPress={() => { setModal(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah Anda yakin ingin mengeluarkan anggota?', - onPress: () => { - handleDeleteUser() - } - }) - + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> } + + { + setShowDeleteModal(false) + handleDeleteUser() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + /> ) } \ No newline at end of file diff --git a/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx b/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx index 37b1b56..de31cf5 100644 --- a/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx @@ -1,4 +1,4 @@ -import AlertKonfirmasi from "@/components/alertKonfirmasi" +import ModalConfirmation from "@/components/ModalConfirmation" import AppHeader from "@/components/AppHeader" import BorderBottomItem from "@/components/borderBottomItem" import ButtonBackHeader from "@/components/buttonBackHeader" @@ -57,6 +57,7 @@ export default function DetailEventCalendar() { 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) @@ -301,18 +302,27 @@ export default function DetailEventCalendar() { title="Keluarkan" onPress={() => { setModalMember(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah Anda yakin ingin mengeluarkan anggota?', - onPress: () => { - handleDeleteUser() - } - }) - + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> + + { + setShowDeleteModal(false) + handleDeleteUser() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Keluar" + cancelText="Batal" + isDestructive + /> ) } \ No newline at end of file diff --git a/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx b/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx index 9d06ec3..3c04209 100644 --- a/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx @@ -1,4 +1,4 @@ -import AlertKonfirmasi from "@/components/alertKonfirmasi"; +import ModalConfirmation from "@/components/ModalConfirmation"; import AppHeader from "@/components/AppHeader"; import BorderBottomItem from "@/components/borderBottomItem"; import BorderBottomItem2 from "@/components/borderBottomItem2"; @@ -92,6 +92,7 @@ export default function DiscussionDetail() { comment: '' }) const [viewEdit, setViewEdit] = useState(false) + const [showDeleteModal, setShowDeleteModal] = useState(false) @@ -541,17 +542,28 @@ export default function DiscussionDetail() { icon={} title="Hapus" onPress={() => { - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah anda yakin ingin menghapus komentar?', - onPress: () => { - handleDeleteKomentar() - } - }) + setVisible(false) + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> + + { + setShowDeleteModal(false) + handleDeleteKomentar() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + isDestructive + /> ); } diff --git a/app/(application)/division/[id]/(fitur-division)/document/index.tsx b/app/(application)/division/[id]/(fitur-division)/document/index.tsx index d308578..c7dc164 100644 --- a/app/(application)/division/[id]/(fitur-division)/document/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/document/index.tsx @@ -1,4 +1,4 @@ -import AlertKonfirmasi from "@/components/alertKonfirmasi"; +import ModalConfirmation from "@/components/ModalConfirmation"; import AppHeader from "@/components/AppHeader"; import { ButtonHeader } from "@/components/buttonHeader"; import HeaderRightDocument from "@/components/document/headerDocument"; @@ -89,6 +89,7 @@ export default function DocumentDivision() { const [loadingOpen, setLoadingOpen] = useState(false) const [isMemberDivision, setIsMemberDivision] = useState(false) const entityUser = useSelector((state: any) => state.user) + const [showDeleteModal, setShowDeleteModal] = useState(false) const [bodyRename, setBodyRename] = useState({ id: "", name: "", @@ -499,13 +500,7 @@ export default function DocumentDivision() { } title="Hapus" onPress={() => { - AlertKonfirmasi({ - title: "Konfirmasi", - desc: "Apakah anda yakin ingin menghapus dokumen?", - onPress: () => { - handleDelete(); - }, - }); + setShowDeleteModal(true) }} column="many" color="white" @@ -612,6 +607,20 @@ export default function DocumentDivision() { value={id} item={selectedFiles[0]?.id} /> + + { + setShowDeleteModal(false) + handleDelete() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + isDestructive + /> ); } diff --git a/app/(application)/division/[id]/info.tsx b/app/(application)/division/[id]/info.tsx index a9aba24..c473939 100644 --- a/app/(application)/division/[id]/info.tsx +++ b/app/(application)/division/[id]/info.tsx @@ -1,4 +1,4 @@ -import AlertKonfirmasi from "@/components/alertKonfirmasi" +import ModalConfirmation from "@/components/ModalConfirmation" import AppHeader from "@/components/AppHeader" import BorderBottomItem from "@/components/borderBottomItem" import HeaderRightDivisionInfo from "@/components/division/headerDivisionInfo" @@ -59,14 +59,13 @@ export default function InformationDivision() { name: '', isAdmin: false }) + const [showDeleteModal, setShowDeleteModal] = useState(false) function handleMemberOut() { setModal(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah anda yakin ingin mengeluarkan anggota?', - onPress: () => { memberOut() } - }) + setTimeout(() => { + setShowDeleteModal(true) + }, 600) } async function memberOut() { @@ -287,6 +286,20 @@ export default function InformationDivision() { + + { + setShowDeleteModal(false) + memberOut() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Keluar" + cancelText="Batal" + isDestructive + /> ) } \ No newline at end of file diff --git a/app/(application)/division/create.tsx b/app/(application)/division/create.tsx index e8ec765..516951a 100644 --- a/app/(application)/division/create.tsx +++ b/app/(application)/division/create.tsx @@ -1,4 +1,4 @@ -import AlertKonfirmasi from "@/components/alertKonfirmasi"; +import ModalConfirmation from "@/components/ModalConfirmation"; import AppHeader from "@/components/AppHeader"; import ButtonNextHeader from "@/components/buttonNextHeader"; import { InputForm } from "@/components/inputForm"; @@ -25,6 +25,7 @@ export default function CreateDivision() { const entityUser = useSelector((state: any) => state.user) const userLogin = useSelector((state: any) => state.entities) const [loadingBtn, setLoadingBtn] = useState(false) + const [showWarningModal, setShowWarningModal] = useState(false) const [error, setError] = useState({ idGroup: false, name: false, @@ -69,12 +70,7 @@ export default function CreateDivision() { const response = await apiCheckDivisionName({ data: { ...dataForm }, user: hasil }) if (response.success) { if (!response.available) { - AlertKonfirmasi({ - title: 'Peringatan', - category: 'warning', - desc: 'Nama divisi sudah ada. Tidak dapat membuat divisi dengan nama yang sama', - onPress: () => { } - }) + setShowWarningModal(true) } else { handleSetData() } @@ -181,6 +177,15 @@ export default function CreateDivision() { open={isSelect} valChoose={chooseGroup.val} /> + + setShowWarningModal(false)} + onCancel={() => setShowWarningModal(false)} + confirmText="Oke" + /> ); } diff --git a/app/(application)/group/index.tsx b/app/(application)/group/index.tsx index deb0932..88def9e 100644 --- a/app/(application)/group/index.tsx +++ b/app/(application)/group/index.tsx @@ -1,4 +1,4 @@ -import AlertKonfirmasi from "@/components/alertKonfirmasi"; +import ModalConfirmation from "@/components/ModalConfirmation"; import BorderBottomItem from "@/components/borderBottomItem"; import { ButtonForm } from "@/components/buttonForm"; import ButtonTab from "@/components/buttonTab"; @@ -35,6 +35,7 @@ export default function Index() { const [search, setSearch] = useState('') const arrSkeleton = Array.from({ length: 5 }, (_, index) => index) const [loading, setLoading] = useState(true) + const [showDeleteModal, setShowDeleteModal] = useState(false) const [status, setStatus] = useState<'true' | 'false'>('true') const [loadingSubmit, setLoadingSubmit] = useState(false) const [idChoose, setIdChoose] = useState('') @@ -205,11 +206,9 @@ export default function Index() { title={activeChoose ? "Non Aktifkan" : "Aktifkan"} onPress={() => { setModal(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: activeChoose ? 'Apakah anda yakin ingin menonaktifkan data?' : 'Apakah anda yakin ingin mengaktifkan data?', - onPress: () => { handleDelete() } - }) + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> + + { + setShowDeleteModal(false) + handleDelete() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText={activeChoose ? "Nonaktifkan" : "Aktifkan"} + cancelText="Batal" + /> ) diff --git a/app/(application)/position/index.tsx b/app/(application)/position/index.tsx index 17a6ab1..f1a5c51 100644 --- a/app/(application)/position/index.tsx +++ b/app/(application)/position/index.tsx @@ -1,4 +1,4 @@ -import AlertKonfirmasi from "@/components/alertKonfirmasi"; +import ModalConfirmation from "@/components/ModalConfirmation"; import BorderBottomItem from "@/components/borderBottomItem"; import { ButtonForm } from "@/components/buttonForm"; import ButtonTab from "@/components/buttonTab"; @@ -50,6 +50,7 @@ export default function Index() { name: false, }); const [refreshing, setRefreshing] = useState(false) + const [showDeleteModal, setShowDeleteModal] = useState(false) const dispatch = useDispatch() const update = useSelector((state: any) => state.positionUpdate) @@ -230,11 +231,9 @@ export default function Index() { title={chooseData.active ? 'Non Aktifkan' : "Aktifkan"} onPress={() => { setModal(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: chooseData.active ? 'Apakah anda yakin ingin menonaktifkan data?' : 'Apakah anda yakin ingin mengaktifkan data?', - onPress: () => { handleDelete() } - }) + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> + + { + setShowDeleteModal(false) + handleDelete() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText={chooseData.active ? "Nonaktifkan" : "Aktifkan"} + cancelText="Batal" + /> ) } \ No newline at end of file diff --git a/app/(application)/profile.tsx b/app/(application)/profile.tsx index 24f1b8a..22ada5d 100644 --- a/app/(application)/profile.tsx +++ b/app/(application)/profile.tsx @@ -1,4 +1,4 @@ -import AlertKonfirmasi from "@/components/alertKonfirmasi"; +import ModalConfirmation from "@/components/ModalConfirmation"; import AppHeader from "@/components/AppHeader"; import { ButtonHeader } from "@/components/buttonHeader"; import ItemDetailMember from "@/components/itemDetailMember"; @@ -23,6 +23,7 @@ export default function Profile() { const [error, setError] = useState(false) const [preview, setPreview] = useState(false) const [showThemeModal, setShowThemeModal] = useState(false) + const [showLogoutModal, setShowLogoutModal] = useState(false) const ThemeOption = ({ label, value, icon }: { label: string, value: 'light' | 'dark' | 'system', icon: string }) => ( } onPress={() => { - AlertKonfirmasi({ - title: 'Keluar', - desc: 'Apakah anda yakin ingin keluar?', - onPress: () => { signOut() } - }) + setShowLogoutModal(true) }} /> } @@ -151,6 +148,20 @@ export default function Profile() { onRequestClose={() => setPreview(false)} doubleTapToZoomEnabled /> + + { + setShowLogoutModal(false) + signOut() + }} + onCancel={() => setShowLogoutModal(false)} + confirmText="Keluar" + cancelText="Batal" + isDestructive + /> ) } \ No newline at end of file diff --git a/components/ModalConfirmation.tsx b/components/ModalConfirmation.tsx new file mode 100644 index 0000000..8bb5d50 --- /dev/null +++ b/components/ModalConfirmation.tsx @@ -0,0 +1,82 @@ +import Styles from '@/constants/Styles'; +import { useTheme } from '@/providers/ThemeProvider'; +import React from 'react'; +import { Modal, TouchableOpacity, View } from 'react-native'; +import Text from './Text'; + +interface ModalConfirmationProps { + visible: boolean; + title: string; + message: string; + onConfirm: () => void; + onCancel: () => void; + confirmText?: string; + cancelText?: string; + isDestructive?: boolean; +} + +const ModalConfirmation: React.FC = ({ + visible, + title, + message, + onConfirm, + onCancel, + confirmText = 'Ya', + cancelText = 'Batal', + isDestructive = false, +}) => { + const { colors } = useTheme(); + + return ( + + + + + {title} + + {message} + + + + + + + + + {cancelText} + + + + + + + + {confirmText} + + + + + + + ); +}; + +export default ModalConfirmation; diff --git a/components/announcement/headerAnnouncementDetail.tsx b/components/announcement/headerAnnouncementDetail.tsx index d39d784..7141706 100644 --- a/components/announcement/headerAnnouncementDetail.tsx +++ b/components/announcement/headerAnnouncementDetail.tsx @@ -9,7 +9,7 @@ import { useState } from "react" import { View } from "react-native" import Toast from "react-native-toast-message" import { useDispatch, useSelector } from "react-redux" -import AlertKonfirmasi from "../alertKonfirmasi" +import ModalConfirmation from "../ModalConfirmation" import ButtonMenuHeader from "../buttonMenuHeader" import DrawerBottom from "../drawerBottom" import MenuItemRow from "../menuItemRow" @@ -22,6 +22,7 @@ export default function HeaderRightAnnouncementDetail({ id }: Props) { const { colors } = useTheme(); const [isVisible, setVisible] = useState(false) const update = useSelector((state: any) => state.announcementUpdate) + const [showDeleteModal, setShowDeleteModal] = useState(false) const dispatch = useDispatch() @@ -60,17 +61,27 @@ export default function HeaderRightAnnouncementDetail({ id }: Props) { title="Hapus" onPress={() => { setVisible(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah anda yakin ingin menghapus pengumuman ini?', - onPress: () => { - handleDelete() - } - }) + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> + + { + setShowDeleteModal(false) + handleDelete() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + isDestructive + /> ) } \ No newline at end of file diff --git a/components/buttonSaveHeader.tsx b/components/buttonSaveHeader.tsx index fdd87f8..230c207 100644 --- a/components/buttonSaveHeader.tsx +++ b/components/buttonSaveHeader.tsx @@ -1,6 +1,7 @@ import { Feather } from "@expo/vector-icons" -import AlertKonfirmasi from "./alertKonfirmasi" +import ModalConfirmation from "./ModalConfirmation" import { ButtonHeader } from "./buttonHeader" +import { useState } from "react" type Props = { category: 'create' | 'update' | 'cancel' | 'update-calendar' @@ -9,29 +10,37 @@ type Props = { } export default function ButtonSaveHeader({ category, onPress, disable }: Props) { + const [showModal, setShowModal] = useState(false) return ( <> } onPress={() => { if (!disable) { - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: category == 'create' - ? 'Apakah anda yakin ingin menambahkan data?' - : category == 'cancel' - ? 'Apakah anda yakin ingin membatalkan kegiatan? Pembatalan bersifat permanen' - : category == 'update-calendar' - ? 'Apakah Anda yakin ingin mengubah data acara ini? Data ini akan mempengaruhi semua data yang terkait' - : 'Apakah anda yakin mengubah data?', - onPress: () => { - onPress && onPress() - } - }) + setShowModal(true) } - } + }} + /> + { + setShowModal(false) + onPress && onPress() + }} + onCancel={() => setShowModal(false)} + confirmText="Ya" + cancelText="Batal" /> ) diff --git a/components/calendar/headerCalendarDetail.tsx b/components/calendar/headerCalendarDetail.tsx index 70f3889..1584986 100644 --- a/components/calendar/headerCalendarDetail.tsx +++ b/components/calendar/headerCalendarDetail.tsx @@ -9,7 +9,7 @@ import { useState } from "react" import { View } from "react-native" import Toast from "react-native-toast-message" import { useDispatch, useSelector } from "react-redux" -import AlertKonfirmasi from "../alertKonfirmasi" +import ModalConfirmation from "../ModalConfirmation" import ButtonMenuHeader from "../buttonMenuHeader" import DrawerBottom from "../drawerBottom" import MenuItemRow from "../menuItemRow" @@ -23,6 +23,7 @@ export default function HeaderRightCalendarDetail({ id, idReminder }: Props) { const { colors } = useTheme() const [isVisible, setVisible] = useState(false) const { token, decryptToken } = useAuthSession() + const [showDeleteModal, setShowDeleteModal] = useState(false) const update = useSelector((state: any) => state.calendarUpdate) const dispatch = useDispatch() @@ -71,17 +72,27 @@ export default function HeaderRightCalendarDetail({ id, idReminder }: Props) { title="Hapus" onPress={() => { setVisible(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah anda yakin ingin menghapus data ini? Data ini akan mempengaruhi semua data yang terkait', - onPress: () => { - handleDeleteCalendar() - } - }) + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> + + { + setShowDeleteModal(false) + handleDeleteCalendar() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + isDestructive + /> ) } \ No newline at end of file diff --git a/components/discussion/headerDiscussionDetail.tsx b/components/discussion/headerDiscussionDetail.tsx index e25af32..454e24a 100644 --- a/components/discussion/headerDiscussionDetail.tsx +++ b/components/discussion/headerDiscussionDetail.tsx @@ -9,7 +9,7 @@ import { useState } from "react" import { View } from "react-native" import Toast from "react-native-toast-message" import { useDispatch, useSelector } from "react-redux" -import AlertKonfirmasi from "../alertKonfirmasi" +import ModalConfirmation from "../ModalConfirmation" import ButtonMenuHeader from "../buttonMenuHeader" import DrawerBottom from "../drawerBottom" import MenuItemRow from "../menuItemRow" @@ -25,6 +25,8 @@ export default function HeaderRightDiscussionDetail({ id, status, isActive }: Pr const [isVisible, setVisible] = useState(false) const { token, decryptToken } = useAuthSession() const update = useSelector((state: any) => state.discussionUpdate) + const [showModal, setShowModal] = useState(false) + const [modalConfig, setModalConfig] = useState({ title: '', message: '', onConfirm: () => { } }) const dispatch = useDispatch() const handleOpenClose = async () => { @@ -86,13 +88,14 @@ export default function HeaderRightDiscussionDetail({ id, status, isActive }: Pr title={status == 1 ? 'Tutup Diskusi' : 'Buka Diskusi'} onPress={() => { setVisible(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: `Apakah anda yakin ingin ${status == 1 ? 'menutup' : 'membuka'} diskusi?`, - onPress: () => { - handleOpenClose() - } - }) + setTimeout(() => { + setModalConfig({ + title: 'Konfirmasi', + message: `Apakah anda yakin ingin ${status == 1 ? 'menutup' : 'membuka'} diskusi?`, + onConfirm: () => handleOpenClose() + }) + setShowModal(true) + }, 600) }} /> @@ -102,17 +105,31 @@ export default function HeaderRightDiscussionDetail({ id, status, isActive }: Pr title={isActive ? 'Arsipkan' : 'Aktifkan Diskusi'} onPress={() => { setVisible(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: isActive ? 'Apakah anda yakin ingin mengarsipkan diskusi?' : 'Apakah anda yakin ingin mengaktifkan diskusi?', - onPress: () => { - handleArchive() - } - }) + setTimeout(() => { + setModalConfig({ + title: 'Konfirmasi', + message: isActive ? 'Apakah anda yakin ingin mengarsipkan diskusi?' : 'Apakah anda yakin ingin mengaktifkan diskusi?', + onConfirm: () => handleArchive() + }) + setShowModal(true) + }, 600) }} /> + + { + setShowModal(false) + modalConfig.onConfirm() + }} + onCancel={() => setShowModal(false)} + confirmText="Ya" + cancelText="Batal" + /> ) } \ No newline at end of file diff --git a/components/discussion_general/headerDiscussionDetail.tsx b/components/discussion_general/headerDiscussionDetail.tsx index 43b1d78..309bed3 100644 --- a/components/discussion_general/headerDiscussionDetail.tsx +++ b/components/discussion_general/headerDiscussionDetail.tsx @@ -9,7 +9,7 @@ import { useState } from "react" import { View } from "react-native" import Toast from "react-native-toast-message" import { useDispatch, useSelector } from "react-redux" -import AlertKonfirmasi from "../alertKonfirmasi" +import ModalConfirmation from "../ModalConfirmation" import ButtonMenuHeader from "../buttonMenuHeader" import DrawerBottom from "../drawerBottom" import MenuItemRow from "../menuItemRow" @@ -25,6 +25,8 @@ export default function HeaderRightDiscussionGeneralDetail({ id, active, status const { colors } = useTheme(); const [isVisible, setVisible] = useState(false) const entityUser = useSelector((state: any) => state.user) + const [showModal, setShowModal] = useState(false) + const [modalConfig, setModalConfig] = useState({ title: '', message: '', onConfirm: () => { } }) const dispatch = useDispatch() const update = useSelector((state: any) => state.discussionGeneralDetailUpdate) @@ -88,13 +90,14 @@ export default function HeaderRightDiscussionGeneralDetail({ id, active, status title={status == 1 ? 'Tutup Diskusi' : 'Buka Diskusi'} onPress={() => { setVisible(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: status == 1 ? 'Apakah anda yakin ingin menutup diskusi?' : 'Apakah anda yakin ingin membuka diskusi?', - onPress: () => { - handleUpdateStatus() - } - }) + setTimeout(() => { + setModalConfig({ + title: 'Konfirmasi', + message: status == 1 ? 'Apakah anda yakin ingin menutup diskusi?' : 'Apakah anda yakin ingin membuka diskusi?', + onConfirm: () => handleUpdateStatus() + }) + setShowModal(true) + }, 600) }} /> @@ -105,11 +108,14 @@ export default function HeaderRightDiscussionGeneralDetail({ id, active, status title="Aktifkan Diskusi" onPress={() => { setVisible(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah anda yakin ingin mengaktifkan diskusi ini?', - onPress: () => { handleDelete() } - }) + setTimeout(() => { + setModalConfig({ + title: 'Konfirmasi', + message: 'Apakah anda yakin ingin mengaktifkan diskusi ini?', + onConfirm: () => handleDelete() + }) + setShowModal(true) + }, 600) }} /> @@ -125,16 +131,32 @@ export default function HeaderRightDiscussionGeneralDetail({ id, active, status title="Arsipkan" onPress={() => { setVisible(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah anda yakin ingin mengarsipkan diskusi?', - onPress: () => { handleDelete() } - }) + setTimeout(() => { + setModalConfig({ + title: 'Konfirmasi', + message: 'Apakah anda yakin ingin mengarsipkan diskusi?', + onConfirm: () => handleDelete() + }) + setShowModal(true) + }, 600) }} /> } + + { + setShowModal(false) + modalConfig.onConfirm() + }} + onCancel={() => setShowModal(false)} + confirmText="Ya" + cancelText="Batal" + /> ) } \ No newline at end of file diff --git a/components/division/headerDivisionInfo.tsx b/components/division/headerDivisionInfo.tsx index 6112fac..7091161 100644 --- a/components/division/headerDivisionInfo.tsx +++ b/components/division/headerDivisionInfo.tsx @@ -8,7 +8,7 @@ import { useState } from "react" import { View } from "react-native" import Toast from "react-native-toast-message" import { useDispatch, useSelector } from "react-redux" -import AlertKonfirmasi from "../alertKonfirmasi" +import ModalConfirmation from "../ModalConfirmation" import ButtonMenuHeader from "../buttonMenuHeader" import DrawerBottom from "../drawerBottom" import MenuItemRow from "../menuItemRow" @@ -23,6 +23,7 @@ export default function HeaderRightDivisionInfo({ id, active }: Props) { const { colors } = useTheme(); const [isVisible, setVisible] = useState(false) const { token, decryptToken } = useAuthSession() + const [showModal, setShowModal] = useState(false) const update = useSelector((state: any) => state.divisionUpdate) const dispatch = useDispatch() @@ -62,15 +63,26 @@ export default function HeaderRightDivisionInfo({ id, active }: Props) { title={active ? "Non Aktifkan" : "Aktifkan"} onPress={() => { setVisible(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: active ? 'Apakah anda yakin ingin menonaktifkan divisi?' : 'Apakah anda yakin ingin mengaktifkan divisi?', - onPress: () => { handleUpdateStatus() } - }) + setTimeout(() => { + setShowModal(true) + }, 600) }} /> + + { + setShowModal(false) + handleUpdateStatus() + }} + onCancel={() => setShowModal(false)} + confirmText="Konfirmasi" + cancelText="Batal" + /> ) } \ No newline at end of file diff --git a/components/document/menuBottomSelectDocument.tsx b/components/document/menuBottomSelectDocument.tsx index f746c71..76bb956 100644 --- a/components/document/menuBottomSelectDocument.tsx +++ b/components/document/menuBottomSelectDocument.tsx @@ -6,7 +6,7 @@ import { useState } from "react"; import { Pressable, ScrollView, View } from "react-native"; import { useSharedValue } from "react-native-reanimated"; import Toast from "react-native-toast-message"; -import AlertKonfirmasi from "../alertKonfirmasi"; +import ModalConfirmation from "../ModalConfirmation"; import DrawerBottom from "../drawerBottom"; import { InputForm } from "../inputForm"; import ItemAccordion from "../itemAccordion"; @@ -29,6 +29,7 @@ export default function MenuBottomSelectDocument({ onDone }: Props) { const [isRename, setRename] = useState(false) const [isShare, setShare] = useState(false) const [isMoveCopy, setMoveCopy] = useState(false) + const [showDeleteModal, setShowDeleteModal] = useState(false) const [valMoveCopy, setValMoveCopy] = useState<'move' | 'copy'>('copy') const open = useSharedValue(false) @@ -58,15 +59,7 @@ export default function MenuBottomSelectDocument({ onDone }: Props) { icon={} title="Hapus" onPress={() => { - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah anda yakin ingin menghapus data?', - - onPress: () => { - onDone() - Toast.show({ type: 'small', text1: 'Berhasil menghapus data', }) - } - }) + setShowDeleteModal(true) }} column="many" color="white" @@ -213,6 +206,21 @@ export default function MenuBottomSelectDocument({ onDone }: Props) { }} /> { }} dataChoose={[]} /> + + { + setShowDeleteModal(false) + onDone() + Toast.show({ type: 'small', text1: 'Berhasil menghapus data', }) + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + isDestructive + /> ) } \ No newline at end of file diff --git a/components/member/headerMemberDetail.tsx b/components/member/headerMemberDetail.tsx index 0e4d18f..6e30e10 100644 --- a/components/member/headerMemberDetail.tsx +++ b/components/member/headerMemberDetail.tsx @@ -9,7 +9,7 @@ import { useState } from "react" import { View } from "react-native" import Toast from "react-native-toast-message" import { useDispatch, useSelector } from "react-redux" -import AlertKonfirmasi from "../alertKonfirmasi" +import ModalConfirmation from "../ModalConfirmation" import ButtonMenuHeader from "../buttonMenuHeader" import DrawerBottom from "../drawerBottom" import MenuItemRow from "../menuItemRow" @@ -23,6 +23,7 @@ export default function HeaderRightMemberDetail({ active, id }: Props) { const { token, decryptToken } = useAuthSession() const [isVisible, setVisible] = useState(false) const update = useSelector((state: any) => state.memberUpdate) + const [showModal, setShowModal] = useState(false) const { colors } = useTheme(); const dispatch = useDispatch() @@ -54,13 +55,9 @@ export default function HeaderRightMemberDetail({ active, id }: Props) { title={active ? "Non Aktifkan" : "Aktifkan"} onPress={() => { setVisible(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: active ? 'Apakah anda yakin ingin menonaktifkan user?' : 'Apakah anda yakin ingin mengaktifkan user?', - onPress: () => { - handleActive() - } - }) + setTimeout(() => { + setShowModal(true) + }, 600) }} /> + + { + setShowModal(false) + handleActive() + }} + onCancel={() => setShowModal(false)} + confirmText="Konfirmasi" + cancelText="Batal" + /> ) } \ No newline at end of file diff --git a/components/project/headerProjectDetail.tsx b/components/project/headerProjectDetail.tsx index 6a9853a..117e488 100644 --- a/components/project/headerProjectDetail.tsx +++ b/components/project/headerProjectDetail.tsx @@ -9,8 +9,9 @@ import { useState } from "react" import { View } from "react-native" import Toast from "react-native-toast-message" import { useDispatch, useSelector } from "react-redux" -import AlertKonfirmasi from "../alertKonfirmasi" +import ModalConfirmation from "../ModalConfirmation" import ButtonMenuHeader from "../buttonMenuHeader" +import BorderBottomItem from "../borderBottomItem"; import DrawerBottom from "../drawerBottom" import { InputForm } from "../inputForm" import MenuItemRow from "../menuItemRow" @@ -29,6 +30,7 @@ export default function HeaderRightProjectDetail({ id, status }: Props) { const dispatch = useDispatch() const update = useSelector((state: any) => state.projectUpdate) const [isAddLink, setAddLink] = useState(false) + const [showDeleteModal, setShowDeleteModal] = useState(false) const [link, setLink] = useState("") async function handleDelete() { @@ -152,11 +154,9 @@ export default function HeaderRightProjectDetail({ id, status }: Props) { title="Hapus" onPress={() => { setVisible(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah Anda yakin ingin menghapus kegiatan ini? Kegiatan yang dihapus tidak dapat dikembalikan', - onPress: () => { handleDelete() } - }) + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> : @@ -173,6 +173,20 @@ export default function HeaderRightProjectDetail({ id, status }: Props) { } + { + setShowDeleteModal(false) + handleDelete() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + isDestructive + /> + (null) + const [showDeleteModal, setShowDeleteModal] = useState(false) const [loadingOpen, setLoadingOpen] = useState(false) async function handleLoad(loading: boolean) { @@ -185,19 +186,28 @@ export default function SectionFile({ status, member, refreshing }: { status: nu onPress={() => { if (status == 3) return setModal(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah Anda yakin ingin menghapus file ini? File yang dihapus tidak dapat dikembalikan', - onPress: () => { - handleDelete() - } - }) - + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> } + + { + setShowDeleteModal(false) + handleDelete() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + isDestructive + /> ) } \ No newline at end of file diff --git a/components/project/sectionLink.tsx b/components/project/sectionLink.tsx index 84498e1..71e5e77 100644 --- a/components/project/sectionLink.tsx +++ b/components/project/sectionLink.tsx @@ -10,7 +10,7 @@ import { useEffect, useState } from "react"; import { Linking, View } from "react-native"; import Toast from "react-native-toast-message"; import { useDispatch, useSelector } from "react-redux"; -import AlertKonfirmasi from "../alertKonfirmasi"; +import ModalConfirmation from "../ModalConfirmation"; import BorderBottomItem from "../borderBottomItem"; import DrawerBottom from "../drawerBottom"; import MenuItemRow from "../menuItemRow"; @@ -32,6 +32,7 @@ export default function SectionLink({ status, member, refreshing }: { status: nu const update = useSelector((state: any) => state.projectUpdate) const dispatch = useDispatch() const [selectLink, setSelectLink] = useState(null) + const [showDeleteModal, setShowDeleteModal] = useState(false) async function handleLoad() { try { @@ -122,19 +123,28 @@ export default function SectionLink({ status, member, refreshing }: { status: nu onPress={() => { if (status == 3) return setModal(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah Anda yakin ingin menghapus link ini? Link yang dihapus tidak dapat dikembalikan', - onPress: () => { - handleDelete() - } - }) - + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> } + + { + setShowDeleteModal(false) + handleDelete() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + isDestructive + /> } diff --git a/components/project/sectionMember.tsx b/components/project/sectionMember.tsx index 864876e..db6bf8a 100644 --- a/components/project/sectionMember.tsx +++ b/components/project/sectionMember.tsx @@ -10,7 +10,7 @@ import { useEffect, useState } from "react"; import { View } from "react-native"; import Toast from "react-native-toast-message"; import { useDispatch, useSelector } from "react-redux"; -import AlertKonfirmasi from "../alertKonfirmasi"; +import ModalConfirmation from "../ModalConfirmation"; import BorderBottomItem from "../borderBottomItem"; import DrawerBottom from "../drawerBottom"; import ImageUser from "../imageNew"; @@ -35,6 +35,8 @@ export default function SectionMember({ status, refreshing }: { status: number | const [isModal, setModal] = useState(false); const { token, decryptToken } = useAuthSession(); const { id } = useLocalSearchParams<{ id: string }>(); + const [selectLink, setSelectLink] = useState(null); + const [showDeleteModal, setShowDeleteModal] = useState(false); const [data, setData] = useState([]); const [loading, setLoading] = useState(true) const arrSkeleton = Array.from({ length: 3 }) @@ -168,16 +170,28 @@ export default function SectionMember({ status, refreshing }: { status: number | title="Keluarkan" onPress={() => { setModal(false) - AlertKonfirmasi({ - title: "Konfirmasi", - desc: "Apakah Anda yakin ingin mengeluarkan anggota?", - onPress: () => { handleDeleteMember() }, - }); + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> } + + { + setShowDeleteModal(false) + handleDeleteMember() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Keluarkan" + cancelText="Batal" + isDestructive + /> ); } diff --git a/components/project/sectionTanggalTugas.tsx b/components/project/sectionTanggalTugas.tsx index ec96d72..ad27bbe 100644 --- a/components/project/sectionTanggalTugas.tsx +++ b/components/project/sectionTanggalTugas.tsx @@ -9,7 +9,7 @@ import { useEffect, useState } from "react"; import { View } from "react-native"; import Toast from "react-native-toast-message"; import { useDispatch, useSelector } from "react-redux"; -import AlertKonfirmasi from "../alertKonfirmasi"; +import ModalConfirmation from "../ModalConfirmation"; import DrawerBottom from "../drawerBottom"; import ItemSectionTanggalTugas from "../itemSectionTanggalTugas"; import MenuItemRow from "../menuItemRow"; @@ -45,6 +45,7 @@ export default function SectionTanggalTugasProject({ status, member, refreshing id: '', status: 0, }) + const [showDeleteModal, setShowDeleteModal] = useState(false) async function handleLoad(loading: boolean) { try { @@ -215,16 +216,28 @@ export default function SectionTanggalTugasProject({ status, member, refreshing title="Hapus Tugas" onPress={() => { setModal(false) - AlertKonfirmasi({ - title: "Konfirmasi", - desc: "Apakah anda yakin ingin menghapus data ini?", - onPress: () => { handleDelete() }, - }); + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> + { + setShowDeleteModal(false) + handleDelete() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + isDestructive + /> + { setSelect(false) }} diff --git a/components/task/headerTaskDetail.tsx b/components/task/headerTaskDetail.tsx index 7f001f0..1d36ddd 100644 --- a/components/task/headerTaskDetail.tsx +++ b/components/task/headerTaskDetail.tsx @@ -9,7 +9,7 @@ import { useState } from "react" import { View } from "react-native" import Toast from "react-native-toast-message" import { useDispatch, useSelector } from "react-redux" -import AlertKonfirmasi from "../alertKonfirmasi" +import ModalConfirmation from "../ModalConfirmation" import ButtonMenuHeader from "../buttonMenuHeader" import DrawerBottom from "../drawerBottom" import { InputForm } from "../inputForm" @@ -31,6 +31,7 @@ export default function HeaderRightTaskDetail({ id, division, status, isAdminDiv const dispatch = useDispatch() const update = useSelector((state: any) => state.taskUpdate) const [isAddLink, setAddLink] = useState(false) + const [showDeleteModal, setShowDeleteModal] = useState(false) const [link, setLink] = useState("") async function handleDelete() { @@ -158,11 +159,9 @@ export default function HeaderRightTaskDetail({ id, division, status, isAdminDiv title="Hapus" onPress={() => { setVisible(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah Anda yakin ingin menghapus tugas ini? Tugas yang dihapus tidak dapat dikembalikan', - onPress: () => { handleDelete() } - }) + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> @@ -180,6 +179,20 @@ export default function HeaderRightTaskDetail({ id, division, status, isAdminDiv } + { + setShowDeleteModal(false) + handleDelete() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + isDestructive + /> + () const [data, setData] = useState([]) @@ -174,14 +178,9 @@ export default function SectionFileTask({ refreshing, isMemberDivision }: { refr title="Hapus" onPress={() => { setModal(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah Anda yakin ingin menghapus file ini? File yang dihapus tidak dapat dikembalikan', - onPress: () => { - handleDelete() - } - }) - + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> : @@ -190,6 +189,20 @@ export default function SectionFileTask({ refreshing, isMemberDivision }: { refr + + { + setShowDeleteModal(false) + handleDelete() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + isDestructive + /> ) } \ No newline at end of file diff --git a/components/task/sectionLinkTask.tsx b/components/task/sectionLinkTask.tsx index e5d568c..37bc4e1 100644 --- a/components/task/sectionLinkTask.tsx +++ b/components/task/sectionLinkTask.tsx @@ -10,7 +10,7 @@ import { useEffect, useState } from "react"; import { Linking, View } from "react-native"; import Toast from "react-native-toast-message"; import { useDispatch, useSelector } from "react-redux"; -import AlertKonfirmasi from "../alertKonfirmasi"; +import ModalConfirmation from "../ModalConfirmation"; import BorderBottomItem from "../borderBottomItem"; import DrawerBottom from "../drawerBottom"; import MenuItemRow from "../menuItemRow"; @@ -30,6 +30,7 @@ export default function SectionLinkTask({ refreshing, isMemberDivision }: { refr const update = useSelector((state: any) => state.taskUpdate) const dispatch = useDispatch() const [selectLink, setSelectLink] = useState(null) + const [showDeleteModal, setShowDeleteModal] = useState(false) const entityUser = useSelector((state: any) => state.user); async function handleLoad() { @@ -112,11 +113,9 @@ export default function SectionLinkTask({ refreshing, isMemberDivision }: { refr title="Hapus" onPress={() => { setModal(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah Anda yakin ingin menghapus link ini? Link yang dihapus tidak dapat dikembalikan', - onPress: () => { handleDelete() } - }) + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> : @@ -125,6 +124,20 @@ export default function SectionLinkTask({ refreshing, isMemberDivision }: { refr + + { + setShowDeleteModal(false) + handleDelete() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + isDestructive + /> } diff --git a/components/task/sectionMemberTask.tsx b/components/task/sectionMemberTask.tsx index cf4b74d..a878991 100644 --- a/components/task/sectionMemberTask.tsx +++ b/components/task/sectionMemberTask.tsx @@ -10,7 +10,7 @@ import { useEffect, useState } from "react"; import { View } from "react-native"; import Toast from "react-native-toast-message"; import { useDispatch, useSelector } from "react-redux"; -import AlertKonfirmasi from "../alertKonfirmasi"; +import ModalConfirmation from "../ModalConfirmation"; import BorderBottomItem from "../borderBottomItem"; import DrawerBottom from "../drawerBottom"; import ImageUser from "../imageNew"; @@ -30,6 +30,8 @@ type Props = { export default function SectionMemberTask({ refreshing, isAdminDivision }: { refreshing: boolean, isAdminDivision: boolean }) { const { colors } = useTheme() const [isModal, setModal] = useState(false); + const [selectLink, setSelectLink] = useState(null) + const [showDeleteModal, setShowDeleteModal] = useState(false) const entityUser = useSelector((state: any) => state.user); const { token, decryptToken } = useAuthSession(); const { id, detail } = useLocalSearchParams<{ id: string; detail: string }>(); @@ -132,7 +134,7 @@ export default function SectionMemberTask({ refreshing, isAdminDivision }: { ref ); }) ) : ( - + Tidak ada anggota ) @@ -177,11 +179,9 @@ export default function SectionMemberTask({ refreshing, isAdminDivision }: { ref title="Keluarkan" onPress={() => { setModal(false) - AlertKonfirmasi({ - title: "Konfirmasi", - desc: "Apakah Anda yakin ingin mengeluarkan anggota?", - onPress: () => { handleDeleteMember() }, - }); + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> : @@ -189,6 +189,20 @@ export default function SectionMemberTask({ refreshing, isAdminDivision }: { ref } + + { + setShowDeleteModal(false) + handleDeleteMember() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Keluarkan" + cancelText="Batal" + isDestructive + /> ); } diff --git a/components/task/sectionTanggalTugasTask.tsx b/components/task/sectionTanggalTugasTask.tsx index 7342503..5443caa 100644 --- a/components/task/sectionTanggalTugasTask.tsx +++ b/components/task/sectionTanggalTugasTask.tsx @@ -9,7 +9,7 @@ import { useEffect, useState } from "react"; import { View } from "react-native"; import Toast from "react-native-toast-message"; import { useDispatch, useSelector } from "react-redux"; -import AlertKonfirmasi from "../alertKonfirmasi"; +import ModalConfirmation from "../ModalConfirmation"; import DrawerBottom from "../drawerBottom"; import ItemSectionTanggalTugas from "../itemSectionTanggalTugas"; import MenuItemRow from "../menuItemRow"; @@ -44,6 +44,7 @@ export default function SectionTanggalTugasTask({ refreshing, isMemberDivision } id: '', status: 0, }) + const [showDeleteModal, setShowDeleteModal] = useState(false) async function handleLoad(loading: boolean) { try { @@ -211,14 +212,9 @@ export default function SectionTanggalTugasTask({ refreshing, isMemberDivision } title="Hapus Tugas" onPress={() => { setModal(false) - AlertKonfirmasi({ - title: 'Konfirmasi', - desc: 'Apakah anda yakin ingin menghapus data ini?', - onPress: () => { - handleDelete() - } - }) - + setTimeout(() => { + setShowDeleteModal(true) + }, 600) }} /> @@ -227,6 +223,20 @@ export default function SectionTanggalTugasTask({ refreshing, isMemberDivision } } + { + setShowDeleteModal(false) + handleDelete() + }} + onCancel={() => setShowDeleteModal(false)} + confirmText="Hapus" + cancelText="Batal" + isDestructive + /> + setSelect(false)} diff --git a/constants/Styles.ts b/constants/Styles.ts index b12a5a9..ca10a22 100644 --- a/constants/Styles.ts +++ b/constants/Styles.ts @@ -502,7 +502,7 @@ const Styles = StyleSheet.create({ iconContent: { padding: 10, borderRadius: 100, - backgroundColor:'#E5E7EB' + backgroundColor: '#E5E7EB' }, wrapHeadViewMember: { backgroundColor: '#19345E', @@ -723,7 +723,58 @@ const Styles = StyleSheet.create({ shadowOpacity: 0.2, shadowRadius: 5, elevation: 50, - } + }, + modalOverlay: { + flex: 1, + backgroundColor: 'rgba(0, 0, 0, 0.6)', + justifyContent: 'center', + alignItems: 'center', + }, + modalConfirmContainer: { + width: '80%', + borderRadius: 14, + overflow: 'hidden', + elevation: 5, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.25, + shadowRadius: 3.84, + }, + modalConfirmContent: { + padding: 20, + alignItems: 'center', + }, + modalConfirmTitle: { + fontSize: 18, + fontWeight: 'bold', + marginBottom: 8, + textAlign: 'center', + }, + modalConfirmMessage: { + fontSize: 14, + textAlign: 'center', + lineHeight: 20, + }, + modalConfirmDivider: { + height: 1, + width: '100%', + }, + modalConfirmFooter: { + flexDirection: 'row', + height: 50, + }, + modalConfirmButton: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, + modalConfirmButtonText: { + fontSize: 16, + }, + modalConfirmVerticalDivider: { + width: 1, + height: '100%', + }, }) export default Styles; \ No newline at end of file -- 2.49.1 From 6ca935483ae67de442910b4bdd205da5e75abdd5 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Sat, 14 Feb 2026 14:12:25 +0800 Subject: [PATCH 3/8] upd: fix Deskripsi - fix tinggi page saat pada tambah anggota pada fitur diskusi umum - isdetrukstif false No Issues --- app/(application)/discussion/add-member/[id].tsx | 6 +++--- .../[id]/(fitur-division)/calendar/[detail]/index.tsx | 1 - .../[id]/(fitur-division)/discussion/[detail]/index.tsx | 6 +++--- .../division/[id]/(fitur-division)/discussion/index.tsx | 1 + .../division/[id]/(fitur-division)/document/index.tsx | 1 - app/(application)/division/[id]/info.tsx | 1 - app/(application)/profile.tsx | 1 - components/announcement/headerAnnouncementDetail.tsx | 1 - components/calendar/headerCalendarDetail.tsx | 1 - components/document/menuBottomSelectDocument.tsx | 1 - components/project/headerProjectDetail.tsx | 1 - components/project/sectionFile.tsx | 1 - components/project/sectionLink.tsx | 1 - components/project/sectionMember.tsx | 1 - components/project/sectionTanggalTugas.tsx | 1 - components/task/headerTaskDetail.tsx | 1 - components/task/sectionFileTask.tsx | 1 - components/task/sectionLinkTask.tsx | 1 - components/task/sectionMemberTask.tsx | 1 - components/task/sectionTanggalTugasTask.tsx | 1 - 20 files changed, 7 insertions(+), 23 deletions(-) diff --git a/app/(application)/discussion/add-member/[id].tsx b/app/(application)/discussion/add-member/[id].tsx index 1e9f4f5..31ce33c 100644 --- a/app/(application)/discussion/add-member/[id].tsx +++ b/app/(application)/discussion/add-member/[id].tsx @@ -94,7 +94,7 @@ export default function AddMemberDiscussionDetail() { return ( - + <> { router.back() }} />, @@ -127,7 +127,7 @@ export default function AddMemberDiscussionDetail() { ) }} /> - + { @@ -188,6 +188,6 @@ export default function AddMemberDiscussionDetail() { } - + ) } \ No newline at end of file diff --git a/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx b/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx index de31cf5..d5ada9e 100644 --- a/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx @@ -321,7 +321,6 @@ export default function DetailEventCalendar() { onCancel={() => setShowDeleteModal(false)} confirmText="Keluar" cancelText="Batal" - isDestructive /> ) diff --git a/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx b/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx index 3c04209..611a2c1 100644 --- a/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx @@ -1,4 +1,3 @@ -import ModalConfirmation from "@/components/ModalConfirmation"; import AppHeader from "@/components/AppHeader"; import BorderBottomItem from "@/components/borderBottomItem"; import BorderBottomItem2 from "@/components/borderBottomItem2"; @@ -8,6 +7,7 @@ import ImageUser from "@/components/imageNew"; import { InputForm } from "@/components/inputForm"; import LabelStatus from "@/components/labelStatus"; import MenuItemRow from "@/components/menuItemRow"; +import ModalConfirmation from "@/components/ModalConfirmation"; import Skeleton from "@/components/skeleton"; import SkeletonContent from "@/components/skeletonContent"; import Text from "@/components/Text"; @@ -23,9 +23,9 @@ import { } from "@/lib/api"; import { getDB } from "@/lib/firebaseDatabase"; import { useAuthSession } from "@/providers/AuthProvider"; +import { useTheme } from "@/providers/ThemeProvider"; import { Feather, Ionicons, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"; import { ref } from "@react-native-firebase/database"; -import { useTheme } from "@/providers/ThemeProvider"; import { useHeaderHeight } from '@react-navigation/elements'; import { router, Stack, useLocalSearchParams } from "expo-router"; import { useEffect, useState } from "react"; @@ -386,6 +386,7 @@ export default function DiscussionDetail() { desc={item.comment} rightBottomInfo={item.isEdited ? "Edited" : ""} descEllipsize={detailMore.includes(item.id) ? false : true} + bgColor="transparent" onPress={() => { setDetailMore((prev: any) => { if (prev.includes(item.id)) { @@ -562,7 +563,6 @@ export default function DiscussionDetail() { onCancel={() => setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" - isDestructive /> ); diff --git a/app/(application)/division/[id]/(fitur-division)/discussion/index.tsx b/app/(application)/division/[id]/(fitur-division)/discussion/index.tsx index c4bac68..43e9f9c 100644 --- a/app/(application)/division/[id]/(fitur-division)/discussion/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/discussion/index.tsx @@ -192,6 +192,7 @@ export default function DiscussionDivision() { } rightBottomInfo={item.total_komentar + ' Komentar'} + bgColor="transparent" /> ) }} diff --git a/app/(application)/division/[id]/(fitur-division)/document/index.tsx b/app/(application)/division/[id]/(fitur-division)/document/index.tsx index c7dc164..1a2b057 100644 --- a/app/(application)/division/[id]/(fitur-division)/document/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/document/index.tsx @@ -619,7 +619,6 @@ export default function DocumentDivision() { onCancel={() => setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" - isDestructive /> ); diff --git a/app/(application)/division/[id]/info.tsx b/app/(application)/division/[id]/info.tsx index c473939..16e6f47 100644 --- a/app/(application)/division/[id]/info.tsx +++ b/app/(application)/division/[id]/info.tsx @@ -298,7 +298,6 @@ export default function InformationDivision() { onCancel={() => setShowDeleteModal(false)} confirmText="Keluar" cancelText="Batal" - isDestructive /> ) diff --git a/app/(application)/profile.tsx b/app/(application)/profile.tsx index 22ada5d..ff7e115 100644 --- a/app/(application)/profile.tsx +++ b/app/(application)/profile.tsx @@ -160,7 +160,6 @@ export default function Profile() { onCancel={() => setShowLogoutModal(false)} confirmText="Keluar" cancelText="Batal" - isDestructive /> ) diff --git a/components/announcement/headerAnnouncementDetail.tsx b/components/announcement/headerAnnouncementDetail.tsx index 7141706..336fcef 100644 --- a/components/announcement/headerAnnouncementDetail.tsx +++ b/components/announcement/headerAnnouncementDetail.tsx @@ -80,7 +80,6 @@ export default function HeaderRightAnnouncementDetail({ id }: Props) { onCancel={() => setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" - isDestructive /> ) diff --git a/components/calendar/headerCalendarDetail.tsx b/components/calendar/headerCalendarDetail.tsx index 1584986..b7a2839 100644 --- a/components/calendar/headerCalendarDetail.tsx +++ b/components/calendar/headerCalendarDetail.tsx @@ -91,7 +91,6 @@ export default function HeaderRightCalendarDetail({ id, idReminder }: Props) { onCancel={() => setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" - isDestructive /> ) diff --git a/components/document/menuBottomSelectDocument.tsx b/components/document/menuBottomSelectDocument.tsx index 76bb956..6bf9fe2 100644 --- a/components/document/menuBottomSelectDocument.tsx +++ b/components/document/menuBottomSelectDocument.tsx @@ -219,7 +219,6 @@ export default function MenuBottomSelectDocument({ onDone }: Props) { onCancel={() => setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" - isDestructive /> ) diff --git a/components/project/headerProjectDetail.tsx b/components/project/headerProjectDetail.tsx index 117e488..3f5a562 100644 --- a/components/project/headerProjectDetail.tsx +++ b/components/project/headerProjectDetail.tsx @@ -184,7 +184,6 @@ export default function HeaderRightProjectDetail({ id, status }: Props) { onCancel={() => setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" - isDestructive /> setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" - isDestructive /> ) diff --git a/components/project/sectionLink.tsx b/components/project/sectionLink.tsx index 71e5e77..f24e511 100644 --- a/components/project/sectionLink.tsx +++ b/components/project/sectionLink.tsx @@ -143,7 +143,6 @@ export default function SectionLink({ status, member, refreshing }: { status: nu onCancel={() => setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" - isDestructive /> } diff --git a/components/project/sectionMember.tsx b/components/project/sectionMember.tsx index db6bf8a..5050309 100644 --- a/components/project/sectionMember.tsx +++ b/components/project/sectionMember.tsx @@ -190,7 +190,6 @@ export default function SectionMember({ status, refreshing }: { status: number | onCancel={() => setShowDeleteModal(false)} confirmText="Keluarkan" cancelText="Batal" - isDestructive /> ); diff --git a/components/project/sectionTanggalTugas.tsx b/components/project/sectionTanggalTugas.tsx index ad27bbe..a5619fa 100644 --- a/components/project/sectionTanggalTugas.tsx +++ b/components/project/sectionTanggalTugas.tsx @@ -235,7 +235,6 @@ export default function SectionTanggalTugasProject({ status, member, refreshing onCancel={() => setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" - isDestructive /> setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" - isDestructive /> setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" - isDestructive /> ) diff --git a/components/task/sectionLinkTask.tsx b/components/task/sectionLinkTask.tsx index 37bc4e1..7ed3ae1 100644 --- a/components/task/sectionLinkTask.tsx +++ b/components/task/sectionLinkTask.tsx @@ -136,7 +136,6 @@ export default function SectionLinkTask({ refreshing, isMemberDivision }: { refr onCancel={() => setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" - isDestructive /> } diff --git a/components/task/sectionMemberTask.tsx b/components/task/sectionMemberTask.tsx index a878991..7e2cc45 100644 --- a/components/task/sectionMemberTask.tsx +++ b/components/task/sectionMemberTask.tsx @@ -201,7 +201,6 @@ export default function SectionMemberTask({ refreshing, isAdminDivision }: { ref onCancel={() => setShowDeleteModal(false)} confirmText="Keluarkan" cancelText="Batal" - isDestructive /> ); diff --git a/components/task/sectionTanggalTugasTask.tsx b/components/task/sectionTanggalTugasTask.tsx index 5443caa..e365afd 100644 --- a/components/task/sectionTanggalTugasTask.tsx +++ b/components/task/sectionTanggalTugasTask.tsx @@ -234,7 +234,6 @@ export default function SectionTanggalTugasTask({ refreshing, isMemberDivision } onCancel={() => setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" - isDestructive /> Date: Sat, 14 Feb 2026 15:43:38 +0800 Subject: [PATCH 4/8] upd: redesign Deskripsi: - login dan konfirmasi kode otp - firebase code env No Issues --- app.config.js | 6 +++ app/verification.tsx | 14 ++++-- assets/images/cogniti-dark.png | Bin 0 -> 1115 bytes assets/images/cogniti-light.png | Bin 0 -> 1040 bytes assets/images/logo-dark.png | Bin 0 -> 52665 bytes components/auth/viewLogin.tsx | 25 +++++++---- components/auth/viewVerification.tsx | 34 +++++++++------ components/buttonForm.tsx | 5 ++- components/inputForm.tsx | 2 +- components/toastCustom.tsx | 21 ++++++--- constants/Colors.ts | 4 +- constants/ConstEnv.ts | 11 ++++- constants/Styles.ts | 12 ++++-- lib/api.ts | 61 ++++++--------------------- lib/pushToPage.ts | 4 ++ lib/useNotification.ts | 27 +++++++----- 16 files changed, 128 insertions(+), 98 deletions(-) create mode 100644 assets/images/cogniti-dark.png create mode 100644 assets/images/cogniti-light.png create mode 100644 assets/images/logo-dark.png diff --git a/app.config.js b/app.config.js index f8aba53..d782fc9 100644 --- a/app.config.js +++ b/app.config.js @@ -79,6 +79,12 @@ export default { URL_FIREBASE_DB: process.env.URL_FIREBASE_DB, PASS_ENC: process.env.PASS_ENC, WA_SERVER_TOKEN: process.env.WA_SERVER_TOKEN, + FIREBASE_API_KEY: process.env.FIREBASE_API_KEY, + FIREBASE_AUTH_DOMAIN: process.env.FIREBASE_AUTH_DOMAIN, + FIREBASE_PROJECT_ID: process.env.FIREBASE_PROJECT_ID, + FIREBASE_STORAGE_BUCKET: process.env.FIREBASE_STORAGE_BUCKET, + FIREBASE_MESSAGING_SENDER_ID: process.env.FIREBASE_MESSAGING_SENDER_ID, + FIREBASE_APP_ID: process.env.FIREBASE_APP_ID, } } }; diff --git a/app/verification.tsx b/app/verification.tsx index d0290d1..98e5769 100644 --- a/app/verification.tsx +++ b/app/verification.tsx @@ -20,10 +20,16 @@ export default function Index() { const { signIn } = useAuthSession(); const login = (): void => { - const random: string = 'contohLoginMobileDarmasaba'; - var mytexttoEncryption = "contohLoginMobileDarmasaba" - const encrypted = CryptoES.AES.encrypt(mytexttoEncryption, ConstEnv.pass_encrypt).toString(); - signIn(encrypted); + // WARNING: This is a hardcoded bypass for development purposes. + // It should be removed or secured before production release. + if (__DEV__) { + const random: string = 'contohLoginMobileDarmasaba'; + var mytexttoEncryption = "contohLoginMobileDarmasaba" + const encrypted = CryptoES.AES.encrypt(mytexttoEncryption, ConstEnv.pass_encrypt).toString(); + signIn(encrypted); + } else { + console.warn("Bypass login disabled in production."); + } } return ( diff --git a/assets/images/cogniti-dark.png b/assets/images/cogniti-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..60de6fd72fe1c3098bada6d18dc0df666c4e8a0d GIT binary patch literal 1115 zcmV-h1f=_kP)iK~#7F?OMHV z6G0FjBMLf>5{Y6hQPB{1g3A-cG*n12JyH^hKF$N6BpuzECrIAFra+WLfDqjsN+=;F z1&WlAVaFNU;idii2lzqfNXbkL7Pc5qTo1%7Hn!-hzu$DB8SA)yQG7*S_i zW^!6tG=JQ?-?9NI{C>5I80a4v-YjYO7%xXl6;3+fs5)0m@)H%;!me_=LXhpAim)sy z!T3a73pB>n;1>EtO+B1r93M3X{dJ}oMqE(Fm)Dt=utHRVafxai4e6k8Gq_BP^IGT@ zWygj{P4QIQ75O?ZMdjMq_&T~jl22IOhL(rx64r27SLGWbk3w|z?!C5aT*7KYd*A_% zrc$CfEl+uBh!$qB9!jL>nNFDZD+l7FCEKv0%29uF!iw@uU6;dVp|Ip`t*EpO@WAeT z2mBg~WZDhep7NP}?5gC3;uE%f-UILGT`0zmCw*VV4a@)}tlU(}x<{r0r>)`-Rh@ks z5XRer{BFSA$yEF{%m5^;^xJwx1jn$?;NMYL@4SJ8m7Ow01jpz!1r+UY|AM@b5mdAB z--Cpe)6ihKONUIn(?%5P_^OH@Kz@`rAdD55Gmx-){o3{kg?tLKuWv*$#kW1>jeRs# zau@RJ?j|}85Nu-;X0+jW5W^(E%j4p)!~KZ|0`q&qd5MR!ha-0^Lbnf^hv)Rj$z&BpTB5#OS^Z}hla34Gql)`WkLh1fkyRuMNW$2C$9*LPgp)o zMc6-whxqfsV{~xRK;6VI<;T*7M84j0D2L33UCSZAajODnS#Esn9xX$$S3&u0m@+Po zj^O0!Gr*G*SN2AL>RPgBkThBF{>gNBA8R|>wvRfDub&bm1PS?QAdr2wn6qD&Bf)9Q zTqg5zZTJRL#M#GBeNitCzdQ4JpgjTiM|Zh~v}DpIy#|+i$X?A+s6S01f47>2?i^hl zaZ)PKjLRQVzB^0>Mwiqqds-{E;xd`3z%>@ z`XEUS<^a5g#vjZ}V-$5C@v3`IB!r&dYgW;R4R0-RJIl#3o z1uSMNl4!%A!wOAt8p(9piWQC4`8q7%2j=fdi;!aIx1|L}K=%#53!R0i6@jClLrA=1 hH9`}d@SiBxjlZ4GwV+*ex(omS002ovPDHLkV1gIE4T}H( literal 0 HcmV?d00001 diff --git a/assets/images/cogniti-light.png b/assets/images/cogniti-light.png new file mode 100644 index 0000000000000000000000000000000000000000..b2515ccef940a1a12a6520897356a8fb55043632 GIT binary patch literal 1040 zcmV+r1n>KaP)`20@j z(t4Q8G2Ai{&_y}1{6T4LR5vU0-hj0gDgi=%cnrC?jzIjsMqU>WbKWUF&i-AK7En?e&`ysh}yJAQ^+#Gdb zm9ZUhtTj?M3Uy(W@gqf59v>QU!loYnz8FXMo1-qQ zMb|NxM2ZGssodHYeIMY*vH8`kZ)gH_p(yP@^tDkBb`SpQp^y5i?!vyO|46hIt^p)0 zUsPhtBQ*sl7wuQ5FJ#9E<9oyPKK$iQA=+!W29U5kCk-PwO0)}H9x?OI6-ZcqQ!0W} zh_-4NFAMH}s0*23Iv=+lBrN|772*yZ3ihAxfL!Q^wkvhC5ym#=3?!_~Uti!C%I<=` zvcAM{Tu`W1S&lyHd3P7?4TQa>aC6YOAV?Tl64{zEVa{!dm3cT+rnQ()R}Gr6d2`gu zs0`yX3VMh=AZmlqgCEKXh(m#@Hl@08Zwk&9CUfi1(dXmlm@!IG z@$Jbv)|Q+nr=eQjm9pq1>7o*xOmVMyX`7hLpBy9v3HcghF;c@hnu5bYeOqvE5IFZY zW{B3x>Y1n;cYUX(XZQ0K?wDPrAqm?-b>`A9?QjWh&_H>%S|D6+x;RaRzl6g09JOHm zDXK-xV$;Db5cX>^JW&e&v3kBhKP`TSxSH|%W}XD~1wJ$5=z(NQm=916)vWypw`)^D zBGcQdO#1*gjF`e-F04PWKqKh$p|??au4~j+=q}~}H<=0)D$y)x!Jy3wed6>c({4)( zP62m*U4Vhgwae7O3j~lNPSjAkSil2?Y8OZ1@hvdDp_GF)<`AdJqAYTAxgG8R0000< KMNUMnLSTXx$JHbN literal 0 HcmV?d00001 diff --git a/assets/images/logo-dark.png b/assets/images/logo-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..7512623972acd108267391e0d2777b47e8bb80ac GIT binary patch literal 52665 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 literal 0 HcmV?d00001 diff --git a/components/auth/viewLogin.tsx b/components/auth/viewLogin.tsx index 2b9e4f3..8173817 100644 --- a/components/auth/viewLogin.tsx +++ b/components/auth/viewLogin.tsx @@ -53,11 +53,10 @@ export default function ViewLogin({ onValidate }: Props) { return ( - +62} info="Kami akan mengirim kode verifikasi melalui WhatsApp, guna mengonfirmasikan nomor Anda." /> - { handleCheckPhone() }} - disabled={disableLogin} - /> + + { handleCheckPhone() }} + disabled={disableLogin} + /> + + + + + { loadingLogin && } + ) diff --git a/components/auth/viewVerification.tsx b/components/auth/viewVerification.tsx index fc921a9..0dc96bf 100644 --- a/components/auth/viewVerification.tsx +++ b/components/auth/viewVerification.tsx @@ -5,7 +5,7 @@ import { useTheme } from "@/providers/ThemeProvider"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { StatusBar } from "expo-status-bar"; import { useState } from "react"; -import { Image, Platform, View } from "react-native"; +import { Image, SafeAreaView, View } from "react-native"; import { OtpInput } from "react-native-otp-entry"; import Toast from 'react-native-toast-message'; import { ButtonForm } from "../buttonForm"; @@ -58,13 +58,12 @@ export default function ViewVerification({ phone, otp }: Props) { } return ( - <> + - - + - { onCheckOtp() }} - /> - - Tidak Menerima kode verifikasi? { resendOtp() }}>Kirim Ulang + + { onCheckOtp() }} + /> + + + Tidak Menerima kode verifikasi? { resendOtp() }} style={[{ color: colors.tint }]}>Kirim Ulang + + + + - + + ) } \ No newline at end of file diff --git a/components/buttonForm.tsx b/components/buttonForm.tsx index 551f60f..9ee7a1d 100644 --- a/components/buttonForm.tsx +++ b/components/buttonForm.tsx @@ -1,5 +1,5 @@ -import { ColorsStatus } from "@/constants/ColorsStatus"; import Styles from "@/constants/Styles"; +import { useTheme } from "@/providers/ThemeProvider"; import { TouchableOpacity } from "react-native"; import Text from './Text'; @@ -10,8 +10,9 @@ type Props = { }; export function ButtonForm({ text, onPress, disabled }: Props) { + const { colors } = useTheme(); return ( - + {text} ); diff --git a/components/inputForm.tsx b/components/inputForm.tsx index ff17cdf..c12509b 100644 --- a/components/inputForm.tsx +++ b/components/inputForm.tsx @@ -74,7 +74,7 @@ export function InputForm({ label, value, placeholder, onChange, info, disable, /> {error && ({errorText})} - {info != undefined && ({info})} + {info != undefined && ({info})} ) } diff --git a/components/toastCustom.tsx b/components/toastCustom.tsx index 3004826..6385743 100644 --- a/components/toastCustom.tsx +++ b/components/toastCustom.tsx @@ -7,12 +7,19 @@ import Text from "./Text"; export default function ToastCustom({ position }: { position?: 'top' | 'bottom' }) { const { colors } = useTheme() return ( - Toast.hide()} visibilityTime={1500} position={position || 'bottom'} config={{ - small: ({ text1 }) => ( - - {text1} - - ) - }} /> + Toast.hide()} + visibilityTime={1500} + position={position || 'bottom'} + bottomOffset={80} + config={{ + small: ({ text1 }) => ( + + {text1} + + ) + }} + /> ) } \ No newline at end of file diff --git a/constants/Colors.ts b/constants/Colors.ts index 520ca2b..22215ae 100644 --- a/constants/Colors.ts +++ b/constants/Colors.ts @@ -6,7 +6,7 @@ export const Colors = { text: '#11181C', background: '#f7f7f7ff', tint: tintColorLight, - primary: '#19345E', + primary: '#1F3C88', icon: '#1F3C88', card: '#ffffff', tabIconDefault: '#687076', @@ -24,7 +24,7 @@ export const Colors = { text: '#ECEDEE', background: '#0F1B2D', tint: tintColorDark, - primary: '#19345E', + primary: '#123A6F', icon: '#9DB9E8', card: '#16233A', // slightly lighter than background #151718 tabIconDefault: '#9BA1A6', diff --git a/constants/ConstEnv.ts b/constants/ConstEnv.ts index 923221e..dc45423 100644 --- a/constants/ConstEnv.ts +++ b/constants/ConstEnv.ts @@ -2,5 +2,14 @@ import Constants from 'expo-constants'; export const ConstEnv = { url_storage: Constants?.expoConfig?.extra?.URL_STORAGE, - pass_encrypt: Constants?.expoConfig?.extra?.PASS_ENC + pass_encrypt: Constants?.expoConfig?.extra?.PASS_ENC, + firebase: { + apiKey: Constants?.expoConfig?.extra?.FIREBASE_API_KEY, + authDomain: Constants?.expoConfig?.extra?.FIREBASE_AUTH_DOMAIN, + projectId: Constants?.expoConfig?.extra?.FIREBASE_PROJECT_ID, + storageBucket: Constants?.expoConfig?.extra?.FIREBASE_STORAGE_BUCKET, + messagingSenderId: Constants?.expoConfig?.extra?.FIREBASE_MESSAGING_SENDER_ID, + appId: Constants?.expoConfig?.extra?.FIREBASE_APP_ID, + databaseURL: Constants?.expoConfig?.extra?.URL_FIREBASE_DB, + } } \ No newline at end of file diff --git a/constants/Styles.ts b/constants/Styles.ts index ca10a22..c6bc640 100644 --- a/constants/Styles.ts +++ b/constants/Styles.ts @@ -88,6 +88,9 @@ const Styles = StyleSheet.create({ mb15: { marginBottom: 15 }, + mb20: { + marginBottom: 20 + }, mb30: { marginBottom: 30 }, @@ -130,6 +133,9 @@ const Styles = StyleSheet.create({ mt15: { marginTop: 15 }, + mt30: { + marginTop: 30 + }, mr05: { marginRight: 5 }, @@ -291,9 +297,9 @@ const Styles = StyleSheet.create({ borderWidth: 1, }, btnRound: { - backgroundColor: '#19345E', + backgroundColor: '#1F3C88', borderWidth: 0, - borderColor: '#19345E', + borderColor: '#1F3C88', alignItems: 'center', borderRadius: 30, marginTop: 15, @@ -309,7 +315,7 @@ const Styles = StyleSheet.create({ }, btnLainnya: { alignSelf: 'flex-start', - backgroundColor: '#19345E', + backgroundColor: '#1F3C88', paddingVertical: 5, marginVertical: 5 }, diff --git a/lib/api.ts b/lib/api.ts index b81d90f..9ce5413 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -96,30 +96,18 @@ export const apiGetGroup = async ({ user, active, search }: { user: string, acti }; export const apiCreateGroup = async (data: { user: string, name: string }) => { - await api.post('mobile/group', data).then(response => { - return response.data; - }) - .catch(error => { - console.error('Error:', error); - }); + const response = await api.post('mobile/group', data); + return response.data; }; export const apiEditGroup = async (data: { user: string, name: string }, id: string) => { - await api.put(`mobile/group/${id}`, data).then(response => { - return response.data; - }) - .catch(error => { - console.error('Error:', error); - }); + const response = await api.put(`mobile/group/${id}`, data); + return response.data; }; export const apiDeleteGroup = async (data: { user: string, isActive: boolean }, id: string) => { - await api.delete(`mobile/group/${id}`, { data }).then(response => { - return response.data; - }) - .catch(error => { - console.error('Error:', error); - }); + const response = await api.delete(`mobile/group/${id}`, { data }); + return response.data; }; export const apiGetPosition = async ({ user, active, search, group }: { user: string, active: string, search: string, group?: string }) => { @@ -128,22 +116,14 @@ export const apiGetPosition = async ({ user, active, search, group }: { user: st }; export const apiCreatePosition = async (data: { user: string, name: string, idGroup: string }) => { - await api.post('mobile/position', data).then(response => { - return response.data; - }) - .catch(error => { - console.error('Error:', error); - }); + const response = await api.post('mobile/position', data); + return response.data; }; export const apiDeletePosition = async (data: { user: string, isActive: boolean }, id: string) => { - await api.delete(`mobile/position/${id}`, { data }).then(response => { - return response.data; - }) - .catch(error => { - console.error('Error:', error); - }); + const response = await api.delete(`mobile/position/${id}`, { data }); + return response.data; }; export const apiEditPosition = async (data: { user: string, name: string, idGroup: string }, id: string) => { @@ -207,12 +187,8 @@ export const apiUpdateDiscussionGeneralCommentar = async ({ id, data }: { id: st export const apiDeleteMemberDiscussionGeneral = async (data: { user: string, idUser: string }, id: string) => { - await api.delete(`mobile/discussion-general/${id}/member`, { data }).then(response => { - return response.data; - }) - .catch(error => { - console.error('Error:', error); - }); + const response = await api.delete(`mobile/discussion-general/${id}/member`, { data }); + return response.data; }; @@ -222,19 +198,10 @@ export const apiUpdateStatusDiscussionGeneral = async ({ id, data }: { id: strin }; export const apiDeleteDiscussionGeneral = async (data: { user: string, active: boolean }, id: string) => { - await api.delete(`mobile/discussion-general/${id}`, { data }).then(response => { - return response.data; - }) - .catch(error => { - console.error('Error:', error); - }); + const response = await api.delete(`mobile/discussion-general/${id}`, { data }); + return response.data; }; -// export const apiEditDiscussionGeneral = async (data: { user: string, title: string, desc: string }, id: string) => { -// const response = await api.put(`/mobile/discussion-general/${id}`, data) -// return response.data; -// }; - export const apiEditDiscussionGeneral = async (data: FormData, id: string) => { const response = await api.put(`/mobile/discussion-general/${id}`, data, { headers: { diff --git a/lib/pushToPage.ts b/lib/pushToPage.ts index e510536..ed23ffc 100644 --- a/lib/pushToPage.ts +++ b/lib/pushToPage.ts @@ -23,6 +23,10 @@ export function pushToPage(category: string, idContent: string) { return router.push(`/member/${idContent}`) } else if (cat[0] == 'project') { return router.push(`/project/${idContent}`) + } else if (cat[0] == 'group') { + return router.push(`/group`) + } else if (cat[0] == 'position') { + return router.push(`/position`) } } } \ No newline at end of file diff --git a/lib/useNotification.ts b/lib/useNotification.ts index b1dd761..4af6e44 100644 --- a/lib/useNotification.ts +++ b/lib/useNotification.ts @@ -8,23 +8,28 @@ import * as Notifications from 'expo-notifications'; import { useEffect } from 'react'; import { PermissionsAndroid, Platform } from 'react-native'; -// Firebase config +import { ConstEnv } from '@/constants/ConstEnv'; + const RNfirebaseConfig = { - apiKey: "AIzaSyB2hbsW91J3oRQx4_jgrCCNY0tNt5-21e8", - authDomain: "googleapis.com", - projectId: "mobile-darmasaba", - storageBucket: "mobile-darmasaba.appspot.com", - messagingSenderId: "867439221179", - appId: "1:867439221179:android:4509f77478c8dce99b0c9e", - databaseURL: "https://mobile-darmasaba-default-rtdb.asia-southeast1.firebasedatabase.app/" + apiKey: ConstEnv.firebase.apiKey, + authDomain: ConstEnv.firebase.authDomain, + projectId: ConstEnv.firebase.projectId, + storageBucket: ConstEnv.firebase.storageBucket, + messagingSenderId: ConstEnv.firebase.messagingSenderId, + appId: ConstEnv.firebase.appId, + databaseURL: ConstEnv.firebase.databaseURL }; const initializeFirebase = async () => { try { - const app = getApps().length ? getApp() : initializeApp(RNfirebaseConfig); + let app; + const apps = getApps(); + if (apps.length) { + app = getApp() as any; + } else { + app = initializeApp(RNfirebaseConfig) as any; + } const mess = getMessaging(app); - // await registerDeviceForRemoteMessages(mess); - // `registerDeviceForRemoteMessages` tidak perlu lagi await setAutoInitEnabled(mess, true); return mess; -- 2.49.1 From 42cb7c8f8e2797facddbf1c71d261a092b2f1de4 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Sat, 14 Feb 2026 15:54:25 +0800 Subject: [PATCH 5/8] fix: coding --- providers/AuthProvider.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/providers/AuthProvider.tsx b/providers/AuthProvider.tsx index bff0a8a..ac1f23c 100644 --- a/providers/AuthProvider.tsx +++ b/providers/AuthProvider.tsx @@ -40,9 +40,8 @@ export default function AuthProvider({ children }: { children: ReactNode }): Rea }, []); const decryptToken = (async (token: string) => { - var C = require("crypto-js"); - var Decrypted = C.AES.decrypt(token, ConstEnv.pass_encrypt); - var result = Decrypted.toString(C.enc.Utf8); + var Decrypted = CryptoES.AES.decrypt(token, ConstEnv.pass_encrypt); + var result = Decrypted.toString(CryptoES.enc.Utf8); return result }) -- 2.49.1 From 64aaafa2bee2fe14afd92ba6e3fe161a1e93d782 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Wed, 18 Feb 2026 15:45:45 +0800 Subject: [PATCH 6/8] upd: setting Deskripsi: - buat halaman setting - isinya edit profile, ganti tema aplikasi, nonaktifkan notifikasi, sign out No Issues --- app/(application)/_layout.tsx | 12 ++ app/(application)/edit-profile.tsx | 48 ++++--- app/(application)/profile.tsx | 92 +------------- app/(application)/setting/index.tsx | 186 ++++++++++++++++++++++++++++ components/buttonSetting.tsx | 33 +++++ lib/useNotification.ts | 27 +++- 6 files changed, 294 insertions(+), 104 deletions(-) create mode 100644 app/(application)/setting/index.tsx create mode 100644 components/buttonSetting.tsx diff --git a/app/(application)/_layout.tsx b/app/(application)/_layout.tsx index 57cced1..5d77e94 100644 --- a/app/(application)/_layout.tsx +++ b/app/(application)/_layout.tsx @@ -112,6 +112,18 @@ export default function RootLayout() { ) }} /> + { router.back() }} />, + title: 'Pengaturan', + headerTitleAlign: 'center', + // headerRight: () => + header: () => ( + router.back()} + /> + ) + }} /> { router.back() }} />, title: 'Anggota', diff --git a/app/(application)/edit-profile.tsx b/app/(application)/edit-profile.tsx index 6b32dda..3157aa4 100644 --- a/app/(application)/edit-profile.tsx +++ b/app/(application)/edit-profile.tsx @@ -1,4 +1,4 @@ -import ButtonBackHeader from "@/components/buttonBackHeader"; +import AppHeader from "@/components/AppHeader"; import ButtonSaveHeader from "@/components/buttonSaveHeader"; import { InputForm } from "@/components/inputForm"; import ModalSelect from "@/components/modalSelect"; @@ -219,24 +219,40 @@ export default function EditProfile() { ( - { - router.back(); - }} - /> - ), + // headerLeft: () => ( + // { + // router.back(); + // }} + // /> + // ), headerTitle: "Edit Profile", headerTitleAlign: "center", - headerRight: () => ( - { - handleEdit() - }} + header: () => ( + router.back()} + right={ + { + handleEdit() + }} + /> + } /> - ), + ) + // headerRight: () => ( + // { + // handleEdit() + // }} + // /> + // ), }} /> state.entities) const [error, setError] = useState(false) const [preview, setPreview] = useState(false) - const [showThemeModal, setShowThemeModal] = useState(false) - const [showLogoutModal, setShowLogoutModal] = useState(false) - - const ThemeOption = ({ label, value, icon }: { label: string, value: 'light' | 'dark' | 'system', icon: string }) => ( - { - setTheme(value); - setShowThemeModal(false); - }} - > - - - {label} - - {theme === value && } - - ); return ( @@ -54,9 +33,9 @@ export default function Profile() { onPressLeft={() => router.back()} right={ } + item={} onPress={() => { - setShowLogoutModal(true) + router.push('/setting') }} /> } @@ -81,31 +60,8 @@ export default function Profile() { {entities.role} - - Tampilan - - - setShowThemeModal(true)} - style={[Styles.wrapItemBorderAll, { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderColor: colors.icon + '40', backgroundColor: colors.background }]} - > - - - Tema Aplikasi - - - - {theme === 'light' ? 'Terang' : theme === 'dark' ? 'Gelap' : 'Sistem'} - - - - - - + Informasi - { - entities.idUserRole != "developer" && { router.push('/edit-profile') }} style={[Styles.textLink]}>Edit - } {/* Note: ItemDetailMember might need updates to support dynamic colors if it uses default text colors */} @@ -118,29 +74,6 @@ export default function Profile() { - setShowThemeModal(false)} - > - setShowThemeModal(false)}> - - - Pilih Tema - setShowThemeModal(false)}> - - - - - - - - - - - - setPreview(false)} doubleTapToZoomEnabled /> - - { - setShowLogoutModal(false) - signOut() - }} - onCancel={() => setShowLogoutModal(false)} - confirmText="Keluar" - cancelText="Batal" - /> ) } \ No newline at end of file diff --git a/app/(application)/setting/index.tsx b/app/(application)/setting/index.tsx new file mode 100644 index 0000000..0a25d8e --- /dev/null +++ b/app/(application)/setting/index.tsx @@ -0,0 +1,186 @@ +import ModalConfirmation from "@/components/ModalConfirmation"; +import Text from "@/components/Text"; +import ButtonSetting from "@/components/buttonSetting"; +import DrawerBottom from "@/components/drawerBottom"; +import Styles from "@/constants/Styles"; +import { apiRegisteredToken, apiUnregisteredToken } from "@/lib/api"; +import { checkPermission, getToken, openSettings, requestPermission } from "@/lib/useNotification"; +import { useAuthSession } from "@/providers/AuthProvider"; +import { useTheme } from "@/providers/ThemeProvider"; +import { Feather, Ionicons } from "@expo/vector-icons"; +import { router } from "expo-router"; +import { useCallback, useEffect, useState } from "react"; +import { AppState, AppStateStatus, Pressable, View } from "react-native"; +import { useSelector } from "react-redux"; + +export default function ListSetting() { + const { theme, setTheme, colors } = useTheme() + const { signOut } = useAuthSession() + const [isNotificationEnabled, setIsNotificationEnabled] = useState(null); + const entities = useSelector((state: any) => state.entities) + const [modalVisible, setModalVisible] = useState(false); + const [modalConfig, setModalConfig] = useState({ + title: '', + message: '', + confirmText: 'Buka Pengaturan', + onConfirm: () => { } + }); + + const [showLogoutModal, setShowLogoutModal] = useState(false) + const [showThemeModal, setShowThemeModal] = useState(false) + + const registerToken = async () => { + try { + const token = await getToken(); + if (token) { + await apiRegisteredToken({ user: entities.id, token }); + } + } catch (error) { + console.warn('Error registering token:', error); + } + }; + + const unregisterToken = async () => { + try { + const token = await getToken(); + if (token) { + await apiUnregisteredToken({ user: entities.id, token }); + } + } catch (error) { + console.warn('Error unregistering token:', error); + } + }; + + const checkNotif = useCallback(async () => { + const status = await checkPermission(); + setIsNotificationEnabled((prev) => { + if (prev === false && status === true) { + registerToken(); + } else if (prev === true && status === false) { + unregisterToken(); + } + return !!status; + }); + }, [entities.id]); + + useEffect(() => { + checkNotif(); + + const subscription = AppState.addEventListener('change', (nextAppState: AppStateStatus) => { + if (nextAppState === 'active') { + checkNotif(); + } + }); + + return () => { + subscription.remove(); + }; + }, [checkNotif]); + + const handleToggleNotif = async () => { + if (isNotificationEnabled) { + setModalConfig({ + title: "Matikan Notifikasi?", + message: "Anda akan diarahkan ke pengaturan sistem untuk mematikan notifikasi.", + confirmText: "Buka Pengaturan", + onConfirm: () => { + setModalVisible(false); + openSettings(); + } + }); + setModalVisible(true); + } else { + const granted = await requestPermission(); + if (granted) { + setIsNotificationEnabled(true); + registerToken(); + } else { + setModalConfig({ + title: "Aktifkan Notifikasi?", + message: "Izin notifikasi tidak diberikan. Buka pengaturan sistem untuk mengaktifkannya?", + confirmText: "Buka Pengaturan", + onConfirm: () => { + setModalVisible(false); + openSettings(); + } + }); + setModalVisible(true); + } + } + }; + + const ThemeOption = ({ label, value, icon }: { label: string, value: 'light' | 'dark' | 'system', icon: string }) => ( + { + setTheme(value); + }} + > + + + {label} + + {theme === value && } + + ); + + return ( + + + { + entities.idUserRole != "developer" && + } + onPress={() => { router.push('/edit-profile') }} + /> + } + } + onPress={() => setShowThemeModal(true)} + value={theme === 'light' ? 'Terang' : theme === 'dark' ? 'Gelap' : 'Sistem'} + /> + } + onPress={handleToggleNotif} + value={isNotificationEnabled === null ? 'Memuat...' : isNotificationEnabled ? 'Aktif' : 'Nonaktif'} + /> + } + onPress={() => setShowLogoutModal(true)} + borderBottom={false} + /> + + + setModalVisible(false)} + /> + + { + setShowLogoutModal(false) + signOut() + }} + onCancel={() => setShowLogoutModal(false)} + confirmText="Keluar" + cancelText="Batal" + /> + + + + + + + ) +} \ No newline at end of file diff --git a/components/buttonSetting.tsx b/components/buttonSetting.tsx new file mode 100644 index 0000000..419b227 --- /dev/null +++ b/components/buttonSetting.tsx @@ -0,0 +1,33 @@ +import Styles from "@/constants/Styles"; +import { useTheme } from "@/providers/ThemeProvider"; +import { ReactNode } from "react"; +import { Pressable, View } from "react-native"; +import Text from "./Text"; + +type Props = { + title: string + onPress?: () => void, + icon?: ReactNode, + borderBottom?: boolean + value?: string +} + +export default function ButtonSetting({ title, onPress, icon, borderBottom = true, value }: Props) { + const { colors } = useTheme(); + return ( + + + + {icon} + {title} + + {value && {value}} + + + ) + +} \ No newline at end of file diff --git a/lib/useNotification.ts b/lib/useNotification.ts index 4af6e44..7a9ac7c 100644 --- a/lib/useNotification.ts +++ b/lib/useNotification.ts @@ -6,8 +6,7 @@ import { } from '@react-native-firebase/messaging'; import * as Notifications from 'expo-notifications'; import { useEffect } from 'react'; -import { PermissionsAndroid, Platform } from 'react-native'; - +import { Linking, PermissionsAndroid, Platform } from 'react-native'; import { ConstEnv } from '@/constants/ConstEnv'; const RNfirebaseConfig = { @@ -38,6 +37,30 @@ const initializeFirebase = async () => { } }; +export const checkPermission = async () => { + try { + if (Platform.OS === 'android') { + return await PermissionsAndroid.check( + PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS + ); + } else if (Platform.OS === 'ios') { + const { status } = await Notifications.getPermissionsAsync(); + return status === 'granted'; + } + } catch (err) { + console.warn('Error checking notification permissions:', err); + return false; + } +}; + +export const openSettings = () => { + if (Platform.OS === 'ios') { + Linking.openURL('app-settings:'); + } else { + Linking.openSettings(); + } +}; + export const requestPermission = async () => { try { if (Platform.OS === 'android') { -- 2.49.1 From 31b7cf6a30c9f30d8b709c750e5105c933bfe5b9 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Wed, 18 Feb 2026 16:35:04 +0800 Subject: [PATCH 7/8] upd: refresh control deskripsi: - warna refresh control pada semua fitur - warna bottom pada modal select No Issues --- app/(application)/announcement/[id].tsx | 2 +- app/(application)/announcement/index.tsx | 2 +- app/(application)/banner/index.tsx | 2 +- app/(application)/discussion/[id].tsx | 2 +- app/(application)/discussion/index.tsx | 4 +- .../calendar/[detail]/index.tsx | 1 + .../[id]/(fitur-division)/calendar/index.tsx | 1 + .../discussion/[detail]/index.tsx | 1 + .../(fitur-division)/discussion/index.tsx | 1 + .../[id]/(fitur-division)/document/index.tsx | 1 + .../(fitur-division)/task/[detail]/index.tsx | 1 + .../[id]/(fitur-division)/task/index.tsx | 2 + app/(application)/division/[id]/index.tsx | 1 + app/(application)/division/[id]/info.tsx | 1 + app/(application)/division/index.tsx | 2 + app/(application)/group/index.tsx | 2 +- app/(application)/home.tsx | 2 +- app/(application)/member/[id].tsx | 2 +- app/(application)/member/index.tsx | 2 +- app/(application)/notification.tsx | 11 ++-- app/(application)/position/index.tsx | 2 +- app/(application)/profile.tsx | 39 ++++++++++++-- app/(application)/project/[id]/index.tsx | 2 +- app/(application)/project/index.tsx | 2 + app/(application)/search.tsx | 2 +- components/borderBottomItemVertical.tsx | 53 +++++++++++++++++++ components/modalSelect.tsx | 4 +- constants/Styles.ts | 3 +- 28 files changed, 124 insertions(+), 26 deletions(-) create mode 100644 components/borderBottomItemVertical.tsx diff --git a/app/(application)/announcement/[id].tsx b/app/(application)/announcement/[id].tsx index f07dca5..88c3158 100644 --- a/app/(application)/announcement/[id].tsx +++ b/app/(application)/announcement/[id].tsx @@ -200,7 +200,7 @@ export default function DetailAnnouncement() { handleRefresh()} - tintColor={colors.primary} + tintColor={colors.icon} /> } > diff --git a/app/(application)/announcement/index.tsx b/app/(application)/announcement/index.tsx index 4319656..b24646c 100644 --- a/app/(application)/announcement/index.tsx +++ b/app/(application)/announcement/index.tsx @@ -130,7 +130,7 @@ export default function Announcement() { } /> diff --git a/app/(application)/banner/index.tsx b/app/(application)/banner/index.tsx index f388242..dc2507f 100644 --- a/app/(application)/banner/index.tsx +++ b/app/(application)/banner/index.tsx @@ -133,7 +133,7 @@ export default function BannerList() { } style={[Styles.h100, { backgroundColor: colors.background }]} diff --git a/app/(application)/discussion/[id].tsx b/app/(application)/discussion/[id].tsx index 09d3351..82d064b 100644 --- a/app/(application)/discussion/[id].tsx +++ b/app/(application)/discussion/[id].tsx @@ -247,7 +247,7 @@ export default function DetailDiscussionGeneral() { handleRefresh()} - tintColor={colors.primary} + tintColor={colors.icon} /> } > diff --git a/app/(application)/discussion/index.tsx b/app/(application)/discussion/index.tsx index 3f01cb6..a8d0f44 100644 --- a/app/(application)/discussion/index.tsx +++ b/app/(application)/discussion/index.tsx @@ -166,7 +166,7 @@ export default function Discussion() { leftBottomInfo={ - Diskusikan + Diskusikan } rightBottomInfo={`${item.total_komentar} Komentar`} @@ -182,7 +182,7 @@ export default function Discussion() { } /> diff --git a/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx b/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx index d5ada9e..5bdf203 100644 --- a/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx @@ -180,6 +180,7 @@ export default function DetailEventCalendar() { } > diff --git a/app/(application)/division/[id]/(fitur-division)/calendar/index.tsx b/app/(application)/division/[id]/(fitur-division)/calendar/index.tsx index 4e4b654..7664f62 100644 --- a/app/(application)/division/[id]/(fitur-division)/calendar/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/calendar/index.tsx @@ -155,6 +155,7 @@ export default function CalendarDivision() { } style={[Styles.h100]} diff --git a/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx b/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx index 611a2c1..805d8a6 100644 --- a/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx @@ -315,6 +315,7 @@ export default function DiscussionDetail() { } > diff --git a/app/(application)/division/[id]/(fitur-division)/discussion/index.tsx b/app/(application)/division/[id]/(fitur-division)/discussion/index.tsx index 43e9f9c..742bc1c 100644 --- a/app/(application)/division/[id]/(fitur-division)/discussion/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/discussion/index.tsx @@ -204,6 +204,7 @@ export default function DiscussionDivision() { } /> diff --git a/app/(application)/division/[id]/(fitur-division)/document/index.tsx b/app/(application)/division/[id]/(fitur-division)/document/index.tsx index 1a2b057..5db917c 100644 --- a/app/(application)/division/[id]/(fitur-division)/document/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/document/index.tsx @@ -415,6 +415,7 @@ export default function DocumentDivision() { }> diff --git a/app/(application)/division/[id]/(fitur-division)/task/[detail]/index.tsx b/app/(application)/division/[id]/(fitur-division)/task/[detail]/index.tsx index 8eecb3c..9a27a0b 100644 --- a/app/(application)/division/[id]/(fitur-division)/task/[detail]/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/task/[detail]/index.tsx @@ -127,6 +127,7 @@ export default function DetailTaskDivision() { } > diff --git a/app/(application)/division/[id]/(fitur-division)/task/index.tsx b/app/(application)/division/[id]/(fitur-division)/task/index.tsx index c708325..24a8d53 100644 --- a/app/(application)/division/[id]/(fitur-division)/task/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/task/index.tsx @@ -235,6 +235,7 @@ export default function ListTask() { } /> @@ -301,6 +302,7 @@ export default function ListTask() { } /> diff --git a/app/(application)/division/[id]/index.tsx b/app/(application)/division/[id]/index.tsx index 57d2bc2..3736681 100644 --- a/app/(application)/division/[id]/index.tsx +++ b/app/(application)/division/[id]/index.tsx @@ -78,6 +78,7 @@ export default function DetailDivisionFitur() { } showsVerticalScrollIndicator={false} diff --git a/app/(application)/division/[id]/info.tsx b/app/(application)/division/[id]/info.tsx index 16e6f47..0132cb9 100644 --- a/app/(application)/division/[id]/info.tsx +++ b/app/(application)/division/[id]/info.tsx @@ -186,6 +186,7 @@ export default function InformationDivision() { } style={[Styles.h100, { backgroundColor: colors.background }]} diff --git a/app/(application)/division/index.tsx b/app/(application)/division/index.tsx index d48fabd..f305370 100644 --- a/app/(application)/division/index.tsx +++ b/app/(application)/division/index.tsx @@ -253,6 +253,7 @@ export default function ListDivision() { } /> @@ -288,6 +289,7 @@ export default function ListDivision() { } /> diff --git a/app/(application)/group/index.tsx b/app/(application)/group/index.tsx index 88def9e..27306cf 100644 --- a/app/(application)/group/index.tsx +++ b/app/(application)/group/index.tsx @@ -190,7 +190,7 @@ export default function Index() { } /> diff --git a/app/(application)/home.tsx b/app/(application)/home.tsx index e8f0dc5..6e0a310 100644 --- a/app/(application)/home.tsx +++ b/app/(application)/home.tsx @@ -67,7 +67,7 @@ export default function Home() { } showsVerticalScrollIndicator={false} diff --git a/app/(application)/member/[id].tsx b/app/(application)/member/[id].tsx index ae575cf..bd12512 100644 --- a/app/(application)/member/[id].tsx +++ b/app/(application)/member/[id].tsx @@ -99,7 +99,7 @@ export default function MemberDetail() { } > diff --git a/app/(application)/member/index.tsx b/app/(application)/member/index.tsx index b789b78..35ede2d 100644 --- a/app/(application)/member/index.tsx +++ b/app/(application)/member/index.tsx @@ -171,7 +171,7 @@ export default function Index() { } /> diff --git a/app/(application)/notification.tsx b/app/(application)/notification.tsx index 7c56cd8..12c5f96 100644 --- a/app/(application)/notification.tsx +++ b/app/(application)/notification.tsx @@ -1,4 +1,5 @@ import BorderBottomItem from "@/components/borderBottomItem"; +import BorderBottomItemVertical from "@/components/borderBottomItemVertical"; import SkeletonTwoItem from "@/components/skeletonTwoItem"; import Text from "@/components/Text"; import { ColorsStatus } from "@/constants/ColorsStatus"; @@ -116,11 +117,11 @@ export default function Notification() { getItem={getItem} renderItem={({ item, index }: { item: Props, index: number }) => { return ( - - + + } title={item.title} @@ -129,8 +130,8 @@ export default function Notification() { textColor={item.isRead ? 'gray' : colors.text} onPress={() => { handleReadNotification(item.id, item.category, item.idContent) - }} + bgColor={'transparent'} /> ) }} @@ -142,7 +143,7 @@ export default function Notification() { } /> diff --git a/app/(application)/position/index.tsx b/app/(application)/position/index.tsx index f1a5c51..5b775d9 100644 --- a/app/(application)/position/index.tsx +++ b/app/(application)/position/index.tsx @@ -216,7 +216,7 @@ export default function Index() { } /> diff --git a/app/(application)/profile.tsx b/app/(application)/profile.tsx index 501ffb9..d25cd22 100644 --- a/app/(application)/profile.tsx +++ b/app/(application)/profile.tsx @@ -5,20 +5,42 @@ import Text from "@/components/Text"; import { assetUserImage } from "@/constants/AssetsError"; import { ConstEnv } from "@/constants/ConstEnv"; import Styles from "@/constants/Styles"; +import { apiGetProfile } from "@/lib/api"; +import { setEntities } from "@/lib/entitiesSlice"; +import { useAuthSession } from "@/providers/AuthProvider"; import { useTheme } from "@/providers/ThemeProvider"; -import { Feather, Ionicons } from "@expo/vector-icons"; +import { Feather } from "@expo/vector-icons"; import { LinearGradient } from "expo-linear-gradient"; import { router, Stack } from "expo-router"; import { useState } from "react"; -import { Image, Modal, Pressable, SafeAreaView, ScrollView, TouchableOpacity, View } from "react-native"; +import { Image, Pressable, RefreshControl, SafeAreaView, ScrollView, View } from "react-native"; import ImageViewing from 'react-native-image-viewing'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; export default function Profile() { const { colors } = useTheme(); const entities = useSelector((state: any) => state.entities) const [error, setError] = useState(false) const [preview, setPreview] = useState(false) + const [refreshing, setRefreshing] = useState(false) + const dispatch = useDispatch() + const { token, decryptToken } = useAuthSession() + + async function handleUserLogin() { + const hasil = await decryptToken(String(token?.current)) + apiGetProfile({ id: hasil }) + .then((data) => dispatch(setEntities(data.data))) + .catch((error) => { + console.error(error) + }); + } + + const handleRefresh = async () => { + setRefreshing(true) + handleUserLogin() + await new Promise(resolve => setTimeout(resolve, 2000)); + setRefreshing(false) + }; return ( @@ -43,7 +65,16 @@ export default function Profile() { ) }} /> - + + } + style={[Styles.h100, { backgroundColor: colors.background }]} + > } > diff --git a/app/(application)/project/index.tsx b/app/(application)/project/index.tsx index c08b44a..adcd1d7 100644 --- a/app/(application)/project/index.tsx +++ b/app/(application)/project/index.tsx @@ -275,6 +275,7 @@ export default function ListProject() { } /> @@ -353,6 +354,7 @@ export default function ListProject() { } /> diff --git a/app/(application)/search.tsx b/app/(application)/search.tsx index 7bc0e47..4412abe 100644 --- a/app/(application)/search.tsx +++ b/app/(application)/search.tsx @@ -103,7 +103,7 @@ export default function Search() { } > diff --git a/components/borderBottomItemVertical.tsx b/components/borderBottomItemVertical.tsx new file mode 100644 index 0000000..56e3403 --- /dev/null +++ b/components/borderBottomItemVertical.tsx @@ -0,0 +1,53 @@ +import Styles from "@/constants/Styles"; +import { useTheme } from "@/providers/ThemeProvider"; +import React, { useState } from "react"; +import { Dimensions, Pressable, View } from "react-native"; +import Text from "./Text"; + +type Props = { + title?: string + icon: React.ReactNode + desc?: string + rightTopInfo?: string + onPress?: () => void + onLongPress?: () => void + borderType: 'all' | 'bottom' | 'none' + titleWeight?: 'normal' | 'bold' + bgColor?: string + descEllipsize?: boolean + textColor?: string, + titleShowAll?: boolean +} + +export default function BorderBottomItemVertical({ title, icon, desc, onPress, onLongPress, rightTopInfo, borderType, titleWeight, bgColor, descEllipsize, textColor, titleShowAll }: Props) { + const { colors } = useTheme(); + const textColorFix = textColor ? textColor : colors.text; + + return ( + [ + borderType == 'bottom' + ? [Styles.wrapItemBorderBottom, { borderBottomColor: colors.icon + '20' }] + : borderType == 'all' + ? [Styles.wrapItemBorderAll, { borderColor: colors.icon + '20' }] + : Styles.wrapItemBorderNone, + bgColor == "transparent" ? { backgroundColor: 'transparent' } : { backgroundColor: colors.card }, + ]} + > + + {icon} + + + + {title} + + { + rightTopInfo && {rightTopInfo} + } + + {desc && {desc}} + + + + ) +} \ No newline at end of file diff --git a/components/modalSelect.tsx b/components/modalSelect.tsx index ea0f500..2b68035 100644 --- a/components/modalSelect.tsx +++ b/components/modalSelect.tsx @@ -162,7 +162,7 @@ export default function ModalSelect({ open, close, title, category, idParent, on category != 'status-task' ? data.length > 0 ? data.map((item: any, index: any) => ( - { onChoose(item.id, item.name, item.img) }}> + { onChoose(item.id, item.name, item.img) }}> { category == 'member' ? @@ -188,7 +188,7 @@ export default function ModalSelect({ open, close, title, category, idParent, on <> { dataStatus.map((item: any, index: any) => ( - { + { onSelect(item) close(false) }}> diff --git a/constants/Styles.ts b/constants/Styles.ts index c6bc640..6abc6d6 100644 --- a/constants/Styles.ts +++ b/constants/Styles.ts @@ -479,7 +479,7 @@ const Styles = StyleSheet.create({ borderColor: '#d6d8f6', borderWidth: 1, borderRadius: 5, - marginBottom: 10 + marginBottom: 5 }, wrapItemBorderNone: { padding: 10, @@ -584,7 +584,6 @@ const Styles = StyleSheet.create({ padding: 10, flexDirection: 'row', justifyContent: 'space-between', - borderColor: '#d6d8f6', borderBottomWidth: 1, alignItems: 'center' }, -- 2.49.1 From 4681f0a0cc753cbbdfa6926a275bf1844fa2a6f0 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Wed, 18 Feb 2026 17:26:19 +0800 Subject: [PATCH 8/8] upd: redesign --- app/(application)/announcement/[id].tsx | 6 +- app/(application)/announcement/index.tsx | 6 +- components/borderBottomItem.tsx | 11 +-- components/borderBottomItemVertical.tsx | 6 +- components/division/FiturGridItem.tsx | 38 ++++++++++ components/division/fiturDivisionDetail.tsx | 84 +++++++-------------- constants/Styles.ts | 12 +++ 7 files changed, 89 insertions(+), 74 deletions(-) create mode 100644 components/division/FiturGridItem.tsx diff --git a/app/(application)/announcement/[id].tsx b/app/(application)/announcement/[id].tsx index 88c3158..da4a254 100644 --- a/app/(application)/announcement/[id].tsx +++ b/app/(application)/announcement/[id].tsx @@ -205,7 +205,7 @@ export default function DetailAnnouncement() { } > - + { loading ? @@ -243,7 +243,7 @@ export default function DetailAnnouncement() { { dataFile.length > 0 && ( - + File @@ -268,7 +268,7 @@ export default function DetailAnnouncement() { ) } - + { loading ? arrSkeleton.map((item, index) => { diff --git a/app/(application)/announcement/index.tsx b/app/(application)/announcement/index.tsx index b24646c..d362143 100644 --- a/app/(application)/announcement/index.tsx +++ b/app/(application)/announcement/index.tsx @@ -112,9 +112,9 @@ export default function Announcement() { borderType="bottom" bgColor="transparent" icon={ - - - + // + + // } title={item.title} desc={item.desc.replace(/<[^>]*>?/gm, '').replace(/\r?\n|\r/g, ' ')} diff --git a/components/borderBottomItem.tsx b/components/borderBottomItem.tsx index 40b06bd..acc64c6 100644 --- a/components/borderBottomItem.tsx +++ b/components/borderBottomItem.tsx @@ -18,17 +18,14 @@ type Props = { rightBottomInfo?: React.ReactNode | string titleWeight?: 'normal' | 'bold' bgColor?: string - width?: number descEllipsize?: boolean textColor?: string, colorPress?: boolean titleShowAll?: boolean } -export default function BorderBottomItem({ title, subtitle, icon, desc, onPress, onLongPress, rightTopInfo, borderType, leftBottomInfo, rightBottomInfo, titleWeight, bgColor, width, descEllipsize, textColor, colorPress, titleShowAll }: Props) { - const lebarDim = Dimensions.get("window").width; +export default function BorderBottomItem({ title, subtitle, icon, desc, onPress, onLongPress, rightTopInfo, borderType, leftBottomInfo, rightBottomInfo, titleWeight, bgColor, descEllipsize, textColor, colorPress, titleShowAll }: Props) { const { colors } = useTheme(); - const lebar = width ? lebarDim * width / 100 : 'auto'; const textColorFix = textColor ? textColor : colors.text; const [isTap, setIsTap] = useState(false); @@ -50,8 +47,8 @@ export default function BorderBottomItem({ title, subtitle, icon, desc, onPress, > {icon} - - + + {title} { subtitle && @@ -81,7 +78,7 @@ export default function BorderBottomItem({ title, subtitle, icon, desc, onPress, } { typeof rightBottomInfo == 'string' ? - {rightBottomInfo} + {rightBottomInfo} : rightBottomInfo } diff --git a/components/borderBottomItemVertical.tsx b/components/borderBottomItemVertical.tsx index 56e3403..94a32c4 100644 --- a/components/borderBottomItemVertical.tsx +++ b/components/borderBottomItemVertical.tsx @@ -1,7 +1,7 @@ import Styles from "@/constants/Styles"; import { useTheme } from "@/providers/ThemeProvider"; import React, { useState } from "react"; -import { Dimensions, Pressable, View } from "react-native"; +import { Pressable, View } from "react-native"; import Text from "./Text"; type Props = { @@ -36,9 +36,9 @@ export default function BorderBottomItemVertical({ title, icon, desc, onPress, o > {icon} - + - + {title} { diff --git a/components/division/FiturGridItem.tsx b/components/division/FiturGridItem.tsx new file mode 100644 index 0000000..e9844e1 --- /dev/null +++ b/components/division/FiturGridItem.tsx @@ -0,0 +1,38 @@ +import Styles from "@/constants/Styles"; +import { useTheme } from "@/providers/ThemeProvider"; +import React from "react"; +import { Pressable, View } from "react-native"; +import Text from "../Text"; + +type Props = { + title: string; + subtitle: string; + icon: React.ReactNode; + onPress: () => void; +}; + +export default function FiturGridItem({ title, subtitle, icon, onPress }: Props) { + const { colors } = useTheme(); + + return ( + [ + Styles.wrapGridItem, + { + backgroundColor: colors.card, + borderColor: colors.icon + '20', + }, + pressed && { opacity: 0.7 } + ]} + > + + {icon} + + + {title} + {subtitle} + + + ); +} diff --git a/components/division/fiturDivisionDetail.tsx b/components/division/fiturDivisionDetail.tsx index ba15e92..1ee740d 100644 --- a/components/division/fiturDivisionDetail.tsx +++ b/components/division/fiturDivisionDetail.tsx @@ -6,9 +6,9 @@ import { AntDesign, Feather, MaterialIcons, SimpleLineIcons } from "@expo/vector import { router, useLocalSearchParams } from "expo-router" import { useEffect, useState } from "react" import { View } from "react-native" -import BorderBottomItem from "../borderBottomItem" import { useTheme } from "@/providers/ThemeProvider" import Text from "../Text" +import FiturGridItem from "./FiturGridItem" type Props = { tugas: number @@ -50,66 +50,34 @@ export default function FiturDivisionDetail({ refreshing }: { refreshing: boolea return ( Fitur - - - - - - } - title="Tugas" - subtitle={`${data.tugas} Tugas`} - width={30} - onPress={() => { router.push(`/division/${id}/task?status=0`) }} - /> + + } + onPress={() => { router.push(`/division/${id}/task?status=0`) }} + /> - - - - } - title="Dokumen" - subtitle={`${data.dokumen} File`} - width={30} - onPress={() => { router.push(`/division/${id}/document`) }} - /> - + } + onPress={() => { router.push(`/division/${id}/document`) }} + /> - - - - - } - title="Diskusi" - subtitle={`${data.diskusi} Diskusi`} - width={30} - onPress={() => { router.push(`/division/${id}/discussion?active=true`) }} - /> + } + onPress={() => { router.push(`/division/${id}/discussion?active=true`) }} + /> - - - - } - title="Kalender" - subtitle={`${data.kalender} Acara`} - width={30} - onPress={() => { router.push(`/division/${id}/calendar`) }} - /> - + } + onPress={() => { router.push(`/division/${id}/calendar`) }} + /> ) diff --git a/constants/Styles.ts b/constants/Styles.ts index 6abc6d6..39267a7 100644 --- a/constants/Styles.ts +++ b/constants/Styles.ts @@ -780,6 +780,18 @@ const Styles = StyleSheet.create({ width: 1, height: '100%', }, + wrapGridItem: { + borderWidth: 1, + borderRadius: 5, + padding: 10, + flexDirection: 'row', + alignItems: 'center', + width: '48.5%', + marginBottom: 10, + }, + flex1: { + flex: 1 + } }) export default Styles; \ No newline at end of file -- 2.49.1