diff --git a/app/(application)/(user)/_layout.tsx b/app/(application)/(user)/_layout.tsx index 77b5c98..ff4bbaf 100644 --- a/app/(application)/(user)/_layout.tsx +++ b/app/(application)/(user)/_layout.tsx @@ -150,6 +150,38 @@ export default function UserLayout() { {/* ========== End Collaboration Section ========= */} + {/* ========== Voting Section ========= */} + , + }} + /> + , + }} + /> + , + }} + /> + , + }} + /> + + {/* ========== End Voting Section ========= */} + {/* ========== Job Section ========= */} + ( - - ), + tabBarIcon: ({ color }) => , }} /> ( - - ), + tabBarIcon: ({ color }) => , }} /> ( - - ), + tabBarIcon: ({ color }) => , }} /> ( - - ), + tabBarIcon: ({ color }) => , }} /> ); } - diff --git a/app/(application)/(user)/voting/(tabs)/_layout.tsx b/app/(application)/(user)/voting/(tabs)/_layout.tsx new file mode 100644 index 0000000..d026f05 --- /dev/null +++ b/app/(application)/(user)/voting/(tabs)/_layout.tsx @@ -0,0 +1,43 @@ +import { + IconContribution, + IconHistory, + IconHome, + IconStatus, +} from "@/components/_Icon"; +import { TabsStyles } from "@/styles/tabs-styles"; +import { Tabs } from "expo-router"; + +export default function VotingTabsLayout() { + return ( + + , + }} + /> + , + }} + /> + , + }} + /> + , + }} + /> + + ); +} diff --git a/app/(application)/(user)/voting/(tabs)/contribution.tsx b/app/(application)/(user)/voting/(tabs)/contribution.tsx new file mode 100644 index 0000000..d25d423 --- /dev/null +++ b/app/(application)/(user)/voting/(tabs)/contribution.tsx @@ -0,0 +1,17 @@ +import { + ViewWrapper +} from "@/components"; +import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection"; + +export default function VotingContribution() { + return ( + + {Array.from({ length: 5 }).map((_, index) => ( + + ))} + + ); +} diff --git a/app/(application)/(user)/voting/(tabs)/history.tsx b/app/(application)/(user)/voting/(tabs)/history.tsx new file mode 100644 index 0000000..23a7387 --- /dev/null +++ b/app/(application)/(user)/voting/(tabs)/history.tsx @@ -0,0 +1,33 @@ +import { ViewWrapper } from "@/components"; +import TabsTwoHeaderCustom from "@/components/_ShareComponent/TabsTwoHeaderCustom"; +import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection"; +import { useState } from "react"; + +export default function VotingHistory() { + const [activeCategory, setActiveCategory] = useState("all"); + + const handlePress = (item: any) => { + setActiveCategory(item); + // tambahkan logika lain seperti filter dsb. + }; + + return ( + + } + > + {Array.from({ length: 10 }).map((_, index) => ( + + ))} + + ); +} diff --git a/app/(application)/(user)/voting/(tabs)/index.tsx b/app/(application)/(user)/voting/(tabs)/index.tsx new file mode 100644 index 0000000..cc1a1a7 --- /dev/null +++ b/app/(application)/(user)/voting/(tabs)/index.tsx @@ -0,0 +1,23 @@ +import { + FloatingButton, + SearchInput, + ViewWrapper +} from "@/components"; +import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection"; +import { router } from "expo-router"; + +export default function VotingBeranda() { + return ( + router.push("/voting/create")} /> + } + headerComponent={} + > + {Array.from({ length: 5 }).map((_, index) => ( + + ))} + + ); +} diff --git a/app/(application)/(user)/voting/(tabs)/status.tsx b/app/(application)/(user)/voting/(tabs)/status.tsx new file mode 100644 index 0000000..006dae6 --- /dev/null +++ b/app/(application)/(user)/voting/(tabs)/status.tsx @@ -0,0 +1,60 @@ +import { + BadgeCustom, + BaseBox, + ScrollableCustom, + StackCustom, + TextCustom, + ViewWrapper, +} from "@/components"; +import { masterStatus } from "@/lib/dummy-data/_master/status"; +import dayjs from "dayjs"; +import { useState } from "react"; + +export default function VotingStatus() { + const [activeCategory, setActiveCategory] = useState( + "publish" + ); + + const handlePress = (item: any) => { + setActiveCategory(item.value); + // tambahkan logika lain seperti filter dsb. + }; + + const scrollComponent = ( + ({ + id: i, + label: e.label, + value: e.value, + }))} + onButtonPress={handlePress} + activeId={activeCategory as any} + /> + ); + + return ( + + {Array.from({ length: 10 }).map((_, i) => ( + + + + Lorem ipsum dolor sit {activeCategory} + + + {dayjs().format("DD/MM/YYYY")} -{" "} + {dayjs().add(1, "day").format("DD/MM/YYYY")} + + + + ))} + + ); +} diff --git a/app/(application)/(user)/voting/[id]/[status]/detail.tsx b/app/(application)/(user)/voting/[id]/[status]/detail.tsx new file mode 100644 index 0000000..bd20106 --- /dev/null +++ b/app/(application)/(user)/voting/[id]/[status]/detail.tsx @@ -0,0 +1,108 @@ +import { + AlertDefaultSystem, + BackButton, + DotButton, + DrawerCustom, + MenuDrawerDynamicGrid, + Spacing, + ViewWrapper, +} from "@/components"; +import { IconArchive, IconContribution, IconEdit } from "@/components/_Icon"; +import { IMenuDrawerItem } from "@/components/_Interface/types"; +import { Voting_BoxDetailSection } from "@/screens/Voting/BoxDetailSection"; +import Voting_ButtonStatusSection from "@/screens/Voting/ButtonStatusSection"; +import { router, Stack, useLocalSearchParams } from "expo-router"; +import { useState } from "react"; + +export default function VotingDetailStatus() { + const { id, status } = useLocalSearchParams(); + const [openDrawerDraft, setOpenDrawerDraft] = useState(false); + const [openDrawerPublish, setOpenDrawerPublish] = useState(false); + + const handlePressDraft = (item: IMenuDrawerItem) => { + console.log("PATH >> ", item.path); + router.navigate(item.path as any); + setOpenDrawerDraft(false); + }; + + const handlePressPublish = (item: IMenuDrawerItem) => { + if (item.path === "") { + AlertDefaultSystem({ + title: "Update Arsip", + message: "Apakah Anda yakin ingin mengarsipkan voting ini?", + textLeft: "Batal", + textRight: "Ya", + onPressRight: () => { + console.log("Hapus"); + router.back(); + }, + }); + } + router.navigate(item.path as any); + setOpenDrawerPublish(false); + }; + + return ( + <> + , + headerRight: () => + status === "draft" ? ( + setOpenDrawerDraft(true)} /> + ) : status === "publish" ? ( + setOpenDrawerPublish(true)} /> + ) : null, + }} + /> + + + + + + + {/* ========= Draft Drawer ========= */} + setOpenDrawerDraft(false)} + height={"auto"} + > + , + label: "Edit", + path: `/voting/${id}/edit`, + }, + ]} + columns={4} + onPressItem={handlePressDraft as any} + /> + + + {/* ========= Publish Drawer ========= */} + setOpenDrawerPublish(false)} + height={"auto"} + > + , + label: "Daftar Kontributor", + path: `/voting/${id}/list-of-contributor`, + }, + { + icon: , + label: "Update Arsip", + path: "" as any, + }, + ]} + onPressItem={handlePressPublish as any} + /> + + + ); +} diff --git a/app/(application)/(user)/voting/[id]/contribution.tsx b/app/(application)/(user)/voting/[id]/contribution.tsx new file mode 100644 index 0000000..34a05b1 --- /dev/null +++ b/app/(application)/(user)/voting/[id]/contribution.tsx @@ -0,0 +1,65 @@ +import { + AvatarUsernameAndOtherComponent, + BackButton, + DotButton, + DrawerCustom, + MenuDrawerDynamicGrid, + Spacing, + ViewWrapper, +} from "@/components"; +import { IconContribution } from "@/components/_Icon"; +import { IMenuDrawerItem } from "@/components/_Interface/types"; +import { Voting_BoxDetailContributionSection } from "@/screens/Voting/BoxDetailContribution"; +import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection"; +import { router, Stack, useLocalSearchParams } from "expo-router"; +import { useState } from "react"; + +export default function VotingDetailContribution() { + const { id } = useLocalSearchParams(); + const [openDrawerPublish, setOpenDrawerPublish] = useState(false); + + const handlePressPublish = (item: IMenuDrawerItem) => { + router.navigate(item.path as any); + setOpenDrawerPublish(false); + }; + + return ( + <> + , + headerRight: () => ( + setOpenDrawerPublish(true)} /> + ), + }} + /> + + + } + /> + + + + + {/* ========= Publish Drawer ========= */} + setOpenDrawerPublish(false)} + height={"auto"} + > + , + label: "Daftar Kontributor", + path: `/voting/${id}/list-of-contributor`, + }, + ]} + onPressItem={handlePressPublish as any} + /> + + + ); +} diff --git a/app/(application)/(user)/voting/[id]/edit.tsx b/app/(application)/(user)/voting/[id]/edit.tsx new file mode 100644 index 0000000..3dc0724 --- /dev/null +++ b/app/(application)/(user)/voting/[id]/edit.tsx @@ -0,0 +1,78 @@ +import { + BoxButtonOnFooter, + ButtonCenteredOnly, + ButtonCustom, + Grid, + Spacing, + StackCustom, + TextAreaCustom, + TextInputCustom, + ViewWrapper, +} from "@/components"; +import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom"; +import { MainColor } from "@/constants/color-palet"; +import { Ionicons } from "@expo/vector-icons"; +import { router } from "expo-router"; +import { TouchableOpacity } from "react-native"; + +export default function VotingEdit() { + const buttonSubmit = () => { + return ( + <> + + + router.back() + } + > + Update + + + + ); + }; + + return ( + + + + + + + + + + + + + console.log("delete")}> + + + + + + console.log("add")}> + Tambah Pilihan + + + + + ); +} diff --git a/app/(application)/(user)/voting/[id]/index.tsx b/app/(application)/(user)/voting/[id]/index.tsx new file mode 100644 index 0000000..5bd3f44 --- /dev/null +++ b/app/(application)/(user)/voting/[id]/index.tsx @@ -0,0 +1,87 @@ +import { + AlertDefaultSystem, + AvatarUsernameAndOtherComponent, + BackButton, + DotButton, + DrawerCustom, + InformationBox, + MenuDrawerDynamicGrid, + StackCustom, + ViewWrapper, +} from "@/components"; +import { IconArchive, IconContribution } from "@/components/_Icon"; +import { IMenuDrawerItem } from "@/components/_Interface/types"; +import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection"; +import { Voting_BoxDetailPublishSection } from "@/screens/Voting/BoxDetailPublishSection"; +import { router, Stack, useLocalSearchParams } from "expo-router"; +import React, { useState } from "react"; + +export default function VotingDetail() { + const { id } = useLocalSearchParams(); + const [openDrawerPublish, setOpenDrawerPublish] = useState(false); + const handlePressPublish = (item: IMenuDrawerItem) => { + if (item.path === "") { + AlertDefaultSystem({ + title: "Update Arsip", + message: "Apakah Anda yakin ingin mengarsipkan voting ini?", + textLeft: "Batal", + textRight: "Ya", + onPressRight: () => { + console.log("Hapus"); + router.back(); + }, + }); + } + router.navigate(item.path as any); + setOpenDrawerPublish(false); + }; + + return ( + <> + , + headerRight: () => ( + setOpenDrawerPublish(true)} /> + ), + }} + /> + + + + + + } + /> + + + + + + {/* ========= Publish Drawer ========= */} + setOpenDrawerPublish(false)} + height={"auto"} + > + , + label: "Daftar Kontributor", + path: `/voting/${id}/list-of-contributor`, + }, + { + icon: , + label: "Update Arsip", + path: "" as any, + }, + ]} + onPressItem={handlePressPublish as any} + /> + + + ); +} diff --git a/app/(application)/(user)/voting/[id]/list-of-contributor.tsx b/app/(application)/(user)/voting/[id]/list-of-contributor.tsx new file mode 100644 index 0000000..eab08a5 --- /dev/null +++ b/app/(application)/(user)/voting/[id]/list-of-contributor.tsx @@ -0,0 +1,26 @@ +import { + AvatarUsernameAndOtherComponent, + BadgeCustom, + BaseBox, + ViewWrapper, +} from "@/components"; + +export default function Voting_ListOfContributor() { + return ( + + {Array.from({ length: 10 }).map((_, index) => ( + + + Pilihan {index + 1} + + } + /> + + ))} + + ); +} diff --git a/app/(application)/(user)/voting/create.tsx b/app/(application)/(user)/voting/create.tsx new file mode 100644 index 0000000..d7e1295 --- /dev/null +++ b/app/(application)/(user)/voting/create.tsx @@ -0,0 +1,78 @@ +import { + BoxButtonOnFooter, + ButtonCenteredOnly, + ButtonCustom, + Grid, + Spacing, + StackCustom, + TextAreaCustom, + TextInputCustom, + ViewWrapper, +} from "@/components"; +import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom"; +import { MainColor } from "@/constants/color-palet"; +import { Ionicons } from "@expo/vector-icons"; +import { router } from "expo-router"; +import { TouchableOpacity } from "react-native"; + +export default function VotingCreate() { + const buttonSubmit = () => { + return ( + <> + + + router.replace("/(application)/(user)/voting/(tabs)/status") + } + > + Simpan + + + + ); + }; + + return ( + + + + + + + + + + + + + console.log("delete")}> + + + + + + console.log("add")}> + Tambah Pilihan + + + + + ); +} diff --git a/bun.lock b/bun.lock index 598c1f0..af418e5 100644 --- a/bun.lock +++ b/bun.lock @@ -36,6 +36,7 @@ "react-native-international-phone-number": "^0.9.3", "react-native-maps": "1.20.1", "react-native-otp-entry": "^1.8.5", + "react-native-pager-view": "6.7.1", "react-native-paper": "^5.14.5", "react-native-reanimated": "~3.17.4", "react-native-safe-area-context": "5.4.0", @@ -1392,6 +1393,8 @@ "react-native-otp-entry": ["react-native-otp-entry@1.8.5", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-TZNkIuUzZKAAWrC8X/A22ZHJdycLysxUNysrGf0yTmDLRUyf4zLXwVFcDYUcRNe763Hjaf5qvtKGILb6lDGzoA=="], + "react-native-pager-view": ["react-native-pager-view@6.7.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-cBSr6xw4g5N7Kd3VGWcf+kmaH7iBWb0DXAf2bVo3bXkzBcBbTOmYSvc0LVLHhUPW8nEq5WjT9LCIYAzgF++EXw=="], + "react-native-paper": ["react-native-paper@5.14.5", "", { "dependencies": { "@callstack/react-theme-provider": "^3.0.9", "color": "^3.1.2", "use-latest-callback": "^0.2.3" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-safe-area-context": "*" } }, "sha512-eaIH5bUQjJ/mYm4AkI6caaiyc7BcHDwX6CqNDi6RIxfxfWxROsHpll1oBuwn/cFvknvA8uEAkqLk/vzVihI3AQ=="], "react-native-reanimated": ["react-native-reanimated@3.17.5", "", { "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", "@babel/plugin-transform-classes": "^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", "@babel/plugin-transform-optional-chaining": "^7.0.0-0", "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", "@babel/plugin-transform-template-literals": "^7.0.0-0", "@babel/plugin-transform-unicode-regex": "^7.0.0-0", "@babel/preset-typescript": "^7.16.7", "convert-source-map": "^2.0.0", "invariant": "^2.2.4", "react-native-is-edge-to-edge": "1.1.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*" } }, "sha512-SxBK7wQfJ4UoWoJqQnmIC7ZjuNgVb9rcY5Xc67upXAFKftWg0rnkknTw6vgwnjRcvYThrjzUVti66XoZdDJGtw=="], diff --git a/components/Badge/BadgeCustom.tsx b/components/Badge/BadgeCustom.tsx new file mode 100644 index 0000000..eada0c7 --- /dev/null +++ b/components/Badge/BadgeCustom.tsx @@ -0,0 +1,189 @@ +import React from "react"; +import { + StyleSheet, + Text, + TextStyle, + View, + ViewProps, + ViewStyle, +} from "react-native"; + +type BadgeVariant = "filled" | "light" | "outline" | "dot"; +type BadgeColor = + | "primary" + | "success" + | "warning" + | "danger" + | "gray" + | "dark"; +type BadgeSize = "xs" | "sm" | "md" | "lg"; + +interface BadgeProps extends ViewProps { + children: React.ReactNode; + variant?: BadgeVariant; + color?: BadgeColor; + size?: BadgeSize; + leftIcon?: React.ReactNode; + rightIcon?: React.ReactNode; + radius?: number; + fullWidth?: boolean; + textColor?: string; +} + +const BadgeCustom: React.FC = ({ + children, + variant = "filled", + color = "primary", + size = "md", + leftIcon, + rightIcon, + radius = 50, + fullWidth = false, + textColor = "#fff", + style, + ...props +}) => { + const colors = { + primary: "#339AF0", + success: "#40C057", + warning: "#FAB005", + danger: "#FA5252", + gray: "#868E96", + dark: "#212529", + }; + + const themeColor = colors[color]; + + // Ganti bagian sizeStyles dan styles.container + const sizeStyles = { + xs: { + fontSize: 10, + paddingHorizontal: 6, + paddingVertical: 2, + height: 18, // Dinaikkan dari 16 → 18 agar teks tidak terpotong + lineHeight: 10, // 👈 Penting: match fontSize agar kontrol vertikal lebih baik + }, + sm: { + fontSize: 11, + paddingHorizontal: 8, + paddingVertical: 3, + height: 20, + lineHeight: 11, + }, + md: { + fontSize: 12, + paddingHorizontal: 10, + paddingVertical: 4, + height: 24, + lineHeight: 12, + }, + lg: { + fontSize: 14, + paddingHorizontal: 12, + paddingVertical: 6, + height: 30, + lineHeight: 14, + }, + }; + + const currentSize = sizeStyles[size]; + + let variantStyles: ViewStyle & { text: TextStyle } = { + backgroundColor: themeColor, + borderColor: themeColor, + borderWidth: 1, + borderRadius: radius, + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + text: { color: textColor, fontWeight: "600" }, + }; + + switch (variant) { + case "light": + variantStyles.backgroundColor = `${themeColor}20`; + variantStyles.text.color = themeColor; + break; + case "outline": + variantStyles.backgroundColor = "transparent"; + variantStyles.text.color = themeColor; + break; + case "dot": + variantStyles.backgroundColor = themeColor; + variantStyles.paddingHorizontal = 0; + variantStyles.paddingVertical = 0; + variantStyles.height = currentSize.fontSize * 2; + variantStyles.width = currentSize.fontSize * 2; + variantStyles.borderRadius = currentSize.fontSize; + break; + default: + break; + } + + if (variant === "dot") { + return ( + + ); + } + + return ( + + {leftIcon && {leftIcon}} + + {children} + + {rightIcon && {rightIcon}} + + ); +}; + +const styles = StyleSheet.create({ + container: { + alignSelf: "flex-start", + flexDirection: "row", + alignItems: "center", // Vertikal center anak-anak (termasuk teks) + justifyContent: "center", // Horizontal center + paddingHorizontal: 10, + paddingVertical: 4, + minWidth: 20, + borderRadius: 6, + // ❌ Jangan gunakan `height` fix di sini — kita override per size + }, + text: { + fontWeight: "600", + textAlign: "center", + // ❌ Hapus marginHorizontal jika mengganggu alignment + // marginHorizontal: 2, // Opsional, bisa dihapus atau dikurangi + includeFontPadding: false, // 👈 Ini penting untuk Android! + padding: 0, // Bersihkan padding tambahan dari font + }, + iconContainer: { + marginHorizontal: 2, // Lebih kecil dari sebelumnya agar tidak ganggu ukuran kecil + }, + fullWidth: { + width: "100%", + alignSelf: "stretch", + justifyContent: "center", + }, +}); + +export default BadgeCustom; diff --git a/components/Container/CircleContainer.tsx b/components/Container/CircleContainer.tsx new file mode 100644 index 0000000..187b994 --- /dev/null +++ b/components/Container/CircleContainer.tsx @@ -0,0 +1,44 @@ +import { MainColor } from "@/constants/color-palet"; +import React from "react"; +import { StyleSheet, TextInput, View } from "react-native"; + +interface CircularInputProps { + value: string | number; + onChange?: (value: string) => void; +} + +const CircularInput: React.FC = ({ value, onChange }) => { + return ( + + + + ); +}; + +const styles = StyleSheet.create({ + circleContainer: { + width: 60, + height: 60, + borderRadius: 40, // Setiap setengah dari lebar/tinggi + borderWidth: 2, + borderColor: MainColor.yellow, // Warna kuning + justifyContent: "center", + alignItems: "center", + }, + input: { + color: MainColor.yellow, // Warna kuning + fontSize: 24, + fontWeight: "bold", + textAlign: "center", + padding: 0, + backgroundColor: "transparent", + }, +}); + +export default CircularInput; diff --git a/components/_Icon/IconArchive.tsx b/components/_Icon/IconArchive.tsx new file mode 100644 index 0000000..e952553 --- /dev/null +++ b/components/_Icon/IconArchive.tsx @@ -0,0 +1,13 @@ +import { MainColor } from "@/constants/color-palet"; +import { ICON_SIZE_SMALL } from "@/constants/constans-value"; +import { Ionicons } from "@expo/vector-icons"; + +export default function IconArchive({ color }: { color?: string }) { + return ( + + ); +} diff --git a/components/_Icon/IconContribution.tsx b/components/_Icon/IconContribution.tsx new file mode 100644 index 0000000..46fb109 --- /dev/null +++ b/components/_Icon/IconContribution.tsx @@ -0,0 +1,14 @@ +import { ICON_SIZE_SMALL } from "@/constants/constans-value"; +import { Ionicons } from "@expo/vector-icons"; + +export default function IconContribution({ color }: { color?: string }) { + return ( + <> + + + ); +} diff --git a/components/_Icon/IconHistory.tsx b/components/_Icon/IconHistory.tsx new file mode 100644 index 0000000..f4a0de9 --- /dev/null +++ b/components/_Icon/IconHistory.tsx @@ -0,0 +1,9 @@ +import { FontAwesome5 } from "@expo/vector-icons"; + +export default function IconHistory({ color }: { color?: string }) { + return ( + <> + + + ); +} diff --git a/components/_Icon/index.ts b/components/_Icon/index.ts index 31da1c7..8d0cd83 100644 --- a/components/_Icon/index.ts +++ b/components/_Icon/index.ts @@ -1,5 +1,15 @@ +import IconContribution from "./IconContribution"; import IconEdit from "./IconEdit"; +import IconHistory from "./IconHistory"; import IconHome from "./IconHome"; import IconStatus from "./IconStatus"; +import IconArchive from "./IconArchive"; -export { IconEdit, IconHome, IconStatus }; +export { + IconContribution, + IconEdit, + IconHistory, + IconHome, + IconStatus, + IconArchive, +}; diff --git a/components/_ShareComponent/AvataraAndOtherHeaderComponent.tsx b/components/_ShareComponent/AvataraAndOtherHeaderComponent.tsx index 42380ba..d25ef4b 100644 --- a/components/_ShareComponent/AvataraAndOtherHeaderComponent.tsx +++ b/components/_ShareComponent/AvataraAndOtherHeaderComponent.tsx @@ -1,8 +1,8 @@ -import { ImageSourcePropType, View } from "react-native"; +import { ImageSourcePropType } from "react-native"; +import Divider from "../Divider/Divider"; import Grid from "../Grid/GridCustom"; import AvatarCustom from "../Image/AvatarCustom"; import TextCustom from "../Text/TextCustom"; -import Divider from "../Divider/Divider" const AvatarUsernameAndOtherComponent = ({ avatarHref, @@ -19,30 +19,28 @@ const AvatarUsernameAndOtherComponent = ({ }) => { return ( <> - - - - + + + + + + + {name || "Username"} + + + {rightComponent && ( - - {name || "Username"} - + {rightComponent} - {rightComponent && ( - - {rightComponent} - - )} - - {withBottomLine && } - - + )} + + {withBottomLine && } ); }; diff --git a/components/_ShareComponent/TabsTwoHeaderCustom.tsx b/components/_ShareComponent/TabsTwoHeaderCustom.tsx new file mode 100644 index 0000000..85dec26 --- /dev/null +++ b/components/_ShareComponent/TabsTwoHeaderCustom.tsx @@ -0,0 +1,61 @@ +import { MainColor, AccentColor } from "@/constants/color-palet"; +import { View } from "react-native"; +import ButtonCustom from "../Button/ButtonCustom"; +import Spacing from "./Spacing"; + +export default function TabsTwoHeaderCustom ({ + leftValue, + rightValue, + leftText, + rightText, + activeCategory, + handlePress, +}: { + leftValue: string; + rightValue: string; + leftText: string; + rightText: string; + activeCategory: string | null; + handlePress: (item: string) => void; +}) { + return ( + <> + + handlePress(leftValue)} + > + {leftText} + + + handlePress(rightValue)} + > + {rightText} + + + + ); +} \ No newline at end of file diff --git a/components/index.ts b/components/index.ts index 564b8b0..41cd990 100644 --- a/components/index.ts +++ b/components/index.ts @@ -7,6 +7,10 @@ import ButtonCenteredOnly from "./Button/ButtonCenteredOnly"; import ButtonCustom from "./Button/ButtonCustom"; import DotButton from "./Button/DotButton"; import FloatingButton from "./Button/FloatingButton"; +// Badge +import BadgeCustom from "./Badge/BadgeCustom"; +// Container +import CircleContainer from "./Container/CircleContainer"; // Checkbox import CheckboxCustom from "./Checkbox/CheckboxCustom"; import CheckboxGroup from "./Checkbox/CheckboxGroup"; @@ -68,6 +72,8 @@ export { ButtonCenteredOnly, ButtonCustom, DotButton, + // Badge + BadgeCustom, // Center CenterCustom, // Checkbox @@ -75,6 +81,8 @@ export { CheckboxGroup, // Clickable ClickableCustom, + // Container + CircleContainer, // Divider Divider, DividerCustom, diff --git a/package.json b/package.json index b913d41..b790716 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "react-native-international-phone-number": "^0.9.3", "react-native-maps": "1.20.1", "react-native-otp-entry": "^1.8.5", + "react-native-pager-view": "6.7.1", "react-native-paper": "^5.14.5", "react-native-reanimated": "~3.17.4", "react-native-safe-area-context": "5.4.0", diff --git a/screens/Forum/ReportListSection.tsx b/screens/Forum/ReportListSection.tsx index c54b143..9774c87 100644 --- a/screens/Forum/ReportListSection.tsx +++ b/screens/Forum/ReportListSection.tsx @@ -1,8 +1,6 @@ -import { BaseBox, ButtonCustom, StackCustom, TextCustom } from "@/components"; +import { BaseBox, StackCustom, TextCustom } from "@/components"; import { RadioCustom, RadioGroup } from "@/components/Radio/RadioCustom"; -import { MainColor } from "@/constants/color-palet"; import { listDummyReportForum } from "@/lib/dummy-data/forum/report-list"; -import { router } from "expo-router"; import { useState } from "react"; import { View } from "react-native"; diff --git a/screens/Home/topFeatureSection.tsx b/screens/Home/topFeatureSection.tsx index 80b4286..a3bfedd 100644 --- a/screens/Home/topFeatureSection.tsx +++ b/screens/Home/topFeatureSection.tsx @@ -16,12 +16,17 @@ export default function Home_FeatureSection() { router.push("/(application)/(user)/collaboration/(tabs)")} + onPress={() => + router.push("/(application)/(user)/collaboration/(tabs)") + } > Collaboration - + router.push("/(application)/(user)/voting/(tabs)")} + > Voting diff --git a/screens/Voting/BoxDetailContribution.tsx b/screens/Voting/BoxDetailContribution.tsx new file mode 100644 index 0000000..d92165d --- /dev/null +++ b/screens/Voting/BoxDetailContribution.tsx @@ -0,0 +1,56 @@ +import { + BadgeCustom, + BoxWithHeaderSection, + Spacing, + StackCustom, + TextCustom, +} from "@/components"; +import { GStyles } from "@/styles/global-styles"; +import dayjs from "dayjs"; +import { View } from "react-native"; + +export function Voting_BoxDetailContributionSection({ + headerAvatar, +}: { + headerAvatar?: React.ReactNode; +}) { + return ( + <> + + {headerAvatar ? headerAvatar : } + + + Title of Voting Here + + + Lorem ipsum dolor sit amet consectetur adipisicing elit. + Perspiciatis corporis blanditiis est provident corrupti facilis iste + cum voluptate. Natus eum aut quos consequatur doloribus fugiat sit + ullam minima non enim? + + + + Batas Voting + + + {dayjs().format("DD/MM/YYYY")} -{" "} + {dayjs().add(1, "day").format("DD/MM/YYYY")} + + + + + + Pilihan Anda + + + Pilihan 1 + + + + + + ); +} diff --git a/screens/Voting/BoxDetailHasilVotingSection.tsx b/screens/Voting/BoxDetailHasilVotingSection.tsx new file mode 100644 index 0000000..7753819 --- /dev/null +++ b/screens/Voting/BoxDetailHasilVotingSection.tsx @@ -0,0 +1,30 @@ +import { + BaseBox, + StackCustom, + TextCustom, + Grid, + CircleContainer, +} from "@/components"; + +export default function Voting_BoxDetailHasilVotingSection() { + return ( + <> + + + + Hasil Voting + + + + {Array.from({ length: 4 }).map((_, i) => ( + + + Pilihan {i + 1} + + ))} + + + + + ); +} diff --git a/screens/Voting/BoxDetailPublishSection.tsx b/screens/Voting/BoxDetailPublishSection.tsx new file mode 100644 index 0000000..132105f --- /dev/null +++ b/screens/Voting/BoxDetailPublishSection.tsx @@ -0,0 +1,71 @@ +import { + BadgeCustom, + BoxWithHeaderSection, + ButtonCustom, + Spacing, + StackCustom, + TextCustom, +} from "@/components"; +import { RadioCustom, RadioGroup } from "@/components/Radio/RadioCustom"; +import { GStyles } from "@/styles/global-styles"; +import dayjs from "dayjs"; +import { useState } from "react"; +import { View } from "react-native"; + +export function Voting_BoxDetailPublishSection({ + headerAvatar, +}: { + headerAvatar?: React.ReactNode; +}) { + const [value, setValue] = useState(""); + return ( + <> + + {headerAvatar ? headerAvatar : } + + + Title of Voting Here + + + Lorem ipsum dolor sit amet consectetur adipisicing elit. + Perspiciatis corporis blanditiis est provident corrupti facilis iste + cum voluptate. Natus eum aut quos consequatur doloribus fugiat sit + ullam minima non enim? + + + + Batas Voting + + + {dayjs().format("DD/MM/YYYY")} -{" "} + {dayjs().add(1, "day").format("DD/MM/YYYY")} + + + + + + Pilihan : + + + {Array.from({ length: 4 }).map((_, i) => ( + + + + ))} + + + + console.log("vote")}> + Vote + + + + + ); +} diff --git a/screens/Voting/BoxDetailSection.tsx b/screens/Voting/BoxDetailSection.tsx new file mode 100644 index 0000000..e385db0 --- /dev/null +++ b/screens/Voting/BoxDetailSection.tsx @@ -0,0 +1,59 @@ +import { + BadgeCustom, + BoxWithHeaderSection, + Spacing, + StackCustom, + TextCustom, +} from "@/components"; +import { GStyles } from "@/styles/global-styles"; +import dayjs from "dayjs"; +import { View } from "react-native"; + +export function Voting_BoxDetailSection({ + headerAvatar, +}: { + headerAvatar?: React.ReactNode; +}) { + return ( + <> + + {headerAvatar ? headerAvatar : } + + + Title of Voting Here + + + Lorem ipsum dolor sit amet consectetur adipisicing elit. + Perspiciatis corporis blanditiis est provident corrupti facilis iste + cum voluptate. Natus eum aut quos consequatur doloribus fugiat sit + ullam minima non enim? + + + + Batas Voting + + + {dayjs().format("DD/MM/YYYY")} -{" "} + {dayjs().add(1, "day").format("DD/MM/YYYY")} + + + + + + Pilihan : + + {Array.from({ length: 3 }).map((_, i) => ( + + Nama Pilihan {i + 1} + + + ))} + + + + + ); +} diff --git a/screens/Voting/BoxPublishSection.tsx b/screens/Voting/BoxPublishSection.tsx new file mode 100644 index 0000000..6728512 --- /dev/null +++ b/screens/Voting/BoxPublishSection.tsx @@ -0,0 +1,54 @@ +import { + BoxWithHeaderSection, + AvatarUsernameAndOtherComponent, + Spacing, + StackCustom, + TextCustom, + BadgeCustom, + Grid, + CircleContainer, +} from "@/components"; +import dayjs from "dayjs"; +import { Href } from "expo-router"; + +export default function Voting_BoxPublishSection({ + href, + id, + bottomComponent, +}: { + href?: Href + id?: string + bottomComponent?: React.ReactNode; +}) { + return ( + <> + + + + + + Voting Title {id} + + + + {dayjs().format("DD/MM/YYYY")} -{" "} + {dayjs().add(1, "day").format("DD/MM/YYYY")} + + + + {Array.from({ length: 4 }).map((_, i) => ( + + + Pilihan {i + 1} + + ))} + + {bottomComponent} + + + + ); +} diff --git a/screens/Voting/ButtonStatusSection.tsx b/screens/Voting/ButtonStatusSection.tsx new file mode 100644 index 0000000..2f1ecde --- /dev/null +++ b/screens/Voting/ButtonStatusSection.tsx @@ -0,0 +1,124 @@ +import { AlertDefaultSystem, ButtonCustom, Grid } from "@/components"; +import { router } from "expo-router"; +import { View } from "react-native"; + +export default function Voting_ButtonStatusSection({ + status, +}: { + status: string; +}) { + const handleBatalkanReview = () => { + AlertDefaultSystem({ + title: "Batalkan Review", + message: "Apakah Anda yakin ingin batalkan review ini?", + textLeft: "Batal", + textRight: "Ya", + onPressRight: () => { + console.log("Hapus"); + router.back(); + }, + }); + }; + + const handleAjukanReview = () => { + AlertDefaultSystem({ + title: "Ajukan Review", + message: "Apakah Anda yakin ingin ajukan review ini?", + textLeft: "Batal", + textRight: "Ya", + onPressRight: () => { + console.log("Hapus"); + router.back(); + }, + }); + }; + + const handleEditKembali = () => { + AlertDefaultSystem({ + title: "Edit Kembali", + message: "Apakah Anda yakin ingin edit kembali ini?", + textLeft: "Batal", + textRight: "Ya", + onPressRight: () => { + console.log("Hapus"); + router.back(); + }, + }); + }; + + const handleOpenDeleteAlert = () => { + AlertDefaultSystem({ + title: "Hapus", + message: "Apakah Anda yakin ingin menghapus data ini?", + textLeft: "Batal", + textRight: "Hapus", + onPressRight: () => { + console.log("Hapus"); + router.back(); + }, + }); + }; + + const DeleteButton = () => { + return ( + <> + + Hapus + + + ); + }; + + switch (status) { + case "publish": + return <>; + + case "review": + return ( + + Batalkan Review + + ); + + case "draft": + return ( + <> + + + + Ajukan Review + + + + + + {DeleteButton()} + + + ); + + case "reject": + return ( + <> + + + + Edit Kembali + + + + + + {DeleteButton()} + + + ); + + default: + return Status Undifined; + } +} diff --git a/styles/global-styles.ts b/styles/global-styles.ts index b5644a0..e017618 100644 --- a/styles/global-styles.ts +++ b/styles/global-styles.ts @@ -315,4 +315,9 @@ export const GStyles = StyleSheet.create({ }, // =============== TEXT INPUT , TEXT AREA , SELECT =============== // + + // =============== Alignment =============== // + alignSelfCenter: { + alignSelf: "center", + }, });