Module 3 Team Project of the Adalab Digital Frontend Development Bootcamp.
This is a profile cards app developed with and
Project URL is available on GitHub Pages.
This project has been developed by Refactorizadas Team:
- Emma Cebada: @Emma-cebada
- Ana Guerra: @anaguerraabaroa
- Celia MartΓn: @Celiamf
- BelΓ©n Matilla: @BMatillaVaras
- Lissandra MΓ©ndez: @lissms
Instructions to start this project:
- Clone repository:
git clone [repository]
- Install NPM packages and dependencies:
npm install
- Run project on local server:
npm start
- Refactor STAR Coding Labs Team Project with
- Render Landing and Card Generator components
class App extends React.Component {
render() {
return (
<Switch>
<Route exact path="/" component={Landing} />
<Route path="/cardgenerator" component={CardGenerator} />
</Switch>
);
}
}
- Handle app, lifecycle, form fields, api request and localStorage
class CardGenerator extends React.Component {
constructor(props) {
super(props);
this.state = {
palette: "",
palettes: "",
fullName: "",
job: "",
email: "",
telephone: "",
linkedin: "",
github: "",
img: "",
apiSuccess: false,
apiCardUrl: "",
apiError: "",
loading: false,
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleReset = this.handleReset.bind(this);
this.sendRequest = this.sendRequest.bind(this);
}
handleInputChange = (name, value) => {
this.setState({
[name]: value,
});
if (value === "paletteBlue") {
this.setState({
palette: 1,
});
} else if (value === "paletteRed") {
this.setState({
palette: 2,
});
} else if (value === "paletteGrey") {
this.setState({
palette: 3,
});
}
};
handleReset = () => {
this.setState({
palette: "",
palettes: "",
fullName: "",
job: "",
email: "",
telephone: "",
linkedin: "",
github: "",
img: "",
});
};
sendRequest = () => {
this.setState({
loading: true,
});
const apiCard = {
name: this.state.fullName,
job: this.state.job,
phone: this.state.telephone,
email: this.state.email,
linkedin: this.state.linkedin,
github: this.state.github,
palette: this.state.palette,
photo: this.state.img,
};
sendData(apiCard).then((response) => {
if (response.success === true) {
this.setState({
apiSuccess: true,
apiCardUrl: response.cardURL,
apiError: "",
loading: false,
});
console.log(this.state.apiCardUrl);
} else {
this.setState({
apiSuccess: false,
apiCardUrl: "",
apiError: response.error,
});
}
});
};
componentDidMount() {
this.getLocalStorage();
}
componentDidUpdate() {
this.setLocalStorage();
}
setLocalStorage() {
const data = JSON.stringify(this.state);
localStorage.setItem("cardData", data);
}
getLocalStorage() {
const savedData = JSON.parse(localStorage.getItem("cardData"));
if (savedData !== null) {
this.setState({
palette: savedData.palette,
palettes: savedData.palettes,
fullName: savedData.fullName,
job: savedData.job,
email: savedData.email,
telephone: savedData.telephone,
linkedin: savedData.linkedin,
github: savedData.github,
img: savedData.img,
});
}
}
render() {
return (
<>
<Header />
<Main
handleInputChange={this.handleInputChange}
state={this.state}
handleReset={this.handleReset}
sendRequest={this.sendRequest}
/>
<Footer />
</>
);
}
}
- Render Design, Fill and Share components
class MainSectionCreate extends React.Component {
render() {
return (
<section className="colapsableForm">
<Design handleInputChange={this.props.handleInputChange}></Design>
<Fill
handleInputChange={this.props.handleInputChange}
state={this.props.state}
></Fill>
<Share
sendRequest={this.props.sendRequest}
state={this.props.state}
></Share>
</section>
);
}
}
- Render collapsables header
class Info extends React.Component {
render() {
return (
<div
className={`info ${this.props.state}`}
onClick={this.props.handleClick}
>
<i className={`fa ${this.props.icon} info__icons`}></i>
<h2 className="info__formTitle">{this.props.title}</h2>
<i className="fa fa-angle-up info__rotate js__rotate"></i>
</div>
);
}
}
- Handle Design collapsable and render palettes
class Design extends React.Component {
constructor(props) {
super(props);
this.state = {
onClick: "close",
};
this.collapseDesign = this.collapseDesign.bind(this);
}
collapseDesign() {
this.setState((prevState) => ({
onClick: prevState.onClick === "close" ? "open" : "close",
}));
}
render() {
return (
<article className="border">
<Info
state={this.state.onClick}
icon="fa-object-ungroup"
title="DiseΓ±a"
handleClick={this.collapseDesign}
></Info>
<div className={`design js__displayNone ${this.state.onClick}`}>
<h3 className="design__title">Colores</h3>
<div>
<Palettes
divClass="blue"
inputClass="js-checkedBlue"
id="paletteBlue"
classColor1="box1"
classColor2="box2"
classColor3="box3"
handleInputChange={this.props.handleInputChange}
/>
<Palettes
divClass="red"
inputClass="js-checkedRed"
id="paletteRed"
classColor1="box4"
classColor2="box5"
classColor3="box6"
handleInputChange={this.props.handleInputChange}
/>
<Palettes
divClass="grey"
inputClass="js-checkedGrey"
id="paletteGrey"
classColor1="box7"
classColor2="box8"
classColor3="box9"
handleInputChange={this.props.handleInputChange}
/>
</div>
</div>
</article>
);
}
}
- Handle selected palette and render individual palette
class Palettes extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange = (ev) => {
this.props.handleInputChange(ev.target.name, ev.target.value);
};
render() {
return (
<div className={`design__colors ${this.props.divClass}`}>
<input
className={`design__colors--check ${this.props.inputClass}`}
id={this.props.id}
type="radio"
value={this.props.id}
name="palettes"
onChange={this.handleChange}
/>
<div className={`design__colors--box ${this.props.classColor1}`}></div>
<div className={`design__colors--box ${this.props.classColor2}`}></div>
<div className={`design__colors--box ${this.props.classColor3}`}></div>
</div>
);
}
}
- Handle Fill collapsable and render form inputs
class Fill extends React.Component {
constructor(props) {
super(props);
this.state = {
onClick: "close",
};
this.collapseFill = this.collapseFill.bind(this);
}
collapseFill() {
this.setState((prevState) => ({
onClick: prevState.onClick === "close" ? "open" : "close",
}));
}
render() {
return (
<article className="border">
<Info
state={this.state.onClick}
icon="fa-keyboard-o"
title="Rellena"
handleClick={this.collapseFill}
></Info>
<form
action=""
method="post"
className={`fill js__displayNone ${this.state.onClick}`}
>
<FormItem
name="fullName"
title="Nombre completo"
placeholder="Ej: Barry Allen"
type="text"
value={this.props.state.fullName}
handleInputChange={this.props.handleInputChange}
></FormItem>
<FormItem
name="job"
title="Puesto"
placeholder="Ej: The fastest man alive"
type="text"
value={this.props.state.job}
handleInputChange={this.props.handleInputChange}
></FormItem>
<CardImage
handleInputChange={this.props.handleInputChange}
img={this.props.state.img}
/>
<FormItem
name="email"
title="E-mail"
placeholder="Ej: barryAllen@gmail.com"
type="email"
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"
value={this.props.state.email}
handleInputChange={this.props.handleInputChange}
></FormItem>
<FormItem
name="telephone"
title="TelΓ©fono"
placeholder="Ej: 555-55-55-55"
type="tel"
pattern="[0-9]{9}"
value={this.props.state.telephone}
handleInputChange={this.props.handleInputChange}
></FormItem>
<FormItem
name="linkedin"
title="LinkedIn"
placeholder="Ej: barryAllen"
type="text"
value={this.props.state.linkedin}
handleInputChange={this.props.handleInputChange}
></FormItem>
<FormItem
name="github"
title="GitHub"
placeholder="Ej: Barry-Allen"
type="text"
pattern="@[A-Za-z][0-9]{1,}"
value={this.props.state.github}
handleInputChange={this.props.handleInputChange}
></FormItem>
</form>
</article>
);
}
}
- Handle form inputs and render individual form input
class FormItem extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange = (ev) => {
this.props.handleInputChange(ev.target.name, ev.target.value);
};
render() {
return (
<>
<label htmlFor={this.props.name} className="fill__label">
{this.props.title}
</label>
<input
className="fill__input"
placeholder={this.props.placeholder}
id={this.props.name}
type={this.props.type}
name={this.props.name}
pattern={this.props.pattern}
value={this.props.value}
onChange={this.handleChange}
required
/>
</>
);
}
}
- Handle get, upload and render image on the profile card
class CardImage extends React.Component {
constructor(props) {
super(props);
this.handleCardImg = this.handleCardImg.bind(this);
this.writeImage = this.writeImage.bind(this);
this.fakeClick = this.fakeClick.bind(this);
this.fileInput = React.createRef();
this.fr = new FileReader();
}
writeImage() {
const url = this.fr.result;
this.props.handleInputChange("img", url);
}
handleCardImg() {
const file = this.fileInput.current.files[0];
this.fr.onload = this.writeImage;
this.fr.readAsDataURL(file);
}
fakeClick() {
this.fileInput.current.click();
}
render() {
return (
<>
<label className="fill__label--img" htmlFor="img-selector">
Imagen de perfil
</label>
<div className="fill__wrapper">
<div className="action">
<button
className="action__upload-btn"
type="button"
title="AΓ±adir imagen"
onClick={this.fakeClick}
>
AΓ±adir imagen
</button>
<input
type="file"
name="img-selector"
id="img-selector"
accept="image/png, image/jpeg"
className="action__hiddenField"
ref={this.fileInput}
onChange={this.handleCardImg}
/>
</div>
<div className="profile">
<div
className="profile__preview"
id="previewImg"
style={{ backgroundImage: `url(${this.props.img})` }}
></div>
</div>
</div>
</>
);
}
}
- Handle Share collapsable, create profile card link and button to share it on Twitter
class Share extends React.Component {
constructor(props) {
super(props);
this.state = {
onClick: "close",
createCard: "close",
};
this.collapseShare = this.collapseShare.bind(this);
this.createCard = this.createCard.bind(this);
}
collapseShare() {
this.setState((prevState) => ({
onClick: prevState.onClick === "close" ? "open" : "close",
}));
}
createCard() {
this.props.sendRequest();
this.setState((prevState) => ({
createCard: prevState.createCard === "close" ? "open" : "close",
}));
}
render() {
const tweet =
"Ya tengo mi tarjeta creada en STAR Labs, soy miembro de La Liga de la Justicia";
const loadingElement =
this.props.state.loading === true ? <Loading /> : null;
return (
<>
<article className="border">
<Info
state={this.state.onClick}
icon="fa-share-alt"
title="Comparte"
handleClick={this.collapseShare}
></Info>
<div className={`share js__displayNone ${this.state.onClick}`}>
<i className="fa fa-address-card-o share__icon"></i>
<button
className="share__button"
type="button"
onClick={this.createCard}
>
Crear tarjeta
</button>
</div>
</article>
<div className={`border sent js__displayNone ${this.state.createCard}`}>
<h3 className="sent__text section__share--subtitle">
La tarjeta ha sido creada
</h3>
{loadingElement}
<a
className="sent__link link--share"
href={this.props.state.apiCardUrl}
target="_blank"
rel="noreferrer"
>
{this.props.state.apiSuccess
? this.props.state.apiCardUrl
: this.props.state.apiError}
</a>
<a
href={`https://twitter.com/intent/tweet?text=${tweet}&url=${this.props.state.apiCardUrl}`}
className="sent__twitter button--share"
target="_blank"
rel="noreferrer"
>
<i className="fa fa-twitter sent__twitter--icon" aria-hidden="true"></i>
Compartir en twitter
</a>
</div>
</>
);
}
}
- Render profile card
class MainSectionCard extends React.Component {
render() {
return (
<section className="sectionOne">
<div>
<button className="sectionOneBtn" onClick={this.props.handleReset}>
<i className="fa fa-trash-o sectionOneBtn__logo" aria-hidden="true"></i>
Reset
</button>
<div className={`sectionOneCard ${this.props.state.palettes}`}>
<header className="sectionOneCard__header js-border">
<h2
className="sectionOneCard__header--name js__name--Card"
id="js-fullName-card"
>
{this.props.state.fullName || "Barry Allen"}
</h2>
<h3 className="sectionOneCard__header--job" id="js-job-card">
{this.props.state.job || "The Fastest Man Alive"}
</h3>
</header>
<div
className="profile__image"
id="profileImg"
style={{
backgroundImage: `url(${this.props.state.img || CardImg})`,
}}
></div>
<nav>
<ul className="sectionOneCard__nav">
<CardIcons
href={`tel:${this.props.state.telephone}`}
id="js-telephone-card"
icon="fa-mobile"
></CardIcons>
<CardIcons
href={`mailto:${this.props.state.email}`}
id="js-mail-card"
icon="fa-envelope-o"
></CardIcons>
<CardIcons
href={`https://www.linkedin.com/in/${this.props.state.linkedin}`}
id="js-linkedin-card"
icon="fa-linkedin"
></CardIcons>
<CardIcons
href={`https://github.com/${this.props.state.github}`}
id="js-github-card"
icon="fa-github-alt"
></CardIcons>
</ul>
</nav>
</div>
</div>
</section>
);
}
}
- Render social media icons on the profile card
class CardIcons extends React.Component {
render() {
return (
<li>
<a
href={this.props.href}
id={this.props.id}
target="_blank"
rel="noreferrer"
>
<i
className={`fa ${this.props.icon} sectionOneCard__nav--logos js-socialLogos`}
aria-hidden="true"
></i>
</a>
</li>
);
}
}
- Handle API request to get profile card link
function sendData(data) {
return fetch(
"https://us-central1-awesome-cards-cf6f0.cloudfunctions.net/card/",
{
method: "POST",
body: JSON.stringify(data),
headers: {
"content-type": "application/json",
},
}
)
.then(function (resp) {
return resp.json();
})
.then(function (result) {
return result;
})
.catch(function (error) {
return {
success: false,
error: "Error, encendemos la batseΓ±al, Batman al rescate!!",
};
});
}
Profile Cards Refactor
βββ docs
βββ node_modules
βββ public
βββ src
β βββ components
β β βββ Create
β β β βββ CardImage.js
β β β βββ Design.js
β β β βββ Fill.js
β β β βββ FormItem.js
β β β βββ Info.js
β β β βββ Palettes.js
β β β βββ Share.js
β β βββ App.js
β β βββ CardGenerator.js
β β βββ CardIcons.js
β β βββ Footer.js
β β βββ Header.js
β β βββ Landing.js
β β βββ Loading.js
β β βββ Main.js
β β βββ MainSectionCard.js
β β βββ MainSectionCreate.js
β βββ images
β β βββ batman_loading.gif
β β βββ Flash.png
β β βββ header_logo.png
β β βββ landing_desktop.png
β β βββ landing_mobile.png
β β βββ landing_tablet.png
β β βββ logoStarCodingLabs-Blue.svg
β β βββ profile_cards_desktop.png
β β βββ profile_cards_mobile.png
β β βββ profile_cards_tablet.png
β β βββ tarjetas-molonas.svg
β βββ services
β β βββ fetch.js
β βββ stylesheets
β β βββ core
β β β βββ _reset.scss
β β β βββ _variables.scss
β β βββ layout
β β β βββ _cardIcons.scss
β β β βββ _design.scss
β β β βββ _fill.scss
β β β βββ _footer.scss
β β β βββ _header.scss
β β β βββ _info.scss
β β β βββ _landing.scss
β β β βββ _loading.scss
β β β βββ _main.scss
β β β βββ _mainSectionCard.scss
β β β βββ _mainSectionCreate.scss
β β β βββ _share.scss
β β β βββ
β β βββ pages
β β β βββ _index.scss
β β βββ App.scss
β βββ index.js
β βββ reportWebVitals.js
βββ .gitignore
βββ debug.log
βββ LICENSE
βββ package-lock.json
βββ package.json
βββ README.md