Merge pull request 'Feature Job' (#4) from job/25-jul-25 into main
Reviewed-on: bip/hipmi-mobile#4
This commit is contained in:
@@ -150,6 +150,38 @@ export default function UserLayout() {
|
||||
|
||||
{/* ========== End Collaboration Section ========= */}
|
||||
|
||||
{/* ========== Job Section ========= */}
|
||||
<Stack.Screen
|
||||
name="job/create"
|
||||
options={{
|
||||
title: "Tambah Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/(tabs)"
|
||||
options={{
|
||||
title: "Job Vacancy",
|
||||
headerLeft: () => <BackButton path="/home" />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/[id]/index"
|
||||
options={{
|
||||
title: "Detail Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/[id]/edit"
|
||||
options={{
|
||||
title: "Edit Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== End Job Section ========= */}
|
||||
|
||||
{/* ========== Forum Section ========= */}
|
||||
<Stack.Screen
|
||||
name="forum/create"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { IconHome } from "@/components/_Icon";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Tabs } from "expo-router";
|
||||
@@ -9,9 +10,7 @@ export default function CollaborationTabsLayout() {
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="home" color={color} />
|
||||
),
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { IconHome, IconStatus } from "@/components/_Icon";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { FontAwesome5, Ionicons } from "@expo/vector-icons";
|
||||
import { Tabs } from "expo-router";
|
||||
@@ -12,7 +13,7 @@ export default function EventTabsLayout() {
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="home" color={color} />
|
||||
<IconHome color={color}/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
@@ -21,7 +22,7 @@ export default function EventTabsLayout() {
|
||||
options={{
|
||||
title: "Status",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="list" color={color} />
|
||||
<IconStatus color={color}/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -73,9 +73,9 @@ export default function EventDetailStatus() {
|
||||
height={250}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={menuDrawerDraftEvent({ id: id as string })}
|
||||
data={menuDrawerDraftEvent({ id: id as string }) as any}
|
||||
columns={4}
|
||||
onPressItem={handlePress}
|
||||
onPressItem={handlePress as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
|
||||
@@ -3,16 +3,14 @@ import {
|
||||
AvatarCustom,
|
||||
BackButton,
|
||||
DrawerCustom,
|
||||
TextInputCustom,
|
||||
SearchInput,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import FloatingButton from "@/components/Button/FloatingButton";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
|
||||
import { listDummyDiscussionForum } from "@/screens/Forum/list-data-dummy";
|
||||
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
@@ -34,20 +32,7 @@ export default function Forum() {
|
||||
/>
|
||||
|
||||
<ViewWrapper
|
||||
headerComponent={
|
||||
<TextInputCustom
|
||||
iconLeft={
|
||||
<Ionicons
|
||||
name="search-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.placeholder}
|
||||
/>
|
||||
}
|
||||
placeholder="Cari topik forum..."
|
||||
borderRadius={50}
|
||||
containerStyle={{ marginBottom: 0 }}
|
||||
/>
|
||||
}
|
||||
headerComponent={<SearchInput placeholder="Cari topik diskusi" />}
|
||||
floatingButton={
|
||||
<FloatingButton
|
||||
onPress={() =>
|
||||
|
||||
34
app/(application)/(user)/job/(tabs)/_layout.tsx
Normal file
34
app/(application)/(user)/job/(tabs)/_layout.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { IconHome, IconStatus } from "@/components/_Icon";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Tabs } from "expo-router";
|
||||
|
||||
export default function JobTabsLayout() {
|
||||
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="archive"
|
||||
options={{
|
||||
title: "Arsip",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="archive" color={color} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
16
app/(application)/(user)/job/(tabs)/archive.tsx
Normal file
16
app/(application)/(user)/job/(tabs)/archive.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { BaseBox, TextCustom, ViewWrapper } from "@/components";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
|
||||
export default function JobArchive() {
|
||||
return (
|
||||
<ViewWrapper hideFooter>
|
||||
{jobDataDummy.map((e, i) => (
|
||||
<BaseBox key={i} paddingTop={20} paddingBottom={20}>
|
||||
<TextCustom align="center" bold truncate size="large">
|
||||
{e.posisi}
|
||||
</TextCustom>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
34
app/(application)/(user)/job/(tabs)/index.tsx
Normal file
34
app/(application)/(user)/job/(tabs)/index.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BoxWithHeaderSection,
|
||||
FloatingButton,
|
||||
SearchInput,
|
||||
Spacing,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function JobBeranda() {
|
||||
return (
|
||||
<ViewWrapper
|
||||
hideFooter
|
||||
floatingButton={
|
||||
<FloatingButton onPress={() => router.push("/job/create")} />
|
||||
}
|
||||
headerComponent={<SearchInput placeholder="Cari pekerjaan" />}
|
||||
>
|
||||
{jobDataDummy.map((item, index) => (
|
||||
<BoxWithHeaderSection key={index} onPress={() => router.push(`/job/${item.id}`)}>
|
||||
<AvatarUsernameAndOtherComponent avatarHref={`/profile/${item.id}`} />
|
||||
<Spacing />
|
||||
<TextCustom truncate={2} align="center" bold size="large">
|
||||
{item.posisi}
|
||||
</TextCustom>
|
||||
<Spacing />
|
||||
</BoxWithHeaderSection>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
50
app/(application)/(user)/job/(tabs)/status.tsx
Normal file
50
app/(application)/(user)/job/(tabs)/status.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
BaseBox,
|
||||
ScrollableCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { masterStatus } from "@/lib/dummy-data/_master/status";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function JobStatus() {
|
||||
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>
|
||||
{jobDataDummy.map((e, i) => (
|
||||
<BaseBox
|
||||
key={i}
|
||||
paddingTop={20}
|
||||
paddingBottom={20}
|
||||
href={`/job/${e.id}/${activeCategory}/detail`}
|
||||
// onPress={() => console.log("pressed")}
|
||||
>
|
||||
<TextCustom align="center" bold truncate size="large">
|
||||
{e.posisi} {activeCategory?.toUpperCase()}
|
||||
</TextCustom>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
65
app/(application)/(user)/job/[id]/[status]/detail.tsx
Normal file
65
app/(application)/(user)/job/[id]/[status]/detail.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import {
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconEdit } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import Job_BoxDetailSection from "@/screens/Job/BoxDetailSection";
|
||||
import Job_ButtonStatusSection from "@/screens/Job/ButtonStatusSection";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function JobDetailStatus() {
|
||||
const { id, status } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const jobDetail = jobDataDummy.find((e) => e.id === Number(id));
|
||||
|
||||
const handlePress = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH >> ", item.path);
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawer(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Detail`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Job_BoxDetailSection data={jobDetail} />
|
||||
<Job_ButtonStatusSection status={status as string} />
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconEdit />,
|
||||
label: "Edit",
|
||||
path: `/job/${id}/edit`,
|
||||
},
|
||||
]}
|
||||
columns={4}
|
||||
onPressItem={handlePress as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
67
app/(application)/(user)/job/[id]/edit.tsx
Normal file
67
app/(application)/(user)/job/[id]/edit.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import {
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function JobEdit() {
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom onPress={() => router.back()}>Update</ButtonCustom>
|
||||
<Spacing />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="Poster atau gambar lowongan kerja bersifat opsional, tidak wajib untuk dimasukkan dan upload lah gambar yang sesuai dengan deskripsi lowongan kerja." />
|
||||
|
||||
<LandscapeFrameUploaded />
|
||||
<ButtonCenteredOnly
|
||||
onPress={() => {
|
||||
router.push("/(application)/(image)/take-picture/123");
|
||||
}}
|
||||
icon="upload"
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<TextInputCustom
|
||||
label="Judul Lowongan"
|
||||
placeholder="Masukan Judul Lowongan Kerja"
|
||||
required
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Syarat & Kualifikasi"
|
||||
placeholder="Masukan Syarat & Kualifikasi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Lowongan"
|
||||
placeholder="Masukan Deskripsi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
{buttonSubmit()}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
79
app/(application)/(user)/job/[id]/index.tsx
Normal file
79
app/(application)/(user)/job/[id]/index.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
ButtonCustom,
|
||||
Spacing,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import Job_BoxDetailSection from "@/screens/Job/BoxDetailSection";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import * as Clipboard from "expo-clipboard";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { Alert, Linking } from "react-native";
|
||||
|
||||
export default function JobDetail() {
|
||||
const { id } = useLocalSearchParams();
|
||||
|
||||
const jobDetail = jobDataDummy.find((e) => e.id === Number(id));
|
||||
|
||||
const OpenLinkButton = () => {
|
||||
const jobUrl =
|
||||
"https://stg-hipmi.wibudev.com/job-vacancy/cm6ijt9w8005zucv4twsct657";
|
||||
|
||||
const openInBrowser = async () => {
|
||||
const supported = await Linking.canOpenURL(jobUrl);
|
||||
if (supported) {
|
||||
await Linking.openURL(jobUrl);
|
||||
} else {
|
||||
Alert.alert("Gagal membuka link", "Browser tidak tersedia.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ButtonCustom
|
||||
iconLeft={
|
||||
<Ionicons name="globe" size={ICON_SIZE_SMALL} color="white" />
|
||||
}
|
||||
onPress={openInBrowser}
|
||||
backgroundColor="green"
|
||||
textColor="white"
|
||||
>
|
||||
Buka Lowongan di Browser
|
||||
</ButtonCustom>
|
||||
);
|
||||
};
|
||||
|
||||
const CopyLinkButton = () => {
|
||||
const jobUrl =
|
||||
"https://stg-hipmi.wibudev.com/job-vacancy/cm6ijt9w8005zucv4twsct657";
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
await Clipboard.setStringAsync(jobUrl);
|
||||
Alert.alert(
|
||||
"Link disalin",
|
||||
"Tautan lowongan telah disalin ke clipboard."
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ButtonCustom
|
||||
iconLeft={<Ionicons name="copy" size={ICON_SIZE_SMALL} color="white" />}
|
||||
onPress={copyToClipboard}
|
||||
backgroundColor={MainColor.orange}
|
||||
textColor="white"
|
||||
>
|
||||
Salin Link
|
||||
</ButtonCustom>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<Job_BoxDetailSection data={jobDetail}/>
|
||||
<OpenLinkButton />
|
||||
<Spacing />
|
||||
<CopyLinkButton />
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
73
app/(application)/(user)/job/create.tsx
Normal file
73
app/(application)/(user)/job/create.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function JobCreate() {
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom
|
||||
onPress={() =>
|
||||
router.replace("/(application)/(user)/job/(tabs)/status")
|
||||
}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
<Spacing />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="Poster atau gambar lowongan kerja bersifat opsional, tidak wajib untuk dimasukkan dan upload lah gambar yang sesuai dengan deskripsi lowongan kerja." />
|
||||
|
||||
<LandscapeFrameUploaded />
|
||||
<ButtonCenteredOnly
|
||||
onPress={() => {
|
||||
router.push("/(application)/(image)/take-picture/123");
|
||||
}}
|
||||
icon="upload"
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<TextInputCustom
|
||||
label="Judul Lowongan"
|
||||
placeholder="Masukan Judul Lowongan Kerja"
|
||||
required
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Syarat & Kualifikasi"
|
||||
placeholder="Masukan Syarat & Kualifikasi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Lowongan"
|
||||
placeholder="Masukan Deskripsi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
{buttonSubmit()}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
3
bun.lock
3
bun.lock
@@ -16,6 +16,7 @@
|
||||
"expo": "53.0.17",
|
||||
"expo-blur": "~14.1.5",
|
||||
"expo-camera": "~16.1.10",
|
||||
"expo-clipboard": "~7.1.5",
|
||||
"expo-constants": "~17.1.7",
|
||||
"expo-font": "~13.3.2",
|
||||
"expo-haptics": "~14.1.4",
|
||||
@@ -827,6 +828,8 @@
|
||||
|
||||
"expo-camera": ["expo-camera@16.1.10", "", { "dependencies": { "invariant": "^2.2.4" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-qoRJeSwPmMbuu0VfnQTC+q79Kt2SqTWColEImgithL9u0qUQcC55U89IfhZk55Hpt6f1DgKuDzUOG5oY+snSWg=="],
|
||||
|
||||
"expo-clipboard": ["expo-clipboard@7.1.5", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-TCANUGOxouoJXxKBW5ASJl2WlmQLGpuZGemDCL2fO5ZMl57DGTypUmagb0CVUFxDl0yAtFIcESd78UsF9o64aw=="],
|
||||
|
||||
"expo-constants": ["expo-constants@17.1.7", "", { "dependencies": { "@expo/config": "~11.0.12", "@expo/env": "~1.0.7" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-byBjGsJ6T6FrLlhOBxw4EaiMXrZEn/MlUYIj/JAd+FS7ll5X/S4qVRbIimSJtdW47hXMq0zxPfJX6njtA56hHA=="],
|
||||
|
||||
"expo-file-system": ["expo-file-system@18.1.11", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-HJw/m0nVOKeqeRjPjGdvm+zBi5/NxcdPf8M8P3G2JFvH5Z8vBWqVDic2O58jnT1OFEy0XXzoH9UqFu7cHg9DTQ=="],
|
||||
|
||||
@@ -72,6 +72,8 @@ export default function BaseBox({
|
||||
marginBottom,
|
||||
paddingBlock,
|
||||
paddingInline,
|
||||
paddingTop,
|
||||
paddingBottom,
|
||||
},
|
||||
style,
|
||||
]}
|
||||
|
||||
10
components/_Icon/IconHome.tsx
Normal file
10
components/_Icon/IconHome.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export default function IconHome({ color }: { color?: string }) {
|
||||
return (
|
||||
<>
|
||||
<Ionicons name="home" size={ICON_SIZE_SMALL} color={color || "white"} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
12
components/_Icon/IconStatus.tsx
Normal file
12
components/_Icon/IconStatus.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
|
||||
export default function IconStatus({ color }: { color?: string }) {
|
||||
return (
|
||||
<MaterialIcons
|
||||
size={ICON_SIZE_SMALL}
|
||||
name="checklist-rtl"
|
||||
color={color || "white"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import IconEdit from "./IconEdit";
|
||||
import IconHome from "./IconHome";
|
||||
import IconStatus from "./IconStatus";
|
||||
|
||||
export { IconEdit };
|
||||
export { IconEdit, IconHome, IconStatus };
|
||||
|
||||
32
components/_ShareComponent/DummyLandscapeImage.tsx
Normal file
32
components/_ShareComponent/DummyLandscapeImage.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { Image } from "expo-image";
|
||||
import { StyleSheet } from "react-native";
|
||||
import ClickableCustom from "../Clickable/ClickableCustom";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function DummyLandscapeImage() {
|
||||
return (
|
||||
<ClickableCustom
|
||||
onPress={() => {
|
||||
router.push("/(application)/(image)/preview-image/1");
|
||||
}}
|
||||
>
|
||||
<Image source={DUMMY_IMAGE.background} style={styles.backgroundImage} />
|
||||
</ClickableCustom>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
backgroundImage: {
|
||||
width: "100%",
|
||||
height: 200, // Tinggi background sesuai kebutuhan
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
borderRadius: 6,
|
||||
overflow: "hidden",
|
||||
borderWidth: 1,
|
||||
borderColor: AccentColor.blue,
|
||||
backgroundColor: "white",
|
||||
},
|
||||
});
|
||||
39
components/_ShareComponent/SearchInput.tsx
Normal file
39
components/_ShareComponent/SearchInput.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import TextInputCustom from "../TextInput/TextInputCustom";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { StyleProp, ViewStyle, TextStyle } from "react-native";
|
||||
|
||||
interface SearchInputProps {
|
||||
placeholder?: string;
|
||||
onPress?: () => void;
|
||||
iconLeft?: React.ReactNode;
|
||||
iconRight?: React.ReactNode;
|
||||
containerStyle?: StyleProp<ViewStyle>;
|
||||
style?: StyleProp<TextStyle>;
|
||||
}
|
||||
export default function SearchInput({
|
||||
placeholder,
|
||||
onPress,
|
||||
iconLeft,
|
||||
iconRight,
|
||||
containerStyle = { marginBottom: 0 },
|
||||
style,
|
||||
...props
|
||||
}: SearchInputProps) {
|
||||
return (
|
||||
<TextInputCustom
|
||||
iconLeft={
|
||||
<Ionicons
|
||||
name="search-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.placeholder}
|
||||
/>
|
||||
}
|
||||
placeholder={placeholder}
|
||||
borderRadius={50}
|
||||
containerStyle={containerStyle}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -49,6 +49,8 @@ import AvatarUsernameAndOtherComponent from "./_ShareComponent/AvataraAndOtherHe
|
||||
import Spacing from "./_ShareComponent/Spacing";
|
||||
import TabBarBackground from "./_ShareComponent/TabBarBackground";
|
||||
import ViewWrapper from "./_ShareComponent/ViewWrapper";
|
||||
import SearchInput from "./_ShareComponent/SearchInput";
|
||||
import DummyLandscapeImage from "./_ShareComponent/DummyLandscapeImage";
|
||||
|
||||
export {
|
||||
AlertCustom,
|
||||
@@ -91,6 +93,8 @@ export {
|
||||
// Select
|
||||
SelectCustom,
|
||||
// ShareComponent
|
||||
SearchInput,
|
||||
DummyLandscapeImage,
|
||||
Spacing,
|
||||
// Stack
|
||||
StackCustom,
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"expo": "53.0.17",
|
||||
"expo-blur": "~14.1.5",
|
||||
"expo-camera": "~16.1.10",
|
||||
"expo-clipboard": "~7.1.5",
|
||||
"expo-constants": "~17.1.7",
|
||||
"expo-font": "~13.3.2",
|
||||
"expo-haptics": "~14.1.4",
|
||||
|
||||
@@ -1,52 +1,55 @@
|
||||
import { TextCustom } from "@/components";
|
||||
import { ClickableCustom, TextCustom } from "@/components";
|
||||
import Spacing from "@/components/_ShareComponent/Spacing";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/FontAwesome";
|
||||
import { stylesHome } from "./homeViewStyle";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function Home_BottomFeatureSection() {
|
||||
return (
|
||||
<>
|
||||
<View style={stylesHome.jobVacancyContainer}>
|
||||
<View style={stylesHome.jobVacancyHeader}>
|
||||
<Icon name="briefcase" size={24} color="white" />
|
||||
<Spacing width={10}/>
|
||||
<TextCustom bold size="large">
|
||||
Job Vacancy
|
||||
</TextCustom>
|
||||
</View>
|
||||
|
||||
<View style={stylesHome.vacancyList}>
|
||||
{/* Vacancy Item 1 */}
|
||||
<View style={stylesHome.vacancyItem}>
|
||||
{/* <Icon name="user" size={20} color="#FFD700" /> */}
|
||||
<View style={stylesHome.vacancyDetails}>
|
||||
<TextCustom bold color="yellow" truncate size="large">
|
||||
Bagas_banuna
|
||||
</TextCustom>
|
||||
<Spacing height={5} />
|
||||
<TextCustom truncate={2}>
|
||||
Dicari perawat kucing dan perawat anjing
|
||||
</TextCustom>
|
||||
</View>
|
||||
<ClickableCustom onPress={() => router.push("/job")}>
|
||||
<View style={stylesHome.jobVacancyContainer}>
|
||||
<View style={stylesHome.jobVacancyHeader}>
|
||||
<Icon name="briefcase" size={24} color="white" />
|
||||
<Spacing width={10} />
|
||||
<TextCustom bold size="large">
|
||||
Job Vacancy
|
||||
</TextCustom>
|
||||
</View>
|
||||
|
||||
{/* Vacancy Item 2 */}
|
||||
<View style={stylesHome.vacancyItem}>
|
||||
{/* <Icon name="user" size={20} color="#FFD700" /> */}
|
||||
<View style={stylesHome.vacancyDetails}>
|
||||
<TextCustom bold color="yellow" truncate size="large">
|
||||
fibramarcell
|
||||
</TextCustom>
|
||||
<Spacing height={5} />
|
||||
<TextCustom truncate={2}>
|
||||
Di Butuhkan Seorang Programer dan Designer
|
||||
</TextCustom>
|
||||
<View style={stylesHome.vacancyList}>
|
||||
{/* Vacancy Item 1 */}
|
||||
<View style={stylesHome.vacancyItem}>
|
||||
{/* <Icon name="user" size={20} color="#FFD700" /> */}
|
||||
<View style={stylesHome.vacancyDetails}>
|
||||
<TextCustom bold color="yellow" truncate size="large">
|
||||
Bagas_banuna
|
||||
</TextCustom>
|
||||
<Spacing height={5} />
|
||||
<TextCustom truncate={2}>
|
||||
Dicari perawat kucing dan perawat anjing
|
||||
</TextCustom>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Vacancy Item 2 */}
|
||||
<View style={stylesHome.vacancyItem}>
|
||||
{/* <Icon name="user" size={20} color="#FFD700" /> */}
|
||||
<View style={stylesHome.vacancyDetails}>
|
||||
<TextCustom bold color="yellow" truncate size="large">
|
||||
fibramarcell
|
||||
</TextCustom>
|
||||
<Spacing height={5} />
|
||||
<TextCustom truncate={2}>
|
||||
Di Butuhkan Seorang Programer dan Designer
|
||||
</TextCustom>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</ClickableCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
28
screens/Job/BoxDetailSection.tsx
Normal file
28
screens/Job/BoxDetailSection.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { BaseBox, StackCustom, DummyLandscapeImage, TextCustom } from "@/components";
|
||||
|
||||
export default function Job_BoxDetailSection({data}: {data: any}) {
|
||||
return (
|
||||
<>
|
||||
<BaseBox>
|
||||
<StackCustom gap={"lg"}>
|
||||
<DummyLandscapeImage />
|
||||
|
||||
<TextCustom align="center" bold size="large">
|
||||
{data?.posisi}
|
||||
</TextCustom>
|
||||
|
||||
<StackCustom gap={"sm"}>
|
||||
<TextCustom bold>Syarat & Ketentuan :</TextCustom>
|
||||
<TextCustom>{data?.syaratKetentuan}</TextCustom>
|
||||
</StackCustom>
|
||||
|
||||
<StackCustom gap={"sm"}>
|
||||
<TextCustom bold>Deskripsi :</TextCustom>
|
||||
<TextCustom>{data?.deskripsi}</TextCustom>
|
||||
</StackCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
135
screens/Job/ButtonStatusSection.tsx
Normal file
135
screens/Job/ButtonStatusSection.tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
import { AlertDefaultSystem, ButtonCustom, Grid } from "@/components";
|
||||
import { router } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function Job_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 (
|
||||
<>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log("Arsipkan");
|
||||
router.replace("/(application)/(user)/job/(tabs)/archive");
|
||||
}}
|
||||
>
|
||||
Arsipkan
|
||||
</ButtonCustom>
|
||||
</>
|
||||
);
|
||||
|
||||
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>;
|
||||
}
|
||||
}
|
||||
82
screens/Job/listDataDummy.ts
Normal file
82
screens/Job/listDataDummy.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
export const jobDataDummy = [
|
||||
{
|
||||
id: 1,
|
||||
posisi: "Front-End Developer",
|
||||
deskripsi:
|
||||
"Kami mencari Front-End Developer yang berpengalaman untuk membangun dan mengembangkan antarmuka pengguna web yang interaktif, responsif, dan menarik menggunakan React.js dan TypeScript. Kandidat akan bekerja sama dengan tim desain dan back-end untuk memastikan pengalaman pengguna yang optimal di berbagai perangkat.",
|
||||
syaratKetentuan:
|
||||
"Minimal 2 tahun pengalaman sebagai Front-End Developer. Menguasai React.js, TypeScript, HTML, CSS (SCSS). Memahami prinsip-prinsip desain responsif dan pengalaman pengguna (UX). Pengalaman dengan Git dan CI/CD pipeline menjadi nilai tambah.",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
posisi: "Back-End Developer",
|
||||
deskripsi:
|
||||
"Posisi ini bertanggung jawab dalam membangun dan memelihara RESTful API, serta mengelola logika server dan interaksi dengan basis data. Anda akan bekerja dengan Node.js dan Express untuk mendukung berbagai kebutuhan aplikasi kami yang dinamis dan real-time.",
|
||||
syaratKetentuan:
|
||||
"Pengalaman minimal 2 tahun sebagai Back-End Developer. Menguasai Node.js, Express.js, dan basis data (MongoDB, PostgreSQL). Paham prinsip REST API, middleware, dan keamanan data. Pengalaman dengan Docker dan testing (Jest/Mocha) menjadi nilai plus.",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
posisi: "UI/UX Designer",
|
||||
deskripsi:
|
||||
"Kami mencari desainer kreatif untuk merancang pengalaman pengguna dan antarmuka yang modern dan intuitif. Anda akan terlibat dalam proses riset pengguna, wireframing, prototyping, dan pengujian desain untuk aplikasi web dan mobile.",
|
||||
syaratKetentuan:
|
||||
"Minimal 1 tahun pengalaman dalam UI/UX. Menguasai alat desain seperti Figma, Sketch, atau Adobe XD. Mampu membuat prototipe interaktif dan melakukan usability testing. Portofolio desain UI/UX yang pernah dikerjakan wajib dilampirkan.",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
posisi: "DevOps Engineer",
|
||||
deskripsi:
|
||||
"Sebagai DevOps Engineer, Anda akan bertanggung jawab atas deployment otomatis, pemantauan sistem, dan infrastruktur cloud kami. Posisi ini akan fokus pada integrasi dan pengiriman berkelanjutan (CI/CD), skalabilitas, serta keamanan sistem.",
|
||||
syaratKetentuan:
|
||||
"Menguasai Docker, Kubernetes, Jenkins, GitLab CI, dan monitoring tools (Prometheus, Grafana). Memiliki pengalaman kerja dengan platform cloud seperti AWS atau GCP. Familiar dengan scripting Bash/Python dan prinsip keamanan DevOps.",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
posisi: "Mobile Developer",
|
||||
deskripsi:
|
||||
"Kami sedang mencari developer mobile yang terampil menggunakan Flutter untuk mengembangkan aplikasi lintas platform (iOS & Android) yang efisien dan user-friendly. Anda akan bekerja dalam tim produk untuk merancang dan mengimplementasikan fitur baru.",
|
||||
syaratKetentuan:
|
||||
"Pengalaman mengembangkan aplikasi Flutter setidaknya 1 tahun. Memahami state management (Provider, BLoC, atau Riverpod). Pernah merilis aplikasi di Google Play Store atau App Store. Pengalaman dengan Firebase menjadi nilai tambah.",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
posisi: "Data Analyst",
|
||||
deskripsi:
|
||||
"Sebagai Data Analyst, Anda akan mengumpulkan, menganalisis, dan menginterpretasikan data untuk mendukung keputusan bisnis. Anda akan membuat dashboard interaktif, laporan rutin, dan memberikan insight yang bisa ditindaklanjuti kepada tim manajemen.",
|
||||
syaratKetentuan:
|
||||
"Pengalaman menggunakan SQL, Excel, dan tools visualisasi data seperti Power BI atau Tableau. Kemampuan analisis statistik dan storytelling data. Pengalaman dengan Python (pandas, numpy) merupakan keunggulan.",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
posisi: "Machine Learning Engineer",
|
||||
deskripsi:
|
||||
"Kami membuka posisi untuk insinyur Machine Learning yang akan mengembangkan dan mengimplementasikan model prediktif untuk mendukung berbagai kebutuhan bisnis. Anda akan bekerja dengan data dalam skala besar dan menerapkan teknik NLP, klasifikasi, dan rekomendasi.",
|
||||
syaratKetentuan:
|
||||
"Memiliki pengalaman minimal 1 tahun dalam ML project. Menguasai Python, scikit-learn, TensorFlow, atau PyTorch. Mampu memproses data besar dengan pandas, NumPy, dan Spark. Familiar dengan deployment model ML ke production.",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
posisi: "System Administrator",
|
||||
deskripsi:
|
||||
"Posisi ini bertanggung jawab atas pengelolaan dan pemeliharaan sistem server Linux, jaringan internal, dan keamanan TI perusahaan. Termasuk setup server, monitoring, dan troubleshooting sistem operasional harian.",
|
||||
syaratKetentuan:
|
||||
"Berpengalaman mengelola server Linux (Ubuntu/CentOS), firewall, dan konfigurasi jaringan. Paham konsep load balancing, backup otomatis, dan disaster recovery. Kemampuan scripting (Bash, Python) menjadi nilai tambah.",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
posisi: "Quality Assurance",
|
||||
deskripsi:
|
||||
"Kami mencari QA Engineer untuk memastikan kualitas produk melalui pengujian manual maupun otomatis. Anda akan menulis test case, menjalankan regresi test, dan bekerja erat dengan tim developer untuk menemukan serta memperbaiki bug lebih awal.",
|
||||
syaratKetentuan:
|
||||
"Pengalaman dalam manual testing dan tools otomasi seperti Selenium, Cypress, atau Playwright. Memahami prinsip SDLC dan metodologi Agile. Mampu menulis dokumentasi QA dan membuat test report secara sistematis.",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
posisi: "Product Manager",
|
||||
deskripsi:
|
||||
"Sebagai Product Manager, Anda akan memimpin pengembangan produk mulai dari perencanaan, peluncuran, hingga peningkatan. Anda akan mengelola roadmap, prioritas fitur, dan bekerja lintas fungsi dengan tim desain, dev, dan bisnis.",
|
||||
syaratKetentuan:
|
||||
"Minimal 2 tahun pengalaman sebagai Product Manager. Memahami Agile/Scrum. Keterampilan komunikasi dan analisis yang kuat. Pengalaman dalam tools seperti Jira, Confluence, dan alat wireframing. Kemampuan mengambil keputusan berbasis data.",
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user