├── assets ├── icon.png ├── favicon.png ├── splash.png ├── icons │ ├── left.png │ ├── search.png │ ├── share.png │ ├── location.png │ ├── chevron-left.png │ ├── chevron-right.png │ └── heart-outline.png ├── adaptive-icon.png └── fonts │ ├── Roboto-Bold.ttf │ ├── Roboto-Medium.ttf │ └── Roboto-Regular.ttf ├── app ├── _layout.js ├── index.js ├── details │ └── [id].js └── search │ └── [id].js ├── babel.config.js ├── components ├── home │ ├── popular-jobs.js │ ├── my-jobs.js │ └── search.js ├── index.js ├── shared │ └── header-btn.js ├── tabs │ ├── about.js │ ├── qualification.js │ ├── responsibility.js │ └── footer.js ├── detailed │ ├── job-tabs.js │ └── job.js └── cards │ ├── job-card.js │ └── my-job-card.js ├── constants ├── data.js ├── index.js ├── icons.js └── theme.js ├── app.json ├── package.json ├── hook └── useRequest.js └── README.md /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/favicon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/splash.png -------------------------------------------------------------------------------- /assets/icons/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/icons/left.png -------------------------------------------------------------------------------- /assets/icons/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/icons/search.png -------------------------------------------------------------------------------- /assets/icons/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/icons/share.png -------------------------------------------------------------------------------- /assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/adaptive-icon.png -------------------------------------------------------------------------------- /assets/icons/location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/icons/location.png -------------------------------------------------------------------------------- /assets/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/fonts/Roboto-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /assets/icons/chevron-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/icons/chevron-left.png -------------------------------------------------------------------------------- /assets/icons/chevron-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/icons/chevron-right.png -------------------------------------------------------------------------------- /assets/icons/heart-outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samarbadriddin0v/hh-clone/HEAD/assets/icons/heart-outline.png -------------------------------------------------------------------------------- /app/_layout.js: -------------------------------------------------------------------------------- 1 | import { Stack } from "expo-router"; 2 | 3 | export default function Layout() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: ["babel-preset-expo"], 5 | plugins: ["expo-router/babel"], 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /components/home/popular-jobs.js: -------------------------------------------------------------------------------- 1 | import { View, Text } from "react-native"; 2 | import React from "react"; 3 | 4 | export default function PopularJobs() { 5 | return ( 6 | 7 | PopularJobs 8 | 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /constants/data.js: -------------------------------------------------------------------------------- 1 | export const filterJobTypes = [ 2 | "Full-time", 3 | "Part-time", 4 | "Contract", 5 | "Temporary", 6 | "Internship", 7 | "Volunteer", 8 | "Remote", 9 | "Freelance", 10 | ]; 11 | 12 | export const tabs = ["About", "Qualifications", "Responsibilities"]; 13 | -------------------------------------------------------------------------------- /constants/index.js: -------------------------------------------------------------------------------- 1 | // data 2 | import { filterJobTypes } from "./data"; 3 | import { tabs } from "./data"; 4 | 5 | // theme 6 | import { COLORS, SIZES, FONTS, SHADOWS } from "./theme"; 7 | 8 | // icon 9 | import icons from "./icons"; 10 | 11 | export { filterJobTypes, COLORS, SIZES, FONTS, SHADOWS, icons, tabs }; 12 | -------------------------------------------------------------------------------- /constants/icons.js: -------------------------------------------------------------------------------- 1 | // icons 2 | import search from "../assets/icons/search.png"; 3 | import heart from "../assets/icons/heart-outline.png"; 4 | import left from "../assets/icons/left.png"; 5 | import share from "../assets/icons/share.png"; 6 | import location from "../assets/icons/location.png"; 7 | import chevronLeft from "../assets/icons/chevron-left.png"; 8 | import chevronRight from "../assets/icons/chevron-right.png"; 9 | 10 | export default { 11 | search, 12 | heart, 13 | left, 14 | share, 15 | location, 16 | chevronLeft, 17 | chevronRight, 18 | }; 19 | -------------------------------------------------------------------------------- /components/index.js: -------------------------------------------------------------------------------- 1 | import Search from "./home/search"; 2 | import MyJobs from "./home/my-jobs"; 3 | import PopularJobs from "./home/popular-jobs"; 4 | import HeaderBtn from "./shared/header-btn"; 5 | import JobTabs from "./detailed/job-tabs"; 6 | import Job from "./detailed/job"; 7 | import About from "./tabs/about"; 8 | import Qualification from "./tabs/qualification"; 9 | import Responsibility from "./tabs/responsibility"; 10 | import Footer from "./tabs/footer"; 11 | 12 | export { 13 | Search, 14 | MyJobs, 15 | PopularJobs, 16 | HeaderBtn, 17 | JobTabs, 18 | Job, 19 | About, 20 | Qualification, 21 | Responsibility, 22 | Footer, 23 | }; 24 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "scheme": "your-app-scheme", 4 | "name": "hh-clone", 5 | "slug": "hh-clone", 6 | "version": "1.0.0", 7 | "orientation": "portrait", 8 | "icon": "./assets/icon.png", 9 | "userInterfaceStyle": "light", 10 | "splash": { 11 | "image": "./assets/splash.png", 12 | "resizeMode": "contain", 13 | "backgroundColor": "#ffffff" 14 | }, 15 | "assetBundlePatterns": ["**/*"], 16 | "ios": { 17 | "supportsTablet": true 18 | }, 19 | "android": { 20 | "adaptiveIcon": { 21 | "foregroundImage": "./assets/adaptive-icon.png", 22 | "backgroundColor": "#ffffff" 23 | } 24 | }, 25 | "web": { 26 | "favicon": "./assets/favicon.png" 27 | }, 28 | "plugins": ["expo-router"] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /components/shared/header-btn.js: -------------------------------------------------------------------------------- 1 | import { TouchableOpacity, StyleSheet, Image } from "react-native"; 2 | import React from "react"; 3 | import { COLORS, SIZES } from "../../constants"; 4 | 5 | export default function HeaderBtn({ icon, onPress, dimensions }) { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | } 12 | 13 | const styles = StyleSheet.create({ 14 | container: { 15 | width: 40, 16 | height: 40, 17 | backgroundColor: COLORS.white, 18 | borderRadius: SIZES.small / 2, 19 | justifyContent: "center", 20 | alignItems: "center", 21 | }, 22 | icon: (dimensions) => ({ 23 | width: dimensions, 24 | height: dimensions, 25 | borderRadius: SIZES.small / 2, 26 | }), 27 | }); 28 | -------------------------------------------------------------------------------- /constants/theme.js: -------------------------------------------------------------------------------- 1 | export const COLORS = { 2 | primary: "#9B2C2C", 3 | secondary: "#63171B", 4 | tertiary: "#E53E3E", 5 | 6 | gray: "#83829A", 7 | gray2: "#C1C0C8", 8 | 9 | white: "#F3F4F8", 10 | lightWhite: "#FAFAFC", 11 | }; 12 | 13 | export const SIZES = { 14 | xSmall: 10, 15 | small: 14, 16 | medium: 16, 17 | large: 18, 18 | xLarge: 20, 19 | }; 20 | 21 | export const SHADOWS = { 22 | small: { 23 | shadowColor: "#000", 24 | shadowOffset: { 25 | width: 0, 26 | height: 2, 27 | }, 28 | shadowOpacity: 0.25, 29 | shadowRadius: 3.84, 30 | elevation: 2, 31 | }, 32 | medium: { 33 | shadowColor: "#000", 34 | shadowOffset: { 35 | width: 0, 36 | height: 2, 37 | }, 38 | shadowOpacity: 0.25, 39 | shadowRadius: 5.84, 40 | elevation: 5, 41 | }, 42 | }; 43 | 44 | export const FONTS = { 45 | regular: "Roboto-Regular", 46 | medium: "Roboto-Medium", 47 | bold: "Roboto-Bold", 48 | }; 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hh-clone", 3 | "version": "1.0.0", 4 | "main": "expo-router/entry", 5 | "scripts": { 6 | "start": "expo start", 7 | "android": "expo start --android", 8 | "ios": "expo start --ios", 9 | "web": "expo start --web" 10 | }, 11 | "dependencies": { 12 | "axios": "^1.5.1", 13 | "expo": "~49.0.13", 14 | "expo-constants": "~14.4.2", 15 | "expo-font": "~11.4.0", 16 | "expo-linking": "~5.0.2", 17 | "expo-router": "^2.0.0", 18 | "expo-status-bar": "~1.6.0", 19 | "react": "18.2.0", 20 | "react-native": "0.72.5", 21 | "react-native-gesture-handler": "~2.12.0", 22 | "react-native-safe-area-context": "4.6.3", 23 | "react-native-screens": "~3.22.0" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.20.0" 27 | }, 28 | "private": true, 29 | "overrides": { 30 | "metro": "0.76.7", 31 | "metro-resolver": "0.76.7" 32 | }, 33 | "resolutions": { 34 | "metro": "0.76.7", 35 | "metro-resolver": "0.76.7" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /components/tabs/about.js: -------------------------------------------------------------------------------- 1 | import { View, Text, StyleSheet } from "react-native"; 2 | import React from "react"; 3 | import { COLORS, FONTS, SIZES } from "../../constants"; 4 | 5 | export default function About({ info }) { 6 | return ( 7 | 8 | About this job: 9 | 10 | 11 | {info} 12 | 13 | 14 | ); 15 | } 16 | 17 | const styles = StyleSheet.create({ 18 | container: { 19 | marginTop: SIZES.large, 20 | backgroundColor: COLORS.lightWhite, 21 | borderRadius: SIZES.medium, 22 | paddingHorizontal: SIZES.medium, 23 | }, 24 | title: { 25 | fontSize: SIZES.large, 26 | color: COLORS.primary, 27 | fontFamily: FONTS.bold, 28 | }, 29 | contentBox: { 30 | marginTop: SIZES.medium, 31 | }, 32 | contentText: { 33 | fontSize: SIZES.small, 34 | color: COLORS.secondary, 35 | fontFamily: FONTS.regular, 36 | }, 37 | }); 38 | -------------------------------------------------------------------------------- /hook/useRequest.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import axios from "axios"; 3 | 4 | export default function useRequest(endpoint, query) { 5 | const [data, setData] = useState([]); 6 | const [isLoading, setIsLoading] = useState(false); 7 | const [error, setError] = useState(null); 8 | 9 | const option = { 10 | method: "GET", 11 | url: `https://jsearch.p.rapidapi.com/${endpoint}`, 12 | headers: { 13 | "X-RapidAPI-Key": "8e99652f73msh0d6b44b17e30ae3p165001jsna1eed97a1b9c", 14 | "X-RapidAPI-Host": "jsearch.p.rapidapi.com", 15 | }, 16 | params: { ...query }, 17 | }; 18 | 19 | const fetchData = async () => { 20 | setIsLoading(true); 21 | 22 | try { 23 | const { data: res } = await axios.request(option); 24 | setData(res.data); 25 | } catch (error) { 26 | setError(error); 27 | console.log(error); 28 | } finally { 29 | setIsLoading(false); 30 | } 31 | }; 32 | 33 | useEffect(() => { 34 | fetchData(); 35 | }, []); 36 | 37 | const refetch = () => { 38 | setIsLoading(true); 39 | fetchData(); 40 | }; 41 | 42 | return { data, isLoading, error, refetch }; 43 | } 44 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | import { View, Text, SafeAreaView } from "react-native"; 2 | import React, { useCallback } from "react"; 3 | import { ScrollView } from "react-native-gesture-handler"; 4 | import { MyJobs, PopularJobs, Search } from "../components"; 5 | import { useFonts } from "expo-font"; 6 | import * as SplashScreen from "expo-splash-screen"; 7 | import { SIZES } from "../constants"; 8 | import { Stack } from "expo-router"; 9 | 10 | SplashScreen.preventAutoHideAsync(); 11 | 12 | export default function Index() { 13 | const [fontsLoaded] = useFonts({ 14 | "Roboto-Regular": require("../assets/fonts/Roboto-Regular.ttf"), 15 | "Roboto-Bold": require("../assets/fonts/Roboto-Bold.ttf"), 16 | "Roboto-Medium": require("../assets/fonts/Roboto-Medium.ttf"), 17 | }); 18 | 19 | const onLayoutRootView = useCallback(async () => { 20 | if (fontsLoaded) { 21 | await SplashScreen.hideAsync(); 22 | } 23 | }, [fontsLoaded]); 24 | 25 | if (!fontsLoaded) { 26 | return null; 27 | } 28 | 29 | return ( 30 | 34 | 35 | 36 | 37 | 38 | 39 | {/* */} 40 | 41 | 42 | 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /components/tabs/qualification.js: -------------------------------------------------------------------------------- 1 | import { View, Text, StyleSheet } from "react-native"; 2 | import React from "react"; 3 | import { COLORS, FONTS, SIZES } from "../../constants"; 4 | 5 | export default function Qualification({ info }) { 6 | return ( 7 | 8 | Qualification: 9 | 10 | 11 | {info.map((item, index) => ( 12 | 13 | 14 | {item} 15 | 16 | ))} 17 | 18 | 19 | ); 20 | } 21 | 22 | const styles = StyleSheet.create({ 23 | container: { 24 | marginTop: SIZES.large, 25 | backgroundColor: COLORS.lightWhite, 26 | borderRadius: SIZES.medium, 27 | paddingHorizontal: SIZES.medium, 28 | }, 29 | title: { 30 | fontSize: SIZES.large, 31 | color: COLORS.primary, 32 | fontFamily: FONTS.bold, 33 | }, 34 | contentBox: { 35 | marginVertical: SIZES.small, 36 | }, 37 | infoWrapper: { 38 | flexDirection: "row", 39 | justifyContent: "flex-start", 40 | alignItems: "flex-start", 41 | marginVertical: SIZES.small / 1.25, 42 | }, 43 | dot: { 44 | width: 6, 45 | height: 6, 46 | borderRadius: 6, 47 | backgroundColor: COLORS.gray2, 48 | marginTop: 6, 49 | }, 50 | contentText: { 51 | fontSize: SIZES.medium - 2, 52 | color: COLORS.gray, 53 | fontFamily: FONTS.regular, 54 | marginLeft: SIZES.small, 55 | }, 56 | }); 57 | -------------------------------------------------------------------------------- /components/tabs/responsibility.js: -------------------------------------------------------------------------------- 1 | import { View, Text, StyleSheet } from "react-native"; 2 | import React from "react"; 3 | import { COLORS, FONTS, SIZES } from "../../constants"; 4 | 5 | export default function Responsibility({ info }) { 6 | return ( 7 | 8 | Responsibility: 9 | 10 | 11 | {info.map((item, index) => ( 12 | 13 | 14 | {item} 15 | 16 | ))} 17 | 18 | 19 | ); 20 | } 21 | 22 | const styles = StyleSheet.create({ 23 | container: { 24 | marginTop: SIZES.large, 25 | backgroundColor: COLORS.lightWhite, 26 | borderRadius: SIZES.medium, 27 | paddingHorizontal: SIZES.medium, 28 | }, 29 | title: { 30 | fontSize: SIZES.large, 31 | color: COLORS.primary, 32 | fontFamily: FONTS.bold, 33 | }, 34 | contentBox: { 35 | marginVertical: SIZES.small, 36 | }, 37 | infoWrapper: { 38 | flexDirection: "row", 39 | justifyContent: "flex-start", 40 | alignItems: "flex-start", 41 | marginVertical: SIZES.small / 1.25, 42 | }, 43 | dot: { 44 | width: 6, 45 | height: 6, 46 | borderRadius: 6, 47 | backgroundColor: COLORS.gray2, 48 | marginTop: 6, 49 | }, 50 | contentText: { 51 | fontSize: SIZES.medium - 2, 52 | color: COLORS.gray, 53 | fontFamily: FONTS.regular, 54 | marginLeft: SIZES.small, 55 | }, 56 | }); 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

React Native Job Search App

2 | 3 |

Job Search: Empowering Your Career Journey! Our cutting-edge mobile application developed using React Native seamlessly connects job seekers with their dream opportunities on both iOS and Android platforms. With user-friendly interfaces and advanced algorithms Catalyst Job Search offers personalized job recommendations real-time notifications and interactive features to streamline your job search process. Explore endless possibilities discover tailored job listings and embark on your career adventure with confidence. Your dream job is just a tap away

4 | 5 |

🚀 Demo

6 | 7 | [https://youtu.be/K270hhdGRTk?si=6HjLBLRjv4BLcq91](https://youtu.be/K270hhdGRTk?si=6HjLBLRjv4BLcq91) 8 | 9 |

Project Screenshots:

10 | 11 | project-screenshot 12 | 13 | 14 | 15 |

🧐 Features

16 | 17 | Here're some of the project's best features: 18 | 19 | * Mobile application 20 | * IOS 21 | * Adnroid 22 | * Mobile developments 23 | 24 |

🛠️ Installation Steps:

25 | 26 |

1. Install all dependencies

27 | 28 | ``` 29 | npm install | yarn install 30 | ``` 31 | 32 |

2. Run application using expo metro

33 | 34 | ``` 35 | npm start | yarn start 36 | ``` 37 | 38 | 39 | 40 |

💻 Built with

41 | 42 | Technologies used in the project: 43 | 44 | * JavaScript 45 | * ReactJS 46 | * React Native 47 | * Expo 48 | * Expo router 49 | * React Navigation 50 | * Custom theme 51 | -------------------------------------------------------------------------------- /components/detailed/job-tabs.js: -------------------------------------------------------------------------------- 1 | import { 2 | View, 3 | Text, 4 | StyleSheet, 5 | FlatList, 6 | TouchableOpacity, 7 | } from "react-native"; 8 | import React from "react"; 9 | import { COLORS, SIZES, tabs, SHADOWS, FONTS } from "../../constants"; 10 | 11 | export default function JobTabs({ activeTab, setActiveTab }) { 12 | return ( 13 | 14 | item} 20 | renderItem={({ item }) => ( 21 | setActiveTab(item)} 24 | > 25 | {item} 26 | 27 | )} 28 | /> 29 | 30 | ); 31 | } 32 | 33 | const styles = StyleSheet.create({ 34 | container: { 35 | marginTop: SIZES.medium, 36 | marginBottom: SIZES.small, 37 | }, 38 | tabBtn: (activeTab, item) => ({ 39 | paddingVertical: SIZES.medium, 40 | paddingHorizontal: SIZES.xLarge, 41 | backgroundColor: item === activeTab ? COLORS.primary : "#F3F4F8", 42 | borderRadius: SIZES.medium, 43 | marginLeft: 2, 44 | ...SHADOWS.medium, 45 | shadowColor: COLORS.white, 46 | }), 47 | tabText: (activeTab, item) => ({ 48 | fontSize: SIZES.small, 49 | color: item === activeTab ? COLORS.gray2 : COLORS.primary, 50 | fontFamily: FONTS.medium, 51 | }), 52 | }); 53 | -------------------------------------------------------------------------------- /components/home/my-jobs.js: -------------------------------------------------------------------------------- 1 | import { 2 | View, 3 | Text, 4 | StyleSheet, 5 | ActivityIndicator, 6 | FlatList, 7 | } from "react-native"; 8 | import React, { useState } from "react"; 9 | import useRequest from "../../hook/useRequest"; 10 | import { COLORS, FONTS, SIZES } from "../../constants"; 11 | import MyJobCard from "../cards/my-job-card"; 12 | 13 | export default function MyJobs() { 14 | const [selectedJob, setSelectedJob] = useState(null); 15 | 16 | const { data, isLoading, error } = useRequest("search", { 17 | query: "React native", 18 | page: "1", 19 | }); 20 | 21 | return ( 22 | 23 | Jobs for you 24 | 25 | 26 | {isLoading ? ( 27 | 28 | ) : error ? ( 29 | Something went wrong 30 | ) : ( 31 | ( 34 | 39 | )} 40 | keyExtractor={(item) => `job-${item.job_id}`} 41 | contentContainerStyle={{ columnGap: SIZES.medium }} 42 | scrollEnabled={false} 43 | nestedScrollEnabled={true} 44 | /> 45 | )} 46 | 47 | 48 | ); 49 | } 50 | 51 | const styles = StyleSheet.create({ 52 | container: { 53 | marginTop: SIZES.xLarge, 54 | }, 55 | title: { 56 | fontSize: SIZES.xLarge, 57 | fontFamily: FONTS.bold, 58 | color: COLORS.primary, 59 | }, 60 | jobsContainer: { 61 | marginTop: SIZES.medium, 62 | }, 63 | }); 64 | -------------------------------------------------------------------------------- /components/tabs/footer.js: -------------------------------------------------------------------------------- 1 | import { 2 | View, 3 | Text, 4 | StyleSheet, 5 | TouchableOpacity, 6 | Image, 7 | Linking, 8 | } from "react-native"; 9 | import React from "react"; 10 | import { COLORS, FONTS, SIZES, icons } from "../../constants"; 11 | 12 | export default function Footer({ url }) { 13 | return ( 14 | 15 | 16 | 21 | 22 | Linking.openURL(url)} 25 | > 26 | Apply 27 | 28 | 29 | ); 30 | } 31 | 32 | const styles = StyleSheet.create({ 33 | container: { 34 | position: "absolute", 35 | bottom: 0, 36 | left: 0, 37 | right: 0, 38 | padding: SIZES.small, 39 | backgroundColor: "#fff", 40 | justifyContent: "space-between", 41 | alignContent: "center", 42 | flexDirection: "row", 43 | }, 44 | heartBtn: { 45 | width: 55, 46 | height: 55, 47 | borderWidth: 1, 48 | borderColor: COLORS.primary, 49 | borderRadius: SIZES.medium, 50 | justifyContent: "center", 51 | alignItems: "center", 52 | }, 53 | heartBtnIcon: { 54 | width: "40%", 55 | height: "40%", 56 | tintColor: COLORS.primary, 57 | }, 58 | applyBtn: { 59 | flex: 1, 60 | backgroundColor: COLORS.primary, 61 | height: "100%", 62 | justifyContent: "center", 63 | alignItems: "center", 64 | marginLeft: SIZES.medium, 65 | borderRadius: SIZES.medium, 66 | }, 67 | applyBtnText: { 68 | color: COLORS.lightWhite, 69 | fontSize: SIZES.medium, 70 | fontFamily: FONTS.bold, 71 | }, 72 | }); 73 | -------------------------------------------------------------------------------- /components/cards/job-card.js: -------------------------------------------------------------------------------- 1 | import { View, Text, TouchableOpacity, StyleSheet, Image } from "react-native"; 2 | import React from "react"; 3 | import { SIZES, SHADOWS, COLORS, FONTS } from "../../constants"; 4 | 5 | export default function JobCard({ item, onPress }) { 6 | return ( 7 | 8 | 9 | 17 | 18 | 19 | 20 | {item?.job_title} 21 | {item?.job_employment_type} 22 | 23 | 24 | ); 25 | } 26 | 27 | const styles = StyleSheet.create({ 28 | container: { 29 | flex: 1, 30 | justifyContent: "space-between", 31 | alignItems: "center", 32 | flexDirection: "row", 33 | padding: SIZES.medium, 34 | borderRadius: SIZES.small, 35 | backgroundColor: "#FFF", 36 | ...SHADOWS.medium, 37 | shadowColor: COLORS.white, 38 | }, 39 | imgWrapper: { 40 | width: 50, 41 | height: 50, 42 | backgroundColor: COLORS.white, 43 | borderRadius: SIZES.medium, 44 | justifyContent: "center", 45 | alignItems: "center", 46 | }, 47 | logoImage: { 48 | width: "70%", 49 | height: "70%", 50 | }, 51 | textWrapper: { 52 | flex: 1, 53 | marginHorizontal: SIZES.medium, 54 | }, 55 | jobName: { 56 | fontSize: SIZES.medium, 57 | fontFamily: FONTS.medium, 58 | color: COLORS.primary, 59 | }, 60 | jobType: { 61 | fontSize: SIZES.small, 62 | marginTop: SIZES.small / 2, 63 | fontFamily: FONTS.regular, 64 | textTransform: "capitalize", 65 | color: COLORS.gray, 66 | }, 67 | }); 68 | -------------------------------------------------------------------------------- /components/detailed/job.js: -------------------------------------------------------------------------------- 1 | import { View, Text, StyleSheet, Image } from "react-native"; 2 | import React from "react"; 3 | import { COLORS, FONTS, SIZES, icons } from "../../constants"; 4 | 5 | export default function Job({ companyLogo, jobTitle, companyName, location }) { 6 | return ( 7 | 8 | 9 | 17 | 18 | 19 | 20 | {jobTitle} 21 | 22 | 23 | 24 | {companyName} 25 | 26 | 31 | {location} 32 | 33 | 34 | 35 | ); 36 | } 37 | 38 | const styles = StyleSheet.create({ 39 | conatiner: { 40 | marginVertical: SIZES.medium, 41 | justifyContent: "center", 42 | alignItems: "center", 43 | }, 44 | imgBox: { 45 | width: 80, 46 | height: 80, 47 | justifyContent: "center", 48 | alignItems: "center", 49 | backgroundColor: "#FFF", 50 | borderRadius: SIZES.large, 51 | }, 52 | logoImage: { 53 | width: "80%", 54 | height: "80%", 55 | }, 56 | jobTitleBox: { 57 | marginTop: SIZES.small, 58 | }, 59 | jobTitle: { 60 | fontSize: SIZES.large, 61 | color: COLORS.primary, 62 | fontFamily: FONTS.bold, 63 | textAlign: "center", 64 | }, 65 | companyInfoBox: { 66 | marginTop: SIZES.small / 2, 67 | flexDirection: "row", 68 | justifyContent: "center", 69 | alignItems: "center", 70 | }, 71 | companyName: { 72 | fontSize: SIZES.medium - 2, 73 | color: COLORS.primary, 74 | fontFamily: FONTS.medium, 75 | }, 76 | locationBox: { 77 | flexDirection: "row", 78 | justifyContent: "center", 79 | alignItems: "center", 80 | }, 81 | locationImage: { 82 | width: 14, 83 | height: 14, 84 | tintColor: COLORS.gray, 85 | }, 86 | locationName: { 87 | fontSize: SIZES.medium - 2, 88 | color: COLORS.gray, 89 | fontFamily: FONTS.regular, 90 | marginLeft: 2, 91 | }, 92 | }); 93 | -------------------------------------------------------------------------------- /app/details/[id].js: -------------------------------------------------------------------------------- 1 | import { 2 | View, 3 | Text, 4 | ScrollView, 5 | ActivityIndicator, 6 | SafeAreaView, 7 | RefreshControl, 8 | } from "react-native"; 9 | import React, { useCallback, useState } from "react"; 10 | import { COLORS, SIZES, icons, tabs } from "../../constants"; 11 | import { Stack, useGlobalSearchParams, useRouter } from "expo-router"; 12 | import HeaderBtn from "../../components/shared/header-btn"; 13 | import useRequest from "../../hook/useRequest"; 14 | import { 15 | About, 16 | Footer, 17 | Job, 18 | JobTabs, 19 | Qualification, 20 | Responsibility, 21 | } from "../../components"; 22 | 23 | export default function Details() { 24 | const params = useGlobalSearchParams(); 25 | const router = useRouter(); 26 | 27 | const [activeTab, setActiveTab] = useState(tabs[0]); 28 | const [refreshing, setRefreshing] = useState(false); 29 | 30 | const { data, isLoading, error, refetch } = useRequest("job-details", { 31 | job_id: params.id, 32 | }); 33 | 34 | const onRefresh = useCallback(() => { 35 | setRefreshing(true); 36 | refetch(); 37 | setRefreshing(false); 38 | }, []); 39 | 40 | const renderTabContent = () => { 41 | switch (activeTab) { 42 | case "About": 43 | return ; 44 | case "Qualifications": 45 | return ( 46 | 49 | ); 50 | case "Responsibilities": 51 | return ( 52 | 55 | ); 56 | default: 57 | return null; 58 | } 59 | }; 60 | 61 | return ( 62 | 63 | ( 71 | router.back()} 75 | /> 76 | ), 77 | headerRight: () => ( 78 | 79 | ), 80 | }} 81 | /> 82 | <> 83 | 87 | } 88 | > 89 | {isLoading ? ( 90 | 91 | ) : error ? ( 92 | Something went wrong 93 | ) : data.length === 0 ? ( 94 | No data available 95 | ) : ( 96 | 97 | 103 | 104 | {renderTabContent()} 105 | 106 | )} 107 | 108 | 109 |