Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions front/assets/translations/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"cancel": "Cancel",
"delete": "Delete"
},
"jobOffer": {
"info": {
"jobOffer": "Job offer"
}
},
"profile": {
"info": {
"contact": "Contact",
Expand Down
5 changes: 5 additions & 0 deletions front/assets/translations/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"cancel": "Annuler",
"delete": "Supprimer"
},
"jobOffer": {
"info": {
"jobOffer": "Offre d'emploi"
}
},
"profile": {
"info": {
"contact": "Contact",
Expand Down
8 changes: 4 additions & 4 deletions front/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"typescript:check": "tsc --noEmit"
},
"dependencies": {
"@react-native-community/slider": "^4.4.3",
"@react-native-community/slider": "4.4.2",
"@react-navigation/material-top-tabs": "^6.6.5",
"@react-navigation/native": "^6.1.9",
"@react-navigation/native-stack": "^6.9.16",
Expand Down
80 changes: 80 additions & 0 deletions front/src/components/jobOffers/JobOfferItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { useLocales } from 'expo-localization';
import { FC } from 'react';
import { StyleSheet, View } from 'react-native';
import { Text, useTheme } from 'react-native-paper';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';

import { JobOffer } from '@/models/entities/jobOffer';

/**
* The styles for the JobOfferItem component.
*/
const styles = StyleSheet.create({
container: {
gap: 16,
},
horizontalContainer: {
flexDirection: 'row',
gap: 12,
},
sectionContainer: {
gap: 8,
},
});

/**
* The props for the JobOfferItem component.
*/
type JobOfferItemProps = {
/**
* The job offer to display.
*/
jobOffer: JobOffer;
};

/**
* Displays a job offer.
* @constructor
*/
const JobOfferItem: FC<JobOfferItemProps> = ({ jobOffer }) => {
// Hooks
const locales = useLocales();
const theme = useTheme();

return (
<View style={styles.container}>
<View style={styles.sectionContainer}>
<Text variant='titleLarge'>{`${jobOffer.title}`}</Text>
<Text>{`${jobOffer.description}`}</Text>
</View>

<View style={styles.sectionContainer}>
<View style={styles.horizontalContainer}>
<MaterialCommunityIcons
name='calendar'
size={24}
style={{ color: theme.colors.onSurface }}
/>
<Text variant='labelLarge'>
{`${new Date(jobOffer.startDate).toLocaleDateString(
locales[0].languageTag,
)} - ${new Date(jobOffer.endDate).toLocaleDateString(
locales[0].languageTag,
)}`}
</Text>
</View>

<View style={styles.horizontalContainer}>
<MaterialCommunityIcons
name='map-marker'
size={24}
style={{ color: theme.colors.onSurface }}
/>
<Text variant='labelLarge'>{`${jobOffer.geographicArea}`}</Text>
</View>
</View>
</View>
);
};

export default JobOfferItem;
47 changes: 47 additions & 0 deletions front/src/components/jobOffers/JobOfferList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { FC } from 'react';
import { StyleSheet, View } from 'react-native';
import { Divider } from 'react-native-paper';

import { JobOffer } from '@/models/entities/jobOffer';

import JobOfferItem from './JobOfferItem';

/**
* The styles for the JobOfferList component.
*/
const styles = StyleSheet.create({
divider: {
height: 1,
marginVertical: 8,
width: '100%',
},
});

/**
* The props for the JobOfferList component.
*/
type JobOfferProps = {
/**
* The job offers to display.
*/
jobOffers: JobOffer[];
};

/**
* Displays a list of job offers.
* @constructor
*/
const JobOfferList: FC<JobOfferProps> = ({ jobOffers }) => {
return (
<View>
{jobOffers?.map((jobOffer) => (
<View key={jobOffer.id}>
<JobOfferItem jobOffer={jobOffer} />
<Divider style={styles.divider} />
</View>
))}
</View>
);
};

export default JobOfferList;
51 changes: 51 additions & 0 deletions front/src/models/entities/jobOffer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* The status of a job offer.
*/
export enum JobOfferStatus {
NOT_APPLIED = -1,
APPLIED = 0,
ACCEPTED = 1,
REJECTED = 2,
}

/**
* A job offer.
*/
export type JobOffer = {
/**
* The id of the job offer.
*/
id: string;

/**
* The title of the job offer.
*/
title: string;

/**
* The description of the job offer.
*/
description: string;

/**
* The start date of the job offer.
* Formatted as an ISO 8601 string.
*/
startDate: string;

/**
* The end date of the job offer.
* Formatted as an ISO 8601 string.
*/
endDate: string;

/**
* The geographic area of the job offer.
*/
geographicArea: string;

/**
* The status of the job offer.
*/
status: JobOfferStatus;
};
8 changes: 8 additions & 0 deletions front/src/pages/internal/InternalPagesNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import PaperNavigationBar from '@/components/utils/PaperNavigationBar';
import InternalPagesPage from '@/pages/internal/InternalPagesPage';
import ProfileNav from '@/pages/profile/ProfileNav';

import JobOffersNav from '../jobOffer/JobOfferNav';

/**
* The parameter list for the InternalPagesNav navigator.
*/
export type InternalPagesStackParamList = {
PagesMain: undefined;
PagesProfile: undefined;
PagesJobOffer: undefined;
};

const InternalPagesStack =
Expand All @@ -35,6 +38,11 @@ const InternalPagesNav = () => {
component={ProfileNav}
options={{ headerShown: false }}
/>
<InternalPagesStack.Screen
name='PagesJobOffer'
component={JobOffersNav}
options={{ headerShown: false }}
/>
</InternalPagesStack.Navigator>
);
};
Expand Down
7 changes: 7 additions & 0 deletions front/src/pages/internal/InternalPagesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ const InternalPagesPage: FC<InternalPagesPageProps> = ({ navigation }) => {
navigation.navigate('PagesProfile');
}, [navigation]);

const handleJobOfferPress = useCallback(() => {
navigation.navigate('PagesJobOffer');
}, [navigation]);

return (
<ScrollView
style={styles.container}
Expand All @@ -42,6 +46,9 @@ const InternalPagesPage: FC<InternalPagesPageProps> = ({ navigation }) => {
<Button mode={'contained'} onPress={handleProfilePress}>
Profile
</Button>
<Button mode={'contained'} onPress={handleJobOfferPress}>
JobOffer
</Button>
</ScrollView>
);
};
Expand Down
35 changes: 35 additions & 0 deletions front/src/pages/jobOffer/JobOfferNav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { createNativeStackNavigator } from '@react-navigation/native-stack';

import PaperNavigationBar from '@/components/utils/PaperNavigationBar';
import JobOfferPage from '@/pages/jobOffer/JobOfferPage';
import i18n from '@/utils/i18n';

/**
* The parameter list for the JobOffersNav navigator.
*/
export type JobOfferStackParamList = {
JobOffer: undefined;
};

const JobOfferStack = createNativeStackNavigator<JobOfferStackParamList>();

/**
* The stack navigator for the job offers pages.
* @constructor
*/
const JobOffersNav = () => {
return (
<JobOfferStack.Navigator
initialRouteName='JobOffer'
screenOptions={{ header: (props) => <PaperNavigationBar {...props} /> }}
>
<JobOfferStack.Screen
name='JobOffer'
component={JobOfferPage}
options={{ headerTitle: `${i18n.t('jobOffer.info.jobOffer')}` }}
/>
</JobOfferStack.Navigator>
);
};

export default JobOffersNav;
62 changes: 62 additions & 0 deletions front/src/pages/jobOffer/JobOfferPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useFocusEffect } from '@react-navigation/native';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { FC, useCallback } from 'react';
import { ScrollView, StyleSheet } from 'react-native';

