Skip to content

Commit

Permalink
feat: completed project
Browse files Browse the repository at this point in the history
  • Loading branch information
its-nedum committed May 27, 2023
1 parent 1f92579 commit c260c84
Show file tree
Hide file tree
Showing 67 changed files with 1,648 additions and 10 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RAPID_API_KEY='9cff363f63msheb30412f338d44bp18f7a1jsnbf269accc9b6'
40 changes: 32 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,38 @@
# Expo Router Example
# Job Search

Use [`expo-router`](https://expo.github.io/router) to build native navigation using files in the `app/` directory.
## Introduction
A mobile app built with React Native that uses RapidApi for searching and displaying jobs.

## 🚀 How to use
## Installation

```sh
npx create-expo-app -e with-router
To install job search follow these steps
1. Clone the repository from GitHub
```bash
$ git clone https://github.com/its-nedum/job-search.git
```
2. Navigate to the project directory
```bash
$ cd job-search
```
3. Install the required dependencies
```bash
$ npm install
```

## Usage

To start the development server, run this command
```bash
$ npm start
```

## Image
- Home page
![Home page](https://res.cloudinary.com/its-nedum/image/upload/v1685189521/Home_hslmmt.png)

- Job details page
![Job details](https://res.cloudinary.com/its-nedum/image/upload/v1685189521/Details_kqgb3l.png)

## 📝 Notes
- Search result page
![Search result](https://res.cloudinary.com/its-nedum/image/upload/v1685189521/Search_Result_ibxupw.png)

- [Expo Router: Docs](https://expo.github.io/router)
- [Expo Router: Repo](https://github.com/expo/router)
26 changes: 26 additions & 0 deletions app/_layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Stack } from "expo-router";
import { useCallback } from "react";
import { useFonts } from "expo-font";
import * as SplashScreen from "expo-splash-screen";

SplashScreen.preventAutoHideAsync();

const Layout = () => {
const [fontsLoaded] = useFonts({
DMBold: require('../assets/fonts/DMSans-Bold.ttf'),
DMMedium: require('../assets/fonts/DMSans-Medium.ttf'),
DMRegular: require('../assets/fonts/DMSans-Regular.ttf'),
});

const onLayoutRootView = useCallback(async () => {
if(fontsLoaded){
await SplashScreen.hideAsync();
}
},[fontsLoaded])

if(!fontsLoaded) return null;

return <Stack onLayout={onLayoutRootView} />
}

export default Layout;
57 changes: 57 additions & 0 deletions app/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useState } from "react";
import { Text, View, ScrollView, SafeAreaView } from "react-native";
import { Stack, useRouter } from "expo-router";
import { COLORS, SIZES, icons, images } from "../constants";
import {
Nearbyjobs,
Welcome,
Popularjobs,
ScreenHeaderBtn,
} from "../components";

const Home = () => {
const router = useRouter();
const [searchTerm, setSearchTerm] = useState("")

return(
<SafeAreaView
style={{
flex: 1,
backgroundColor: COLORS.lightWhite
}}
>
<Stack.Screen
options={{
headerStyle: COLORS.lightWhite,
headerShadowVisible: false,
headerLeft: () => <ScreenHeaderBtn iconUrl={icons.menu} dimension={"60%"} />,
headerRight: () => <ScreenHeaderBtn iconUrl={images.profile} dimension={"100%"} />,
headerTitle: "",
}}
/>

<ScrollView showsVerticalScrollIndicator={false}>
<View
style={{
flex: 1,
padding: SIZES.medium
}}
>
<Welcome
searchTerm={searchTerm}
setSearchTerm={setSearchTerm}
handleClick={() => {
if(searchTerm){
router.push(`/search/${searchTerm}`)
}
}}
/>
<Popularjobs />
<Nearbyjobs />
</View>
</ScrollView>
</SafeAreaView>
)
}

export default Home;
138 changes: 138 additions & 0 deletions app/job-details/[id].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {
View,
Text,
SafeAreaView,
ScrollView,
ActivityIndicator,
RefreshControl
} from 'react-native';
import { Stack, useRouter, useSearchParams } from 'expo-router';
import { useCallback, useState } from 'react';
import {
Company,
JobAbout,
JobFooter,
JobTabs,
ScreenHeaderBtn,
Specifics
} from "../../components"
import useFetch from '../../hook/useFetch';
import { COLORS, SIZES, icons } from '../../constants';

const tabs = ["About", "Qualifications", "Responsibilities"];

const JobDetails = () => {
const params = useSearchParams();
const router = useRouter();
const [refreshing, setRefreshing] = useState(false);
const [activeTab, setActiveTab] = useState(tabs[0]);

const { data, isLoading, error, refetch } = useFetch("job-details", { job_id: params.id })

const onRefresh = useCallback(() => {
setRefreshing(true);
refetch();
setRefreshing(false);
}, []);

const displayTabContent = () => {
switch (activeTab) {
case "About": {
return <JobAbout
info={data[0].job_description ?? "No data provided"}
/>
}
case "Qualifications": {
return <Specifics
title={"Qualifications"}
points={data[0].job_highlights?.Qualifications ?? ["N/A"]}
/>
}
case "Responsibilities": {
return <Specifics
title={"Responsibilities"}
points={data[0].job_highlights?.Responsibilities ?? ["N/A"]}
/>
}
default:
break;
}
}

return (
<SafeAreaView
style={{ flex: 1, backgroundColor: COLORS.lightWhite }}
>
<Stack.Screen
options={{
headerStyle: { backgroundColor: COLORS.lightWhite },
headerShadowVisible: false,
headerBackVisible: false,
headerLeft: () => (
<ScreenHeaderBtn
iconUrl={icons.left}
dimension={"60%"}
handlePress={() => router.back()}
/>
),
headerRight: () => (
<ScreenHeaderBtn
iconUrl={icons.share}
dimension={"60%"}
/>
),
headerTitle: ""
}}
/>
<>
<ScrollView
showsVerticalScrollIndicator={false}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
>
{
isLoading ?
(
<ActivityIndicator size={"large"} color={COLORS.primary} />
)
: error ?
(
<Text>Something went wrong!</Text>
)
: data.length === 0 ?
(
<Text>No data</Text>
)
:
(
<View
style={{
padding: SIZES.medium,
paddingBottom: 100
}}
>
<Company
companyLogo={data[0].employer_logo}
jobTitle={data[0].job_title}
companyName={data[0].employer_name}
location={data[0].job_country}
/>

<JobTabs
tabs={tabs}
activeTab={activeTab}
setActiveTab={setActiveTab}
/>

{ displayTabContent() }
</View>
)
}
</ScrollView>

<JobFooter url={data[0]?.job_google_link ?? "https://careers.google.com/jobs/results"}/>
</>
</SafeAreaView>
)
}

export default JobDetails
Loading

0 comments on commit c260c84

Please sign in to comment.