-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
67 changed files
with
1,648 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
RAPID_API_KEY='9cff363f63msheb30412f338d44bp18f7a1jsnbf269accc9b6' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.