import JobOfferList from '@/components/jobOffers/JobOfferList';
import { useGetJobOffersQuery } from '@/store/api/jobApiSlice';

import { JobOfferStackParamList } from './JobOfferNav';

/**
* The styles for the JobOfferPage component.
*/
const styles = StyleSheet.create({
container: {
flex: 1,
},
contentContainer: {
gap: 8,
paddingBottom: 8,
paddingHorizontal: 16,
},
});

/**
* The props for the JobOfferPage component.
*/
type JobOfferPageProps = NativeStackScreenProps<
JobOfferStackParamList,
'JobOffer'
>;

/**
* Displays the page of job offers for the current user.
* @constructor
*/
const JobOfferPage: FC<JobOfferPageProps> = () => {
// API calls
const { data: jobOffers, refetch: refetchJobOffers } = useGetJobOffersQuery();

// Fetch data from the API when the page is focused
useFocusEffect(
useCallback(() => {
refetchJobOffers();
}, [refetchJobOffers]),
);

if (jobOffers === undefined) {
return null;
}

return (
<ScrollView
style={styles.container}
contentContainerStyle={styles.contentContainer}
>
<JobOfferList jobOffers={jobOffers} />
</ScrollView>
);
};

export default JobOfferPage;
1 change: 1 addition & 0 deletions front/src/store/api/apiSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ export const apiSlice = createApi({
'Profile',
'References',
'Job',
'JobOffer',
],
});
Loading