added companies company

This commit is contained in:
2019-06-20 17:04:04 +02:00
parent 177bf112aa
commit 55322567ae
19 changed files with 516 additions and 70 deletions
+20
View File
@@ -5,6 +5,9 @@ import {
FETCH_AD_REQUEST,
FETCH_AD_SUCCESS,
FETCH_AD_FAILURE,
FETCH_PROMOTED_ADS_REQUEST,
FETCH_PROMOTED_ADS_SUCCESS,
FETCH_PROMOTED_ADS_FAILURE,
} from '../types'
@@ -25,6 +28,23 @@ export const fetchAdsRequest = (payload) => ({
res
});
//Ads list
export const fetchPromotedAdsRequest = (payload) => ({
type: FETCH_PROMOTED_ADS_REQUEST,
payload
});
export const fetchPromotedAdsSuccess = res => ({
type: FETCH_PROMOTED_ADS_SUCCESS,
res
});
export const fetchPromotedAdsFailure = res => ({
type: FETCH_PROMOTED_ADS_FAILURE,
res
});
//Single ad
export const fetchAdRequest = payload => ({
+23
View File
@@ -0,0 +1,23 @@
import {
FETCH_COMPANIES_REQUEST,
FETCH_COMPANIES_SUCCESS,
FETCH_COMPANIES_FAILURE,
} from '../types'
//COMPANIES list
export const fetchCompaniesRequest = (payload) => ({
type: FETCH_COMPANIES_REQUEST,
payload
});
export const fetchCompaniesSuccess = res => ({
type: FETCH_COMPANIES_SUCCESS,
res
});
export const fetchCompaniesFailure = res => ({
type: FETCH_COMPANIES_FAILURE,
res
});
+103 -6
View File
@@ -1,7 +1,9 @@
import React from 'react';
import PropTypes from "prop-types";
import Router from 'next/router'
import getConfig from 'next/config'
import { withStyles } from '@material-ui/core/styles';
import Avatar from '@material-ui/core/Avatar';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import CardHeader from '@material-ui/core/CardHeader';
@@ -9,23 +11,81 @@ import CardMedia from '@material-ui/core/CardMedia';
import Typography from '@material-ui/core/Typography';
const { publicRuntimeConfig } = getConfig()
const styles = theme => ({
card: {
display: 'flex',
position: 'relative',
height: '50vh',
width: '100%',
[theme.breakpoints.up('md')]: {
width : '30%',
},
[theme.breakpoints.down('md')]: {
width : '40%',
},
[theme.breakpoints.down('xs')]: {
width : '100%',
},
margin: '1vh 2vh 1vh 2vh',
[theme.breakpoints.up('sm')]: {
margin: '1vh 0 1vh 0',
},
borderRadius: 0
},
details: {
details_container: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: '8vh 0 0 0',
},
company_title: {
fontSize: '1em',
fontWeight: 'normal',
textTransform: 'uppercase',
},
content: {
flex: '1 0 auto',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
flexWrap: 'wrap',
width: '100%',
padding: 0,
},
cover: {
width: 151,
height: 151,
width: '100%',
height: '50%',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
},
logo_container: {
display: 'flex',
alignItems: 'center',
width: 100,
height: 100,
position: 'absolute',
left: '50%',
top: '50%',
transform: 'translate(-50%, -66%)',
backgroundColor: 'white',
boxShadow: '0 8px 6px -6px grey',
},
logo: {
width: '100%',
height: 'auto',
objectfit: 'contain',
borderRadius: 0,
},
chip: {
margin: '0 1vh 0 1vh',
}
});
class CompanyRowComponent extends React.Component {
render() {
@@ -34,11 +94,48 @@ class CompanyRowComponent extends React.Component {
const imgDefaultUrl = "https://www.talentstube.com/img/default_picture_company.svg"
const imgUrl = (rowData.presentation_video && rowData.presentation_video.thumbnails[0] && rowData.presentation_video.thumbnails[0].sizes[4]) ? rowData.presentation_video.thumbnails[0].sizes[4].link : imgDefaultUrl
const logoUrl = publicRuntimeConfig.logoRootUrl + rowData.logo
return (
<Card onClick={() => Router.push(`/Company?id=${rowData.id}`)}>
<CardHeader />
<CardContent className={classes.content}>
<CardMedia
className={classes.cover}
image={imgUrl}
title={rowData.title}
/>
<div className={classes.logo_container}>
<Avatar className={classes.logo} src={logoUrl} />
</div>
<div className={classes.details_container}>
<Typography
gutterBottom
variant="headline"
variant="h6"
component="h4"
align="center"
className={classes.company_title}>
{rowData.business_name}
</Typography>
{/* <Typography
gutterBottom
variant="headline"
variant="h6"
component="h3"
align="center"
className={classes.job_title}>
{rowData.title}
</Typography> */}
</div>
<div>
{/* <Chip label={rowData.extra.contract_type} className={classes.chip} />
<Chip label={rowData.location.gmap_address.address_components[0].long_name} className={classes.chip} /> */}
</div>
</CardContent>
{/* <CardHeader />
<CardContent>
<CardMedia
className={classes.cover}
@@ -57,7 +154,7 @@ class CompanyRowComponent extends React.Component {
className={classes.details}>
{rowData.description}
</Typography>
</CardContent>
</CardContent> */}
</Card>
);
}
@@ -0,0 +1,46 @@
import React, { Component } from 'react'
import GridList from '@material-ui/core/GridList';
import GridListTile from '@material-ui/core/GridListTile';
import { withStyles } from '@material-ui/core/styles';
import OffreRow from './OffreRow'
const styles = theme => ({
root: {
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'space-around',
overflow: 'hidden',
backgroundColor: theme.palette.background.paper,
},
gridList: {
flexWrap: 'nowrap',
// Promote the list into his own layer on Chrome. This cost memory but helps keeping high FPS.
transform: 'translateZ(0)',
height: '400px',
},
gridListTile:{
height: '400px',
}
});
class HorizontalAdScroller extends Component {
render() {
const {classes} = this.props
return (
<div>
<GridList className={classes.gridList} cols={2.5}>
{this.props.data.map(tile => (
<GridListTile className={classes.gridListTile} key={tile.title}>
<OffreRow rowData={tile}/>
</GridListTile>
))}
</GridList>
</div>
)
}
}
export default withStyles(styles)(HorizontalAdScroller)
@@ -0,0 +1,47 @@
import React, { Component } from 'react'
import GridList from '@material-ui/core/GridList';
import GridListTile from '@material-ui/core/GridListTile';
import { withStyles } from '@material-ui/core/styles';
import OffreRow from './OffreRow'
const styles = theme => ({
root: {
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'space-around',
overflow: 'hidden',
backgroundColor: theme.palette.background.paper,
},
gridList: {
flexWrap: 'nowrap',
// Promote the list into his own layer on Chrome. This cost memory but helps keeping high FPS.
transform: 'translateZ(0)',
height: '400px',
},
gridListTile:{
height: '400px',
}
});
class HorizontalCompanyScroller extends Component {
render() {
const {classes} = this.props
return (
<div>
<GridList className={classes.gridList} cols={2.5}>
{this.props.data.map(tile => (
<GridListTile className={classes.gridListTile} key={tile.title}>
<OffreRow rowData={tile}/>
</GridListTile>
))}
</GridList>
</div>
)
}
}
export default withStyles(styles)(HorizontalCompanyScroller)
@@ -0,0 +1,34 @@
import React, { Component } from 'react'
import { Typography, Button } from '@material-ui/core';
import { withStyles } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';
const styles = theme => ({
textField: {
textAlign: 'center',
justifyContent: 'center',
},
})
class NewsLetterRegistration extends Component {
render() {
let {classes} = this.props
return (
<div>
<Typography gutterBottom component="h3">
Recevez les dernières nouvelles de Talents Tube par e-mail
</Typography>
<TextField
id="outlined-bare"
className={classes.textField}
margin="normal"
variant="outlined"
inputProps={{ 'aria-label': 'bare' }}
/>
<Button variant="outlined" onClick={() => alert("Salut, tu veux faire un tuto ?")}>Ok</Button>
</div>
)
}
}
export default withStyles(styles)(NewsLetterRegistration)
+1 -1
View File
@@ -89,7 +89,7 @@ class OffreRowComponent extends React.Component {
render() {
const { classes, theme, rowData } = this.props;
const { classes, rowData } = this.props;
const imgUrl = (rowData.video.thumbnails[0] && rowData.video.thumbnails[0].sizes[4]) ? rowData.video.thumbnails[0].sizes[4].link : publicRuntimeConfig.adListDefaultUrl
const logoUrl = publicRuntimeConfig.logoRootUrl + rowData.company.logo
+5 -3
View File
@@ -21,11 +21,13 @@ export default {
},
//offset is the index position of the first element to start requesting at
fetchAds: ({ limit = "", order = "", keyword = "", offset = 1 }) => {
// use 0|1 for isPromoted
fetchAds: ({ limit = null, order = "", keyword = "", offset = 1, status='PUBLISHED', isPromoted=null}) => {
console.debug("--- fetchAds" + offset, limit, order, keyword)
return axios.get('api/ads', { params: { offset, limit, order, keyword } })
return axios.get('api/ads', { params: { offset, limit, order, keyword, status, isPromoted } })
.then(res => res.data)
}
},
},
companies: {
fetchCompany: id =>
+20 -3
View File
@@ -3,12 +3,15 @@ import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import _ from "lodash"
import Grid from "../components/templates/Grid"
import LayoutGrid from "../components/templates/LayoutGrid"
import { Typography } from '@material-ui/core';
import CompanyRow from "../components/organisms/CompanyRow"
import api from '../data/api'
import { fetchCompaniesRequest } from '../actions/companies'
import { CompaniesSelector } from '../reducers/companies'
import { connect } from "react-redux";
const styles = theme => ({
root: {
@@ -33,7 +36,12 @@ const styles = theme => ({
return (
<LayoutGrid backLink="/">
<div className={classes.container}>
<Grid CustomRowComponent={CompanyRow} dataFetcher={api.companies.fetchCompanies}>
<Grid CustomRowComponent={CompanyRow}
dataFetcher={this.props.fetchCompaniesRequest}
data={this.props.companies}
currentPage={this.props.currentPage}
recordCount={this.props.recordCount}
>
</Grid>
</div>
</LayoutGrid>
@@ -46,4 +54,13 @@ Companies.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(Companies);
function mapStateToProps(state) {
return {
companies: Object.keys(state.companies).length !== 0 && CompaniesSelector(state),
// //companies: state.companies.data,
currentPage: _.toNumber(!!state.companies.meta && _.ceil((state.companies.meta.offset + 1) /state.companies.meta.limit) ),
recordCount: _.toNumber(!!state.companies.meta && state.companies.meta.total_items)
};
}
export default withStyles(styles)(connect(mapStateToProps, { fetchCompaniesRequest })(Companies));
+15 -14
View File
@@ -31,28 +31,36 @@ const styles = theme => ({
});
class Company extends React.Component {
static async getInitialProps(context) {
console.log("Company ")
const { id } = context.query
let res = await api.companies.fetchCompany(id)
console.log(res)
return {data:res};
}
onVimeoError = (err) => {
console.error(err);
}
render() {
const { classes, company } = this.props;
console.log(company);
const { classes, data } = this.props;
return (
<LayoutDetail backLink="/Companies">
<div className={classes.video}>
<Plyr autoplay
{ data.presentation_video && (<Plyr autoplay
type="vimeo" // or "vimeo"
videoId={Ar.last(_.split(company.presentation_video.cdn_url, '/'))}
/>
videoId={Ar.last(_.split(data.presentation_video.cdn_url, '/'))}
/>)}
</div>
<div className={classes.container}>
<Typography gutterBottom variant="title" >
{company.brand_name}
{data.brand_name}
</Typography>
</div>
<div className={classes.container}>
<Typography gutterBottom variant="subheading" >
{company.description}
{data.description}
</Typography>
</div>
{/* TODO:list of ads */}
@@ -62,13 +70,6 @@ class Company extends React.Component {
}
Company.getInitialProps = async function (context) {
const { id } = context.query
return api.companies.fetchCompany(id);
}
Company.propTypes = {
classes: PropTypes.object.isRequired,
};
-3
View File
@@ -177,9 +177,6 @@ class LoginPage extends React.Component {
}
function mapStateToProps(state) {
console.log('mapStateToProps Login')
console.log(state)
return {
loading: !!!state.user.loaded,
errors: state.user.errors
+59 -12
View File
@@ -12,9 +12,19 @@ import CardMedia from '@material-ui/core/CardMedia';
import CardContent from '@material-ui/core/CardContent';
import Layout from "../components/templates/Layout"
import { Typography } from '@material-ui/core';
import Button from '@material-ui/core/Button';
import Router from 'next/router'
import { connect } from 'react-redux'
import ShortVideoHooker from '../components/organisms/ShortVideoHooker'
import HorizontalAdScroller from '../components/organisms/HorizontalAdScroller'
import HorizontalCompanyScroller from '../components/organisms/HorizontalCompanyScroller'
import NewsLetterRegistration from '../components/organisms/NewsLetterRegistration'
import { fetchPromotedAdsRequest } from "../actions/ads";
import dynamic from 'next/dynamic'
import WhatshotIcon from '@material-ui/icons/Whatshot';
const PWAInstallSnack = dynamic(() => import('../Components/Organisms/PWAInstallSnack.js'), {
ssr: false
});
const styles = theme => ({
root: {
@@ -57,8 +67,8 @@ const styles = theme => ({
},
title: {
position: 'fixed',
top: '0px',
textAlign: 'center',
margin: theme.spacing(1),
},
@@ -68,7 +78,17 @@ const styles = theme => ({
});
class Index extends React.Component {
state = {
promotedAds: []
}
componentDidMount() {
this.props.fetchPromotedAdsRequest({ isPromoted: 1 })
}
componentWillReceiveProps(nextProps) {
this.setState({
promotedAds: nextProps.promotedAds
})
}
render() {
const { classes } = this.props;
@@ -76,21 +96,41 @@ class Index extends React.Component {
<Layout>
<div className={classes.container}>
<ShortVideoHooker/>
{/* <HorizontalAdScroller/>
<Button "AllAds">
<ShortVideoHooker />
<Typography gutterBottom component="h1"
className={classes.title}>
LES OFFRES D'EMPLOI EN VIDEO
</Typography>
<HorizontalAdScroller data={this.state.promotedAds} />
<Button variant="outlined" onClick={() => Router.push('/offres')}>Toutes les offres</Button>
<HorizontalCompanyScroller/>
<Button "AllCompanies">
<HorizontalCompanyScroller data={this.state.promotedAds} />
<Installer/>
<Button variant="outlined" onClick={() => Router.push('/companies')}>Toutes les entreprises</Button>
<Typography gutterBottom component="h3">
Installez l'application Talents Tube pour suivre vos candidatures
</Typography>
<PWAInstallSnack >
{({ initInstall }) =>
<Button
aria-label='install' variant="contained" color="primary" onClick={initInstall}>
<WhatshotIcon ></WhatshotIcon>
<Typography variant="h6" color="inherit">
Installer l'app
</Typography>
</Button>
}
</PWAInstallSnack>
<NewsLetterRegistration/>
{/*
<Footer/> */}
</div>
</Layout>
@@ -98,8 +138,15 @@ class Index extends React.Component {
}
}
function mapStateToProps(state) {
return {
promotedAds: state.ads.promoted.data
};
}
Index.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(connect()(Index));
export default withStyles(styles)(connect(mapStateToProps, { fetchPromotedAdsRequest })(Index));
+21 -3
View File
@@ -3,10 +3,13 @@ import {
FETCH_ADS_SUCCESS,
FETCH_ADS_FAILURE,
FETCH_ADS_REQUEST,
FETCH_PROMOTED_ADS_SUCCESS,
FETCH_PROMOTED_ADS_FAILURE,
FETCH_PROMOTED_ADS_REQUEST,
} from "../types";
export function ads(state = {
export function ads(state = {promoted:[]
}, action = {}) {
switch (action.type) {
@@ -22,7 +25,22 @@ export function ads(state = {
case FETCH_ADS_FAILURE:
return {
...state,
errors:action.error
errors: action.error
};
case FETCH_PROMOTED_ADS_REQUEST:
return {
...state
};
case FETCH_PROMOTED_ADS_SUCCESS:
return {
...state,
promoted: action.res
};
case FETCH_PROMOTED_ADS_FAILURE:
return {
...state,
errors: action.error
};
default:
@@ -34,5 +52,5 @@ export function ads(state = {
export const AdsHashSelector = state => state.ads.data;
export const AdsSelector = createSelector(AdsHashSelector, hash =>
Object.values(hash)
Object.values(hash)
);
+36
View File
@@ -0,0 +1,36 @@
import { createSelector } from "reselect";
import {
FETCH_COMPANIES_SUCCESS,
FETCH_COMPANIES_FAILURE,
FETCH_COMPANIES_REQUEST,
} from "../types";
export function companies(state = {
}, action = {}) {
switch (action.type) {
case FETCH_COMPANIES_REQUEST:
return {
...state
};
case FETCH_COMPANIES_SUCCESS:
return {
...state,
...action.res
};
case FETCH_COMPANIES_FAILURE:
return {
...state,
errors: action.error
};
default:
return state;
}
}
export const CompaniesHashSelector = state => state.companies.data;
export const CompaniesSelector = createSelector(CompaniesHashSelector, hash =>
Object.values(hash)
);
+16 -10
View File
@@ -1,25 +1,31 @@
import { combineReducers } from "redux";
import {user} from "./reducers/user";
import {ads} from "./reducers/ads";
import {ad} from "./reducers/ad";
import { user } from "./reducers/user";
import { ads } from "./reducers/ads";
import { ad } from "./reducers/ad";
import { companies } from "./reducers/companies";
export const exampleInitialState = {
user :{
loaded :false,
token : ""
user: {
loaded: false,
token: ""
},
ads:{
data:{}
ads: {
data: {},
promoted: {}
},
ad:{
ad: {
},
companies: {
data: {}
}
}
export default combineReducers({
user,
ads,
ad
ad,
companies
});
+10 -4
View File
@@ -4,25 +4,31 @@ import { all, call, delay, put, take, takeLatest } from 'redux-saga/effects'
import es6promise from 'es6-promise'
import { requestLoginSaga, requestUserSaga } from './sagas/userSaga.js'
import { requestAdsSaga, requestAdSaga } from './sagas/offreSaga.js'
import { requestAdsSaga, requestAdSaga, requestPromotedAdsSaga } from './sagas/offreSaga.js'
import {requestCompaniesSaga} from './sagas/companySaga.js'
import {
REQUEST_LOGIN,
FETCH_CURRENT_USER_REQUEST,
FETCH_ADS_REQUEST,
FETCH_AD_REQUEST
}from "./types";
FETCH_AD_REQUEST,
FETCH_PROMOTED_ADS_REQUEST,
FETCH_COMPANIES_REQUEST
} from "./types";
es6promise.polyfill()
function * rootSaga () {
function* rootSaga() {
yield all([
takeLatest(REQUEST_LOGIN, requestLoginSaga),
takeLatest(FETCH_CURRENT_USER_REQUEST, requestUserSaga),
takeLatest(FETCH_ADS_REQUEST, requestAdsSaga),
takeLatest(FETCH_AD_REQUEST, requestAdSaga),
takeLatest(FETCH_PROMOTED_ADS_REQUEST, requestPromotedAdsSaga),
takeLatest(FETCH_COMPANIES_REQUEST, requestCompaniesSaga),
])
}
+24
View File
@@ -0,0 +1,24 @@
import { call, put } from "redux-saga/effects";
import {
fetchCompaniesRequest,
fetchCompaniesSuccess,
fetchCompaniesFailure
} from "../actions/companies";
import api from "../data/api";
export function* requestCompaniesSaga(action) {
try {
const res = yield call(api.companies.fetchCompanies, action.payload);
if (!!res.data)
yield put(fetchCompaniesSuccess(res));
else
yield put(fetchCompaniesFailure(res));
} catch (err) {
yield put(fetchCompaniesFailure(err.response));
}
}
+23 -9
View File
@@ -1,16 +1,18 @@
import { call, put } from "redux-saga/effects";
import { fetchAdsSuccess, fetchAdsFailure, fetchAdSuccess, fetchAdFailure } from "../actions/ads";
import { fetchAdsSuccess, fetchAdsFailure,
fetchAdSuccess, fetchAdFailure,
fetchPromotedAdsSuccess,fetchPromotedAdsFailure
} from "../actions/ads";
import api from "../data/api";
export function* requestAdsSaga(action) {
try {
// console.log("requestAdsSaga")
// console.debug(action)
const res = yield call(api.ads.fetchAds, action.payload);
// console.log(res)
// console.log("---")
if (!!res.data)
yield put(fetchAdsSuccess(res));
else
@@ -23,12 +25,9 @@ export function* requestAdsSaga(action) {
export function* requestAdSaga(action) {
try {
console.log("requestAdSaga")
console.log(action)
const res = yield call(api.ads.fetchAd, action.payload);
console.log(res)
console.log("---")
if (!!res)
yield put(fetchAdSuccess(res));
else
@@ -38,3 +37,18 @@ export function* requestAdSaga(action) {
yield put(fetchAdFailure(err.response));
}
}
export function* requestPromotedAdsSaga(action) {
try {
const res = yield call(api.ads.fetchAds, action.payload);
if (!!res.data)
yield put(fetchPromotedAdsSuccess(res));
else
yield put(fetchPromotedAdsFailure(res));
} catch (err) {
yield put(fetchPromotedAdsFailure(err.response));
}
}
+11
View File
@@ -18,3 +18,14 @@ export const FETCH_ADS_FAILURE = "FETCH_ADS_FAILURE";
export const FETCH_AD_REQUEST = "FETCH_AD_REQUEST";
export const FETCH_AD_SUCCESS = "FETCH_AD_SUCCESS";
export const FETCH_AD_FAILURE = "FETCH_AD_FAILURE";
export const FETCH_PROMOTED_ADS_REQUEST = "FETCH_PROMOTED_ADS_REQUEST";
export const FETCH_PROMOTED_ADS_SUCCESS = "FETCH_PROMOTED_ADS_SUCCESS";
export const FETCH_PROMOTED_ADS_FAILURE = "FETCH_PROMOTED_ADS_FAILURE";
//COMPANIES
export const FETCH_COMPANIES_REQUEST = "FETCH_COMPANIES_REQUEST";
export const FETCH_COMPANIES_SUCCESS = "FETCH_COMPANIES_SUCCESS";
export const FETCH_COMPANIES_FAILURE = "FETCH_COMPANIES_FAILURE";