Merge pull request 'Fix Voting' (#5) from voting/28-jul-25 into main
Reviewed-on: bip/hipmi-mobile#5
This commit is contained in:
@@ -150,6 +150,38 @@ export default function UserLayout() {
|
|||||||
|
|
||||||
{/* ========== End Collaboration Section ========= */}
|
{/* ========== End Collaboration Section ========= */}
|
||||||
|
|
||||||
|
{/* ========== Voting Section ========= */}
|
||||||
|
<Stack.Screen
|
||||||
|
name="voting/create"
|
||||||
|
options={{
|
||||||
|
title: "Tambah Voting",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="voting/(tabs)"
|
||||||
|
options={{
|
||||||
|
title: "Voting",
|
||||||
|
headerLeft: () => <BackButton path="/home" />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="voting/[id]/edit"
|
||||||
|
options={{
|
||||||
|
title: "Edit Voting",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="voting/[id]/list-of-contributor"
|
||||||
|
options={{
|
||||||
|
title: "Daftar Kontributor",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* ========== End Voting Section ========= */}
|
||||||
|
|
||||||
{/* ========== Job Section ========= */}
|
{/* ========== Job Section ========= */}
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="job/create"
|
name="job/create"
|
||||||
|
|||||||
@@ -1,50 +1,43 @@
|
|||||||
import { IconHome, IconStatus } from "@/components/_Icon";
|
import {
|
||||||
|
IconContribution,
|
||||||
|
IconHistory,
|
||||||
|
IconHome,
|
||||||
|
IconStatus,
|
||||||
|
} from "@/components/_Icon";
|
||||||
import { TabsStyles } from "@/styles/tabs-styles";
|
import { TabsStyles } from "@/styles/tabs-styles";
|
||||||
import { FontAwesome5, Ionicons } from "@expo/vector-icons";
|
|
||||||
import { Tabs } from "expo-router";
|
import { Tabs } from "expo-router";
|
||||||
|
|
||||||
export default function EventTabsLayout() {
|
export default function EventTabsLayout() {
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs screenOptions={TabsStyles}>
|
||||||
screenOptions={TabsStyles}
|
|
||||||
>
|
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="index"
|
name="index"
|
||||||
options={{
|
options={{
|
||||||
title: "Beranda",
|
title: "Beranda",
|
||||||
tabBarIcon: ({ color }) => (
|
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||||
<IconHome color={color}/>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="status"
|
name="status"
|
||||||
options={{
|
options={{
|
||||||
title: "Status",
|
title: "Status",
|
||||||
tabBarIcon: ({ color }) => (
|
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||||
<IconStatus color={color}/>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="contribution"
|
name="contribution"
|
||||||
options={{
|
options={{
|
||||||
title: "Kontribusi",
|
title: "Kontribusi",
|
||||||
tabBarIcon: ({ color }) => (
|
tabBarIcon: ({ color }) => <IconContribution color={color} />,
|
||||||
<Ionicons size={20} name="extension-puzzle" color={color} />
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="history"
|
name="history"
|
||||||
options={{
|
options={{
|
||||||
title: "Riwayat",
|
title: "Riwayat",
|
||||||
tabBarIcon: ({ color }) => (
|
tabBarIcon: ({ color }) => <IconHistory color={color} />,
|
||||||
<FontAwesome5 size={20} name="history" color={color} />
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
43
app/(application)/(user)/voting/(tabs)/_layout.tsx
Normal file
43
app/(application)/(user)/voting/(tabs)/_layout.tsx
Normal file
@@ -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 (
|
||||||
|
<Tabs screenOptions={TabsStyles}>
|
||||||
|
<Tabs.Screen
|
||||||
|
name="index"
|
||||||
|
options={{
|
||||||
|
title: "Beranda",
|
||||||
|
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tabs.Screen
|
||||||
|
name="status"
|
||||||
|
options={{
|
||||||
|
title: "Status",
|
||||||
|
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tabs.Screen
|
||||||
|
name="contribution"
|
||||||
|
options={{
|
||||||
|
title: "Kontribusi",
|
||||||
|
tabBarIcon: ({ color }) => <IconContribution color={color} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tabs.Screen
|
||||||
|
name="history"
|
||||||
|
options={{
|
||||||
|
title: "Riwayat",
|
||||||
|
tabBarIcon: ({ color }) => <IconHistory color={color} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
|
}
|
||||||
17
app/(application)/(user)/voting/(tabs)/contribution.tsx
Normal file
17
app/(application)/(user)/voting/(tabs)/contribution.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import {
|
||||||
|
ViewWrapper
|
||||||
|
} from "@/components";
|
||||||
|
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
|
||||||
|
|
||||||
|
export default function VotingContribution() {
|
||||||
|
return (
|
||||||
|
<ViewWrapper hideFooter>
|
||||||
|
{Array.from({ length: 5 }).map((_, index) => (
|
||||||
|
<Voting_BoxPublishSection
|
||||||
|
key={index}
|
||||||
|
href={`/voting/${index}/contribution`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ViewWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
33
app/(application)/(user)/voting/(tabs)/history.tsx
Normal file
33
app/(application)/(user)/voting/(tabs)/history.tsx
Normal file
@@ -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<string | null>("all");
|
||||||
|
|
||||||
|
const handlePress = (item: any) => {
|
||||||
|
setActiveCategory(item);
|
||||||
|
// tambahkan logika lain seperti filter dsb.
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ViewWrapper
|
||||||
|
hideFooter
|
||||||
|
headerComponent={
|
||||||
|
<TabsTwoHeaderCustom
|
||||||
|
leftValue="all"
|
||||||
|
rightValue="main"
|
||||||
|
leftText="Semua Riwayat"
|
||||||
|
rightText="Riwayat Saya"
|
||||||
|
activeCategory={activeCategory}
|
||||||
|
handlePress={handlePress}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{Array.from({ length: 10 }).map((_, index) => (
|
||||||
|
<Voting_BoxPublishSection key={index} id={activeCategory as any} />
|
||||||
|
))}
|
||||||
|
</ViewWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
app/(application)/(user)/voting/(tabs)/index.tsx
Normal file
23
app/(application)/(user)/voting/(tabs)/index.tsx
Normal file
@@ -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 (
|
||||||
|
<ViewWrapper
|
||||||
|
hideFooter
|
||||||
|
floatingButton={
|
||||||
|
<FloatingButton onPress={() => router.push("/voting/create")} />
|
||||||
|
}
|
||||||
|
headerComponent={<SearchInput placeholder="Cari voting" />}
|
||||||
|
>
|
||||||
|
{Array.from({ length: 5 }).map((_, index) => (
|
||||||
|
<Voting_BoxPublishSection key={index} href={`/voting/${index}`} />
|
||||||
|
))}
|
||||||
|
</ViewWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
60
app/(application)/(user)/voting/(tabs)/status.tsx
Normal file
60
app/(application)/(user)/voting/(tabs)/status.tsx
Normal file
@@ -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<string | null>(
|
||||||
|
"publish"
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlePress = (item: any) => {
|
||||||
|
setActiveCategory(item.value);
|
||||||
|
// tambahkan logika lain seperti filter dsb.
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollComponent = (
|
||||||
|
<ScrollableCustom
|
||||||
|
data={masterStatus.map((e, i) => ({
|
||||||
|
id: i,
|
||||||
|
label: e.label,
|
||||||
|
value: e.value,
|
||||||
|
}))}
|
||||||
|
onButtonPress={handlePress}
|
||||||
|
activeId={activeCategory as any}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ViewWrapper headerComponent={scrollComponent} hideFooter>
|
||||||
|
{Array.from({ length: 10 }).map((_, i) => (
|
||||||
|
<BaseBox
|
||||||
|
key={i}
|
||||||
|
paddingTop={20}
|
||||||
|
paddingBottom={20}
|
||||||
|
href={`/voting/${i}/${activeCategory}/detail`}
|
||||||
|
>
|
||||||
|
<StackCustom>
|
||||||
|
<TextCustom align="center" bold truncate size="large">
|
||||||
|
Lorem ipsum dolor sit {activeCategory}
|
||||||
|
</TextCustom>
|
||||||
|
<BadgeCustom
|
||||||
|
style={{ width: "70%", alignSelf: "center" }}
|
||||||
|
variant="light"
|
||||||
|
>
|
||||||
|
{dayjs().format("DD/MM/YYYY")} -{" "}
|
||||||
|
{dayjs().add(1, "day").format("DD/MM/YYYY")}
|
||||||
|
</BadgeCustom>
|
||||||
|
</StackCustom>
|
||||||
|
</BaseBox>
|
||||||
|
))}
|
||||||
|
</ViewWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
108
app/(application)/(user)/voting/[id]/[status]/detail.tsx
Normal file
108
app/(application)/(user)/voting/[id]/[status]/detail.tsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<Stack.Screen
|
||||||
|
options={{
|
||||||
|
title: `Detail ${status}`,
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
headerRight: () =>
|
||||||
|
status === "draft" ? (
|
||||||
|
<DotButton onPress={() => setOpenDrawerDraft(true)} />
|
||||||
|
) : status === "publish" ? (
|
||||||
|
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||||
|
) : null,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ViewWrapper>
|
||||||
|
<Voting_BoxDetailSection />
|
||||||
|
<Voting_ButtonStatusSection status={status as string} />
|
||||||
|
<Spacing />
|
||||||
|
</ViewWrapper>
|
||||||
|
|
||||||
|
{/* ========= Draft Drawer ========= */}
|
||||||
|
<DrawerCustom
|
||||||
|
isVisible={openDrawerDraft}
|
||||||
|
closeDrawer={() => setOpenDrawerDraft(false)}
|
||||||
|
height={"auto"}
|
||||||
|
>
|
||||||
|
<MenuDrawerDynamicGrid
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
icon: <IconEdit />,
|
||||||
|
label: "Edit",
|
||||||
|
path: `/voting/${id}/edit`,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
columns={4}
|
||||||
|
onPressItem={handlePressDraft as any}
|
||||||
|
/>
|
||||||
|
</DrawerCustom>
|
||||||
|
|
||||||
|
{/* ========= Publish Drawer ========= */}
|
||||||
|
<DrawerCustom
|
||||||
|
isVisible={openDrawerPublish}
|
||||||
|
closeDrawer={() => setOpenDrawerPublish(false)}
|
||||||
|
height={"auto"}
|
||||||
|
>
|
||||||
|
<MenuDrawerDynamicGrid
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
icon: <IconContribution />,
|
||||||
|
label: "Daftar Kontributor",
|
||||||
|
path: `/voting/${id}/list-of-contributor`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconArchive />,
|
||||||
|
label: "Update Arsip",
|
||||||
|
path: "" as any,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onPressItem={handlePressPublish as any}
|
||||||
|
/>
|
||||||
|
</DrawerCustom>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
65
app/(application)/(user)/voting/[id]/contribution.tsx
Normal file
65
app/(application)/(user)/voting/[id]/contribution.tsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<Stack.Screen
|
||||||
|
options={{
|
||||||
|
title: "Detail Kontribusi",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
headerRight: () => (
|
||||||
|
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ViewWrapper>
|
||||||
|
<Voting_BoxDetailContributionSection
|
||||||
|
headerAvatar={<AvatarUsernameAndOtherComponent />}
|
||||||
|
/>
|
||||||
|
<Voting_BoxDetailHasilVotingSection />
|
||||||
|
<Spacing />
|
||||||
|
</ViewWrapper>
|
||||||
|
|
||||||
|
{/* ========= Publish Drawer ========= */}
|
||||||
|
<DrawerCustom
|
||||||
|
isVisible={openDrawerPublish}
|
||||||
|
closeDrawer={() => setOpenDrawerPublish(false)}
|
||||||
|
height={"auto"}
|
||||||
|
>
|
||||||
|
<MenuDrawerDynamicGrid
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
icon: <IconContribution />,
|
||||||
|
label: "Daftar Kontributor",
|
||||||
|
path: `/voting/${id}/list-of-contributor`,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onPressItem={handlePressPublish as any}
|
||||||
|
/>
|
||||||
|
</DrawerCustom>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
78
app/(application)/(user)/voting/[id]/edit.tsx
Normal file
78
app/(application)/(user)/voting/[id]/edit.tsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
onPress={() =>
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Update
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ViewWrapper footerComponent={buttonSubmit()}>
|
||||||
|
<StackCustom gap={"xs"}>
|
||||||
|
<TextInputCustom
|
||||||
|
label="Judul Voting"
|
||||||
|
placeholder="MasukanJudul Voting"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<TextAreaCustom
|
||||||
|
label="Deskripsi"
|
||||||
|
placeholder="Masukan Deskripsi"
|
||||||
|
required
|
||||||
|
showCount
|
||||||
|
maxLength={1000}
|
||||||
|
/>
|
||||||
|
<DateTimePickerCustom label="Mulai Voting" required />
|
||||||
|
<DateTimePickerCustom label="Voting Berakhir" required />
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col span={10}>
|
||||||
|
<TextInputCustom
|
||||||
|
label="Pilihan"
|
||||||
|
placeholder="Masukan Pilihan"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col
|
||||||
|
span={2}
|
||||||
|
style={{ alignItems: "center", justifyContent: "center" }}
|
||||||
|
>
|
||||||
|
<TouchableOpacity onPress={() => console.log("delete")}>
|
||||||
|
<Ionicons name="trash" size={24} color={MainColor.red} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<ButtonCenteredOnly onPress={() => console.log("add")}>
|
||||||
|
Tambah Pilihan
|
||||||
|
</ButtonCenteredOnly>
|
||||||
|
<Spacing />
|
||||||
|
</StackCustom>
|
||||||
|
</ViewWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
87
app/(application)/(user)/voting/[id]/index.tsx
Normal file
87
app/(application)/(user)/voting/[id]/index.tsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<Stack.Screen
|
||||||
|
options={{
|
||||||
|
title: `Detail Voting`,
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
headerRight: () => (
|
||||||
|
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ViewWrapper>
|
||||||
|
<StackCustom>
|
||||||
|
<InformationBox text="Untuk sementara voting ini belum di buka. Voting akan dimulai sesuai dengan tanggal awal pemilihan, dan akan ditutup sesuai dengan tanggal akhir pemilihan." />
|
||||||
|
|
||||||
|
<Voting_BoxDetailPublishSection
|
||||||
|
headerAvatar={<AvatarUsernameAndOtherComponent />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Voting_BoxDetailHasilVotingSection />
|
||||||
|
</StackCustom>
|
||||||
|
</ViewWrapper>
|
||||||
|
|
||||||
|
{/* ========= Publish Drawer ========= */}
|
||||||
|
<DrawerCustom
|
||||||
|
isVisible={openDrawerPublish}
|
||||||
|
closeDrawer={() => setOpenDrawerPublish(false)}
|
||||||
|
height={"auto"}
|
||||||
|
>
|
||||||
|
<MenuDrawerDynamicGrid
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
icon: <IconContribution />,
|
||||||
|
label: "Daftar Kontributor",
|
||||||
|
path: `/voting/${id}/list-of-contributor`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconArchive />,
|
||||||
|
label: "Update Arsip",
|
||||||
|
path: "" as any,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onPressItem={handlePressPublish as any}
|
||||||
|
/>
|
||||||
|
</DrawerCustom>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
26
app/(application)/(user)/voting/[id]/list-of-contributor.tsx
Normal file
26
app/(application)/(user)/voting/[id]/list-of-contributor.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import {
|
||||||
|
AvatarUsernameAndOtherComponent,
|
||||||
|
BadgeCustom,
|
||||||
|
BaseBox,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
|
|
||||||
|
export default function Voting_ListOfContributor() {
|
||||||
|
return (
|
||||||
|
<ViewWrapper>
|
||||||
|
{Array.from({ length: 10 }).map((_, index) => (
|
||||||
|
<BaseBox paddingTop={5} paddingBottom={5} key={index.toString()}>
|
||||||
|
<AvatarUsernameAndOtherComponent
|
||||||
|
rightComponent={
|
||||||
|
<BadgeCustom
|
||||||
|
style={{alignSelf: "flex-end" }}
|
||||||
|
>
|
||||||
|
Pilihan {index + 1}
|
||||||
|
</BadgeCustom>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</BaseBox>
|
||||||
|
))}
|
||||||
|
</ViewWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
78
app/(application)/(user)/voting/create.tsx
Normal file
78
app/(application)/(user)/voting/create.tsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
onPress={() =>
|
||||||
|
router.replace("/(application)/(user)/voting/(tabs)/status")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ViewWrapper footerComponent={buttonSubmit()}>
|
||||||
|
<StackCustom gap={"xs"}>
|
||||||
|
<TextInputCustom
|
||||||
|
label="Judul Voting"
|
||||||
|
placeholder="MasukanJudul Voting"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<TextAreaCustom
|
||||||
|
label="Deskripsi"
|
||||||
|
placeholder="Masukan Deskripsi"
|
||||||
|
required
|
||||||
|
showCount
|
||||||
|
maxLength={1000}
|
||||||
|
/>
|
||||||
|
<DateTimePickerCustom label="Mulai Voting" required />
|
||||||
|
<DateTimePickerCustom label="Voting Berakhir" required />
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col span={10}>
|
||||||
|
<TextInputCustom
|
||||||
|
label="Pilihan"
|
||||||
|
placeholder="Masukan Pilihan"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col
|
||||||
|
span={2}
|
||||||
|
style={{ alignItems: "center", justifyContent: "center" }}
|
||||||
|
>
|
||||||
|
<TouchableOpacity onPress={() => console.log("delete")}>
|
||||||
|
<Ionicons name="trash" size={24} color={MainColor.red} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<ButtonCenteredOnly onPress={() => console.log("add")}>
|
||||||
|
Tambah Pilihan
|
||||||
|
</ButtonCenteredOnly>
|
||||||
|
<Spacing />
|
||||||
|
</StackCustom>
|
||||||
|
</ViewWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
3
bun.lock
3
bun.lock
@@ -36,6 +36,7 @@
|
|||||||
"react-native-international-phone-number": "^0.9.3",
|
"react-native-international-phone-number": "^0.9.3",
|
||||||
"react-native-maps": "1.20.1",
|
"react-native-maps": "1.20.1",
|
||||||
"react-native-otp-entry": "^1.8.5",
|
"react-native-otp-entry": "^1.8.5",
|
||||||
|
"react-native-pager-view": "6.7.1",
|
||||||
"react-native-paper": "^5.14.5",
|
"react-native-paper": "^5.14.5",
|
||||||
"react-native-reanimated": "~3.17.4",
|
"react-native-reanimated": "~3.17.4",
|
||||||
"react-native-safe-area-context": "5.4.0",
|
"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-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-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=="],
|
"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=="],
|
||||||
|
|||||||
189
components/Badge/BadgeCustom.tsx
Normal file
189
components/Badge/BadgeCustom.tsx
Normal file
@@ -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<BadgeProps> = ({
|
||||||
|
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 (
|
||||||
|
<View
|
||||||
|
style={[variantStyles, fullWidth && styles.fullWidth, style]}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.container,
|
||||||
|
variantStyles,
|
||||||
|
currentSize,
|
||||||
|
{ borderRadius: radius },
|
||||||
|
fullWidth && styles.fullWidth,
|
||||||
|
style,
|
||||||
|
]}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{leftIcon && <View style={styles.iconContainer}>{leftIcon}</View>}
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.text,
|
||||||
|
variantStyles.text,
|
||||||
|
{ fontSize: currentSize.fontSize },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
{rightIcon && <View style={styles.iconContainer}>{rightIcon}</View>}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
44
components/Container/CircleContainer.tsx
Normal file
44
components/Container/CircleContainer.tsx
Normal file
@@ -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<CircularInputProps> = ({ value, onChange }) => {
|
||||||
|
return (
|
||||||
|
<View style={styles.circleContainer}>
|
||||||
|
<TextInput
|
||||||
|
value={String(value)}
|
||||||
|
onChangeText={onChange}
|
||||||
|
style={styles.input}
|
||||||
|
keyboardType="numeric"
|
||||||
|
maxLength={2} // Batasan maksimal karakter
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
13
components/_Icon/IconArchive.tsx
Normal file
13
components/_Icon/IconArchive.tsx
Normal file
@@ -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 (
|
||||||
|
<Ionicons
|
||||||
|
name="archive"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color={color || MainColor.white}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
14
components/_Icon/IconContribution.tsx
Normal file
14
components/_Icon/IconContribution.tsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<Ionicons
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
name="people"
|
||||||
|
color={color || "white"}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
9
components/_Icon/IconHistory.tsx
Normal file
9
components/_Icon/IconHistory.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { FontAwesome5 } from "@expo/vector-icons";
|
||||||
|
|
||||||
|
export default function IconHistory({ color }: { color?: string }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FontAwesome5 size={20} name="history" color={color} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,15 @@
|
|||||||
|
import IconContribution from "./IconContribution";
|
||||||
import IconEdit from "./IconEdit";
|
import IconEdit from "./IconEdit";
|
||||||
|
import IconHistory from "./IconHistory";
|
||||||
import IconHome from "./IconHome";
|
import IconHome from "./IconHome";
|
||||||
import IconStatus from "./IconStatus";
|
import IconStatus from "./IconStatus";
|
||||||
|
import IconArchive from "./IconArchive";
|
||||||
|
|
||||||
export { IconEdit, IconHome, IconStatus };
|
export {
|
||||||
|
IconContribution,
|
||||||
|
IconEdit,
|
||||||
|
IconHistory,
|
||||||
|
IconHome,
|
||||||
|
IconStatus,
|
||||||
|
IconArchive,
|
||||||
|
};
|
||||||
|
|||||||
@@ -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 Grid from "../Grid/GridCustom";
|
||||||
import AvatarCustom from "../Image/AvatarCustom";
|
import AvatarCustom from "../Image/AvatarCustom";
|
||||||
import TextCustom from "../Text/TextCustom";
|
import TextCustom from "../Text/TextCustom";
|
||||||
import Divider from "../Divider/Divider"
|
|
||||||
|
|
||||||
const AvatarUsernameAndOtherComponent = ({
|
const AvatarUsernameAndOtherComponent = ({
|
||||||
avatarHref,
|
avatarHref,
|
||||||
@@ -19,30 +19,28 @@ const AvatarUsernameAndOtherComponent = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid containerStyle={{ zIndex: 10 }}>
|
<Grid containerStyle={{ zIndex: 10 }}>
|
||||||
<Grid.Col span={2}>
|
<Grid.Col span={2}>
|
||||||
<AvatarCustom source={avatar} href={avatarHref as any} />
|
<AvatarCustom source={avatar} href={avatarHref as any} />
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
|
<Grid.Col
|
||||||
|
span={rightComponent ? 6 : 10}
|
||||||
|
style={{ justifyContent: "center" }}
|
||||||
|
>
|
||||||
|
<TextCustom truncate bold>
|
||||||
|
{name || "Username"}
|
||||||
|
</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
{rightComponent && (
|
||||||
<Grid.Col
|
<Grid.Col
|
||||||
span={rightComponent ? 6 : 10}
|
span={4}
|
||||||
style={{ justifyContent: "center" }}
|
style={{ alignItems: "flex-end", justifyContent: "center" }}
|
||||||
>
|
>
|
||||||
<TextCustom truncate bold>
|
{rightComponent}
|
||||||
{name || "Username"}
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
{rightComponent && (
|
)}
|
||||||
<Grid.Col
|
</Grid>
|
||||||
span={4}
|
{withBottomLine && <Divider marginTop={0} />}
|
||||||
style={{ alignItems: "flex-end", justifyContent: "center" }}
|
|
||||||
>
|
|
||||||
{rightComponent}
|
|
||||||
</Grid.Col>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
{withBottomLine && <Divider marginTop={0} />}
|
|
||||||
<View>
|
|
||||||
</View>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
61
components/_ShareComponent/TabsTwoHeaderCustom.tsx
Normal file
61
components/_ShareComponent/TabsTwoHeaderCustom.tsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
padding: 5,
|
||||||
|
backgroundColor: MainColor.soft_darkblue,
|
||||||
|
borderRadius: 50,
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ButtonCustom
|
||||||
|
backgroundColor={
|
||||||
|
activeCategory === leftValue ? MainColor.yellow : AccentColor.blue
|
||||||
|
}
|
||||||
|
textColor={
|
||||||
|
activeCategory === leftValue ? MainColor.black : MainColor.white
|
||||||
|
}
|
||||||
|
style={{ width: "49%" }}
|
||||||
|
onPress={() => handlePress(leftValue)}
|
||||||
|
>
|
||||||
|
{leftText}
|
||||||
|
</ButtonCustom>
|
||||||
|
<Spacing width={"2%"} />
|
||||||
|
<ButtonCustom
|
||||||
|
backgroundColor={
|
||||||
|
activeCategory === rightValue ? MainColor.yellow : AccentColor.blue
|
||||||
|
}
|
||||||
|
textColor={
|
||||||
|
activeCategory === rightValue ? MainColor.black : MainColor.white
|
||||||
|
}
|
||||||
|
style={{ width: "49%" }}
|
||||||
|
onPress={() => handlePress(rightValue)}
|
||||||
|
>
|
||||||
|
{rightText}
|
||||||
|
</ButtonCustom>
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,6 +7,10 @@ import ButtonCenteredOnly from "./Button/ButtonCenteredOnly";
|
|||||||
import ButtonCustom from "./Button/ButtonCustom";
|
import ButtonCustom from "./Button/ButtonCustom";
|
||||||
import DotButton from "./Button/DotButton";
|
import DotButton from "./Button/DotButton";
|
||||||
import FloatingButton from "./Button/FloatingButton";
|
import FloatingButton from "./Button/FloatingButton";
|
||||||
|
// Badge
|
||||||
|
import BadgeCustom from "./Badge/BadgeCustom";
|
||||||
|
// Container
|
||||||
|
import CircleContainer from "./Container/CircleContainer";
|
||||||
// Checkbox
|
// Checkbox
|
||||||
import CheckboxCustom from "./Checkbox/CheckboxCustom";
|
import CheckboxCustom from "./Checkbox/CheckboxCustom";
|
||||||
import CheckboxGroup from "./Checkbox/CheckboxGroup";
|
import CheckboxGroup from "./Checkbox/CheckboxGroup";
|
||||||
@@ -68,6 +72,8 @@ export {
|
|||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
DotButton,
|
DotButton,
|
||||||
|
// Badge
|
||||||
|
BadgeCustom,
|
||||||
// Center
|
// Center
|
||||||
CenterCustom,
|
CenterCustom,
|
||||||
// Checkbox
|
// Checkbox
|
||||||
@@ -75,6 +81,8 @@ export {
|
|||||||
CheckboxGroup,
|
CheckboxGroup,
|
||||||
// Clickable
|
// Clickable
|
||||||
ClickableCustom,
|
ClickableCustom,
|
||||||
|
// Container
|
||||||
|
CircleContainer,
|
||||||
// Divider
|
// Divider
|
||||||
Divider,
|
Divider,
|
||||||
DividerCustom,
|
DividerCustom,
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
"react-native-international-phone-number": "^0.9.3",
|
"react-native-international-phone-number": "^0.9.3",
|
||||||
"react-native-maps": "1.20.1",
|
"react-native-maps": "1.20.1",
|
||||||
"react-native-otp-entry": "^1.8.5",
|
"react-native-otp-entry": "^1.8.5",
|
||||||
|
"react-native-pager-view": "6.7.1",
|
||||||
"react-native-paper": "^5.14.5",
|
"react-native-paper": "^5.14.5",
|
||||||
"react-native-reanimated": "~3.17.4",
|
"react-native-reanimated": "~3.17.4",
|
||||||
"react-native-safe-area-context": "5.4.0",
|
"react-native-safe-area-context": "5.4.0",
|
||||||
|
|||||||
@@ -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 { RadioCustom, RadioGroup } from "@/components/Radio/RadioCustom";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { listDummyReportForum } from "@/lib/dummy-data/forum/report-list";
|
import { listDummyReportForum } from "@/lib/dummy-data/forum/report-list";
|
||||||
import { router } from "expo-router";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,17 @@ export default function Home_FeatureSection() {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={stylesHome.gridItem}
|
style={stylesHome.gridItem}
|
||||||
onPress={() => router.push("/(application)/(user)/collaboration/(tabs)")}
|
onPress={() =>
|
||||||
|
router.push("/(application)/(user)/collaboration/(tabs)")
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Ionicons name="share" size={48} color="white" />
|
<Ionicons name="share" size={48} color="white" />
|
||||||
<Text style={stylesHome.gridLabel}>Collaboration</Text>
|
<Text style={stylesHome.gridLabel}>Collaboration</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity style={stylesHome.gridItem}>
|
<TouchableOpacity
|
||||||
|
style={stylesHome.gridItem}
|
||||||
|
onPress={() => router.push("/(application)/(user)/voting/(tabs)")}
|
||||||
|
>
|
||||||
<Ionicons name="cube" size={48} color="white" />
|
<Ionicons name="cube" size={48} color="white" />
|
||||||
<Text style={stylesHome.gridLabel}>Voting</Text>
|
<Text style={stylesHome.gridLabel}>Voting</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|||||||
56
screens/Voting/BoxDetailContribution.tsx
Normal file
56
screens/Voting/BoxDetailContribution.tsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<BoxWithHeaderSection>
|
||||||
|
{headerAvatar ? headerAvatar : <Spacing />}
|
||||||
|
<StackCustom gap={"lg"}>
|
||||||
|
<TextCustom align="center" bold size="large">
|
||||||
|
Title of Voting Here
|
||||||
|
</TextCustom>
|
||||||
|
<TextCustom>
|
||||||
|
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?
|
||||||
|
</TextCustom>
|
||||||
|
<View>
|
||||||
|
<TextCustom bold size="small" align="center">
|
||||||
|
Batas Voting
|
||||||
|
</TextCustom>
|
||||||
|
<BadgeCustom
|
||||||
|
style={[GStyles.alignSelfCenter, { width: "70%" }]}
|
||||||
|
variant="light"
|
||||||
|
>
|
||||||
|
{dayjs().format("DD/MM/YYYY")} -{" "}
|
||||||
|
{dayjs().add(1, "day").format("DD/MM/YYYY")}
|
||||||
|
</BadgeCustom>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<StackCustom gap={"xs"}>
|
||||||
|
<TextCustom bold size="small" align="center">
|
||||||
|
Pilihan Anda
|
||||||
|
</TextCustom>
|
||||||
|
<BadgeCustom style={[GStyles.alignSelfCenter]}>
|
||||||
|
Pilihan 1
|
||||||
|
</BadgeCustom>
|
||||||
|
</StackCustom>
|
||||||
|
</StackCustom>
|
||||||
|
</BoxWithHeaderSection>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
30
screens/Voting/BoxDetailHasilVotingSection.tsx
Normal file
30
screens/Voting/BoxDetailHasilVotingSection.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import {
|
||||||
|
BaseBox,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
Grid,
|
||||||
|
CircleContainer,
|
||||||
|
} from "@/components";
|
||||||
|
|
||||||
|
export default function Voting_BoxDetailHasilVotingSection() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<BaseBox>
|
||||||
|
<StackCustom>
|
||||||
|
<TextCustom bold align="center">
|
||||||
|
Hasil Voting
|
||||||
|
</TextCustom>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
{Array.from({ length: 4 }).map((_, i) => (
|
||||||
|
<Grid.Col span={3} style={{ alignItems: "center" }} key={i}>
|
||||||
|
<CircleContainer value={9 % (i + 4)} />
|
||||||
|
<TextCustom size="small">Pilihan {i + 1}</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</StackCustom>
|
||||||
|
</BaseBox>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
71
screens/Voting/BoxDetailPublishSection.tsx
Normal file
71
screens/Voting/BoxDetailPublishSection.tsx
Normal file
@@ -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<any | number>("");
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<BoxWithHeaderSection>
|
||||||
|
{headerAvatar ? headerAvatar : <Spacing />}
|
||||||
|
<StackCustom gap={"lg"}>
|
||||||
|
<TextCustom align="center" bold size="large">
|
||||||
|
Title of Voting Here
|
||||||
|
</TextCustom>
|
||||||
|
<TextCustom>
|
||||||
|
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?
|
||||||
|
</TextCustom>
|
||||||
|
<View>
|
||||||
|
<TextCustom bold size="small" align="center">
|
||||||
|
Batas Voting
|
||||||
|
</TextCustom>
|
||||||
|
<BadgeCustom
|
||||||
|
style={[GStyles.alignSelfCenter, { width: "70%" }]}
|
||||||
|
variant="light"
|
||||||
|
>
|
||||||
|
{dayjs().format("DD/MM/YYYY")} -{" "}
|
||||||
|
{dayjs().add(1, "day").format("DD/MM/YYYY")}
|
||||||
|
</BadgeCustom>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View>
|
||||||
|
<TextCustom bold size="small">
|
||||||
|
Pilihan :
|
||||||
|
</TextCustom>
|
||||||
|
<RadioGroup value={value} onChange={setValue}>
|
||||||
|
{Array.from({ length: 4 }).map((_, i) => (
|
||||||
|
<View key={i}>
|
||||||
|
<RadioCustom
|
||||||
|
label={`Pilihan ${i + 1}`}
|
||||||
|
value={`Pilihan ${i + 1}`}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<ButtonCustom onPress={() => console.log("vote")}>
|
||||||
|
Vote
|
||||||
|
</ButtonCustom>
|
||||||
|
</StackCustom>
|
||||||
|
</BoxWithHeaderSection>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
59
screens/Voting/BoxDetailSection.tsx
Normal file
59
screens/Voting/BoxDetailSection.tsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<BoxWithHeaderSection>
|
||||||
|
{headerAvatar ? headerAvatar : <Spacing />}
|
||||||
|
<StackCustom>
|
||||||
|
<TextCustom align="center" bold size="large">
|
||||||
|
Title of Voting Here
|
||||||
|
</TextCustom>
|
||||||
|
<TextCustom>
|
||||||
|
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?
|
||||||
|
</TextCustom>
|
||||||
|
<View>
|
||||||
|
<TextCustom bold size="small" align="center">
|
||||||
|
Batas Voting
|
||||||
|
</TextCustom>
|
||||||
|
<BadgeCustom
|
||||||
|
style={[GStyles.alignSelfCenter, { width: "70%" }]}
|
||||||
|
variant="light"
|
||||||
|
>
|
||||||
|
{dayjs().format("DD/MM/YYYY")} -{" "}
|
||||||
|
{dayjs().add(1, "day").format("DD/MM/YYYY")}
|
||||||
|
</BadgeCustom>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View>
|
||||||
|
<TextCustom bold size="small">
|
||||||
|
Pilihan :
|
||||||
|
</TextCustom>
|
||||||
|
{Array.from({ length: 3 }).map((_, i) => (
|
||||||
|
<View key={i}>
|
||||||
|
<TextCustom>Nama Pilihan {i + 1}</TextCustom>
|
||||||
|
<Spacing />
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</StackCustom>
|
||||||
|
</BoxWithHeaderSection>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
54
screens/Voting/BoxPublishSection.tsx
Normal file
54
screens/Voting/BoxPublishSection.tsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<BoxWithHeaderSection href={href}>
|
||||||
|
<AvatarUsernameAndOtherComponent avatarHref="/profile/1" />
|
||||||
|
<Spacing />
|
||||||
|
<StackCustom>
|
||||||
|
<TextCustom align="center" bold truncate size="large">
|
||||||
|
Voting Title {id}
|
||||||
|
</TextCustom>
|
||||||
|
|
||||||
|
<BadgeCustom
|
||||||
|
style={{ width: "70%", alignSelf: "center" }}
|
||||||
|
variant="light"
|
||||||
|
>
|
||||||
|
{dayjs().format("DD/MM/YYYY")} -{" "}
|
||||||
|
{dayjs().add(1, "day").format("DD/MM/YYYY")}
|
||||||
|
</BadgeCustom>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
{Array.from({ length: 4 }).map((_, i) => (
|
||||||
|
<Grid.Col span={3} style={{ alignItems: "center" }} key={i}>
|
||||||
|
<CircleContainer value={9 % (i + 4)} />
|
||||||
|
<TextCustom size="small">Pilihan {i + 1}</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
{bottomComponent}
|
||||||
|
</StackCustom>
|
||||||
|
</BoxWithHeaderSection>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
124
screens/Voting/ButtonStatusSection.tsx
Normal file
124
screens/Voting/ButtonStatusSection.tsx
Normal file
@@ -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 (
|
||||||
|
<>
|
||||||
|
<ButtonCustom
|
||||||
|
backgroundColor="red"
|
||||||
|
textColor="white"
|
||||||
|
onPress={handleOpenDeleteAlert}
|
||||||
|
>
|
||||||
|
Hapus
|
||||||
|
</ButtonCustom>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case "publish":
|
||||||
|
return <></>;
|
||||||
|
|
||||||
|
case "review":
|
||||||
|
return (
|
||||||
|
<ButtonCustom onPress={handleBatalkanReview}>
|
||||||
|
Batalkan Review
|
||||||
|
</ButtonCustom>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "draft":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col span={5}>
|
||||||
|
<ButtonCustom onPress={handleAjukanReview}>
|
||||||
|
Ajukan Review
|
||||||
|
</ButtonCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={2}>
|
||||||
|
<View />
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={5}>{DeleteButton()}</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "reject":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col span={5}>
|
||||||
|
<ButtonCustom onPress={handleEditKembali}>
|
||||||
|
Edit Kembali
|
||||||
|
</ButtonCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={2}>
|
||||||
|
<View />
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={5}>{DeleteButton()}</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return <ButtonCustom disabled>Status Undifined</ButtonCustom>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -315,4 +315,9 @@ export const GStyles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
|
|
||||||
// =============== TEXT INPUT , TEXT AREA , SELECT =============== //
|
// =============== TEXT INPUT , TEXT AREA , SELECT =============== //
|
||||||
|
|
||||||
|
// =============== Alignment =============== //
|
||||||
|
alignSelfCenter: {
|
||||||
|
alignSelf: "center",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user