Component
Add : - CheckboxGroup - components/_Icon/ Fix: - CheckboxCustom: penambahan fitur untuk checkbox group Feature: Collaboration Add : - ProjectMainSelectedSection Fix : - detail-participant - detail-project-main # No Issue
This commit is contained in:
@@ -140,13 +140,14 @@ export default function UserLayout() {
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="collaboration/[id]/detail-project-main"
|
||||
name="collaboration/[id]/edit"
|
||||
options={{
|
||||
title: "Proyek Saya",
|
||||
title: "Edit Proyek",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
{/* ========== End Collaboration Section ========= */}
|
||||
|
||||
{/* ========== Forum Section ========= */}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from "@/components";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
|
||||
import { Feather, MaterialIcons } from "@expo/vector-icons";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
@@ -21,7 +21,6 @@ export default function CollaborationDetailParticipant() {
|
||||
<ViewWrapper>
|
||||
<Collaboration_BoxDetailSection id={id as string} />
|
||||
<BaseBox style={{ height: 500 }}>
|
||||
<StackCustom>
|
||||
<TextCustom align="center" bold size="large">
|
||||
Partisipan
|
||||
</TextCustom>
|
||||
@@ -40,7 +39,6 @@ export default function CollaborationDetailParticipant() {
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</ViewWrapper>
|
||||
|
||||
|
||||
@@ -1,78 +1,91 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BaseBox,
|
||||
AlertDefaultSystem,
|
||||
BackButton,
|
||||
ButtonCustom,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
Grid,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import CheckboxCustom from "@/components/Checkbox/CheckboxCustom";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { IconEdit } from "@/components/_Icon";
|
||||
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
|
||||
import { Feather, MaterialIcons } from "@expo/vector-icons";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import Collaboration_MainParticipanSelectedSection from "@/screens/Collaboration/ProjectMainSelectedSection";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function CollaborationDetailProjectMain() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [openDrawerParticipant, setOpenDrawerParticipant] = useState(false);
|
||||
const [agreed, setAgreed] = useState(false);
|
||||
const [newsletter, setNewsletter] = useState(false);
|
||||
const [selected, setSelected] = useState<(string | number)[]>([]);
|
||||
|
||||
const handleEdit = () => {
|
||||
console.log("Edit collaboration");
|
||||
router.push("/(application)/(user)/collaboration/(id)/edit");
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Proyek Saya",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Collaboration_BoxDetailSection id={id as string} />
|
||||
<BaseBox style={{ height: 500 }}>
|
||||
<StackCustom>
|
||||
<TextCustom size="default" color="red" bold>
|
||||
*{" "}
|
||||
<TextCustom size="small" semiBold>
|
||||
Pilih user yang akan menjadi tim proyek anda
|
||||
</TextCustom>
|
||||
</TextCustom>
|
||||
<Collaboration_MainParticipanSelectedSection
|
||||
selected={selected}
|
||||
setSelected={setSelected}
|
||||
setOpenDrawerParticipant={setOpenDrawerParticipant}
|
||||
/>
|
||||
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Grid key={index}>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<CheckboxCustom
|
||||
value={newsletter}
|
||||
onChange={() => {
|
||||
console.log("newsletter", newsletter);
|
||||
setNewsletter(!newsletter);
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2} style={{ alignItems: "center" }}>
|
||||
<AvatarCustom />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
||||
<TextCustom bold truncate>
|
||||
Username
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<MaterialIcons
|
||||
name="notes"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
))}
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
AlertDefaultSystem({
|
||||
title: "Buat Grup",
|
||||
message:
|
||||
"Apakah anda yakin ingin membuat grup untuk proyek ini ?",
|
||||
textLeft: "Tidak",
|
||||
textRight: "Ya",
|
||||
onPressLeft: () => {},
|
||||
onPressRight: () => {
|
||||
router.navigate(
|
||||
"/(application)/(user)/collaboration/(tabs)/group"
|
||||
);
|
||||
console.log("selected :", selected);
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Buat Grup
|
||||
</ButtonCustom>
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
label: "Edit",
|
||||
path: "/(application)/(user)/collaboration/(tabs)/group",
|
||||
icon: <IconEdit />,
|
||||
},
|
||||
]}
|
||||
onPressItem={(item) => {
|
||||
handleEdit();
|
||||
}}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerParticipant}
|
||||
closeDrawer={() => setOpenDrawerParticipant(false)}
|
||||
|
||||
53
app/(application)/(user)/collaboration/[id]/edit.tsx
Normal file
53
app/(application)/(user)/collaboration/[id]/edit.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
ButtonCustom,
|
||||
SelectCustom,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function CollaborationEdit() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom label="Judul" placeholder="Masukan judul" required />
|
||||
<TextInputCustom label="Lokasi" placeholder="Masukan lokasi" required />
|
||||
<SelectCustom
|
||||
label="Pilih Industri"
|
||||
data={[
|
||||
{ label: "Industri 1", value: "industri-1" },
|
||||
{ label: "Industri 2", value: "industri-2" },
|
||||
{ label: "Industri 3", value: "industri-3" },
|
||||
]}
|
||||
onChange={(value) => console.log(value)}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Tujuan Proyek"
|
||||
placeholder="Masukan tujuan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Keuntungan Proyek"
|
||||
placeholder="Masukan keuntungan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<ButtonCustom
|
||||
title="Update"
|
||||
onPress={() => {
|
||||
console.log("Update proyek");
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,61 +1,82 @@
|
||||
import React, { useState } from "react";
|
||||
import { View, Text, TouchableOpacity, Animated, Easing } from "react-native";
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import { MaterialIcons } from "@expo/vector-icons"; // Bisa diganti dengan ikon lain
|
||||
import React, { useContext } from "react";
|
||||
import { Animated, Text, TouchableOpacity, View } from "react-native";
|
||||
import { checkboxStyles } from "./checkbox-styles";
|
||||
|
||||
// Context untuk Group
|
||||
interface CheckboxGroupContextType {
|
||||
value: (string | number)[];
|
||||
onChange: (value: (string | number)[]) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const CheckboxGroupContext =
|
||||
React.createContext<CheckboxGroupContextType | null>(null);
|
||||
|
||||
// Tipe props
|
||||
// Tambahkan prop baru: groupValueKey
|
||||
interface CheckboxProps {
|
||||
label?: string;
|
||||
description?: string;
|
||||
error?: string;
|
||||
value?: boolean;
|
||||
value?: boolean; // controlled value (untuk standalone)
|
||||
onChange?: (checked: boolean) => void;
|
||||
disabled?: boolean;
|
||||
size?: number; // ukuran checkbox (default: 20)
|
||||
color?: string; // warna utama (default: '#3b82f6' - biru tailwind)
|
||||
size?: number;
|
||||
color?: string;
|
||||
style?: object;
|
||||
component?: React.ReactNode;
|
||||
// Prop tambahan untuk Group
|
||||
valueKey?: string | number; // nilai unik untuk identifikasi di group
|
||||
}
|
||||
|
||||
export const CheckboxCustom: React.FC<CheckboxProps> = ({
|
||||
const CheckboxCustom: React.FC<CheckboxProps> = ({
|
||||
label,
|
||||
description,
|
||||
error,
|
||||
value: controlledValue,
|
||||
onChange,
|
||||
disabled = false,
|
||||
disabled: propDisabled,
|
||||
size = 20,
|
||||
color = "#3b82f6",
|
||||
color = AccentColor.softblue,
|
||||
style,
|
||||
component,
|
||||
valueKey,
|
||||
}) => {
|
||||
const [uncontrolledChecked, setUncontrolledChecked] = useState(false);
|
||||
const isChecked = controlledValue ?? uncontrolledChecked;
|
||||
// const [uncontrolledChecked, setUncontrolledChecked] = useState(false);
|
||||
// const isChecked = controlledValue ?? uncontrolledChecked;
|
||||
// const scaleValue = new Animated.Value(isChecked ? 1 : 0);
|
||||
|
||||
const group = useContext(CheckboxGroupContext);
|
||||
const isInsideGroup = !!group && valueKey !== undefined;
|
||||
|
||||
// Jika di dalam group, gunakan logika group
|
||||
const isChecked = isInsideGroup
|
||||
? group.value.includes(valueKey!)
|
||||
: controlledValue ?? false;
|
||||
|
||||
const disabled = propDisabled || (isInsideGroup && group.disabled);
|
||||
|
||||
const scaleValue = new Animated.Value(isChecked ? 1 : 0);
|
||||
|
||||
const toggle = () => {
|
||||
if (disabled) return;
|
||||
|
||||
const newValue = !isChecked;
|
||||
if (onChange) onChange(newValue);
|
||||
if (controlledValue === undefined) {
|
||||
setUncontrolledChecked(newValue);
|
||||
if (isInsideGroup) {
|
||||
const newValue = isChecked
|
||||
? group.value.filter((v) => v !== valueKey)
|
||||
: [...group.value, valueKey!];
|
||||
group.onChange(newValue);
|
||||
} else if (onChange) {
|
||||
onChange(!controlledValue);
|
||||
}
|
||||
|
||||
// Animasi scale
|
||||
Animated.spring(scaleValue, {
|
||||
toValue: newValue ? 1 : 0,
|
||||
friction: 7,
|
||||
tension: 40,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
};
|
||||
|
||||
const styles = checkboxStyles({
|
||||
size,
|
||||
color,
|
||||
disabled,
|
||||
disabled: disabled as boolean,
|
||||
error: !!error,
|
||||
});
|
||||
|
||||
@@ -106,4 +127,7 @@ export const CheckboxCustom: React.FC<CheckboxProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckboxCustom
|
||||
export default CheckboxCustom;
|
||||
|
||||
// Export context agar bisa digunakan
|
||||
export { CheckboxGroupContext };
|
||||
|
||||
75
components/Checkbox/CheckboxGroup.tsx
Normal file
75
components/Checkbox/CheckboxGroup.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import React, { useState, useMemo } from "react";
|
||||
import { View, Text, StyleSheet } from "react-native";
|
||||
import { CheckboxGroupContext } from "./CheckboxCustom";
|
||||
|
||||
interface CheckboxGroupProps {
|
||||
value?: (string | number)[];
|
||||
onChange?: (values: (string | number)[]) => void;
|
||||
defaultValue?: (string | number)[];
|
||||
label?: string;
|
||||
description?: string;
|
||||
error?: string;
|
||||
disabled?: boolean;
|
||||
children: React.ReactNode;
|
||||
style?: object;
|
||||
}
|
||||
const CheckboxGroup: React.FC<CheckboxGroupProps> = ({
|
||||
value: controlledValue,
|
||||
onChange,
|
||||
defaultValue = [],
|
||||
label,
|
||||
description,
|
||||
error,
|
||||
disabled = false,
|
||||
children,
|
||||
style,
|
||||
}) => {
|
||||
const [uncontrolledValue, setUncontrolledValue] =
|
||||
useState<(string | number)[]>(defaultValue);
|
||||
|
||||
const value = controlledValue ?? uncontrolledValue;
|
||||
const handleChange = onChange ?? setUncontrolledValue;
|
||||
|
||||
const contextValue = useMemo(
|
||||
() => ({ value, onChange: handleChange, disabled }),
|
||||
[value, handleChange, disabled]
|
||||
);
|
||||
|
||||
return (
|
||||
<CheckboxGroupContext.Provider value={contextValue}>
|
||||
<View style={[styles.container, style]}>
|
||||
{label ? <Text style={styles.label}>{label}</Text> : null}
|
||||
{description ? (
|
||||
<Text style={styles.description}>{description}</Text>
|
||||
) : null}
|
||||
{children}
|
||||
{error ? <Text style={styles.errorText}>{error}</Text> : null}
|
||||
</View>
|
||||
</CheckboxGroupContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
gap: 8,
|
||||
},
|
||||
label: {
|
||||
fontSize: 16,
|
||||
fontWeight: "600",
|
||||
color: "#f8f9fa",
|
||||
marginBottom: 4,
|
||||
},
|
||||
description: {
|
||||
fontSize: 14,
|
||||
color: "#ced4da",
|
||||
marginBottom: 8,
|
||||
},
|
||||
errorText: {
|
||||
color: "#e03131",
|
||||
fontSize: 14,
|
||||
marginTop: 4,
|
||||
},
|
||||
});
|
||||
|
||||
export default CheckboxGroup;
|
||||
|
||||
@@ -11,8 +11,7 @@ export const checkboxStyles = (props: {
|
||||
container: {
|
||||
flexDirection: "row",
|
||||
alignItems: "flex-start",
|
||||
// marginBottom: 12,
|
||||
|
||||
// marginBottom: 12,
|
||||
},
|
||||
innerContainer: {
|
||||
flexDirection: "row",
|
||||
|
||||
10
components/_Icon/IconEdit.tsx
Normal file
10
components/_Icon/IconEdit.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { FontAwesome5 } from "@expo/vector-icons";
|
||||
|
||||
export default function IconEdit() {
|
||||
return (
|
||||
<>
|
||||
<FontAwesome5 name="edit" size={ICON_SIZE_SMALL} color="white" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
3
components/_Icon/index.ts
Normal file
3
components/_Icon/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import IconEdit from "./IconEdit";
|
||||
|
||||
export { IconEdit };
|
||||
@@ -7,6 +7,9 @@ import ButtonCenteredOnly from "./Button/ButtonCenteredOnly";
|
||||
import ButtonCustom from "./Button/ButtonCustom";
|
||||
import DotButton from "./Button/DotButton";
|
||||
import FloatingButton from "./Button/FloatingButton";
|
||||
// Checkbox
|
||||
import CheckboxCustom from "./Checkbox/CheckboxCustom";
|
||||
import CheckboxGroup from "./Checkbox/CheckboxGroup";
|
||||
// Drawer
|
||||
import DrawerCustom from "./Drawer/DrawerCustom";
|
||||
import MenuDrawerDynamicGrid from "./Drawer/MenuDrawerDynamicGird";
|
||||
@@ -59,18 +62,23 @@ export {
|
||||
// Box
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
BoxWithHeaderSection, ButtonCenteredOnly,
|
||||
BoxWithHeaderSection,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
DotButton,
|
||||
// Center
|
||||
CenterCustom,
|
||||
// Checkbox
|
||||
CheckboxCustom,
|
||||
CheckboxGroup,
|
||||
// Clickable
|
||||
ClickableCustom,
|
||||
// Divider
|
||||
Divider,
|
||||
DividerCustom,
|
||||
DividerCustom,
|
||||
// Drawer
|
||||
DrawerCustom, FloatingButton,
|
||||
DrawerCustom,
|
||||
FloatingButton,
|
||||
// Grid
|
||||
Grid,
|
||||
InformationBox,
|
||||
@@ -85,7 +93,8 @@ export {
|
||||
// ShareComponent
|
||||
Spacing,
|
||||
// Stack
|
||||
StackCustom, TabBarBackground,
|
||||
StackCustom,
|
||||
TabBarBackground,
|
||||
// TextArea
|
||||
TextAreaCustom,
|
||||
// Text
|
||||
@@ -93,6 +102,5 @@ export {
|
||||
// TextInput
|
||||
TextInputCustom,
|
||||
// ViewWrapper
|
||||
ViewWrapper
|
||||
ViewWrapper,
|
||||
};
|
||||
|
||||
|
||||
69
screens/Collaboration/ProjectMainSelectedSection.tsx
Normal file
69
screens/Collaboration/ProjectMainSelectedSection.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
BaseBox,
|
||||
CheckboxCustom,
|
||||
CheckboxGroup,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom
|
||||
} from "@/components";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function Collaboration_ProjectMainSelectedSection({
|
||||
selected,
|
||||
setSelected,
|
||||
setOpenDrawerParticipant,
|
||||
}: {
|
||||
selected: (string | number)[];
|
||||
setSelected: (value: (string | number)[]) => void;
|
||||
setOpenDrawerParticipant: (value: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<BaseBox style={{ height: 500 }}>
|
||||
<StackCustom>
|
||||
<TextCustom size="default" color="red" bold>
|
||||
*{" "}
|
||||
<TextCustom size="small" semiBold>
|
||||
Pilih user yang akan menjadi tim proyek anda
|
||||
</TextCustom>
|
||||
</TextCustom>
|
||||
|
||||
<CheckboxGroup value={selected} onChange={setSelected}>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<View key={index}>
|
||||
<Grid key={index}>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<CheckboxCustom valueKey={`user-${index}`} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2} style={{ alignItems: "center" }}>
|
||||
<AvatarCustom />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
||||
<TextCustom bold truncate>
|
||||
Username
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<MaterialIcons
|
||||
name="notes"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
onPress={() => setOpenDrawerParticipant(true)}
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</View>
|
||||
))}
|
||||
</CheckboxGroup>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user