Compare commits

..

31 Commits

Author SHA1 Message Date
llaniau 36571b5cca suppresion ville dans url Entreprise 2020-09-15 17:49:20 +02:00
llaniau 7ad96db928 maj referencement 2020-09-15 17:43:34 +02:00
llaniau 5ce9f108cd changement d email de ref 2020-09-15 14:11:44 +02:00
llaniau 99bb737647 changement page contact (blog) 2020-09-15 12:43:27 +02:00
llaniau e2ef540aac Modif adblock maj de ads->jobs 2020-09-15 11:46:21 +02:00
llaniau 6e6119ffaf Correction recette avant MEP sept 2020 2020-09-15 10:53:40 +02:00
llaniau 473edf7472 Ajout Etape CV apres candidature 2020-09-14 18:26:04 +02:00
llaniau 96b08352a0 Minheight à 200px sur les offres pour les écrans mac 2020-09-09 17:09:37 +02:00
llaniau 7a45d19d2b Amelioration parcours talent 2020-09-08 17:37:18 +02:00
llaniau c73b08c1aa referencement 2020-09-07 18:44:54 +02:00
llaniau b6e4347abb Mise a jour Borganisation cliente 2020-09-07 15:58:55 +02:00
llaniau adb9d2ccde Changement de la page Help pour intéger les pages comment faire un cv video 2020-09-03 17:35:11 +02:00
llaniau edd7f705ab Cv video detachement 2020-09-02 18:39:08 +02:00
llaniau ea372ba0f2 AccountEtape3 modif du lien Front 2020-09-02 08:53:53 +02:00
llaniau 779b722662 bug requete offre 2020-08-24 18:29:00 +02:00
llaniau 6bb8baf636 variablisation url symfony 2020-08-20 17:40:42 +02:00
llaniau 083f7b9c16 cas cabinet recrutement (champ entreprise) 2020-08-18 18:15:31 +02:00
llaniau 0eed896aec next config 2020-08-18 10:40:41 +02:00
llaniau e8ae224414 VARIABILISATION DES URL EN DUR (CHANGEMENT DE NOM DE DOMAINE) 2020-08-17 11:28:32 +02:00
llaniau fa3dcd8fd2 suppression d'un attribut (job) pour jobposting 2020-08-13 18:14:56 +02:00
llaniau 2c0c0a2632 Mise en place Job Posting 2020-08-13 17:12:45 +02:00
llaniau 501ee57430 referencement index.js 2020-08-13 12:43:28 +02:00
llaniau 0f6ec17fef OgHead 2020-08-13 11:40:03 +02:00
llaniau 112541275b Modif Referencement 2020-08-13 11:36:41 +02:00
llaniau e08b879691 gestion adresse json webapp 2020-08-12 15:43:53 +02:00
llaniau 527351c232 video de pres Web app V2 2020-08-11 17:30:55 +02:00
llaniau 0ed8da11e1 package.json 2020-07-22 12:11:35 +02:00
llaniau f522e4389e bug 2020-07-22 11:55:33 +02:00
llaniau efb6527b9b v2.0.1 2020-07-22 08:23:55 +02:00
llaniau ba43bcddc7 mise en place modif profile 2020-07-20 14:38:00 +02:00
llaniau 3523cc95ee Design update profile et video 2020-07-16 17:10:27 +02:00
60 changed files with 19168 additions and 1488 deletions
+27 -27
View File
@@ -1,13 +1,13 @@
import {
FETCH_ADS_REQUEST,
FETCH_ADS_SUCCESS,
FETCH_ADS_FAILURE,
FETCH_AD_REQUEST,
FETCH_AD_SUCCESS,
FETCH_AD_FAILURE,
FETCH_EMPHASIS_ADS_REQUEST,
FETCH_EMPHASIS_ADS_SUCCESS,
FETCH_EMPHASIS_ADS_FAILURE,
FETCH_JOBS_REQUEST,
FETCH_JOBS_SUCCESS,
FETCH_JOBS_FAILURE,
FETCH_JOB_REQUEST,
FETCH_JOB_SUCCESS,
FETCH_JOB_FAILURE,
FETCH_EMPHASIS_JOBS_REQUEST,
FETCH_EMPHASIS_JOBS_SUCCESS,
FETCH_EMPHASIS_JOBS_FAILURE,
CLEAR_AD,
COUNT_AD_VIEW,
TOGGLE_PROMOTED_FILTER_ADS
@@ -28,52 +28,52 @@ export const togglePromotedFilterRequest = () => ({
//Ads list
export const fetchAdsRequest = (payload) => ({
type: FETCH_ADS_REQUEST,
export const fetchJobsRequest = (payload) => ({
type: FETCH_JOBS_REQUEST,
payload
});
export const fetchAdsSuccess = res => ({
type: FETCH_ADS_SUCCESS,
export const fetchJobsSuccess = res => ({
type: FETCH_JOBS_SUCCESS,
res
});
export const fetchAdsFailure = res => ({
type: FETCH_ADS_FAILURE,
export const fetchJobsFailure = res => ({
type: FETCH_JOBS_FAILURE,
res
});
//Ads list
export const fetchEmphasisAdsRequest = (payload) => ({
type: FETCH_EMPHASIS_ADS_REQUEST,
export const fetchEmphasisJobsRequest = (payload) => ({
type: FETCH_EMPHASIS_JOBS_REQUEST,
payload
});
export const fetchEmphasisAdsSuccess = res => ({
type: FETCH_EMPHASIS_ADS_SUCCESS,
export const fetchEmphasisJobsSuccess = res => ({
type: FETCH_EMPHASIS_JOBS_SUCCESS,
res
});
export const fetchEmphasisAdsFailure = res => ({
type: FETCH_EMPHASIS_ADS_FAILURE,
export const fetchEmphasisJobsFailure = res => ({
type: FETCH_EMPHASIS_JOBS_FAILURE,
res
});
//Single ad
export const fetchAdRequest = payload => ({
type: FETCH_AD_REQUEST,
export const fetchJobRequest = payload => ({
type: FETCH_JOB_REQUEST,
payload
});
export const fetchAdSuccess = res => ({
type: FETCH_AD_SUCCESS,
export const fetchJobSuccess = res => ({
type: FETCH_JOB_SUCCESS,
res
});
export const fetchAdFailure = res => ({
type: FETCH_AD_FAILURE,
export const fetchJobFailure = res => ({
type: FETCH_JOB_FAILURE,
res
});
+9 -9
View File
@@ -2,9 +2,9 @@ import {
POST_AD_ANSWER_REQUEST,
POST_AD_ANSWER_FAILURE,
POST_AD_ANSWER_SUCCESS,
FETCH_ADS_ANSWER_REQUEST,
FETCH_ADS_ANSWER_SUCCESS,
FETCH_ADS_ANSWER_FAILURE,
FETCH_JOBS_ANSWER_REQUEST,
FETCH_JOBS_ANSWER_SUCCESS,
FETCH_JOBS_ANSWER_FAILURE,
FETCH_ANSWER_REQUEST,
FETCH_ANSWER_SUCCESS,
FETCH_ANSWER_FAILURE,
@@ -47,19 +47,19 @@ export const postMessageAnswerSuccess = res => ({
});
export const fetchAdsAnsweredRequest = payload => ({
type: FETCH_ADS_ANSWER_REQUEST,
export const fetchJobsAnsweredRequest = payload => ({
type: FETCH_JOBS_ANSWER_REQUEST,
payload
});
export const fetchAdsAnsweredSuccess = res => ({
type: FETCH_ADS_ANSWER_SUCCESS,
export const fetchJobsAnsweredSuccess = res => ({
type: FETCH_JOBS_ANSWER_SUCCESS,
res
});
export const fetchAdsAnsweredFailure = errors => ({
type: FETCH_ADS_ANSWER_FAILURE,
export const fetchJobsAnsweredFailure = errors => ({
type: FETCH_JOBS_ANSWER_FAILURE,
errors
});
+2
View File
@@ -6,6 +6,7 @@ import AssignmentIcon from '@material-ui/icons/Assignment';
import RoomIcon from '@material-ui/icons/Room';
import LocationOnIcon from '@material-ui/icons/LocationOn';
import BusinessCenterIcon from '@material-ui/icons/BusinessCenter';
import Business from '@material-ui/icons/Business';
import Typography from '@material-ui/core/Typography';
const variantIcon = {
@@ -13,6 +14,7 @@ const variantIcon = {
room: RoomIcon,
location_on: LocationOnIcon,
business_center: BusinessCenterIcon,
business: Business,
};
const styles = theme => ({
@@ -6,12 +6,14 @@ import RoomIcon from '@material-ui/icons/Room';
import LocationOnIcon from '@material-ui/icons/LocationOn';
import BusinessCenterIcon from '@material-ui/icons/BusinessCenter';
import Typography from '@material-ui/core/Typography';
import Business from '@material-ui/icons/Business';
const variantIcon = {
assignment: AssignmentIcon,
room: RoomIcon,
location_on: LocationOnIcon,
business_center: BusinessCenterIcon,
business: Business,
};
const styles = theme => ({
+34
View File
@@ -0,0 +1,34 @@
import React from 'react'
import { JobPostingJsonLd } from 'next-seo';
const JobPosting = props => {
return (
<JobPostingJsonLd
datePosted="2020-01-06T03:37:40Z"
description={props.job_description}
hiringOrganization={{
name: props.job_brand,
sameAs: props.job_brand_link,
}}
jobLocation={{
addressLocality: props.job_location,
}}
title={props.job_title}
validThrough="2020-01-06"
applicantLocationRequirements="FR"
/>
)
}
JobPosting.propTypes = {
// seo_title: PropTypes.string.isRequired,
// seo_description: PropTypes.string.isRequired,
// seo_url: PropTypes.string.isRequired,
// image: PropTypes.string.isRequired,
}
export default JobPosting
+13 -3
View File
@@ -6,7 +6,8 @@ const OgHead = props => {
return (
<Head>
<title key="title">{props.seo_title}</title>
<meta name="description" key="description" content={props.seo_description} />
<meta name="robots" content="noindex, nofollow"></meta>
<meta name="description" kenoindexy="description" content={props.seo_description} />
<meta name="keywords" content={props.seo_key}/>
<meta key="og:url" property="og:url" content={props.seo_url} />
<meta key="og:type" property="og:type" content="article" />
@@ -18,9 +19,18 @@ const OgHead = props => {
/>
<meta name="keywords" content={props.seo_key}/>
<meta key="og:image" property="og:image" content={props.image} />
<link rel="canonical" href={ props.seo_url} />
<meta name="twitter:card" content="summary"/>
<meta name="twitter:site" content="@TalentsTube"/>
<meta name="twitter:title" content={props.seo_title}/>
<meta name="twitter:description" content={props.seo_title}/>
<meta name="twitter:image" content={props.image}/>
<meta http-equiv='cache-control' content='no-store,no-cache'></meta>
<meta http-equiv='expires' content='0'></meta>
<meta http-equiv='pragma' content='no-cache'></meta>
<link rel="canonical" href={ props.seo_url} />
</Head>
)
}
+34 -2
View File
@@ -30,6 +30,32 @@ const useStyles = makeStyles(theme => ({
height: '100%',
},
},
},
videoPresentation: {
display: 'flex',
flexDirection: 'column',
position: 'relative',
paddingBottom: '56%', /* 16:9 58%*/
'& iframe': {
position: 'absolute',
top: 0,
left: 'calc(50% - 50vw)',
width: '100vw',
height: '100%',
},
[theme.breakpoints.up('md')]: {
display: 'flex',
flexDirection: 'column',
position: 'relative',
paddingBottom: '56.5%', /* 16:9 */
'& iframe': {
position: 'absolute',
top: 0,
left: 'calc(50% - 32vw)',
width: '64vw',
height: '100%',
},
},
}
}));
@@ -37,14 +63,20 @@ export default function VideoVimeo(props) {
const {videoId, title, options, classesOverride, autoplay} = props;
const classes = useStyles();
let className = classes.video;
if (classesOverride) {
className = classes.videoPresentation;
}
return (
<section className={classes.video +' '+classesOverride}>
<section className={className +' '+classesOverride}>
<iframe className='player'
src={"https://player.vimeo.com/video/"+ videoId + options+"?autoplay="+autoplay}
frameBorder="0"
title={title}
webkitallowfullscreen="true" mozallowfullscreen="true" allowFullScreen></iframe>
</section>
)
}
@@ -1,386 +0,0 @@
import React, { Component, Fragment } from 'react'
import { withStyles } from '@material-ui/core/styles';
import { Button, Typography } from '@material-ui/core';
import TextField from '@material-ui/core/TextField';
import { Alert, AlertTitle } from '@material-ui/lab';
import Input from '@material-ui/core/Input';
import { connect } from 'react-redux'
import { postAdAnswerRequest } from '../../actions/answer'
import { updateUserRequest, updateUserPictureRequest } from '../../actions/user'
import Avatar from '@material-ui/core/Avatar';
import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight';
import Loader from './../templates/loader'
import getConfig from 'next/config'
import Validator from "validator";
import ReactHtmlParser, { processNodes, convertNodeToElement, htmlparser2 } from 'react-html-parser';
const styles = theme => ({
container: {
width: '100%',
},
form: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
padding: '24px',
paddingBottom: theme.spacing(8),
marginBottom: theme.spacing(8),
'&>div': {
marginTop: theme.spacing(1.5),
marginBottom: theme.spacing(1.5),
},
[theme.breakpoints.up('md')]: {
padding: '0 calc(25vw + 24px)',
},
},
importContainer: {
alignSelf: 'flex-start',
},
textField: {
width: '100%',
'&:disabled': {
color: theme.palette.secondary.dark,
},
'& label': {
color: theme.palette.primary.dark,
},
'& fieldset:hover': {
borderColor: theme.palette.primary.main + ' !important',
},
},
foot: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: '10',
width: '100%',
position: 'fixed',
bottom: 0,
padding: theme.spacing(1.2, 0),
backgroundColor: theme.palette.secondary.main,
boxShadow: '0px 0px 10px rgba(30, 35, 39, 0.10)',
},
rootButton: {
textTransform: 'inherit',
border: '1px solid',
'&:hover': {
border: '1px solid',
},
},
button: {
textTransform: 'inherit',
marginLeft: theme.spacing(8),
marginRight: theme.spacing(8),
padding: theme.spacing(1.2, 2),
color: theme.palette.secondary.main,
backgroundColor: theme.palette.primary.important,
},
importButton: {
color: theme.palette.primary.main,
backgroundColor: theme.palette.secondary.main,
padding: theme.spacing(3, 2),
margin: theme.spacing(1),
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.14)',
'&:hover': {
backgroundColor: theme.palette.secondary.main,
},
InputKeyboardArrowRightIcon: {
fontSize: '2.5em',
color: theme.palette.secondary.main,
},
},
});
const { publicRuntimeConfig } = getConfig()
//https://xd.adobe.com/view/ba98331a-9145-4d67-7e27-25a36e519ef4-cca9/screen/a96779d7-bdb3-410a-8f19-e6987acb98ee/Candidature-Step-3
class Accountdetails extends Component {
constructor(props) {
super(props);
// This binding is necessary to make `this` work in the callback
this.handleChange = this.handleChange.bind(this);
this.onBlur = this.onBlur.bind(this)
this.onSubmit = this.onSubmit.bind(this);
}
state = {
file: null,
firstname: '',
lastname: '',
phone: '',
currentJob: '',
linkedin: '',
twitter: '',
website: '',
mediaId: null,
companyId: null,
};
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
//console.log(this.refs.myTextField.input.value);
//this.soumettre(e);
}
onBlur(event) {
console.log("blur")
this.soumettre();
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
firstname: nextProps.user.firstname,
lastname: nextProps.user.lastname,
phone: nextProps.user.phone,
currentJob: nextProps.user.currentJob,
linkedin: nextProps.user.linkedin,
twitter: nextProps.user.twitter,
website: nextProps.user.website,
mediaId: nextProps.mediaId,
companyId: nextProps.companyId,
errors: !!!nextProps.errors ? { errors: { message: "" } } : nextProps.errors,
})
}
componentDidMount() {
this.setState({
firstname: this.props.user.firstname,
lastname: this.props.user.lastname,
phone: this.props.user.phone,
currentJob: this.props.user.currentJob,
linkedin: this.props.user.linkedin,
twitter: this.props.user.twitter,
website: this.props.user.website,
})
}
onSubmit = (e) => {
e.preventDefault();
this.soumettre(e);
}
soumettre(e){
const errors = this.validate(this.state);
this.setState({ errors });
if (!this.state.errors || !this.state.errors.message){
const data = new FormData()
data.append('firstname', this.state.firstname)
data.append('lastname', this.state.lastname)
data.append('phone', this.state.phone)
//data.append('email', this.state.email)
data.append('currentJob', this.state.currentJob)
data.append('linkedin', this.state.linkedin)
data.append('twitter', this.state.twitter)
data.append('website', this.state.website)
//TODO:maybe add this two in a single transactionnal saga
this.props.updateUserRequest(data)
}
}
validate = data => {
const errors = {};
errors.message ="";
//if (!Validator.isEmail(data.currentJob)) errors.message += "Le poste actuel doit etre un email\n";
if (!data.firstname || data.firstname==="") errors.message += "Le prénom est obligatoire<br />";
if (!data.lastname || data.lastname==="") errors.message += "Le nom est obligatoire<br />";
if (!Validator.isMobilePhone(data.phone, "fr-FR")) errors.message += "Le téléphone n'est pas correct<br />";
//console.log(errors.email);
return errors;
};
AfficherErreurs() {
if (!this.state.errors || !this.state.errors.message){
return "";
}else if (this.state.errors.length != 0 && this.state.errors.message!="") {
console.log("Erreur"+this.state.errors.message);
console.log("afficher erreur");
return (
<Alert severity="error">
{ ReactHtmlParser(this.state.errors.message)}
</Alert>
);
}
return "";
}
inputFilesystemChange = (e) => {
console.log("inputFilesystemChange");
var file = e.target.files[0]
this.setState({ picture: file })
const data = new FormData()
data.append('file', file)
this.props.updateUserPictureRequest(data)
}
render() {
const { classes, user } = this.props
const logoUrl = user.picture;
return (
<section className={classes.container}>
<form className={classes.form}
autoComplete="off" focused="true" name="monForm" ref="formToSubmit">
{!!logoUrl && <Avatar className={classes.logo}
classes={{ img: classes.MuiAvatarImg }}
src={publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.postedContentUrl + logoUrl} alt="candidatLogo" />}
{!!user.pictureLoading && <Loader />}
{!!!user.pictureLoading && !!!logoUrl &&
(<Fragment><input
type="file" accept="image/*" capture="filesystem"
className={classes.input}
style={{ display: 'none' }}
id="raised-button-file"
multiple
type="file"
onChange={this.inputFilesystemChange}
/>
<label htmlFor="raised-button-file">
<Button component="span" className={classes.importButton} classes={{ root: classes.rootButton }}>
<img alt="talents-tube" src='/static/images/default/account-avatar-default.svg'
className={classes.iconButton} />
<Typography align="left" className={classes.textButton}>
J'<span className={classes.underlined}>importe</span> ma photo de profil
</Typography>
<KeyboardArrowRightIcon className={classes.InputKeyboardArrowRightIcon} />
</Button>
</label></Fragment>)
}
{this.AfficherErreurs()}
<TextField
name="lastname"
required
id="outlined-required"
label="Nom :"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.lastname}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="firstname"
required
id="outlined-required"
label="Prénom"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.firstname}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="phone"
id="outlined"
label="Téléphone"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.phone}
onChange={this.handleChange}
type="tel"
onBlur={this.onBlur}
/>
<TextField
name="email"
id="outlined"
label="E-mail"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={user.email}
onChange={this.handleChange}
type="mail"
onBlur={this.onBlur}
/>
<TextField
name="currentJob"
id="outlined"
label="Poste actuel"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.currentJob}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="linkedin"
id="outlined"
label="Linkedin"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
placeholder="http://www.linkedin.com/in/"
value={this.state.linkedin}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="twitter"
id="outlined"
label="Twitter"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
placeholder="http://twitter.com/.."
value={this.state.twitter}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="website"
id="outlined"
label="Site internet"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
placeholder="http://monsite.fr"
value={this.state.website}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
</form>
<div id="F" className={classes.foot}>
<Button variant="contained" className={classes.button + ' cypButtonSendApplication'} onClick={this.onSubmit}>Mettre à jour!</Button>
</div>
</section>
)
}
}
function mapStateToProps(state, ownProps) {
return {
user: state.user,
loading: !!state.user.loading,
pictureLoading: !!state.user.pictureLoading,
errors: state.user.errors || ownProps.errors,
mediaId: state.media.message && state.media.message.mediaId,
};
}
export default withStyles(styles)(connect(mapStateToProps, { postAdAnswerRequest, updateUserRequest, updateUserPictureRequest })(Accountdetails))
+489 -17
View File
@@ -1,25 +1,497 @@
import React from 'react';
import { makeStyles } from '@material-ui/styles';
import AccountDetails from '../organisms/AccountDetails';
import Button from '@material-ui/core/Button';
import React, { useState, Component, Fragment } from 'react'
import { withStyles } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';
import { Alert, AlertTitle } from '@material-ui/lab';
import Snackbar from '@material-ui/core/Snackbar';
import { connect } from 'react-redux'
import { updateUserRequest } from '../../actions/user'
import getConfig from 'next/config'
import Validator from "validator";
import AccountProfile from '../organisms/AccountProfile';
import ReactHtmlParser, { processNodes, convertNodeToElement, htmlparser2 } from 'react-html-parser';
import {
Button,
} from '@material-ui/core';
class AccountEtape1 extends React.Component {
const styles = theme => ({
container: {
width: '85%',
},
form: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
padding: '24px',
paddingBottom: theme.spacing(8),
marginBottom: theme.spacing(8),
'&>div': {
marginTop: theme.spacing(1.5),
marginBottom: theme.spacing(1.5),
}
},
textField: {
width: '85%',
'&:disabled': {
color: theme.palette.secondary.dark,
},
'& label': {
color: theme.palette.primary.dark,
},
'& fieldset:hover': {
borderColor: theme.palette.primary.main + ' !important',
},
},
textFieldSmall: {
width: '48%',
'&:disabled': {
color: theme.palette.secondary.dark,
},
'& label': {
color: theme.palette.primary.dark,
},
'& fieldset:hover': {
borderColor: theme.palette.primary.main + ' !important',
},
padding: '5px',
},
});
const { publicRuntimeConfig } = getConfig();
//https://xd.adobe.com/view/ba98331a-9145-4d67-7e27-25a36e519ef4-cca9/screen/a96779d7-bdb3-410a-8f19-e6987acb98ee/Candidature-Step-3
class AccountEtape1 extends Component {
constructor(props) {
super(props);
// This binding is necessary to make `this` work in the callback
this.handleChange = this.handleChange.bind(this);
this.handleClose = this.handleClose.bind(this);
this.onBlur = this.onBlur.bind(this)
this.onSubmit = this.onSubmit.bind(this);
}
state = {
file: null,
firstname: '',
lastname: '',
email:'',
phone: '',
currentJob: '',
address1:'',
address2 : '',
zipCode : '',
city : '',
country : '',
linkedin: '',
twitter: '',
website: '',
mediaId: null,
companyId: null,
};
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
//console.log(this.refs.myTextField.input.value);
//this.soumettre(e);
}
handleClose = (e, reason) => {
if (reason === 'clickaway') {
return;
}
//setOpen(false);
};
onBlur(e) {
console.log("blur")
this.setState({ [e.target.name]: e.target.value });
console.log("phone=>"+this.state.phone);
this.soumettre();
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
firstname: nextProps.user.firstname,
lastname: nextProps.user.lastname,
phone: nextProps.user.phone,
email: nextProps.user.email,
currentJob: nextProps.user.currentJob,
address1 : nextProps.user.address1,
address2 : nextProps.user.address2,
zipCode : nextProps.user.zipCode,
city : nextProps.user.city,
country : nextProps.user.country,
linkedin: nextProps.user.linkedin,
twitter: nextProps.user.twitter,
website: nextProps.user.website,
companyId: nextProps.companyId,
errors: !!!nextProps.errors ? { errors: { message: "" } } : nextProps.errors,
})
}
componentDidMount() {
this.setState({
firstname: this.props.user.firstname,
lastname: this.props.user.lastname,
phone: this.props.user.phone,
email: this.props.user.email,
address1: this.props.user.address1,
address2 : this.props.user.address2,
zipCode : this.props.user.zipCode,
city : this.props.user.city,
country : this.props.user.country,
currentJob: this.props.user.currentJob,
linkedin: this.props.user.linkedin,
twitter: this.props.user.twitter,
website: this.props.user.website,
})
}
onSubmit = (e) => {
e.preventDefault();
this.soumettre(e);
}
soumettre(e){
const errors = this.validate(this.state);
const success = {};
this.setState({ errors });
if (!this.state.errors || !this.state.errors.message){
success.message ="Profil mis à jour";
this.setState({ success });
const data = new FormData()
data.append('firstname', this.state.firstname)
data.append('lastname', this.state.lastname)
data.append('phone', this.state.phone)
//data.append('email', this.state.email)
data.append('currentJob', this.state.currentJob)
data.append('address1', this.state.address1)
data.append('address2', this.state.address2)
data.append('zipCode', this.state.zipCode)
data.append('city', this.state.city)
data.append('country', this.state.country)
data.append('linkedin', this.state.linkedin)
data.append('twitter', this.state.twitter)
data.append('website', this.state.website)
//TODO:maybe add this two in a single transactionnal saga
this.props.updateUserRequest(data)
}
}
validate = data => {
const errors = {};
errors.message ="";
//if (!Validator.isEmail(data.currentJob)) errors.message += "Le poste actuel doit etre un email\n";
if (!data.firstname || data.firstname==="") errors.message += "Veuillez saisir votre prénom<br />";
if (!data.lastname || data.lastname==="") errors.message += " Veuillez saisir saisir votre nom<br />";
if (data.phone=="") errors.message += "Veuillez saisir un numéro de téléphone<br />";
if (!data.currentJob || data.currentJob=="") errors.message += "Veuillez saisir votre intitulé de poste actuel ou poste recherché<br />";
if ((data.zipCode && data.zipCode!="") && !Validator.isPostalCode(data.zipCode, "FR")) errors.message += "Le code postal n'est pas correct<br />";
// if ((data.website && data.website!="") && !Validator.isURL(data.website)) errors.message += " Veuillez ajouter un site internet avec une url valide<br />";
// if (!Validator.isMobilePhone(data.phone, "fr-FR") && !Validator.isNumeric(data.phone, "fr-FR")) errors.message += "Le téléphone n'est pas correct<br />";
//console.log(errors.email);
return errors;
};
AfficherErreurs() {
if (!this.state.errors || !this.state.errors.message){
return "";
}else if (this.state.errors.length != 0 && this.state.errors.message!="") {
return (
<Alert severity="error">
{ ReactHtmlParser(this.state.errors.message)}
</Alert>
);
}
return "";
}
AfficherSuccess() {
// if (this.state.success && this.state.success.length != 0 && this.state.success.message != "") {
// return (
// <Snackbar open={open} autoHideDuration={1000} onClose={() => this.setState({open: false})}>
// <Alert onClose={() => this.setState({open: false})} severity="success">
// {ReactHtmlParser(this.state.success.message)}
// </Alert>
// </Snackbar>
// );
// }
return "";
}
inputFilesystemChange = (e) => {
console.log("inputFilesystemChange");
var file = e.target.files[0]
this.setState({ picture: file })
const data = new FormData()
data.append('file', file)
this.props.updateUserPictureRequest(data)
}
render() {
const { classes, user } = this.props
const logoUrl = user.picture;
function refresh() {
document
.location
.reload(true);
}
return (
<div><AccountDetails />
<Button
variant="contained"
color="primary"
onClick={this.props.test}
>
Suivant
</Button></div>
);
<section className={classes.container}>
<form className={classes.form}
autoComplete="off" focused="true" name="monForm" ref="formToSubmit">
{/* {!!logoUrl && <Avatar className={classes.logo}
classes={{ img: classes.MuiAvatarImg }}
src={publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.postedContentUrl + logoUrl} alt="candidatLogo" />} */}
{/* {!!user.pictureLoading && <Loader />}
{!!!user.pictureLoading && !!!logoUrl &&
(<Fragment><input
type="file" accept="image/*" capture="filesystem"
className={classes.input}
style={{ display: 'none' }}
id="raised-button-file"
multiple
type="file"
onChange={this.inputFilesystemChange}
/>
<label htmlFor="raised-button-file">
<Button component="span" className={classes.importButton} classes={{ root: classes.rootButton }}>
<img alt="talents-tube" src='/static/images/default/account-avatar-default.svg'
className={classes.iconButton} />
<Typography align="left" className={classes.textButton}>
J'<span className={classes.underlined}>importe</span> ma photo de profil
</Typography>
<KeyboardArrowRightIcon className={classes.InputKeyboardArrowRightIcon} />
</Button>
</label></Fragment>)
} */}
{this.AfficherErreurs()}
{this.AfficherSuccess()}
<TextField
name="firstname"
required
id="outlined-required"
label="Prénom"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.firstname}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="lastname"
required
id="outlined-required"
label="Nom"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.lastname}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
disabled
name="email"
id="outlined"
label="E-mail"
required
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.email}
onChange={this.handleChange}
type="mail"
onBlur={this.onBlur}
/>
<TextField
name="phone"
id="outlined"
label="Téléphone"
required
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.phone ? this.state.phone: ''}
onChange={this.handleChange}
type="tel"
onBlur={this.onBlur}
/>
<TextField
name="currentJob"
id="outlined"
label="Poste actuel"
required
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.currentJob && this.state.currentJob !='null' ? this.state.currentJob : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="address1"
id="outlined"
label="Adresse"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.address1 && this.state.address1 !='null' ? this.state.address1 : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="address2"
id="outlined"
label="Adresse complémentaire"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
value={this.state.address2 && this.state.address2 !='null' ? this.state.address2 : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<div className={classes.container}>
<TextField
name="zipCode"
id="outlined"
label="Code postal"
className={classes.textField}
classes={{ root: classes.textFieldSmall }}
variant="outlined"
value={this.state.zipCode && this.state.zipCode !='null' ? this.state.zipCode : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="city"
id="outlined"
label="Ville"
className={classes.textField}
classes={{ root: classes.textFieldSmall }}
variant="outlined"
value={this.state.city && this.state.city !='null' ? this.state.city : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="country"
id="outlined"
label="Pays"
className={classes.textField}
classes={{ root: classes.textFieldSmall }}
variant="outlined"
value={this.state.country && this.state.country !='null' ? this.state.country : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
</div>
<TextField
name="linkedin"
id="outlined"
label="Linkedin"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
placeholder="http://www.linkedin.com/in/"
value={this.state.linkedin && this.state.linkedin !='null' ? this.state.linkedin : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="twitter"
id="outlined"
label="Twitter"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
placeholder="http://twitter.com/.."
value={this.state.twitter && this.state.twitter !='null' ? this.state.twitter : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<TextField
name="website"
id="outlined"
label="Site internet"
className={classes.textField}
classes={{ root: classes.rootTextfield }}
variant="outlined"
placeholder="http://monsite.fr"
value={this.state.website && this.state.website !='null' ? this.state.website : ''}
onChange={this.handleChange}
onBlur={this.onBlur}
/>
<Button
variant="contained"
color="primary"
onClick={refresh}
className={classes.button}>
Enregistrer
</Button>
</form>
{/* <div id="F" className={classes.foot}>
<Button variant="contained" className={classes.button + ' cypButtonSendApplication'} onClick={this.onSubmit}>Mettre à jour!</Button>
</div> */}
</section>
)
}
}
function mapStateToProps(state, ownProps) {
return {
user: state.user ,
loading: !!state.user.loading,
pictureLoading: !!state.user.pictureLoading,
errors: state.user.errors || ownProps.errors,
mediaId: state.media.message && state.media.message.mediaId,
};
}
export default AccountEtape1;
export default withStyles(styles)(connect(mapStateToProps, { updateUserRequest })(AccountEtape1));
+285 -337
View File
@@ -1,369 +1,317 @@
import React, {Fragment} from 'react'
import { connect } from "react-redux";
import { makeStyles } from '@material-ui/styles';
import {connect} from "react-redux";
import {makeStyles} from '@material-ui/styles';
import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight';
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
import { Button, Typography } from '@material-ui/core';
import {Button, Typography} from '@material-ui/core';
import dynamic from 'next/dynamic'
import { withStyles } from '@material-ui/core/styles'
import { uploadMediaPres } from '../../actions/media'
import { errorMessageSelector } from '../../reducers/media'
import { fetchCompanyRequest } from '../../actions/companies'
import { fetchCurrentUserRequest } from '../../actions/user'
import {withStyles} from '@material-ui/core/styles'
import {uploadMediaPres} from '../../actions/media'
import {errorMessageSelector} from '../../reducers/media'
import {fetchCompanyRequest} from '../../actions/companies'
import {fetchCurrentUserRequest} from '../../actions/user'
import VideoVimeo from '../atoms/VideoVimeo'
import Router from 'next/router'
import Loader from '../templates/loader'
import Ar from "lodash/array"
import Snackbar from '@material-ui/core/Snackbar';
import { Alert, AlertTitle } from '@material-ui/lab';
import ReactHtmlParser, { processNodes, convertNodeToElement, htmlparser2 } from 'react-html-parser';
import Grid from '@material-ui/core/Grid';
import Container from '@material-ui/core/Container';
import Hidden from '@material-ui/core/Hidden';
const styles = theme => ({
root: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'flex-start',
margin: theme.spacing(1),
},
rootStepperContainer: {
padding: theme.spacing(2, 12),
width: '25vw',
[theme.breakpoints.up('md')]: {
width: '7vw',
alignItems: 'center',
container: {
width: '100%',
},
'& .MuiStep-horizontal, & .MuiStepLabel-iconContainer': {
padding: '0 !important',
[theme.breakpoints.up('md')]: {
padding: 'inherit',
},
root: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'flex-start',
margin: theme.spacing(1),
flexWrap: 'wrap',
overflow: 'hidden',
backgroundColor: theme.palette.background.paper,
},
'& .Mui-disabled .MuiStepConnector-line': {
borderColor: theme.palette.answer.main + '!important',
},
'& .Mui-disabled .MuiStepIcon-root': {
color: theme.palette.answer.main + '!important',
},
},
rootStepperDefault: {
'& .MuiStepConnector-line': {
borderColor: theme.palette.secondary.dark,
}
},
rootStepperActive: {
'& .MuiStepConnector-line': {
borderColor: theme.palette.primary.main,
}
},
headerContainer: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
padding: theme.spacing(2),
},
infoContainer: {
display: 'flex',
flexDirection: 'column',
padding: theme.spacing(0, 1.5),
},
logoContainer: {
display: 'flex',
alignItems: 'center',
width: 60,
height: 60,
backgroundColor: 'white',
boxShadow: '0px 6px 15px rgba(30, 35, 39, 0.10)',
border: 'solid 5px white',
borderRadius: '6px',
},
recorderContainer: {
//display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '400px',
},
helpContainer: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '400px',
},
logo: {
width: 'inherit',
height: '100%',
objectFit: 'contain',
borderRadius: '3px',
},
MuiAvatarImg: {
objectFit: 'contain',
},
title: {
fontSize: '1em',
fontWeight: '500',
},
companyName: {
fontSize: '0.9em',
fontWeight: '300',
},
textContainer: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
'& p': {
fontWeight: '300',
},
'& > p:nth-child(1)': {
'& span': {
fontWeight: '500',
}
},
'& > p:nth-child(2)': {
paddingBottom: theme.spacing(2),
borderBottom: '1px solid #E8E8E8',
'& span': {
fontWeight: '500',
color: theme.palette.primary.main,
}
},
},
rootButton: {
textTransform: 'inherit',
},
underlined: {
textDecoration: 'underline',
},
textButton: {
fontWeight: '700',
fontSize: '1.15em',
paddingLeft: theme.spacing(2),
},
iconHelpButton: {
fontSize: '1.5em',
fontWeight: '500',
padding: theme.spacing(0, 2),
color: theme.palette.secondary.important,
},
helpButton: {
color: theme.palette.primary.dark,
backgroundColor: 'transparent',
padding: theme.spacing(0, 2),
margin: theme.spacing(1),
width: '100%',
'& span.MuiButton-label': {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}
},
captureButton: {
color: theme.palette.secondary.main,
backgroundColor: theme.palette.primary.main,
padding: theme.spacing(5, 2),
margin: theme.spacing(1),
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 4px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.14)',
'&:hover': {
backgroundColor: theme.palette.primary.main,
},
},
importButton: {
color: theme.palette.primary.main,
backgroundColor: theme.palette.secondary.main,
padding: theme.spacing(3, 2),
margin: theme.spacing(1),
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.14)',
'&:hover': {
backgroundColor: theme.palette.secondary.main,
},
},
HelpKeyboardArrowRightIcon: {
fontSize: '2.5em',
color: theme.palette.primary.dark,
},
InputKeyboardArrowRightIcon: {
fontSize: '2.5em',
color: theme.palette.secondary.main,
},
InputKeyboardArrowDownIcon: {
fontSize: '2.5em',
color: theme.palette.primary.main,
},
});
const Recorder = dynamic(() => import('../organisms/Recorder'), {
ssr: false
})
class Etape2 extends React.Component {
state = {
loading: true,
activeStep: 0,
data:{},
openSnack: false,
errors: {},
};
componentDidMount() {
localStorage.setItem('loginRedirect', window.location)
if(!!!localStorage.getItem('ttJWT'))
{
Router.push("/Login")
}
else
this.setState({
loading: false,
firstname: this.props.user.firstname,
videoPres : this.props.user.videoPres,
video: this.props.user.presentation_video_id,
})
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
loading: nextProps.loading,
openSnack: !!!_.isEmpty(nextProps.errors),
errors: !!!nextProps.errors ? { errors: { message: "" } } : nextProps.errors,
})
window.location.reload(false);
if(nextProps.media.status === 'success'){
this.setActiveStep(1);
recorderContainer: {
//display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '100%'
},
rootButton: {
textTransform: 'inherit'
},
underlined: {
textDecoration: 'underline'
},
textButton: {
fontWeight: '700',
fontSize: '1.15em',
paddingLeft: theme.spacing(2)
},
videoPresentation: {
width: "100%",
height: "100%"
},
conseilButton: {
color: "#ffffff",
backgroundColor: "#25D89A",
padding: theme.spacing(3, 2),
margin: theme.spacing(1),
width: "100%",
textAlign: "right",
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1' +
'px rgba(0, 0, 0, 0.14)',
'&:hover': {
color: "#ffffff",
backgroundColor: "#25D89A",
}
},
importButton: {
color: theme.palette.primary.main,
backgroundColor: theme.palette.secondary.main,
padding: theme.spacing(3, 2),
margin: theme.spacing(1),
width: "100%",
textAlign: "right",
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1' +
'px rgba(0, 0, 0, 0.14)',
'&:hover': {
color: theme.palette.primary.main,
backgroundColor: theme.palette.secondary.main,
}
},
InputKeyboardArrowDownIconBlank: {
fontSize: '2.5em',
color: "#ffffff",
textAlign: "right"
},
InputKeyboardArrowDownIcon: {
fontSize: '2.5em',
color: "#1689FB",
textAlign: "right"
}
}
});
const Recorder = dynamic(() => import ('../organisms/Recorder'), {ssr: false})
class Etape2 extends React.Component {
state = {
loading: true,
activeStep: 0,
data: {},
openSnack: false,
errors: {},
success : {}
};
setActiveStep = (step) => {
this.setState({ activeStep: step })
}
componentDidMount() {
localStorage.setItem('loginRedirect', window.location)
inputFilesystemChange = (e) => {
this.uploadMediaPres(e)
}
inputCamRecorderChange = (e) => {
this.uploadMediaPres(e)
}
uploadMediaPres = (e) => {
var file = e.target.files[0]
const data = new FormData()
data.append('upload', file)
this.props.uploadMediaPres(data)
}
handleClose = (event, reason) => {
this.setState({ openSnack: false });
}
render() {
const { classes, company } = this.props;
const { loading, activeStep } = this.state;
let video;
if (this.state.videoPres) {
video = <VideoVimeo videoId={Ar.last(_.split(this.state.videoPres, '/'))} autoplay='1' />;
} else {
video = "";
if (!!!localStorage.getItem('ttJWT')) {
Router.push("/Login")
} else
this.setState({loading: false, firstname: this.props.user.firstname, videoPres: this.props.user.videoPres, video: this.props.user.presentation_video_id})
}
return (
<Fragment>
{ loading}
<div className={classes.root}>
{video}
<div className={classes.textContainer}>
<Typography gutterBottom align="center">
Vous allez créer une <span>vidéo</span> de motivation !
</Typography>
<Typography gutterBottom align="center">
C'est <span>simple</span>,<span> rapide</span> et <span>plus humain</span>
</Typography>
</div>
<div className={ classes.helpContainer}>
<Button component="span" className={classes.helpButton} classes={{ root: classes.rootButton }} onClick={() => Router.push(`/Conseils`)}>
<Typography align="center" className={classes.textButton}>
Par <span className={classes.underlined}>ici</span> pour quelques conseils !
</Typography>
<KeyboardArrowRightIcon className={ classes.HelpKeyboardArrowRightIcon }/>
</Button>
</div>
{/* <Fragment>
<input
type="file" accept="video/*" capture="camcorder"
style={{ display: 'none' }}
id="record-button-file"
multiple
onChange={this.inputCamRecorderChange}
/>
<label htmlFor="record-button-file">
<Button component="span" >
<img alt="talents-tube" src='/statique/images/capture.svg' />
<Typography align="left" >
Je <span >réalise</span> ma vidéo de réponse
</Typography>
<KeyboardArrowRightIcon />
</Button>
</label>
</Fragment> */}
<div className={classes.recorderContainer}>
<Recorder uploader={this.props.uploadMediaPres} />
</div>
<input
type="file"
accept="video/*"
className={classes.input+' cypButtonImportVid'}
style={{ display: 'none' }}
id="import-button-file"
onChange={this.inputFilesystemChange}
/>
<label htmlFor="import-button-file">
<Button component="span" className={classes.importButton} classes={{ root: classes.rootButton }}>
<img alt="talents-tube" src='/statique/images/Import.svg' className={classes.iconButton} />
<Typography align="left" className={classes.textButton}>
J'<span className={classes.underlined}>importe</span> ma vidéo de réponse
</Typography>
<KeyboardArrowDownIcon className={ classes. InputKeyboardArrowDownIcon }/>
</Button>
</label>
</div>
</Fragment>
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
loading: nextProps.loading,
openSnack: !!!_.isEmpty(nextProps.errors),
errors: !!!nextProps.errors
? {
errors: {
message: ""
}
}
: nextProps.errors,
videoPres: nextProps.videoPres,
video: nextProps.video
})
//window.location.reload(false);
if (nextProps.media.status === 'success') {
const success = {};
success.message ="Vidéo mis à jour";
this.setState({ success });
this.setActiveStep(1);
}
}
setActiveStep = (step) => {
this.setState({activeStep: step})
}
inputFilesystemChange = (e) => {
this.uploadMediaPres(e)
}
inputCamRecorderChange = (e) => {
this.uploadMediaPres(e)
}
uploadMediaPres = (e) => {
var file = e.target.files[0]
const data = new FormData()
data.append('upload', file)
//this.setState({ videoPres: videoPres })
);
}
this
.props
.uploadMediaPres(data)
}
AfficherSuccess() {
if (!this.state.success || !this.state.success.message) {
} else if (this.state.success.length != 0 && this.state.success.message != "") {
return (
<Snackbar open={open} autoHideDuration={1000} onClose={() => this.setState({open: false})}>
<Alert onClose={() => this.setState({open: false})} severity="success">
{ReactHtmlParser(this.state.success.message)}
</Alert>
</Snackbar>
);
}
return "";
}
handleClose = (event, reason) => {
this.setState({openSnack: false});
}
render() {
const {classes, company} = this.props;
const {loading, activeStep} = this.state;
let video;
if (this.state.videoPres) {
video = <VideoVimeo videoId={Ar.last(_.split(this.state.videoPres, '/'))} autoplay='1' classesOverride="videoPresentation"/>;
} else {
video = "";
}
return (
<section className={classes.container}>
<Fragment>
{this.AfficherSuccess()}
{ loading ? <Loader/> : <div></div> }
<div className={classes.root}>
<Hidden xsUp>
&nbsp;
</Hidden>
<Container maxWidth="md">
<Grid container
justify="center"
alignItems="center"
spacing={2}>
<Grid item xs={12} sm={12} md={12} lg={6} lg={6}>
<div className={classes.recorderContainer}>
<Recorder uploader={this.props.uploadMediaPres} titreBouton="Remplacer ma vidéo"/>
</div>
</Grid>
<Grid item xs={12} sm={12} md={12} lg={6} xl={6}>
<div>
<label htmlFor="import-conseil">
<Button
component="span"
className={classes.conseilButton}
classes={{
root: classes.rootButton}}
onClick={() => Router.push(`/Conseils`)}>
<img
alt="talents-tube"
src='/statique/images/conseils-talentstube.svg'
className={classes.iconButton}/>
<Typography align="left" className={classes.textButton}>
<span className={classes.underlined}>Comment</span> créer ma vidéo
</Typography>
<KeyboardArrowDownIcon className={classes.InputKeyboardArrowDownIconBlank}/>
</Button>
</label>
</div>
<div>
<input
type="file"
accept="video/*"
className={classes.input + ' cypButtonImportVid'}
style={{display: 'none'}}
id="import-button-file"
onChange={this.inputFilesystemChange}/>
<label htmlFor="import-button-file">
<Button
component="span"
className={classes.importButton}
classes={{
root: classes.rootButton}}>
<img
alt="talents-tube"
src='/statique/images/Import.svg'
className={classes.iconButton}/>
<Typography align="left" className={classes.textButton}>
J'<span className={classes.underlined}>importe</span> ma vidéo de réponse
</Typography>
<KeyboardArrowDownIcon className={classes.InputKeyboardArrowDownIcon}/>
</Button>
</label>
</div>
</Grid>
<Hidden xsUp>
&nbsp;
</Hidden>
<Grid item xs={12} sm={12} md={12} lg={12} xl={12}>
<div className={classes.videoPresentation}>
{video}
</div>
</Grid>
</Grid>
</Container>
</div>
{/* <div className={classes.textContainer}>
</div> */}
</Fragment>
</section>
);
}
}
Etape2.getInitialProps = function (context) {
const { companyId } = context.query
const {res} = context
const {companyId} = context.query
const {res} = context
//TODO :really needed ?
context.store.dispatch(fetchCurrentUserRequest())
context
.store
.dispatch(fetchCurrentUserRequest())
if(!!companyId)
context.store.dispatch(fetchCompanyRequest(companyId))
if (!!companyId)
context.store.dispatch(fetchCompanyRequest(companyId))
return {};//leave this please
return {}; //leave this please
}
function mapStateToProps(state) {
return {
loading: !!state.media.loading,
media: state.media,
offre: state.ad,
//errors: errorMessageSelector(state),
user: state.user,
company: state.company
};
return {
loading: !!state.media.loading,
media: state.media,
offre: state.ad,
//errors: errorMessageSelector(state),
user: state.user,
company: state.company
};
}
export default withStyles(styles)(connect(mapStateToProps, { fetchCurrentUserRequest, fetchCompanyRequest, uploadMediaPres })(Etape2));
export default withStyles(styles)(connect(mapStateToProps, {fetchCurrentUserRequest, fetchCompanyRequest, uploadMediaPres})(Etape2));
+192 -176
View File
@@ -1,13 +1,23 @@
import React from 'react';
import React, { Fragment } from 'react';
import { connect } from "react-redux";
import { fetchCurrentUserRequest } from '../../actions/user'
import { fetchCompanyRequest } from '../../actions/companies'
import { uploadMediaCv } from '../../actions/media'
import { withStyles } from '@material-ui/core/styles'
import { makeStyles } from '@material-ui/styles';
import { Button, Typography } from '@material-ui/core';
import { Button, Typography, Divider, Hidden } from '@material-ui/core';
import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight';
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
import Loader from '../../components/templates/loader'
import Snack from '../../components/atoms/snack';
import Snackbar from '@material-ui/core/Snackbar';
import { Alert, AlertTitle } from '@material-ui/lab';
import ReactHtmlParser from 'react-html-parser';
import getConfig from 'next/config'
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
const styles = theme => ({
root: {
@@ -17,108 +27,6 @@ const styles = theme => ({
justifyContent: 'flex-start',
margin: theme.spacing(1),
},
rootStepperContainer: {
padding: theme.spacing(2, 12),
width: '25vw',
[theme.breakpoints.up('md')]: {
width: '7vw',
alignItems: 'center',
},
'& .MuiStep-horizontal, & .MuiStepLabel-iconContainer': {
padding: '0 !important',
[theme.breakpoints.up('md')]: {
padding: 'inherit',
},
},
'& .Mui-disabled .MuiStepConnector-line': {
borderColor: theme.palette.answer.main + '!important',
},
'& .Mui-disabled .MuiStepIcon-root': {
color: theme.palette.answer.main + '!important',
},
},
rootStepperDefault: {
'& .MuiStepConnector-line': {
borderColor: theme.palette.secondary.dark,
}
},
rootStepperActive: {
'& .MuiStepConnector-line': {
borderColor: theme.palette.primary.main,
}
},
headerContainer: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
padding: theme.spacing(2),
},
infoContainer: {
display: 'flex',
flexDirection: 'column',
padding: theme.spacing(0, 1.5),
},
logoContainer: {
display: 'flex',
alignItems: 'center',
width: 60,
height: 60,
backgroundColor: 'white',
boxShadow: '0px 6px 15px rgba(30, 35, 39, 0.10)',
border: 'solid 5px white',
borderRadius: '6px',
},
recorderContainer: {
//display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '400px',
},
helpContainer: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '400px',
},
logo: {
width: 'inherit',
height: '100%',
objectFit: 'contain',
borderRadius: '3px',
},
MuiAvatarImg: {
objectFit: 'contain',
},
title: {
fontSize: '1em',
fontWeight: '500',
},
companyName: {
fontSize: '0.9em',
fontWeight: '300',
},
textContainer: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
'& p': {
fontWeight: '300',
},
'& > p:nth-child(1)': {
'& span': {
fontWeight: '500',
}
},
'& > p:nth-child(2)': {
paddingBottom: theme.spacing(2),
borderBottom: '1px solid #E8E8E8',
'& span': {
fontWeight: '500',
color: theme.palette.primary.main,
}
},
},
rootButton: {
textTransform: 'inherit',
},
@@ -130,62 +38,71 @@ const styles = theme => ({
fontSize: '1.15em',
paddingLeft: theme.spacing(2),
},
iconHelpButton: {
fontSize: '1.5em',
fontWeight: '500',
padding: theme.spacing(0, 2),
color: theme.palette.secondary.important,
},
helpButton: {
color: theme.palette.primary.dark,
backgroundColor: 'transparent',
padding: theme.spacing(0, 2),
margin: theme.spacing(1),
width: '100%',
'& span.MuiButton-label': {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}
},
captureButton: {
color: theme.palette.secondary.main,
backgroundColor: theme.palette.primary.main,
padding: theme.spacing(5, 2),
margin: theme.spacing(1),
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 4px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.14)',
'&:hover': {
backgroundColor: theme.palette.primary.main,
},
},
importButton: {
color: theme.palette.primary.main,
backgroundColor: theme.palette.secondary.main,
padding: theme.spacing(3, 2),
margin: theme.spacing(1),
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.14)',
'&:hover': {
backgroundColor: theme.palette.secondary.main,
},
},
HelpKeyboardArrowRightIcon: {
fontSize: '2.5em',
color: theme.palette.primary.dark,
},
InputKeyboardArrowRightIcon: {
fontSize: '2.5em',
color: theme.palette.secondary.main,
},
InputKeyboardArrowDownIcon: {
fontSize: '2.5em',
color: theme.palette.primary.main,
},
importButton: {
color: theme.palette.primary.main,
backgroundColor: theme.palette.secondary.main,
padding: theme.spacing(3, 2),
margin: theme.spacing(1),
width: "370px",
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1' +
'px rgba(0, 0, 0, 0.14)',
'&:hover': {
color: theme.palette.primary.main,
backgroundColor: theme.palette.secondary.main,
}
},
});
const { publicRuntimeConfig } = getConfig();
class Etape3 extends React.Component {
state = {
loading: true,
openSnack: false,
cv:false,
errors: {},
success : {}
};
componentDidMount() {
localStorage.setItem('loginRedirect', window.location)
if(!!!localStorage.getItem('ttJWT'))
{
Router.push("/Login")
}
else
this.setState({
loading: false,
cvLink: this.props.user.cv
})
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
cvLink: nextProps.cvLink,
loading: nextProps.loading,
openSnack: !!!_.isEmpty(nextProps.errors),
errors: !!!nextProps.errors ? { errors: { message: "" } } : nextProps.errors,
})
this.state.cvLink = nextProps.cvLink;
if (nextProps.loading) {
const success = {};
success.message ="CV mis à jour";
console.log("CV mis à jour!!!")
if(this.state.cvLink) console.log(this.state.cvLink)
this.setState({ success });
}
}
inputFilesystemChange = (e) => {
this.uploadMediaCv(e)
}
@@ -199,40 +116,139 @@ class Etape3 extends React.Component {
this.props.uploadMediaCv(data)
}
AfficherSuccess() {
if (!this.state.success || !this.state.success.message) {
console.log("Erreur Upload CV");
return "";
} else if (this.state.success.length != 0 && this.state.success.message != "") {
return (
<Snackbar open={open} autoHideDuration={1000} onClose={() => this.setState({open: false})}>
<Alert onClose={() => this.setState({open: false})} severity="success">
{ReactHtmlParser(this.state.success.message)}
</Alert>
</Snackbar>
);
}
return "";
}
AfficherLienCv() {
console.log("afficher lien");
if (this.state.cvLink !="") {
return (
<Fragment>
<Hidden>&nbsp;</Hidden>
<Hidden>&nbsp;</Hidden>
<Hidden>&nbsp;</Hidden>
<Hidden>&nbsp;</Hidden>
<Card variant="" style={{
width:370+"px",
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1' +
'px rgba(0, 0, 0, 0.14)',
marginLeft : '10px',
marginTop:'10px'
}}>
<Hidden>&nbsp;</Hidden>
<CardActions>
<Button size="small">MON CV</Button>
</CardActions>
<CardContent>
<Typography >
<a href={publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.postedContentCv + this.state.cvLink}
target="_blank"
style={{
color:'#1689FB',
'&:hover': {
color: '#1689FB',
},
fontWeight: '700',
fontSize: '1em',
}}
>
Visionner mon CV
</a>
<br />
{/* <a href="https://www.permatheque.fr/PDF/mansobufukuoka-larevolutiondunseulbrindepaille.pdf" target="_blank">lien</a> */}
</Typography>
</CardContent>
</Card>
</Fragment>
);
}
return "";
}
render() {
const { classes, company } = this.props;
const { loading } = this.state;
return (
<div>
<input
type="file"
accept=".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document, .pdf, .odt"
className={classes.input+' cypButtonImportVid'}
style={{ display: 'none' }}
id="import-button-file"
onChange={this.inputFilesystemChange}
/>
<label htmlFor="import-button-file">
<Button component="span" className={classes.importButton} classes={{ root: classes.rootButton }}>
<img alt="talents-tube" src='/statique/images/Import.svg' className={classes.iconButton} />
<Typography align="left" className={classes.textButton}>
J'<span className={classes.underlined}>importe</span> mon CV au format PDF
</Typography>
<KeyboardArrowDownIcon className={ classes. InputKeyboardArrowDownIcon }/>
</Button>
</label>
</div>
<Fragment>
{ loading ?
<Loader /> :
<Fragment>
{this.AfficherSuccess()}
<input
type="file"
accept=".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document, .pdf, .odt"
className={classes.input+' cypButtonImportVid'}
style={{ display: 'none' }}
id="import-button-file"
onChange={this.inputFilesystemChange}
/>
<label htmlFor="import-button-file">
<Button component="span" className={classes.importButton} classes={{ root: classes.rootButton }}>
<img alt="talents-tube" src='/statique/images/Import-cv-candidat.svg' className={classes.iconButton} />
<Typography className={classes.textButton}>
J'<span className={classes.underlined}>importe</span> mon CV
</Typography>
<KeyboardArrowDownIcon className={ classes. InputKeyboardArrowDownIcon }/>
</Button>
</label>
<Hidden>&nbsp;</Hidden>
{this.AfficherLienCv()}
</Fragment>
}
</Fragment>
);
}
}
Etape3.getInitialProps = function (context) {
const { adId, companyId } = context.query
const {res} = context
//TODO :really needed ?
context.store.dispatch(fetchCurrentUserRequest())
if(!!adId)
context.store.dispatch(fetchJobRequest(adId))
else if(!!companyId)
context.store.dispatch(fetchCompanyRequest(companyId))
return {};//leave this please
}
function mapStateToProps(state) {
// console.log("mapStateToProps")
// console.log(state.loading)
// console.log(state.user)
// console.log(state.media.message)
return {
user: state.user ,
cvLink : state.media.message,
loading: !!state.media.loading,
media: state.media,
offre: state.ad,
cv : true,
//errors: errorMessageSelector(state),
user: state.user,
company: state.company
+414 -277
View File
@@ -1,306 +1,419 @@
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import moment from 'moment';
import AccountStep from "../../components/organisms/AccountStep";
// import AccountStep from "../../components/organisms/AccountStep";
import AccountEtape1 from '../organisms/AccountEtape1';
import AccountEtape2 from '../organisms/AccountEtape2';
import AccountEtape3 from '../organisms/AccountEtape3';
import { makeStyles } from '@material-ui/styles';
import AccountEtape3 from '../organisms/AccountEtape3';
import {withStyles} from '@material-ui/core/styles';
// import Switch from '@material-ui/core/Switch';
import PhoneIcon from '@material-ui/icons/Phone';
import MailIcon from '@material-ui/icons/Mail';
import PublicIcon from '@material-ui/icons/Public';
import DuoIcon from '@material-ui/icons/Duo';
import LinkedInIcon from '@material-ui/icons/LinkedIn';
import Facebook from '@material-ui/icons/Facebook';
// import FormGroup from '@material-ui/core/FormGroup'; import FormControlLabel
// from '@material-ui/core/FormControlLabel'; import FormControl from
// '@material-ui/core/FormControl';
import {updateUserRequest, updateUserPictureRequest} from '../../actions/user'
import {makeStyles} from '@material-ui/styles';
import {connect} from 'react-redux'
import getConfig from 'next/config'
import BottomNavigation from '@material-ui/core/BottomNavigation';
// import BottomNavigationAction from
// '@material-ui/core/BottomNavigationAction';
import RefreshIcon from '@material-ui/icons/Refresh';
// import FavoriteIcon from '@material-ui/icons/Favorite'; import LocationOnIcon
// from '@material-ui/icons/LocationOn';
import Box from '@material-ui/core/Box';
import {
Card,
CardActions,
CardContent,
Avatar,
Typography,
Divider,
Button,
LinearProgress,
Stepper,
Step,
StepLabel,
StepContent,
StepButton,
Paper,
Grid
Card,
CardActions,
CardContent,
Avatar,
Typography,
Divider,
Button,
LinearProgress,
Stepper,
Step,
StepLabel,
StepContent,
StepButton,
Paper,
Grid
} from '@material-ui/core';
const styles = theme => ({
container: {
width: '100%'
},
button: {
textTransform: 'inherit',
marginLeft: theme.spacing(8),
marginRight: theme.spacing(8),
padding: theme.spacing(1.2, 2),
color: theme.palette.secondary.main,
backgroundColor: theme.palette.primary.important
},
stickToBottom: {
width: '100%',
position: 'fixed',
bottom: 0
}
});
const useStyles = makeStyles(theme => ({
root: {},
details: {
display: 'flex',
},
avatar: {
marginLeft: 'auto',
height: 110,
width: 100,
flexShrink: 0,
flexGrow: 0
},
progress: {
marginTop: theme.spacing(2)
},
uploadButton: {
marginRight: theme.spacing(2)
},
button: {
marginTop: theme.spacing(1),
marginRight: theme.spacing(1),
},
actionsContainer: {
marginBottom: theme.spacing(2),
},
resetContainer: {
padding: theme.spacing(3),
},
root: {},
details: {
display: 'flex'
},
avatar: {
marginLeft: 'auto',
height: 110,
width: 100,
flexShrink: 0,
flexGrow: 0
},
progress: {
marginTop: theme.spacing(2)
},
information: {
display: 'flex',
flexDirection: 'column',
backgroundColor: theme.palette.primary.main,
[
theme
.breakpoints
.up('md')
]: {
display: 'flex',
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'space-around',
backgroundColor: theme.palette.primary.main,
padding: theme.spacing(7, 7)
},
'& a': {
textDecoration: 'none'
}
},
texteBlanc: {
color: "#fff"
},
uploadButton: {
marginRight: theme.spacing(2)
},
button: {
marginTop: theme.spacing(1),
marginRight: theme.spacing(1)
},
actionsContainer: {
marginBottom: theme.spacing(2)
},
resetContainer: {
padding: theme.spacing(3)
}
}));
function getSteps() {
return ['Informations personnelles', 'Vidéo de présentation', 'Expériences professionnelles'];
return ['Informations personnelles', 'Vidéo de présentation', 'Expériences professionnelles'];
}
function getStepContent(step) {
switch (step) {
case 0:
return <AccountEtape1 ></AccountEtape1>;
case 1:
return <AccountEtape2></AccountEtape2>;
case 2:
return <AccountEtape3></AccountEtape3>;
default:
return 'Unknown step';
}
switch (step) {
case 0:
return <AccountEtape1 ></AccountEtape1>;
case 1:
return <AccountEtape2></AccountEtape2>;
case 2:
return <AccountEtape3></AccountEtape3>;
default:
return 'Unknown step';
}
}
const AccountProfile = props => {
const { className, ...rest } = props;
const {
className,
user,
...rest
} = props;
const logoUrl = user.picture;
const classes = useStyles();
const classes = useStyles();
// const user = { name: 'Laurent Laniau', city: 'Questembert', country:
// 'France', timezone: '', avatar: '/images/avatars/avatar_11.png' };
const {publicRuntimeConfig} = getConfig()
const [activeStep,
setActiveStep] = React.useState(0);
const [completed,
setCompleted] = React.useState(new Set());
const [skipped,
setSkipped] = React.useState(new Set());
const [state,
setState] = React.useState({checkedA: true, checkedB: true});
const steps = getSteps();
const user = {
name: 'Laurent Laniau',
city: 'Questembert',
country: 'France',
timezone: '',
avatar: '/images/avatars/avatar_11.png'
};
const totalSteps = () => {
return getSteps().length;
};
const [activeStep, setActiveStep] = React.useState(0);
const [completed, setCompleted] = React.useState(new Set());
const [skipped, setSkipped] = React.useState(new Set());
const steps = getSteps();
const isStepOptional = (step) => {
return step === 4;
};
const totalSteps = () => {
return getSteps().length;
};
const handleSkip = () => {
if (!isStepOptional(activeStep)) {
// You probably want to guard against something like this it should never occur
// unless someone's actively trying to break something.
throw new Error("You can't skip a step that isn't optional.");
}
const isStepOptional = (step) => {
return step === 4;
};
setActiveStep((prevActiveStep) => prevActiveStep + 1);
setSkipped((prevSkipped) => {
const newSkipped = new Set(prevSkipped.values());
newSkipped.add(activeStep);
return newSkipped;
});
};
const handleSkip = () => {
if (!isStepOptional(activeStep)) {
// You probably want to guard against something like this
// it should never occur unless someone's actively trying to break something.
throw new Error("You can't skip a step that isn't optional.");
}
const skippedSteps = () => {
return skipped.size;
};
setActiveStep((prevActiveStep) => prevActiveStep + 1);
setSkipped((prevSkipped) => {
const newSkipped = new Set(prevSkipped.values());
newSkipped.add(activeStep);
return newSkipped;
});
};
const completedSteps = () => {
return completed.size;
};
const skippedSteps = () => {
return skipped.size;
};
const allStepsCompleted = () => {
return completedSteps() === totalSteps() - skippedSteps();
};
const completedSteps = () => {
return completed.size;
};
const isLastStep = () => {
return activeStep === totalSteps() - 1;
};
const allStepsCompleted = () => {
return completedSteps() === totalSteps() - skippedSteps();
};
const handleNext = () => {
const newActiveStep = isLastStep() && !allStepsCompleted()
? // It's the last step, but not all steps have been completed
// find the first step that has been completed
steps.findIndex((step, i) => !completed.has(i))
: activeStep + 1;
const isLastStep = () => {
return activeStep === totalSteps() - 1;
};
setActiveStep(newActiveStep);
};
const handleNext = () => {
const newActiveStep =
isLastStep() && !allStepsCompleted()
? // It's the last step, but not all steps have been completed
// find the first step that has been completed
steps.findIndex((step, i) => !completed.has(i))
: activeStep + 1;
const handleBack = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
setActiveStep(newActiveStep);
};
const handleStep = (step) => () => {
console.log("step");
console.log(step);
setActiveStep(step);
};
const handleBack = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
const handleComplete = () => {
const newCompleted = new Set(completed);
newCompleted.add(activeStep);
setCompleted(newCompleted);
const handleStep = (step) => () => {
console.log("step");
console.log(step);
setActiveStep(step);
};
const handleComplete = () => {
const newCompleted = new Set(completed);
newCompleted.add(activeStep);
setCompleted(newCompleted);
/**
/**
* Sigh... it would be much nicer to replace the following if conditional with
* `if (!this.allStepsComplete())` however state is not set when we do this,
* thus we have to resort to not being very DRY.
*/
if (completed.size !== totalSteps() - skippedSteps()) {
handleNext();
if (completed.size !== totalSteps() - skippedSteps()) {
handleNext();
}
};
const handleReset = () => {
setActiveStep(0);
setCompleted(new Set());
setSkipped(new Set());
};
const isStepSkipped = (step) => {
return skipped.has(step);
};
const handleChange = (event) => {
setState({
...state,
[event.target.name]: event.target.checked
});
};
function isStepComplete(step) {
return completed.has(step);
}
function refresh() {
document
.location
.reload(true);
console.log("refresh")
}
};
const handleReset = () => {
setActiveStep(0);
setCompleted(new Set());
setSkipped(new Set());
};
return (
<Grid
container
spacing={4}
direction="row"
justify="space-between"
alignItems="flex-start">
<Grid item lg={3} md={6} xl={4} xs={12}>
<Card {...rest} className={clsx(classes.root, className)}>
<CardContent>
<div id="fb-root"></div>
<script
async
defer
crossorigin="anonymous"
src="https://connect.facebook.net/fr_FR/sdk.js#xfbml=1&version=v8.0&appId=2332071840142634&autoLogAppEvents=1"
nonce="O28M4TTm"></script>
<div className={classes.details}>
<div>
<Typography gutterBottom variant="h5">
{user.lastname}
&nbsp; {user.firstname}
<a href="#" onClick={refresh}><RefreshIcon className={classes.menuButton}/></a>
</Typography>
<Typography
className={classes.locationText}
color="textSecondary"
variant="body1">
<MailIcon className={classes.menuButton}/>
: {user.email}<br/>
<PhoneIcon className={classes.menuButton}/>
: {user.phone}<br/>
<LinkedInIcon className={classes.menuButton}/>
: {(user.linkedin && user.linkedin != '' && user.linkedin != 'null')
? <a href={user.linkedin} target="_blank">Lien sur mon linkendIn</a>
: 'Non renseigné'}<br/>
<PublicIcon className={classes.menuButton}/>
: {(user.website && user.website != '' && user.website != 'null')
? <a href={user.website} target="_blank">Lien sur mon site</a>
: 'Non renseigné'}<br/>
</Typography>
</div>
{/* <Avatar
className={classes.avatar}
src={publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.postedContentUrl + logoUrl}/> */}
</div>
<div>
<Box m={3}/>
<Divider/>
<Box m={3}/>
<Typography
className={classes.locationText}
color="textSecondary"
variant="body1">
<DuoIcon className={classes.menuButton}/>
: {(user.alias && user.alias != '' && user.alias != 'null')
? <a
href={publicRuntimeConfig.cvThequeUrl + "talent/" + user.alias}
target="_blank">Voir mon profil TalentsTube
</a>
: 'Non renseigné'}<br/>
<div
class="fb-share-button"
data-href={publicRuntimeConfig.cvThequeUrl + "talent/" + user.alias}
data-layout="button_count"
data-size="small">
const isStepSkipped = (step) => {
return skipped.has(step);
};
<Facebook className={classes.menuButton}/>
:
<a
target="_blank"
href={"https://www.facebook.com/sharer/sharer.php?u=" + publicRuntimeConfig.cvThequeUrl + "talent/" + user.alias + "%2F&amp;src=sdkpreparse"}
class="fb-xfbml-parse-ignore"> Partager mon profil
</a>
</div>
</Typography>
</div>
{/* <div className={classes.progress}>
<Typography variant="body1">Etat du Profil: 70%</Typography>
<LinearProgress value={70} variant="determinate"/>
</div> */}
function isStepComplete(step) {
return completed.has(step);
}
</CardContent>
<Box m={2}/>
<Divider/>
<Box m={2}/>
<div className={classes.information}>
<Typography className={classes.texteBlanc} align="left"></Typography>
</div>
{/* <CardActions>
<Button className={classes.uploadButton} color="primary" variant="text">
Remplacer une photo de profil
</Button>
<Button variant="text">Supprimer photo de profil</Button>
</CardActions> */}
<Box m={1}/>
<Divider/>
{/* <CardContent>
<Typography variant="body1"></Typography>
<FormControlLabel
value="end"
control={< Switch checked = {
state.checkedA
}
onChange = {
handleChange
}
name = "checkedA" color = "primary" />}
label="Je souhaite afficher mon profil sur la CV Vidéothèque Talents Tube"
labelPlacement="start"/>
return (
<Grid
container
spacing={4}
direction="row"
justify="space-between"
alignItems="flex-start">
<Grid
item
lg={3}
md={6}
xl={4}
xs={12}>
<Card
{...rest}
className={clsx(classes.root, className)}
>
<CardContent>
<div className={classes.details}>
<div>
<Typography
gutterBottom
variant="h2"
>
Laurent Laniau
</Typography>
<Typography
className={classes.locationText}
color="textSecondary"
variant="body1"
>
{user.city}, {user.country}
</Typography>
<Typography
className={classes.dateText}
color="textSecondary"
variant="body1"
>
{moment().format('HH:mm')}
</Typography>
</div>
<Avatar
className={classes.avatar}
src={user.avatar}
/>
</div>
<div className={classes.progress}>
<Typography variant="body1">Etat du Profil: 70%</Typography>
<LinearProgress
value={70}
variant="determinate"
/>
</div>
</CardContent>
<Divider />
<CardActions>
<Button
className={classes.uploadButton}
color="primary"
variant="text"
>
Télécharger une photo de profil
</Button>
<Button variant="text">Supprimer photo de profil</Button>
</CardActions>
</CardContent> */}
<Stepper nonLinear activeStep={activeStep} orientation="vertical">
{steps.map((label, index) => {
const stepProps = {};
const buttonProps = {};
if (isStepOptional(index)) {
buttonProps.optional = <Typography variant="caption">Optional</Typography>;
}
if (isStepSkipped(index)) {
stepProps.completed = false;
}
return (
<Step key={label} className={classes.actionsContainer} {...stepProps}>
<StepButton
onClick={handleStep(index)}
completed={isStepComplete(index)}
{...buttonProps}
>
{label}
</StepButton>
</Step>
);
})}
</Stepper>
<CardActions>
<Stepper nonLinear activeStep={activeStep} orientation="vertical">
{steps.map((label, index) => {
const stepProps = {};
const buttonProps = {};
if (isStepOptional(index)) {
buttonProps.optional = <Typography variant="caption">Optional</Typography>;
}
if (isStepSkipped(index)) {
stepProps.completed = false;
}
return (
<Step key={label} className={classes.actionsContainer} {...stepProps}>
<StepButton
onClick={handleStep(index)}
completed={isStepComplete(index)}
{...buttonProps}>
{label}
</StepButton>
</Step>
);
})}
</Stepper>
</CardActions>
<Divider/>
<div>
{allStepsCompleted() ? (
<div>
<Typography className={classes.instructions}>
All steps completed - you&apos;re finished
</Typography>
<Button onClick={handleReset}>Reset</Button>
</div>
) : (
<div>
<div>
<Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={handleNext}
className={classes.button}
>
Next
</Button>
{isStepOptional(activeStep) && !completed.has(activeStep) && (
<div>
<div>
<div>
{/* {isStepOptional(activeStep) && !completed.has(activeStep) && (
<Button
variant="contained"
color="primary"
@@ -309,9 +422,9 @@ const AccountProfile = props => {
>
Skip
</Button>
)}
)} */}
{activeStep !== steps.length &&
{/* {activeStep !== steps.length &&
(completed.has(activeStep) ? (
<Typography variant="caption" className={classes.completed}>
Step {activeStep + 1} already completed
@@ -320,29 +433,53 @@ const AccountProfile = props => {
<Button variant="contained" color="primary" onClick={handleComplete}>
{completedSteps() === totalSteps() - 1 ? 'Finish' : 'Complete Step'}
</Button>
))}
</div>
</div>
)}
</div>
))} */}
</div>
</div>
</Card>
</Grid>
<Grid
item
lg={9}
md={6}
xl={8}
xs={12}>
{getStepContent(activeStep)}
</Grid>
</Grid>
);
</div>
<Box m={2}/>
<CardContent>
<BottomNavigation className={classes.stickToBottom}>
<Button
disabled={activeStep === 0}
onClick={handleBack}
className={classes.button}>
Etape précédente
</Button>
<Button
variant="contained"
color="primary"
onClick={handleNext}
className={classes.button}>
Etape suivante
</Button>
{/* <BottomNavigationAction label="Recents" icon={< RestoreIcon />}/>
<BottomNavigationAction label="Favorites" icon={< FavoriteIcon />}/>
<BottomNavigationAction label="Nearby" icon={< LocationOnIcon />}/> */}
</BottomNavigation>
</CardContent>
</Card>
</Grid>
<Grid item lg={9} md={6} xl={8} xs={12}>
{getStepContent(activeStep)}
</Grid>
</Grid>
);
};
AccountProfile.propTypes = {
className: PropTypes.string
className: PropTypes.string
};
function mapStateToProps(state, ownProps) {
return {
user: state.user,
loading: !!state.user.loading,
errors: state.user.errors || ownProps.errors
};
}
export default AccountProfile;
export default withStyles(styles)(connect(mapStateToProps, {updateUserRequest})(AccountProfile))
@@ -0,0 +1,218 @@
import React, {Fragment} from 'react';
import {Hidden} from '@material-ui/core';
import PropTypes from 'prop-types';
import clsx from 'clsx';
// import AccountStep from "../../components/organisms/AccountStep";
import AccountEtape1 from './AccountEtape1';
import AccountEtape2 from './AccountEtape2';
import AccountEtape3 from './AccountEtape3';
import {withStyles} from '@material-ui/core/styles';
// import Switch from '@material-ui/core/Switch';
import PhoneIcon from '@material-ui/icons/Phone';
import MailIcon from '@material-ui/icons/Mail';
import PublicIcon from '@material-ui/icons/Public';
import DuoIcon from '@material-ui/icons/Duo';
import LinkedInIcon from '@material-ui/icons/LinkedIn';
import Facebook from '@material-ui/icons/Facebook';
// import FormGroup from '@material-ui/core/FormGroup'; import FormControlLabel
// from '@material-ui/core/FormControlLabel'; import FormControl from
// '@material-ui/core/FormControl';
import {updateUserRequest, updateUserPictureRequest} from '../../actions/user'
import {makeStyles} from '@material-ui/styles';
import {connect} from 'react-redux'
import getConfig from 'next/config'
import Router from 'next/router'
import BottomNavigation from '@material-ui/core/BottomNavigation';
// import BottomNavigationAction from
// '@material-ui/core/BottomNavigationAction';
import RefreshIcon from '@material-ui/icons/Refresh';
// import FavoriteIcon from '@material-ui/icons/Favorite'; import LocationOnIcon
// from '@material-ui/icons/LocationOn';
import Box from '@material-ui/core/Box';
import {
Card,
CardActions,
CardContent,
Avatar,
Typography,
Divider,
Button,
LinearProgress,
Stepper,
Step,
StepLabel,
StepContent,
StepButton,
Paper,
Grid
} from '@material-ui/core';
const styles = theme => ({
container: {
width: '100%'
},
button: {
textTransform: 'inherit',
marginLeft: theme.spacing(8),
marginRight: theme.spacing(8),
padding: theme.spacing(1.2, 2),
color: theme.palette.secondary.main,
backgroundColor: theme.palette.primary.important
},
stickToBottom: {
width: '100%',
position: 'fixed',
bottom: 0
}
});
const useStyles = makeStyles(theme => ({
root: {},
details: {
display: 'flex'
},
avatar: {
marginLeft: 'auto',
height: 110,
width: 100,
flexShrink: 0,
flexGrow: 0
},
progress: {
marginTop: theme.spacing(2)
},
information: {
display: 'flex',
flexDirection: 'column',
backgroundColor: theme.palette.primary.main,
[
theme
.breakpoints
.up('md')
]: {
display: 'flex',
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'space-around',
backgroundColor: theme.palette.primary.main,
padding: theme.spacing(7, 7)
},
'& a': {
textDecoration: 'none'
}
},
texteBlanc: {
color: "#fff"
},
uploadButton: {
marginRight: theme.spacing(2)
},
button: {
marginTop: theme.spacing(1),
marginRight: theme.spacing(1)
},
actionsContainer: {
marginBottom: theme.spacing(2)
},
resetContainer: {
padding: theme.spacing(3)
}
}));
const AccountProfile = props => {
const {
className,
user,
...rest
} = props;
const logoUrl = user.picture;
const classes = useStyles();
// const user = { name: 'Laurent Laniau', city: 'Questembert', country:
// 'France', timezone: '', avatar: '/images/avatars/avatar_11.png' };
const {publicRuntimeConfig} = getConfig()
return (
<Grid
container
spacing={4}
direction="row"
justify="space-between"
alignItems="flex-start">
<Grid item lg={9} md={6} xl={8} xs={12}>
<Fragment>
<Hidden>&nbsp;</Hidden>
<Hidden>&nbsp;</Hidden>
<Card
variant="outlined"
style={{
width: 420 + "px",
textAlign:"center"
}}>
<CardActions></CardActions>
<CardContent>
<Typography>
Tu n'as pas tout dit dans ta vidéo?<br/>
Pour mettre toutes les chances de ton côté,
<b> dépose ton CV old school (format pdf, word)!</b>
</Typography>
</CardContent>
</Card>
<Hidden>&nbsp;</Hidden>
<Card
variant="outlined"
style={{
width: 420 + "px",
textAlign:"center"
}}>
<CardContent>
<AccountEtape3></AccountEtape3>
</CardContent>
</Card>
<Hidden>&nbsp;</Hidden>
<Hidden>&nbsp;</Hidden>
<Card
variant="outlined"
style={{
width: 420 + "px",
textAlign:"center"
}}>
<CardContent>
<Button
variant="contained"
color="primary"
onClick={() => Router.push(`/Candidatures`)}
className={classes.button}>
Voir mes candidatures
</Button>
</CardContent>
</Card>
</Fragment>
</Grid>
<Grid item lg={3} md={6} xl={4} xs={12}></Grid>
</Grid>
);
};
AccountProfile.propTypes = {
className: PropTypes.string
};
function mapStateToProps(state, ownProps) {
return {
user: state.user,
loading: !!state.user.loading,
errors: state.user.errors || ownProps.errors
};
}
export default withStyles(styles)(connect(mapStateToProps, {updateUserRequest})(AccountProfile))
@@ -20,7 +20,7 @@ const useStyles = makeStyles(() => ({
root: {}
}));
const AccountDetails = props => {
const AccountVideoPres = props => {
const { className, ...rest } = props;
const classes = useStyles();
@@ -35,8 +35,8 @@ const AccountDetails = props => {
);
};
AccountDetails.propTypes = {
AccountVideoPres.propTypes = {
className: PropTypes.string
};
export default AccountDetails;
export default AccountVideoPres;
+1 -1
View File
@@ -151,7 +151,7 @@ class CompanyRowComponent extends React.Component {
const nbOffre = (rowData.count_ads > 0) ? offre : 'Candidature spontanée';
return (
<Link href={`/Entreprise/${rowData.id}-${_.kebabCase(rowData.brand_name)}-${_.kebabCase(rowData.addresses.billing.city)}`} >
<Link href={`/Entreprise/${rowData.id}-${_.kebabCase(rowData.brand_name)}`} >
<Card className={classes.card + ' cypCardCompany'}>
<CardContent className={classes.content}>
<CardMedia
+10 -23
View File
@@ -39,6 +39,7 @@ import { withRouter } from "next/router";
import ActiveLink from "./../atoms/Activelink";
import axios from 'axios';
import setAuthorizationHeader from "../../data/setAuthorizationHeader";
import getConfig from 'next/config'
const styles = theme => ({
root: {
@@ -329,7 +330,7 @@ class Header extends React.Component {
render() {
const { classes } = this.props;
const { publicRuntimeConfig } = getConfig();
return (
@@ -385,7 +386,7 @@ class Header extends React.Component {
</div>
<div>
<div className={classes.blogLinkGroup}>
<a href="https://blog.talentstube.com" className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<a href={publicRuntimeConfig.blogUrl} className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<NotesIcon className={classes.icon} />
<ListItemText classes={{ primary: classes.ListItemText }} primary='Blog'>
</ListItemText>
@@ -393,7 +394,7 @@ class Header extends React.Component {
</a>
</div>
<div className={classes.recruteurLinkGroup}>
<a href="https://www.talentstube.com/faire-offre-emploi-video" className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<a href={publicRuntimeConfig.symphonyUrl } className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<SupervisorAccountIcon className={classes.icon} />
<ListItemText classes={{ primary: classes.ListItemText }} primary='Recruteurs'>
</ListItemText>
@@ -451,7 +452,7 @@ class Header extends React.Component {
<Divider light={true} />
<div className={classes.linkGroup}>
<a href="https://www.talentstube.com/faire-offre-emploi-video" className={classes.lightMenuItem}><ListItem classes={{ primary: classes.ListItemText }} classes={{ root: classes.rootListItemIcon }}>
<a href={publicRuntimeConfig.symphonyUrl + "/faire-offre-emploi-video"} className={classes.lightMenuItem}><ListItem classes={{ primary: classes.ListItemText }} classes={{ root: classes.rootListItemIcon }}>
<WorkOutlineIcon className={classes.icon} />
<ListItemText primary='Connexion recruteurs'>
</ListItemText>
@@ -463,7 +464,7 @@ class Header extends React.Component {
{!!!this.props.loading && !!this.props.fetched &&
<Fragment>
{this.props.user.picture ?
<img alt="talents-tube" src={`http://localhost/uploads/img/${this.props.user.picture}`} className={classes.menuAvatar} />
<img alt="talents-tube" src={publicRuntimeConfig.symphonyUrl +publicRuntimeConfig.postedContentUrl+ this.props.user.picture} className={classes.menuAvatar} />
:
<img alt="talents-tube" src='/static/images/default/account-avatar-default.svg' className={classes.menuAvatar} />
}
@@ -487,27 +488,13 @@ class Header extends React.Component {
</ListItemText>
</ListItem></a>
</Link>
<Link href="" >
<a onClick={this.onRedirectAccount} className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<Link href="/profileStep" >
<a className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<AccountCircleIcon className={classes.icon} />
<ListItemText primary='Mon compte'>
</ListItemText>
</ListItem></a>
</Link>
<Link href="" >
<a onClick={this.onRedirectAccount} className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<PermContactCalendar className={classes.icon} />
<ListItemText primary='Mon profil'>
</ListItemText>
</ListItem></a>
</Link>
<Link href="" >
<a onClick={this.onRedirectAccount} className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
<VideoCam className={classes.icon} />
<ListItemText primary='Mon CV vidéo'>
</ListItemText>
</ListItem></a>
</Link>
<Link href="/Parametres" >
<a className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
@@ -559,7 +546,7 @@ class Header extends React.Component {
</ActiveLink>
<ActiveLink activeClassName={classes.active} href="/Entreprises" >
<a className={classes.desktopMenuItem}>
<ListItemText primary='Nos entreprises !'>
<ListItemText primary='Nos entreprises'>
</ListItemText>
</a>
</ActiveLink>
@@ -618,7 +605,7 @@ class Header extends React.Component {
</section>
<section className={classes.appBarDesktopSection + ' ' + classes.appBarDesktopSection3}>
<a href="https://www.talentstube.com/faire-offre-emploi-video" className={classes.desktopMenuItem}>
<a href={publicRuntimeConfig.symphonyUrl + "/faire-offre-emploi-video"} className={classes.desktopMenuItem}>
<ListItemText primary='Espace recruteurs'>
</ListItemText>
<ArrowForward className={classes.icon} />
+24 -12
View File
@@ -18,6 +18,8 @@ import Link from 'next/link'
import withWidth, { isWidthUp } from '@material-ui/core/withWidth'
import VideoVimeo from '../atoms/VideoVimeo'
import OgHead from '../atoms/OgHead'
import JobPosting from '../atoms/JobPosting'
import Moment from 'react-moment';
const styles = theme => ({
primary: {
@@ -155,7 +157,7 @@ const styles = theme => ({
objectFit: 'contain',
},
title: {
fontSize: '1em',
fontSize: '1.3em',
fontWeight: '500',
[theme.breakpoints.up('md')]: {
paddingTop: theme.spacing(2),
@@ -163,7 +165,7 @@ const styles = theme => ({
},
offreTitle: {
[theme.breakpoints.up('md')]: {
fontSize: '1.2em',
fontSize: '1.3em',
fontWeight: '600',
},
},
@@ -190,7 +192,7 @@ const styles = theme => ({
textAlign: 'center',
},
companyName: {
fontSize: '0.9em',
fontSize: '1.3em',
fontWeight: '300',
[theme.breakpoints.up('md')]: {
fontSize: '1em',
@@ -209,18 +211,18 @@ const styles = theme => ({
},
iconTextIcon: {
color: theme.palette.offerlistitem.icon,
fontSize: '1em',
fontSize: '1.3em',
},
iconTextText: {
fontWeight: 'lighter',
color: theme.palette.offerlistitem.text,
fontSize: '0.7em',
fontSize: '1em',
paddingLeft: theme.spacing(1),
},
iconTextTextDesktop: {
fontWeight: 'lighter',
color: theme.palette.offerlistitem.text,
fontSize: '0.7em',
fontSize: '1em',
paddingLeft: theme.spacing(1),
width: '100%',
},
@@ -297,18 +299,27 @@ function OffreDetail(props) {
const DefaultUrl = "/static/images/default/default_picture_company.svg"
const imgUrl = (offre.company && offre.company.presentation_video && offre.company.presentation_video.thumbnails && offre.company.presentation_video.thumbnails[0] && offre.company.presentation_video.thumbnails[0].sizes[2]) ? offre.company.presentation_video.thumbnails[0].sizes[2].link : DefaultUrl
const imgUrlDesktop = (offre.company && offre.company.presentation_video && offre.company.presentation_video.thumbnails && offre.company.presentation_video.thumbnails[0] && offre.company.presentation_video.thumbnails[0].sizes[4]) ? offre.company.presentation_video.thumbnails[0].sizes[4].link : DefaultUrl
const socialShareUrl = typeof window !== 'undefined' ? window.location : publicRuntimeConfig.frontServerUrl + `/Offre/${offre.id}-${_.kebabCase(offre.title)}-${_.kebabCase(offre.location.gmap_address.name)}`;
const entrepriseLink = (offre.company && `/Entreprise/${offre.company.id}-${_.kebabCase(offre.company.brand_name)}-${_.kebabCase(offre.location.gmap_address.name)}`);
const socialShareUrl = typeof window !== 'undefined' ? window.location : publicRuntimeConfig.frontServerUrl + `/Offre/${offre.id}-${_.kebabCase(offre.title)}`;
const entrepriseLink = (offre.company && `/Entreprise/${offre.company.id}-${_.kebabCase(offre.company.brand_name)}`);
const territoryVideoUrlTab = (offre.territories && offre.territories[0] && offre.territories[0].territory) ? offre.territories[0].territory.presentation_video.cdn_url.split('/') : '';
const territoryVideoId = territoryVideoUrlTab[2]
return (
<Fragment>
<OgHead seo_title={offre.title}
seo_description={offre.description}
seo_url={socialShareUrl}
image={imgUrlDesktop}
></OgHead>
<JobPosting job_title={offre.title}
job_description={offre.description}
job_brand= {offre.company.brand_name && offre.company.brand_name}
job_brand_link= {entrepriseLink}
job_title = {offre.title}
job_location = {offre.location.gmap_address.formatted_address}
job_date = {offre.published_at}
></JobPosting>
{offre.video && (
<Fragment>
<Hidden smDown>
@@ -319,17 +330,17 @@ function OffreDetail(props) {
<div className={classes.logoContainer}>
<Avatar className={classes.logo} classes={{ img: classes.MuiAvatarImg }}
src={logoUrl} alt="companyLogo" />
src={logoUrl} alt={offre.company.brand_name && offre.company.brand_name} />
</div>
</a>
<div className={classes.infoContainer}>
<Typography gutterBottom className={classes.offreTitle}>
{offre.title}
{offre.title} {/*- publié le <Moment format="DD/MM/YYYY" date={offre.published_at} /> */}
</Typography>
<a href={entrepriseLink} >
<Typography gutterBottom variant="subtitle1" className={classes.companyName}>
{offre.company.brand_name && offre.company.brand_name}
{offre.company.brand_name ? offre.company.brand_name : ""}
</Typography>
</a>
{offre.is_promoted && (
@@ -390,6 +401,7 @@ function OffreDetail(props) {
<section className={classes.IconTextContainerDesktop}>
<IconTextInline classes={{ icon: classes.iconTextIcon, text: classes.iconTextText }} variant="assignment" text={offre.extra.contract_type} />
<IconTextInline classes={{ icon: classes.iconTextIcon, text: classes.iconTextText }} variant="location_on" text={offre.location.gmap_address.formatted_address} />
{offre.beneficiaire && (<IconTextInline classes={{ icon: classes.iconTextIcon, text: classes.iconTextText }} variant="business" text={offre.beneficiaire} /> )}
</section>
</Hidden>
<SimpleCollapse>
@@ -437,7 +449,7 @@ function OffreDetail(props) {
<a href={entrepriseLink}>
<div className={classes.discoverContainerImage}>
<img src={isWidthUp('sm', props.width) ? imgUrlDesktop : imgUrl} alt="company" />
<img src={isWidthUp('sm', props.width) ? imgUrlDesktop : imgUrl} alt={offre.company.brand_name && offre.company.brand_name} />
<KeyboardArrowleftIcon className={classes.iconPlay} />
</div>
</a>
+13 -10
View File
@@ -51,14 +51,15 @@ const styles = theme => ({
alignItems: 'flex-start',
padding: theme.spacing(2),
width: '75%',
// minHeight: '200px',
},
jobTitle: {
fontSize: '1em',
fontSize: '1.2em',
fontWeight: 'bold',
color: theme.palette.offerlistitem.title,
},
companyTitle: {
fontSize: '0.8em',
fontSize: '1.2em',
fontWeight: '300',
color: theme.palette.offerlistitem.title,
},
@@ -96,11 +97,11 @@ const styles = theme => ({
},
iconTextIcon: {
color: theme.palette.offerlistitem.icon,
fontSize: '0.8em',
fontSize: '1.2em',
},
iconTextText: {
color: theme.palette.offerlistitem.text,
fontSize: '0.6em',
fontSize: '1em',
paddingLeft: theme.spacing(1),
},
chip: {
@@ -126,7 +127,7 @@ class OffreRowComponent extends React.Component {
const imgUrl = (rowData.video && rowData.video.thumbnails && rowData.video.thumbnails[0] && rowData.video.thumbnails[0].sizes[4]) ? rowData.video.thumbnails[0].sizes[4].link : publicRuntimeConfig.adListDefaultUrl
return (
<Link href={`/Offre/${rowData.id}-${_.kebabCase(rowData.title)}-${_.kebabCase(rowData.location.gmap_address.name)}`} >
<Link href={`/Offre/${rowData.id}-${_.kebabCase(rowData.title)}`} >
<Card className={classes.card + ' cypCardOffre'} >
<CardContent className={classes.content}>
<CardMedia
@@ -144,14 +145,14 @@ class OffreRowComponent extends React.Component {
variant="h6"
component="h3"
className={classes.jobTitle}>
{rowData.title}
{rowData.title}
</Typography>
<Typography
variant="headline"
variant="h6"
component="h4"
component="h3"
className={classes.companyTitle}>
{!!companyR.brand_name?companyR.brand_name:"-"}
{companyR.brand_name ? companyR.brand_name : "Non renseigné"}
</Typography>
<div>
{rowData.is_promoted && (
@@ -160,8 +161,10 @@ class OffreRowComponent extends React.Component {
{!rowData.is_promoted && (
<Fragment>
<IconText classes={{ root: classes.iconText, icon: classes.iconTextIcon, text: classes.iconTextText }} variant="location_on" text={rowData.extra.contract_type} />
<IconText classes={{ root: classes.iconText, icon: classes.iconTextIcon, text: classes.iconTextText }} variant="business_center" text={rowData.location.gmap_address.formatted_address} />
<IconText classes={{ root: classes.iconText, icon: classes.iconTextIcon, text: classes.iconTextText }} variant="business_center" text={rowData.extra.contract_type} />
<IconText classes={{ root: classes.iconText, icon: classes.iconTextIcon, text: classes.iconTextText }} variant="location_on" text={rowData.location.gmap_address.formatted_address} />
{rowData.beneficiaire ? <IconText classes={{ root: classes.iconText, icon: classes.iconTextIcon, text: classes.iconTextText }} variant="business" text={rowData.beneficiaire} /> : ""}
</Fragment>
)}
+10 -2
View File
@@ -130,7 +130,15 @@ class Recorder extends Component {
}
render() {
const { classes } = this.props;
const { classes, titreBouton } = this.props;
let titreButtonLabel= "Envoyer";
if (titreBouton) {
titreButtonLabel = titreBouton;
console.log("titre");
console.log(titreButtonLabel);
}
return (
<div>
<div data-vjs-player>
@@ -140,7 +148,7 @@ class Recorder extends Component {
{ this.state.canSend && <Button type="submit" variant="outlined" color="primary"
className={classes.button}
onClick={this.sendBlob}>
Envoyer
{titreButtonLabel}
</Button>}
</div>
</div>
@@ -8,6 +8,7 @@ import withWidth, { isWidthUp } from '@material-ui/core/withWidth'
import VideoVimeo from '../atoms/VideoVimeo'
const styles = theme => ({
root: {
position: 'relative',
@@ -169,7 +170,7 @@ class ShortVideoHooker extends Component {
return (
<div className={classes.video}>
<VideoVimeo classesOverride={ classes.videoVimeo } videoId={videoList[this.state.i].vimeoId} title={videoList[this.state.i].title}
{/* <VideoVimeo classesOverride={ classes.videoVimeo } videoId={videoId}
options={`?byline=0&title=0&background=1&autoplay=1&mute=1`} />
<section className={classes.text}>
<Typography gutterBottom
@@ -199,7 +200,7 @@ class ShortVideoHooker extends Component {
>
Voir l'offre
</Button>
</Link>
</Link> */}
</div>
)
}
+2 -2
View File
@@ -99,7 +99,7 @@ class Tile extends React.Component {
const logoName = (rowData.company) ? rowData.company.logo : rowData.logo
const logoUrl = publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.cachedMediaUrl + logoName
const router = (rowData.company) ? `/Offre/${rowData.id}-${_.kebabCase(rowData.title)}-${_.kebabCase(rowData.location.gmap_address.name)}` : `/Entreprise/${rowData.id}-${_.kebabCase(rowData.brand_name)}-${_.kebabCase(rowData.addresses.billing.city)}`
const router = (rowData.company) ? `/Offre/${rowData.id}-${_.kebabCase(rowData.title)}` : `/Entreprise/${rowData.id}-${_.kebabCase(rowData.brand_name)}}`
//rowData._embedded.company.brand_name
return (
@@ -113,7 +113,7 @@ class Tile extends React.Component {
/>
<KeyboardArrowleftIcon className={classes.iconPlay} />
<div className={classes.logoContainer}>
<Avatar className={classes.logo} src={logoUrl} alt="company"/>
<Avatar className={classes.logo} src={logoUrl} alt={rowData.title}/>
</div>
{ children }
</CardContent>
+8 -4
View File
@@ -10,11 +10,11 @@ const styles = theme => ({
flexDirection: 'column',
justifyContent: 'space-between',
position: 'absolute',
top: '51%',
top: '44%',
width: '100%',
},
companyTitle: {
fontSize: '0.8em',
fontSize: '1em',
fontWeight: '100',
padding: theme.spacing(0, 2),
marginTop: '4vh',
@@ -24,7 +24,7 @@ const styles = theme => ({
},
},
jobTitle: {
fontSize: '1em',
fontSize: '1.2em',
padding: theme.spacing(0, 2),
minHeight: '8vh',
[theme.breakpoints.up('lg')]: {
@@ -59,7 +59,7 @@ class TileOffre extends React.Component {
component="h4"
align="center"
className={classes.companyTitle}>
{tile.company.brand_name && tile.company.brand_name}
{tile.company.brand_name && tile.company.brand_name}
</Typography>
<Typography
gutterBottom
@@ -82,6 +82,10 @@ class TileOffre extends React.Component {
{tile.extra && tile.extra.contract_type &&
<IconText classes={{ root: classes.rootIconText }} variant="assignment" text={tile.extra.contract_type} />
}
{tile.beneficiaire &&
<IconText classes={{ root: classes.rootIconText }} variant="business" text={tile.beneficiaire} />
}
</section>
</Fragment>
);
+5 -5
View File
@@ -67,17 +67,17 @@ export default {
},
},
ads: {
fetchAd: (id) => {
return axios.get(`api/ads/${id}`).then(res => res.data)
fetchJob: (id) => {
return axios.get(`api/jobs/${id}`).then(res => res.data)
},
//offset is the index position of the first element to start requesting at
// use 0|1 for isPromoted
fetchAds: ({ limit = null, order = "", keyword = "", offset = 1, status = 'PUBLISHED', isPromoted = null, isEmphasis = null }) => {
fetchJobs: ({ limit = null, order = "", keyword = "", offset = 1, status = 'PUBLISHED', isPromoted = false, isEmphasis = false }) => {
isPromoted = isPromoted !== null ? isPromoted * 1 : isPromoted;
isEmphasis = isEmphasis !== null ? isEmphasis * 1 : isEmphasis;
return axios.get('api/ads', { params: { offset, limit, order, keyword, status, isPromoted, isEmphasis } })
return axios.get('api/jobs', { params: { offset, limit, order, keyword, status, isPromoted, isEmphasis } })
.then(res => res.data)
}
@@ -96,7 +96,7 @@ export default {
postAnswer: (payload) => {
return axios.post("/api/answers", { adId: payload.adId, mediaId: payload.mediaId, companyId: payload.companyId }).then(res => res)
},
fetchAdsAnswered: ({ limit = null, order = "", keyword = "", offset = 1 }) => {
fetchJobsAnswered: ({ limit = null, order = "", keyword = "", offset = 1 }) => {
return axios.get(`api/answers`, { params: { offset, limit, order, keyword } }).then(res => res.data)
},
fetchAnswer: (payload) => {
+8 -5
View File
@@ -33,18 +33,21 @@ module.exports = withPlugins(
}
],
importScripts:
['/static/js/Edeclic-service-worker.js',
'/static/js/backgroundSync-sw.js'
['/statique/js/Edeclic-service-worker.js',
'/statique/js/backgroundSync-sw.js'
]
},
}],
[{
publicRuntimeConfig: {
symphonyUrl: 'https://www.talentstube.com',
frontServerUrl: 'https://pp.app.talentstube.com',
adListDefaultUrl: '/static/images/default/default_picture_company.svg',
symphonyUrl: 'https://recruteur.talentstube.com',
blogUrl: 'https://blog.talentstube.com',
frontServerUrl: 'https://www.talentstube.com',
cvThequeUrl: 'https://cvtheque.talentstube.com/',
adListDefaultUrl: '/statique/images/default/default_picture_company.svg',
cachedMediaUrl: '/media/cache/profile_thumb/uploads/img/',
postedContentUrl: '/uploads/img/',
postedContentCv: '/uploads/cv/',
ReCAPTCHA: {
"siteKey": "6LdyqLwUAAAAADKI5uC7d5W9VqVgzd8VEmMU2Yi-"
},
+16912
View File
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -7,8 +7,8 @@
},
"dependencies": {
"@blunck/next-alias": "^1.0.0",
"@material-ui/core": "^4.2.0",
"@material-ui/icons": "^3.0.1",
"@material-ui/core": "^4.9.10",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.56",
"@ostai/next-css": "^1.0.2",
"axios": "^0.18.0",
@@ -34,6 +34,7 @@
"next-optimized-images": "^1.4.1",
"next-redux-saga": "^4.0.2",
"next-redux-wrapper": "^3.0.0-alpha.2",
"next-seo": "^4.7.3",
"next-transpile-modules": "^2.3.1",
"node-mailjet": "^3.3.1",
"nodemailer": "^4.6.8",
@@ -45,11 +46,13 @@
"passport-oauth2": "https://github.com/Huaoe/passport-oauth2.git",
"prop-types": "latest",
"react": "^16.9.0",
"react-ad-block-detect": "^1.0.1",
"react-dom": "^16.9.0",
"react-ga": "^2.5.7",
"react-google-recaptcha": "^2.0.1",
"react-html-parser": "^2.0.2",
"react-jss": "latest",
"react-moment": "^0.9.7",
"react-redux": "^7.0.3",
"react-swipeable-views": "^0.13.3",
"reactstrap": "^6.4.0",
+1
View File
@@ -147,6 +147,7 @@ class Candidature extends React.Component {
<LayoutCandidature>
<Head>
<title key="title">Candidature</title>
<meta name="robots" content="noindex, nofollow"></meta>
</Head>
<Fragment>
<div className={classes.container}>
+44
View File
@@ -0,0 +1,44 @@
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Layout from "../components/templates/Layout";
import AccountProfileCandidature from "../components/organisms/AccountProfileCandidature";
import Head from 'next/head'
// import AdBlockDetect from 'react-ad-block-detect';
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
},
button: {
marginRight: theme.spacing(1),
},
completed: {
display: 'inline-block',
},
instructions: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
},
}));
export default function HorizontalNonLinearStepper() {
const classes = useStyles();
return (
<Layout>
<Head>
<title key="title">Profile Talents Tube</title>
<meta key="robots" name="robots" content="noindex, nofollow"></meta>
</Head>
<div >
<AccountProfileCandidature />
</div>
{/* <AdBlockDetect>
<p>AdBlocks a été détecté. Pour que Talents Tube puisse fonctionner correctement, veuillez désactiver Ad Blocks</p>
</AdBlockDetect> */}
</Layout>
);
}
+23 -3
View File
@@ -8,9 +8,10 @@ import { Typography } from '@material-ui/core';
import LayoutGrid from "../components/templates/LayoutGrid";
import Grid from "../components/templates/Grid"
import AnswerRow from "../components/organisms/AnswerRow"
import { fetchAdsAnsweredRequest } from '../actions/answer'
import { fetchJobsAnsweredRequest } from '../actions/answer'
import { AnswersSelector } from '../reducers/answers'
import Head from 'next/head'
import Link from 'next/link';
const styles = theme => ({
root: {
@@ -24,6 +25,13 @@ const styles = theme => ({
flexDirection: 'column',
padding: theme.spacing(0,3),
},
profil: {
display: 'flex',
flexDirection: 'column',
padding: theme.spacing(3,3),
textAlign: 'center',
backgroundColor: theme.palette.primary.main,
},
title: {
fontFamily: 'Dosis',
fontSize: '1.5em',
@@ -45,16 +53,18 @@ class Candidatures extends React.Component {
<LayoutGrid backLink="/">
<Head>
<title key="title">Candidatures</title>
<meta name="robots" content="noindex, nofollow"></meta>
</Head>
{/* TODO */}
<Typography component="h1" align="center" className={classes.title} gutterBottom>
Mes candidatures
</Typography>
<div className={classes.container}>
<Grid CustomRowComponent={AnswerRow}
dataFetcher={this.props.fetchAdsAnsweredRequest}
dataFetcher={this.props.fetchJobsAnsweredRequest}
data={this.props.answers}
currentPage={this.props.currentPage}
recordCount={this.props.recordCount}
@@ -63,6 +73,16 @@ class Candidatures extends React.Component {
searchable={false}
/>
</div>
</LayoutGrid>
);
}
@@ -83,4 +103,4 @@ function mapStateToProps(state) {
};
}
export default withStyles(styles)(connect(mapStateToProps, {fetchAdsAnsweredRequest })(Candidatures))
export default withStyles(styles)(connect(mapStateToProps, {fetchJobsAnsweredRequest })(Candidatures))
+3 -3
View File
@@ -57,7 +57,7 @@ class Confidentialite extends Component {
</Head>
<div className={classes.container}>
<Typography className={classes.text} gutterBottom align="left">
Nous vous invitons à lire avec attention le présent document. Pour toute question concernant ce document et, dune manière générale, sur la collecte et le traitement de vos informations personnelles par la société talentstube.com, nhésitez pas à nous contacter via le formulaire présent sur la page contact : <a href="https://blog.talentstube.com/contact.html" className={classes.link} target="_blank">https://blog.talentstube.com/contact.html</a>
Nous vous invitons à lire avec attention le présent document. Pour toute question concernant ce document et, dune manière générale, sur la collecte et le traitement de vos informations personnelles par la société talentstube.com, nhésitez pas à nous contacter via le formulaire présent sur la page contact : <a href="https://blog.talentstube.com/contact" className={classes.link} target="_blank">https://blog.talentstube.com/contact</a>
</Typography>
<Typography className={classes.title} component="h2" gutterBottom align="left">
@@ -155,7 +155,7 @@ class Confidentialite extends Component {
`
Vous disposez d'un droit d'accès, de rectification, de suppression, dopposition et de portabilité des données. Nous disposons dun délai de 30 jours à compter de votre demande pour apporter une réponse.
Vous pouvez demander une limitation du traitement des données collectées. Lexercice de ces droits peut exercer ce droit auprès de TALENTS TUBE en adressant une requête soit par voie postale à l'adresse suivante : 3 Rue Louis de Broglie, Village By CA 56000 Vannes, soit par courrier électronique via le formulaire présent sur la page Contact `
}<a href="https://blog.talentstube.com/contact.html" className={classes.link} target="_blank">https://blog.talentstube.com/contact.html</a>{`. Cette requête devra être accompagnée d'une pièce d'identité revêtant la signature de l'Utilisateur et devra préciser l'adresse et le moyen par lesquelles une réponse pourra être envoyée..
}<a href="https://blog.talentstube.com/contact" className={classes.link} target="_blank">https://blog.talentstube.com/contact</a>{`. Cette requête devra être accompagnée d'une pièce d'identité revêtant la signature de l'Utilisateur et devra préciser l'adresse et le moyen par lesquelles une réponse pourra être envoyée..
`
}
</Typography>
@@ -178,7 +178,7 @@ class Confidentialite extends Component {
<a href="http://windows.microsoft.com/fr-fr/internet-explorer/delete-manage-cookies#ie=ie-11" className={classes.link} target="_blank">Internet Explorer</a><br />
`
Pour plus dinformations sur les cookies et la façon de les gérer, nous vous recommandons de consulter le site `} <a href="http://www.youronlinechoices.com/fr/" className={classes.link} target="_blank">www.youronlinechoices.com</a>{`, développé par lorganisme EDAA (European Interactive Digital Advertising Alliance).
Pour plus dinformations sur les cookies et la façon de les gérer, nous vous recommandons de consulter le site ` <a href="http://www.youronlinechoices.com/fr/" className={classes.link} target="_blank">www.youronlinechoices.com</a>{`, développé par lorganisme EDAA (European Interactive Digital Advertising Alliance).
`
}
</Typography>
+1 -1
View File
@@ -31,7 +31,7 @@ class Conseils extends React.Component {
<title>Quelques conseils !</title>
<meta name="description" content="Quelques conseils !" />
<meta key="robots" name="robots" content="noindex, nofollow"></meta>
<link rel="canonical" href="https://app.talentstube.com" />
</Head>
<section className={classes.container}>
<VideoVimeo videoId='356411947' autoplay='1' />
+6 -5
View File
@@ -357,9 +357,9 @@ class Entreprise extends React.Component {
render() {
const { classes, company } = this.props;
const logoUrl = (company.logo ? publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.cachedMediaUrl + company.logo : "https://www.talentstube.com/img/default_picture_svg")
const logoUrl = (company.logo ? publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.cachedMediaUrl + company.logo : publicRuntimeConfig.symphonyUrl + "/img/default_picture_svg")
const DefaultUrl = "https://www.talentstube.com/img/default_picture_svg"
const DefaultUrl = publicRuntimeConfig.symphonyUrl + "/img/default_picture_svg"
const imgUrl = (company.presentation_video && company.presentation_video.thumbnails[0] && company.presentation_video.thumbnails[0].sizes[2]) ? company.presentation_video.thumbnails[0].sizes[2].link : DefaultUrl
const offre = (company.count_ads == 1) ? company.count_ads + ' offre' : company.count_ads + ' offres'
@@ -384,11 +384,12 @@ class Entreprise extends React.Component {
const { loading } = this.state;
const socialShareUrl = typeof window !== 'undefined' ? window.location
: publicRuntimeConfig.frontServerUrl + `/Entreprise/${company.id}`;
const descriptionVille = company.addresses.billing.city ? company.addresses.billing.city : "";
const descriptionCompany = company.brand_name ? company.brand_name : "";
return (
<LayoutCompany backLink="/Entreprises" companyId={company.id}>
<OgHead seo_title={company.brand_name && company.brand_name}
seo_description={company.description}
<OgHead seo_title={company.brand_name && company.brand_name+" recrute des candidats en vidéo | Talents Tube"}
seo_description={"Découvrez l'entreprise "+descriptionCompany +" et ses offres en vidéo - "+descriptionVille+ ". Envoyez votre candidature spontanée en vidéo."}
seo_url={socialShareUrl}
image={imgUrl}
></OgHead>
+5 -4
View File
@@ -69,7 +69,7 @@ class Entreprises extends React.Component {
const { publicRuntimeConfig } = getConfig();
const socialShareUrl = typeof window !== 'undefined' ? window.location
: publicRuntimeConfig.frontServerUrl + `/Entreprises`;
const DefaultUrl = "https://www.talentstube.com/img/default_picture_svg"
const DefaultUrl = publicRuntimeConfig.symphonyUrl + "/img/default_picture_svg"
const imgUrl = DefaultUrl ;
return (
<LayoutGrid backLink="/">
@@ -94,11 +94,12 @@ class Entreprises extends React.Component {
content=""/>
</Head> */}
<OgHead seo_title="Entreprises qui recrutent Talents Tube"
seo_description="Les entreprises en vidéo."
<OgHead seo_title="Vidéos marque employeur d'entreprises qui recrutent | Talents Tube"
seo_description="Vidéos marque employeur d'entreprises qui recrutent, découvrez leurs offres d'emploi en vidéo. Postulez simplement en vidéo à l'offre de votre choix."
seo_url={socialShareUrl}
image={imgUrl}
seo_key="test"
seo_key="Recrutement, video"
></OgHead>
<Typography gutterBottom component="h1"
className={classes.title}>
+32 -3
View File
@@ -8,8 +8,8 @@ import { withStyles } from '@material-ui/core/styles';
import Layout from "../components/templates/Layout"
import Footer from '../components/organisms/Footer';
import Link from 'next/link';
import Head from 'next/head'
import VideoVimeo from './../components/atoms/VideoVimeo'
import Head from 'next/head';
import VideoVimeo from './../components/atoms/VideoVimeo';
const styles = theme => ({
root: {
@@ -102,6 +102,7 @@ function Help(props) {
<Layout>
<Head>
<title key="title">Recrutement vidéo : fonctionnement Talents Tube</title>
<meta key="robots" name="robots" content="noindex, nofollow"></meta>
</Head>
<section className={classes.root}>
<Typography gutterBottom component="h1"
@@ -128,7 +129,35 @@ function Help(props) {
gutterBottom>
Suivez nos conseils pour postuler simplement aux offres demploi. La vidéo de motivation vous permet de dévoiler votre personnalité, vos compétences, votre parcours, vos valeurs, vos soft-skills, votre ambition,…. et bien plus encore.
</Typography>
<Typography gutterBottom component="h2"
className={classes.subTitle}>
Comment réussir votre CV vidéo
</Typography>
<Typography
className={classes.text}
gutterBottom>
Suivez ce <a href="https://blog.talentstube.com/faire-cv-video" target="_blank" >lien</a>
</Typography>
<Typography gutterBottom component="h2"
className={classes.subTitle}>
Accompagnement à la création d'un CV vidéo professionnel
</Typography>
<Typography
className={classes.text}
gutterBottom>
Suivez ce <a href="https://blog.talentstube.com/accompagnement-cv-video" target="_blank" >lien</a>
</Typography>
<Typography gutterBottom component="h2"
className={classes.subTitle}>
Pourquoi faire un CV vidéo ?
</Typography>
<Typography
className={classes.text}
gutterBottom>
Suivez ce <a href="https://blog.talentstube.com/faire-cv-video" target="_blank" >lien</a>
</Typography>
</section>
+3 -3
View File
@@ -6,7 +6,7 @@ import getConfig from 'next/config'
import { withStyles } from '@material-ui/core/styles';
import LayoutOffre from "../components/templates/LayoutOffre";
import OffreDetail from "../components/organisms/OffreDetail";
import { fetchAdRequest, clearAd, countAdViewRequest } from '../actions/ads'
import { fetchJobRequest, clearAd, countAdViewRequest } from '../actions/ads'
import _ from "lodash/string";
const styles = theme => ({
@@ -42,7 +42,7 @@ class Offre extends React.Component {
Offre.getInitialProps = function (context) {
const { id } = context.query
context.store.dispatch(fetchAdRequest(id))
context.store.dispatch(fetchJobRequest(id))
return id;
}
@@ -57,4 +57,4 @@ Offre.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(connect(mapStateToProps, { fetchAdRequest, clearAd, countAdViewRequest})(Offre));
export default withStyles(styles)(connect(mapStateToProps, { fetchJobRequest, clearAd, countAdViewRequest})(Offre));
+17 -9
View File
@@ -9,10 +9,12 @@ import LayoutGrid from "../components/templates/LayoutGrid";
import Grid from "../components/templates/Grid"
import OffreRow from "../components/organisms/OffreRow"
import Typography from '@material-ui/core/Typography';
import { fetchAdsRequest, togglePromotedFilterRequest} from '../actions/ads'
import { fetchJobsRequest, togglePromotedFilterRequest} from '../actions/ads'
import { AdsSelector } from '../reducers/ads'
import { TextField } from '@material-ui/core';
import Head from 'next/head'
import OgHead from '../components/atoms/OgHead';
import getConfig from 'next/config'
const styles = theme => ({
@@ -84,14 +86,20 @@ class Offres extends React.Component {
const { classes } = this.props;
const countColor = this.props.promotedFilter ? classes.provided : classes.primary;
const btnClass = this.props.promotedFilter ? classes.buttonDefault : classes.buttonProvided;
const { publicRuntimeConfig } = getConfig();
const socialShareUrl = typeof window !== 'undefined' ? window.location
: publicRuntimeConfig.frontServerUrl + `/Offres`;
const DefaultUrl = publicRuntimeConfig.symphonyUrl + "/img/default_picture_svg"
const imgUrl = DefaultUrl ;
return (
<LayoutGrid backLink="/">
<Head>
<title key="title">Offres demploi en vidéo Talents Tube</title>
<meta name="description" content="Les offres d'emploi en vidéo." />
</Head>
<OgHead seo_title="Vidéos d'offres d'emploi, CDI, CDD, alternance et Stage | Talents Tube"
seo_description="Vidéos d'offres d'emploi, CDI, CDD, alternance, stage et apprentissage d'entreprises qui recrutent sur Talents Tube."
seo_url={socialShareUrl}
image={imgUrl}
seo_key="Recrutement, video"
></OgHead>
<Typography gutterBottom component="h1"
className={classes.title}>
@@ -99,7 +107,7 @@ class Offres extends React.Component {
</Typography>
<div className={classes.container}>
<Grid CustomRowComponent={OffreRow}
dataFetcher={this.props.fetchAdsRequest}
dataFetcher={this.props.fetchJobsRequest}
data={this.props.ads}
currentPage={this.props.currentPage}
recordCount={this.props.recordCount}
@@ -140,4 +148,4 @@ function mapStateToProps(state) {
};
}
export default withStyles(styles)(connect(mapStateToProps, { fetchAdsRequest, togglePromotedFilterRequest })(Offres))
export default withStyles(styles)(connect(mapStateToProps, { fetchJobsRequest, togglePromotedFilterRequest })(Offres))
+4 -4
View File
@@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
import { fetchAdRequest } from '../actions/ads'
import { fetchJobRequest } from '../actions/ads'
import { fetchCompanyRequest } from '../actions/companies'
import { fetchCurrentUserRequest } from '../actions/user'
import { uploadMedia } from '../actions/media'
@@ -282,7 +282,7 @@ class Reponse extends React.Component {
<title>Candidature vidéo</title>
<meta name="description" content="Talent's tube, postulez en video." />
<meta key="robots" name="robots" content="noindex, nofollow"></meta>
<link rel="canonical" href="https://app.talentstube.com" />
</Head>
{/* https://codesandbox.io/s/ii0us */}
{ loading ?
@@ -429,7 +429,7 @@ Reponse.getInitialProps = function (context) {
context.store.dispatch(fetchCurrentUserRequest())
if(!!adId)
context.store.dispatch(fetchAdRequest(adId))
context.store.dispatch(fetchJobRequest(adId))
else if(!!companyId)
context.store.dispatch(fetchCompanyRequest(companyId))
@@ -452,4 +452,4 @@ Reponse.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(connect(mapStateToProps, { fetchCurrentUserRequest, fetchAdRequest, fetchCompanyRequest, uploadMedia })(Reponse));
export default withStyles(styles)(connect(mapStateToProps, { fetchCurrentUserRequest, fetchJobRequest, fetchCompanyRequest, uploadMedia })(Reponse));
+1
View File
@@ -107,6 +107,7 @@ class ResetPassword extends Component {
<Layout>
<Head>
<title key="title">Reset password</title>
<meta key="robots" name="robots" content="noindex, nofollow"></meta>
</Head>
<div className={classes.container}>
<Typography
+27 -32
View File
@@ -8,18 +8,19 @@ import { Typography } from '@material-ui/core';
import Button from '@material-ui/core/Button';
import Router from 'next/router'
import { connect } from 'react-redux'
import { fetchEmphasisAdsRequest } from "../actions/ads";
import { fetchEmphasisJobsRequest } from "../actions/ads";
import { fetchEmphasisCompaniesRequest } from "../actions/companies";
import Loader from './../components/templates/loader'
import { isEmpty } from "lodash"
import NoResult from './../components/atoms/NoResult';
import Head from 'next/head'
import dynamic from 'next/dynamic'
import OgHead from '../components/atoms/OgHead';
import getConfig from 'next/config'
const VideoSlider = dynamic(() => import('../components/organisms/ShortVideoHooker'), {
ssr: false
});
// const VideoSlider = dynamic(() => import('../components/organisms/ShortVideoHooker'), {
// ssr: false
// });
const HorizontalScroller = dynamic(() => import('../components/organisms/HorizontalScroller'), {
ssr: false
});
@@ -35,7 +36,7 @@ const TileOffre = dynamic(() => import('../components/organisms/TileOffre'), {
const Footer = dynamic(() => import('../components/organisms/Footer'), {
ssr: false
});
const { publicRuntimeConfig } = getConfig();
const styles = theme => ({
root: {
position: 'relative',
@@ -139,45 +140,39 @@ const styles = theme => ({
class Index extends React.Component {
state = {
emphasisAds: [],
emphasisJobs: [],
emphasisCompanies: []
}
componentDidMount() {
localStorage.setItem("loginRedirect", "/")
this.props.fetchEmphasisAdsRequest({ isEmphasis: 1 })
this.props.fetchEmphasisJobsRequest({ isEmphasis: 1 })
this.props.fetchEmphasisCompaniesRequest({ isEmphasis: 1 })
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
emphasisAds: nextProps.emphasisAds,
emphasisJobs: nextProps.emphasisJobs,
emphasisCompanies: nextProps.emphasisCompanies
})
}
render() {
const { classes } = this.props;
const socialShareUrl = typeof window !== 'undefined' ? window.location
: publicRuntimeConfig.frontServerUrl ;
const DefaultUrl = publicRuntimeConfig.symphonyUrl + "/img/default_picture_svg"
const imgUrl = DefaultUrl
return (
<Layout>
<Head>
<title key="title"> Talents tube - La recherche d'emploi en vidéo</title>
<meta name="description" content="Talents Tube est la première plateforme réservée aux offres d'emploi et aux CV vidéos. Découvrez un nouvelle méthode de recrutement ! Installez notre application." />
<OgHead seo_title="Talents Tube : Plateforme de recrutement 100 % vidéo"
seo_description="Talents Tube est la première plateforme réservée aux offres d'emploi vidéo, vidéo marque employeur et aux CV vidéos. Découvrez une nouvelle méthode de recrutement."
seo_url={socialShareUrl}
image={imgUrl}
seo_key="Recrutement, video"
<meta name="twitter:card" content="summary"/>
<meta name="twitter:site" content="@TalentsTube"/>
<meta name="twitter:title" content="Talents Tube : Le 1er job board 100% vidéo"/>
<meta name="twitter:description" content="Talents Tube est la première plateforme réservée aux offres d'emploi et aux CV vidéos. Découvrez un nouvelle méthode de recrutement !"/>
<meta name="twitter:image" content=""/>
{/* Open Graph data pour le partage sur les réseaux sociaux (ex Facebook..) */}
<meta property="og:title" content="Talents Tube : Le 1er job board 100% vidéo" />
<meta property="og:type" content="article" />
<meta property="og:image" content="" />
<meta property="og:description" content="Talents Tube est la première plateforme réservée aux offres d'emploi et aux CV vidéos. Découvrez un nouvelle méthode de recrutement !" />
<meta property="og:site_name" content="Talents Tube" />
<meta property="og:url" content="" />
</Head>
></OgHead>
<div className={classes.container}>
<VideoSlider />
{/* <VideoSlider /> */}
<section className={classes.scrollerSection + ' ' + classes.scrollerSectionOffer}>
<div
@@ -194,11 +189,11 @@ class Index extends React.Component {
</Typography>
</div>
{this.state.emphasisAds.loading ?
{this.state.emphasisJobs.loading ?
(<Loader />)
: !isEmpty(this.state.emphasisAds.data) ? (
: !isEmpty(this.state.emphasisJobs.data) ? (
<HorizontalScroller
data={this.state.emphasisAds.data}
data={this.state.emphasisJobs.data}
className={classes.HorizontalScroller}
type="offre"
>
@@ -268,7 +263,7 @@ class Index extends React.Component {
function mapStateToProps(state) {
return {
emphasisAds: state.emphasisAds,
emphasisJobs: state.emphasisJobs,
emphasisCompanies: state.emphasisCompanies
};
}
@@ -277,4 +272,4 @@ Index.propTypes = {
classes: PropTypes.object.isRequired,
};
export default (connect(mapStateToProps, { fetchEmphasisAdsRequest, fetchEmphasisCompaniesRequest })(withStyles(styles)(Index)));
export default (connect(mapStateToProps, { fetchEmphasisJobsRequest, fetchEmphasisCompaniesRequest })(withStyles(styles)(Index)));
+1 -2
View File
@@ -3,7 +3,6 @@ import { makeStyles } from '@material-ui/styles';
import { Grid } from '@material-ui/core';
import Layout from "../components/templates/Layout";
import AccountProfile from "../components/organisms/AccountProfile";
import AccountDetails from "../components/organisms/AccountDetails";
import AccountVideoPres from "../components/organisms/AccountVideoPres";
const useStyles = makeStyles(theme => ({
@@ -43,7 +42,7 @@ const Account = () => {
xl={8}
xs={12}
>
<AccountDetails />
</Grid>
</Grid>
+68 -27
View File
@@ -1,38 +1,79 @@
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import {makeStyles} from '@material-ui/core/styles';
import Layout from "../components/templates/Layout";
import AccountProfile from "../components/organisms/AccountProfile";
import AccountDetails from "../components/organisms/AccountDetails";
import AccountVideoPres from "../components/organisms/AccountVideoPres";
import AccountStep from "../components/organisms/AccountStep";
import { Grid } from '@material-ui/core';
import { Typography } from '@material-ui/core';
import Head from 'next/head'
import AdBlockDetect from 'react-ad-block-detect';
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
},
button: {
marginRight: theme.spacing(1),
},
completed: {
display: 'inline-block',
},
instructions: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
root: {
width: '100%'
},
button: {
marginRight: theme.spacing(1)
},
completed: {
display: 'inline-block'
},
instructions: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1)
},
information: {
display: 'flex',
flexDirection: 'column',
backgroundColor: theme.palette.primary.main,
color: "#fff",
fontWeight: '700',
fontSize: '1.15em',
fontFamily: 'Roboto',
[
theme
.breakpoints
.up('md')
]: {
display: 'flex',
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'space-around',
backgroundColor: theme.palette.primary.main,
padding: theme.spacing(2, 2)
},
'& a': {
textDecoration: 'none'
},
texteBlanc: {
color: "#fff",
},
},
}));
export default function HorizontalNonLinearStepper() {
const classes = useStyles();
const classes = useStyles();
return (
<Layout>
<div className={classes.root}>
<AccountProfile />
</div>
</Layout>
);
return (
<Layout>
<Head>
<title key="title">Profile Talents Tube</title>
<meta key="robots" name="robots" content="noindex, nofollow"></meta>
</Head>
<div >
<AdBlockDetect >
<div className={classes.information}>
<p>AdBlocks a été détecté. Pour que Talents Tube puisse fonctionner
correctement, veuillez désactiver Ad Blocks</p>
</div>
</AdBlockDetect>
</div>
<div >
<AccountProfile/>
</div>
</Layout>
);
}
+73
View File
@@ -0,0 +1,73 @@
{
"name": "Talents Tube",
"short_name": "Talents Tube",
"lang": "fr",
"orientation":"portrait",
"icons": [
{
"src": "/static/images/icons/talentstubeIco16x16.png",
"sizes": "16x16",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco32x32.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco57x57.png",
"sizes": "57x57",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco114x114.png",
"sizes": "114x114",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco180x180.png",
"sizes": "180x180",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/static/images/icons/talentstubeIco512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "/",
"display": "standalone",
"background_color": "#1689FB",
"theme_color": "#1689FB",
"gcm_sender_id":"103953800507"
}
View File
@@ -0,0 +1,35 @@
<svg xmlns="http://www.w3.org/2000/svg" width="73.762" height="69.644" viewBox="0 0 73.762 69.644">
<g id="Import" transform="translate(0 13.219)">
<g id="Groupe_7895" data-name="Groupe 7895" transform="translate(53.502 2.911) rotate(131)">
<rect id="Rectangle_316" data-name="Rectangle 316" width="37.465" height="23.87" transform="translate(0.976 1.838)"/>
<rect id="Rectangle_321" data-name="Rectangle 321" width="4.656" height="4.656" transform="translate(2.783 18.724)" fill="#fff"/>
<rect id="Rectangle_398" data-name="Rectangle 398" width="1.656" height="12.656" transform="translate(2.781 4.725)" fill="#fff"/>
<rect id="Rectangle_399" data-name="Rectangle 399" width="1.656" height="8.656" transform="translate(5.781 8.725)" fill="#fff"/>
<rect id="Rectangle_400" data-name="Rectangle 400" width="1.656" height="18.656" transform="translate(10.781 4.725)" fill="#fff"/>
<path id="Tracé_396" data-name="Tracé 396" d="M0-3H1.656V8.656H0Z" transform="translate(13.781 14.724)" fill="#fff"/>
<rect id="Rectangle_401" data-name="Rectangle 401" width="1.656" height="18.656" transform="translate(17.781 4.725)" fill="#fff"/>
<path id="Tracé_397" data-name="Tracé 397" d="M0-3H1.656V15.656H0Z" transform="translate(20.781 7.725)" fill="#fff"/>
<path id="Tracé_398" data-name="Tracé 398" d="M0-3H1.656V10.788H0Z" transform="translate(23.781 12.593)" fill="#fff"/>
<rect id="Rectangle_402" data-name="Rectangle 402" width="1.656" height="18.656" transform="translate(27.781 4.725)" fill="#fff"/>
<path id="Tracé_399" data-name="Tracé 399" d="M0,9H1.656v6.656H0Z" transform="translate(33.781 7.725)" fill="#fff"/>
<path id="Tracé_400" data-name="Tracé 400" d="M0-3H1.656V10.788H0Z" transform="translate(30.781 12.593)" fill="#fff"/>
</g>
<path id="Tracé_279" data-name="Tracé 279" d="M217.367,282.859l-6.93,6.93-48.507-6.93L155,275.93l6.93-6.93h55.437Z" transform="translate(-155 -233.363)" fill="#155ee4"/>
<path id="Tracé_280" data-name="Tracé 280" d="M173,269v6.93h48.507l6.93-6.93Z" transform="translate(-166.07 -233.363)" opacity="0.25"/>
<g id="Groupe_7897" data-name="Groupe 7897" transform="translate(28.442 19.099)">
<g id="Groupe_7896" data-name="Groupe 7896" transform="translate(3.551)">
<path id="Tracé_281" data-name="Tracé 281" d="M239.065,257.45a.488.488,0,0,1-.231-.039.939.939,0,0,1-.693-1.155,50.925,50.925,0,0,0,1.463-12.088,51.857,51.857,0,0,0-.346-6.006.969.969,0,1,1,1.925-.231,55.915,55.915,0,0,1,.346,6.237,52.807,52.807,0,0,1-1.5,12.55A.952.952,0,0,1,239.065,257.45Z" transform="translate(-238.104 -232.827)" fill="#25d89a"/>
<path id="Tracé_282" data-name="Tracé 282" d="M240.5,229.337a1.021,1.021,0,0,1-.962-.808c-.077-.462-.154-.885-.27-1.347a.944.944,0,0,1,1.848-.385c.077.462.192.924.269,1.386a.969.969,0,0,1-.77,1.116A.141.141,0,0,1,240.5,229.337Z" transform="translate(-238.804 -226.042)" fill="#25d89a"/>
</g>
<path id="Tracé_283" data-name="Tracé 283" d="M233.084,279.525l-4.042-6.237a.972.972,0,1,1,1.617-1.078l3,4.62,4.62-3a.972.972,0,0,1,1.078,1.617Z" transform="translate(-228.879 -254.171)" fill="#25d89a"/>
</g>
<path id="Tracé_284" data-name="Tracé 284" d="M316.393,300.545h-9.008a.385.385,0,1,1,0-.77h8.7l5.505-5.659a.381.381,0,1,1,.539.539Z" transform="translate(-248.483 -248.739)" fill="#25d89a"/>
<path id="Tracé_285" data-name="Tracé 285" d="M317.93,299.2h-6.545a.385.385,0,0,1,0-.77h6.237l4.2-4.312a.381.381,0,1,1,.539.539Z" transform="translate(-250.943 -248.739)" fill="#fa6362"/>
<g id="Groupe_7899" data-name="Groupe 7899" transform="translate(30.144 40.641)">
<g id="Groupe_7898" data-name="Groupe 7898" transform="translate(0 0)">
<path id="Tracé_286" data-name="Tracé 286" d="M233.3,282l2.5,3.811,5.89-3.811h-3.542l-.654.423a1.636,1.636,0,0,0,.077-.423H233.3Z" transform="translate(-233.3 -282)" fill="#21b77e"/>
</g>
</g>
<rect id="Rectangle_325" data-name="Rectangle 325" width="55.437" height="13.859" transform="translate(0 42.566)" fill="#1689fb"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

@@ -0,0 +1,12 @@
<svg id="Groupe_8099" data-name="Groupe 8099" xmlns="http://www.w3.org/2000/svg" width="65.497" height="40.618" viewBox="0 0 65.497 40.618">
<path id="Tracé_401" data-name="Tracé 401" d="M0,0H62.62V35.2H0Z" transform="translate(0 5.559) rotate(-5.093)"/>
<rect id="Rectangle_317" data-name="Rectangle 317" width="3.917" height="3.917" transform="translate(2.066 9.081) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_318" data-name="Rectangle 318" width="3.917" height="3.917" transform="translate(2.751 16.917) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_319" data-name="Rectangle 319" width="3.917" height="3.917" transform="translate(3.431 24.696) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_320" data-name="Rectangle 320" width="3.917" height="3.917" transform="translate(4.167 32.47) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_321" data-name="Rectangle 321" width="3.917" height="3.917" transform="translate(57.023 4.199) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_322" data-name="Rectangle 322" width="3.917" height="3.917" transform="translate(57.703 11.978) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_323" data-name="Rectangle 323" width="3.917" height="3.917" transform="translate(58.444 19.809) rotate(-5.093)" fill="#fff"/>
<rect id="Rectangle_324" data-name="Rectangle 324" width="3.917" height="3.917" transform="translate(59.124 27.588) rotate(-5.093)" fill="#fff"/>
<text id="_" data-name="?" transform="matrix(0.996, -0.087, 0.087, 0.996, 26.298, 31.189)" fill="#fff" font-size="29" font-family="Roboto-Bold, Roboto" font-weight="700"><tspan x="0" y="0">?</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+6 -6
View File
@@ -1,26 +1,26 @@
import { createSelector } from "reselect";
import {
FETCH_AD_REQUEST,
FETCH_AD_SUCCESS,
FETCH_AD_FAILURE,
FETCH_JOB_REQUEST,
FETCH_JOB_SUCCESS,
FETCH_JOB_FAILURE,
CLEAR_AD
} from "../types";
export function ad(state = {
}, action = {}) {
switch (action.type) {
case FETCH_AD_REQUEST:
case FETCH_JOB_REQUEST:
return {
...state,
loading:true
};
case FETCH_AD_SUCCESS:
case FETCH_JOB_SUCCESS:
return {
...state,
...action.res,
loading: false
};
case FETCH_AD_FAILURE:
case FETCH_JOB_FAILURE:
return {
...state,
errors: action.error,
+6 -6
View File
@@ -1,8 +1,8 @@
import { createSelector } from "reselect";
import {
FETCH_ADS_SUCCESS,
FETCH_ADS_FAILURE,
FETCH_ADS_REQUEST,
FETCH_JOBS_SUCCESS,
FETCH_JOBS_FAILURE,
FETCH_JOBS_REQUEST,
TOGGLE_PROMOTED_FILTER_ADS,
} from "../types";
@@ -10,18 +10,18 @@ export function ads(state = {
}, action = {}) {
switch (action.type) {
case FETCH_ADS_REQUEST:
case FETCH_JOBS_REQUEST:
return {
...state,
loading:true
};
case FETCH_ADS_SUCCESS:
case FETCH_JOBS_SUCCESS:
return {
...state,
...action.res,
loading:false
};
case FETCH_ADS_FAILURE:
case FETCH_JOBS_FAILURE:
return {
...state,
errors: action.error,
+6 -6
View File
@@ -1,25 +1,25 @@
import { createSelector } from "reselect";
import {
FETCH_ADS_ANSWER_REQUEST,
FETCH_ADS_ANSWER_SUCCESS,
FETCH_ADS_ANSWER_FAILURE,
FETCH_JOBS_ANSWER_REQUEST,
FETCH_JOBS_ANSWER_SUCCESS,
FETCH_JOBS_ANSWER_FAILURE,
} from "../types";
export function answers(state = {
}, action = {}) {
switch (action.type) {
case FETCH_ADS_ANSWER_REQUEST:
case FETCH_JOBS_ANSWER_REQUEST:
return {
...state,
loading: true
};
case FETCH_ADS_ANSWER_SUCCESS:
case FETCH_JOBS_ANSWER_SUCCESS:
return {
...state,
...action.res,
loading: false
};
case FETCH_ADS_ANSWER_FAILURE:
case FETCH_JOBS_ANSWER_FAILURE:
return {
...state,
errors: action.error,
@@ -1,27 +1,27 @@
import { createSelector } from "reselect";
import {
FETCH_EMPHASIS_ADS_SUCCESS,
FETCH_EMPHASIS_ADS_FAILURE,
FETCH_EMPHASIS_ADS_REQUEST,
FETCH_EMPHASIS_JOBS_SUCCESS,
FETCH_EMPHASIS_JOBS_FAILURE,
FETCH_EMPHASIS_JOBS_REQUEST,
} from "../types";
export function emphasisAds(state = {
export function emphasisJobs(state = {
}, action = {}) {
switch (action.type) {
case FETCH_EMPHASIS_ADS_REQUEST:
case FETCH_EMPHASIS_JOBS_REQUEST:
return {
...state,
loading:true
};
case FETCH_EMPHASIS_ADS_SUCCESS:
case FETCH_EMPHASIS_JOBS_SUCCESS:
return {
...action.res,
loading:false
};
case FETCH_EMPHASIS_ADS_FAILURE:
case FETCH_EMPHASIS_JOBS_FAILURE:
return {
...state,
errors: action.error,
@@ -34,8 +34,8 @@ export function emphasisAds(state = {
}
export const emphasisAdsHashSelector = state => state.emphasisAds.data;
export const emphasisJobsHashSelector = state => state.emphasisJobs.data;
export const emphasisAdsSelector = createSelector(emphasisAdsHashSelector, hash =>
export const emphasisJobsSelector = createSelector(emphasisJobsHashSelector, hash =>
Object.values(hash)
);
+4 -4
View File
@@ -3,7 +3,7 @@ import { combineReducers } from "redux";
import { user } from "./reducers/user";
import { ad } from "./reducers/ad";
import { ads } from "./reducers/ads";
import { emphasisAds } from "./reducers/emphasisAds";
import { emphasisJobs } from "./reducers/emphasisJobs";
import { company } from "./reducers/company";
import { companies } from "./reducers/companies";
import { emphasisCompanies } from "./reducers/emphasisCompanies";
@@ -24,8 +24,8 @@ export const exampleInitialState = {
ad: {
},
emphasisAds: {
emphasisJobs: {
},
companies: {
data: {}
@@ -50,7 +50,7 @@ export const exampleInitialState = {
export default combineReducers({
user,
ads,
emphasisAds,
emphasisJobs,
ad,
companies,
company,
+10 -10
View File
@@ -9,18 +9,18 @@ import {
requestRegisterSaga, requestLogoutSaga, requestNewsletterSubscribitionSaga,
requestUpdateUserRequestSaga, requestUserSubscriptionToPushSaga
} from './sagas/userSaga.js'
import { requestAdsSaga, requestAdSaga, requestEmphasisAdsSaga, countAdViewRequestSaga } from './sagas/offreSaga.js'
import { requestAdsSaga, requestAdSaga, requestEmphasisJobsSaga, countAdViewRequestSaga } from './sagas/offreSaga.js'
import { requestCompaniesSaga, requestCompanySaga, requestEmphasisCompaniesSaga, countCompanyViewRequestSaga } from './sagas/companySaga.js'
import { postAnswerMessageSaga, postAdAnswerRequestSaga, uploadMediaSaga, uploadMediaPresSaga, uploadMediaCvSaga, fetchAdsAnsweredRequestSaga, fetchAnswerRequestSaga, fetchAnswerMessagesRequestSaga } from './sagas/answerSaga.js'
import { postAnswerMessageSaga, postAdAnswerRequestSaga, uploadMediaSaga, uploadMediaPresSaga, uploadMediaCvSaga, fetchJobsAnsweredRequestSaga, fetchAnswerRequestSaga, fetchAnswerMessagesRequestSaga } from './sagas/answerSaga.js'
import {
REQUEST_LOGIN,
REQUEST_LOGOUT,
REQUEST_REGISTER,
FETCH_CURRENT_USER_REQUEST,
FETCH_ADS_REQUEST,
FETCH_AD_REQUEST,
FETCH_EMPHASIS_ADS_REQUEST,
FETCH_JOBS_REQUEST,
FETCH_JOB_REQUEST,
FETCH_EMPHASIS_JOBS_REQUEST,
FETCH_EMPHASIS_COMPANIES_REQUEST,
FETCH_COMPANIES_REQUEST,
FETCH_COMPANY_REQUEST,
@@ -32,7 +32,7 @@ import {
UPLOAD_MEDIA_PRES_REQUEST,
UPLOAD_MEDIA_CV_REQUEST,
UPDATE_USER_REQUEST,
FETCH_ADS_ANSWER_REQUEST,
FETCH_JOBS_ANSWER_REQUEST,
FETCH_ANSWER_REQUEST,
VERIFY_USER_REQUEST,
RESET_PASSWORD_REQUEST,
@@ -57,9 +57,9 @@ function* rootSaga() {
takeLatest(FETCH_CURRENT_USER_REQUEST, requestUserSaga),
takeLatest(UPDATE_USER_REQUEST, requestUpdateUserSaga),
takeLatest(SUBSCRIBE_NEWSLETTER_REQUEST, requestNewsletterSubscribitionSaga),
takeLatest(FETCH_ADS_REQUEST, requestAdsSaga),
takeLatest(FETCH_AD_REQUEST, requestAdSaga),
takeLatest(FETCH_EMPHASIS_ADS_REQUEST, requestEmphasisAdsSaga),
takeLatest(FETCH_JOBS_REQUEST, requestAdsSaga),
takeLatest(FETCH_JOB_REQUEST, requestAdSaga),
takeLatest(FETCH_EMPHASIS_JOBS_REQUEST, requestEmphasisJobsSaga),
takeLatest(FETCH_EMPHASIS_COMPANIES_REQUEST, requestEmphasisCompaniesSaga),
takeLatest(FETCH_COMPANIES_REQUEST, requestCompaniesSaga),
takeLatest(FETCH_COMPANY_REQUEST, requestCompanySaga),
@@ -69,7 +69,7 @@ function* rootSaga() {
takeLatest(UPLOAD_MEDIA_REQUEST, uploadMediaSaga),
takeLatest(UPLOAD_MEDIA_PRES_REQUEST, uploadMediaPresSaga),
takeLatest(UPLOAD_MEDIA_CV_REQUEST, uploadMediaCvSaga),
takeLatest(FETCH_ADS_ANSWER_REQUEST, fetchAdsAnsweredRequestSaga),
takeLatest(FETCH_JOBS_ANSWER_REQUEST, fetchJobsAnsweredRequestSaga),
takeLatest(FETCH_ANSWER_REQUEST, fetchAnswerRequestSaga),
takeLatest(VERIFY_USER_REQUEST, verifyUserSaga),
takeLatest(RESET_PASSWORD_REQUEST, requestResetPasswordSaga),
+8 -8
View File
@@ -8,8 +8,8 @@ import _ from "lodash";
import {
postAdAnswerRequestSuccess,
postAdAnswerRequestFailure,
fetchAdsAnsweredSuccess,
fetchAdsAnsweredFailure,
fetchJobsAnsweredSuccess,
fetchJobsAnsweredFailure,
fetchAnswerSuccess,
fetchAnswerFailure,
postMessageAnswerSuccess,
@@ -38,7 +38,7 @@ export function* postAdAnswerRequestSaga(action) {
if (!!res.data) {
yield put(postAdAnswerRequestSuccess(res));
Router.push("/Candidatures");
Router.push("/CandidatureValidation");
}
else
yield put(postAdAnswerRequestFailure(res));
@@ -114,14 +114,14 @@ export function* uploadMediaCvSaga(action) {
}
}
export function* fetchAdsAnsweredRequestSaga(action) {
export function* fetchJobsAnsweredRequestSaga(action) {
try {
const res = yield call(api.answers.fetchAdsAnswered, action.payload);
const res = yield call(api.answers.fetchJobsAnswered, action.payload);
if (!!res.data)
yield put(fetchAdsAnsweredSuccess(res));
yield put(fetchJobsAnsweredSuccess(res));
else
yield put(fetchAdsAnsweredFailure(res));
yield put(fetchJobsAnsweredFailure(res));
} catch (err) {
if (_.includes(err, "401") || err.response.status === 401) {
@@ -129,7 +129,7 @@ export function* fetchAdsAnsweredRequestSaga(action) {
Router.push("/Login");
}
yield put(fetchAdsAnsweredFailure(err.response));
yield put(fetchJobsAnsweredFailure(err.response));
}
}
// {
+16 -16
View File
@@ -1,8 +1,8 @@
import { call, put } from "redux-saga/effects";
import { fetchAdsSuccess, fetchAdsFailure,
fetchAdSuccess, fetchAdFailure,
fetchEmphasisAdsSuccess,fetchEmphasisAdsFailure
import { fetchJobsSuccess, fetchJobsFailure,
fetchJobSuccess, fetchJobFailure,
fetchEmphasisJobsSuccess,fetchEmphasisJobsFailure
} from "../actions/ads";
@@ -18,44 +18,44 @@ export function* countAdViewRequestSaga(action) {
export function* requestAdsSaga(action) {
try {
const res = yield call(api.ads.fetchAds, action.payload);
const res = yield call(api.ads.fetchJobs, action.payload);
if (!!res.data)
yield put(fetchAdsSuccess(res));
yield put(fetchJobsSuccess(res));
else
yield put(fetchAdsFailure(res));
yield put(fetchJobsFailure(res));
} catch (err) {
yield put(fetchAdsFailure(err.response));
yield put(fetchJobsFailure(err.response));
}
}
export function* requestAdSaga(action) {
try {
const res = yield call(api.ads.fetchAd, action.payload);
const res = yield call(api.ads.fetchJob, action.payload);
if (!!res)
yield put(fetchAdSuccess(res));
yield put(fetchJobSuccess(res));
else
yield put(fetchAdFailure(res));
yield put(fetchJobFailure(res));
} catch (err) {
yield put(fetchAdFailure(err.response));
yield put(fetchJobFailure(err.response));
}
}
export function* requestEmphasisAdsSaga(action) {
export function* requestEmphasisJobsSaga(action) {
try {
const res = yield call(api.ads.fetchAds, action.payload);
const res = yield call(api.ads.fetchJobs, action.payload);
if (!!res.data)
yield put(fetchEmphasisAdsSuccess(res));
yield put(fetchEmphasisJobsSuccess(res));
else
yield put(fetchEmphasisAdsFailure(res));
yield put(fetchEmphasisJobsFailure(res));
} catch (err) {
yield put(fetchEmphasisAdsFailure(err.response));
yield put(fetchEmphasisJobsFailure(err.response));
}
}
+2 -2
View File
@@ -16,8 +16,8 @@ exports.sendConfirmationEmail = function (nom, prenom, tokenUrl, mail) {
const tranport = setup();
const email = {
from: process.env.EMAIL_FROM,
to: process.env.NODE_ENV === 'production'? mail : 'thomas.berrod@e-declic.com',
subject: `Bienvenue ${mail} 🔑`,
to: process.env.NODE_ENV === 'production'? mail : 'l.laniau@talentstube.com',
subject: `Bienvenue ${mail} `,
text: `
Bonjour ${prenom} ${nom},
+3 -3
View File
@@ -4,14 +4,14 @@ const formatDate = require("./formatDate");
// ROBOTS.txt
// const robotsTxt = `User-agent: *
// Sitemap: https://app.talentstube.com/sitemap_local.xml
// Sitemap: https://www.talentstube.com/sitemap_local.xml
// Disallow:`;
const robotsTxt = `# crawl setup
User-agent: *
Disallow: /Login*
# Sitemap
Sitemap: https://app.talentstube.com/sitemap_local.xml`
Sitemap: https://www.talentstube.com/sitemap_local.xml`
fs.writeFileSync("out/robots.txt", robotsTxt);
console.log("robots.txt saved!");
@@ -23,7 +23,7 @@ const sitemapXml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${Object.keys(pathsObj).map(
path => `<url>
<loc>https://app.talentstube.com${path}</loc>
<loc>https://www.talentstube.com${path}</loc>
<lastmod>${
pathsObj[path].lastModified
? formatDate(new Date(pathsObj[path].lastModified))
+12 -12
View File
@@ -58,9 +58,9 @@ export const POST_AD_ANSWER_REQUEST = "POST_AD_ANSWER_REQUEST";
export const POST_AD_ANSWER_SUCCESS = "POST_AD_ANSWER_SUCCESS";
export const POST_AD_ANSWER_FAILURE = "POST_AD_ANSWER_FAILURE";
export const FETCH_ADS_ANSWER_REQUEST = "FETCH_ADS_ANSWER_REQUEST";
export const FETCH_ADS_ANSWER_SUCCESS = "FETCH_ADS_ANSWER_SUCCESS";
export const FETCH_ADS_ANSWER_FAILURE = "FETCH_ADS_ANSWER_FAILURE";
export const FETCH_JOBS_ANSWER_REQUEST = "FETCH_JOBS_ANSWER_REQUEST";
export const FETCH_JOBS_ANSWER_SUCCESS = "FETCH_JOBS_ANSWER_SUCCESS";
export const FETCH_JOBS_ANSWER_FAILURE = "FETCH_JOBS_ANSWER_FAILURE";
export const FETCH_ANSWER_REQUEST = "FETCH_ANSWER_REQUEST";
export const FETCH_ANSWER_SUCCESS = "FETCH_ANSWER_SUCCESS";
@@ -76,17 +76,17 @@ export const FETCH_ANSWER_MESAGES_FAILURE = "FETCH_ANSWER_MESAGES_FAILURE";
export const FETCH_ANSWER_MESAGES_SUCCESS = "FETCH_ANSWER_MESAGES_SUCCESS";
//ADS
export const FETCH_ADS_REQUEST = "FETCH_ADS_REQUEST";
export const FETCH_ADS_SUCCESS = "FETCH_ADS_SUCCESS";
export const FETCH_ADS_FAILURE = "FETCH_ADS_FAILURE";
export const FETCH_JOBS_REQUEST = "FETCH_JOBS_REQUEST";
export const FETCH_JOBS_SUCCESS = "FETCH_JOBS_SUCCESS";
export const FETCH_JOBS_FAILURE = "FETCH_JOBS_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_JOB_REQUEST = "FETCH_JOB_REQUEST";
export const FETCH_JOB_SUCCESS = "FETCH_JOB_SUCCESS";
export const FETCH_JOB_FAILURE = "FETCH_JOB_FAILURE";
export const FETCH_EMPHASIS_ADS_REQUEST = "FETCH_EMPHASIS_ADS_REQUEST";
export const FETCH_EMPHASIS_ADS_SUCCESS = "FETCH_EMPHASIS_ADS_SUCCESS";
export const FETCH_EMPHASIS_ADS_FAILURE = "FETCH_EMPHASIS_ADS_FAILURE";
export const FETCH_EMPHASIS_JOBS_REQUEST = "FETCH_EMPHASIS_JOBS_REQUEST";
export const FETCH_EMPHASIS_JOBS_SUCCESS = "FETCH_EMPHASIS_JOBS_SUCCESS";
export const FETCH_EMPHASIS_JOBS_FAILURE = "FETCH_EMPHASIS_JOBS_FAILURE";
export const CLEAR_AD = "CLEAR_AD";
export const COUNT_AD_VIEW = "COUNT_AD_VIEW";
+4 -4
View File
@@ -61,10 +61,10 @@ class Image extends React.Component {
],
links: [
'https://www.talentstube.com/entreprise/5-e-declic/',
'https://www.facebook.com/edeclic/',
'https://twitter.com/edeclic',
'https://www.linkedin.com/company/edeclic/',
'#',
'#',
'#',
'#',
//
],
+1
View File
@@ -171,6 +171,7 @@ class Contact extends React.Component {
<title>Devis Développement PWA</title>
<meta name="description" content="Vous souhaitez créer une PWA, développer des fonctionnalités à votre site web et améliorer l'expérience utilisateur sur votre site internet? Contactez-nous pour parler de votre projet." />
<link rel="canonical" href="https://www.application.bzh/contact"/>
<meta key="robots" name="robots" content="noindex, nofollow"></meta>
</Head>
<div className={classes.root}>
<div className={classes.wrapper}>
+1
View File
@@ -57,6 +57,7 @@ class Index extends React.Component {
<title>Boilerplate PWA</title>
<meta name="description" content="Vous souhaitez créer une PWA, développer des fonctionnalités à votre site web et améliorer l'expérience utilisateur sur votre site internet? Contactez-nous pour parler de votre projet." />
<link rel="canonical" href="https://pwa-boiler.bzh" />
<meta key="robots" name="robots" content="noindex, nofollow"></meta>
</Head>
<div className={classes.root}>
+1 -1
View File
@@ -16,7 +16,7 @@ function setup() {
const tranport = setup();
const email = {
from: process.env.EMAIL_FROM,
to: "thomas.berrod@e-declic.com",
to: "l.laniau@talentstube.com",
subject: "PWA feedback 💌⚡️",
text: `
Hello Team,