Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add filters to Adoption page #140

Merged
merged 11 commits into from
Jan 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ group :development do
gem 'guard-livereload'
gem 'guard-rspec'
gem 'rubocop', '~> 0.62.0', require: false
gem 'byebug'
end

group :production do
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ GEM
font-awesome-rails
jquery-rails
builder (3.2.4)
byebug (11.0.1)
capybara (3.29.0)
addressable
mini_mime (>= 0.1.3)
Expand Down Expand Up @@ -392,6 +393,7 @@ DEPENDENCIES
aws-sdk-s3
bootstrap (>= 4.3.1)
bootstrap_sb_admin_base_v2
byebug
capybara
coffee-rails (~> 4.2)
cpf_cnpj
Expand Down
8 changes: 7 additions & 1 deletion app/controllers/v1/pets_for_adoption_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ class V1::PetsForAdoptionController < ApplicationController
skip_before_action :verify_authenticity_token

def index
pets = Pet.active
pets = pets.by_sex(params[:sex]) if params[:sex].present?
pets = pets.by_name_or_description(params[:name_or_description]) if params[:name_or_description].present?
pets = pets.by_city(params[:city]) if params[:city].present?
pets = pets.by_ngo_id(params[:ngo_id]) if params[:ngo_id].present?

render json: {
pets: ListPets.new.all(Pet.active, params[:user_email])
pets: ListPets.new.all(pets, params[:user_email])
}.to_json
end

Expand Down
26 changes: 26 additions & 0 deletions app/javascript/actions/adoptionFilters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export const types = {
SET_CITY_FILTER: 'ADOPTION_FILTERS/SET_CITY_FILTER',
SET_NGO_ID_FILTER: 'ADOPTION_FILTERS/SET_NGO_ID_FILTER',
SET_SEX_FILTER: 'ADOPTION_FILTERS/SET_SEX_FILTER',
SET_NAME_OR_DESCRIPTION_FILTER: 'ADOPTION_FILTERS/SET_DESCRIPTION_FILTER'
};

export const setCityFilter = (city = '') => ({
type: types.SET_CITY_FILTER,
city
});

export const setNgoIdFilter = (ngoId = '') => ({
type: types.SET_NGO_ID_FILTER,
ngoId
});

export const setSexFilter = (sex = '') => ({
type: types.SET_SEX_FILTER,
sex
});

export const setNameOrDescriptionFilter = (nameOrDescription = '') => ({
type: types.SET_NAME_OR_DESCRIPTION_FILTER,
nameOrDescription
});
32 changes: 11 additions & 21 deletions app/javascript/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,7 @@ import SideNavigation from "./Navigation/SideNavigation/SideNavigation";

require('typeface-roboto');

const store = configureStore();

class App extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {
email: this.props.userEmail,
group: this.props.userGroup,
},
sideNavigationVisible: false,
};
}

toggleDrawerButtonHandler = () => {
this.setState({sideNavigationVisible: !this.state.sideNavigationVisible});
};
Expand All @@ -34,22 +21,25 @@ class App extends React.Component {
};

render() {
const {user, sideNavigationVisible} = this.props;
const store = configureStore(user);

return (
<ErrorBoundary>
<Provider store={store}>
<BrowserRouter>
<Navigation toggleDrawerButton={this.toggleDrawerButtonHandler} user={this.state.user} />
<Navigation toggleDrawerButton={this.toggleDrawerButtonHandler} user={user} />
<SideNavigation
user={this.state.user}
visible={this.state.sideNavigationVisible}
user={user}
visible={sideNavigationVisible}
close={this.sideDrawerCloseHandler}
/>
<Switch>
<Route exact path="/" render={() => window.location.href = '/'}/>
<Route exact path="/ongs" render={() => window.location.href = '/ongs'}/>
<Route path="/new/ongs" render={() => <NgosList />}/>
<Route exact path="/new/ong/:id" component={NgoPage}/>
<Route path="/adocao" render={() => <AdoptionList userEmail={this.state.user.email} />}/>
<Route exact path="/" render={() => window.location.href = '/'} />
<Route exact path="/ongs" render={() => window.location.href = '/ongs'} />
<Route path="/new/ongs" render={() => <NgosList />} />
<Route exact path="/new/ong/:id" component={NgoPage} />
<Route path="/adocao" component={AdoptionList} />
</Switch>
</BrowserRouter>
</Provider>
Expand Down
18 changes: 18 additions & 0 deletions app/javascript/components/Button/Button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './Button.sass';

const Button = ({ children, onClick }) => (
<button
children={children}
className={styles.Button}
onClick={onClick}
/>
);

Button.propTypes = {
children: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired
}

export default Button;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.SimpleSubmitButton
.Button
width: 200px
height: 50px
border-radius: 4px
Expand All @@ -14,5 +14,5 @@
text-transform: uppercase
border: 0

.SimpleSubmitButton:hover
.Button:hover
background-color: #ff8a8a
41 changes: 28 additions & 13 deletions app/javascript/components/SelectInput/SelectInput.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './SelectInput.sass';

const SelectInput = (props) => {
return (
<div className={styles.SelectInput} style={{width: props.width, marginRight: props.marginRight}}>
<label>{props.label}</label>
<select name="color">
<option value="">{props.placeholder}</option>
{props.options && props.options.length > 0 && props.options.map(opt => {
return <option key={opt.id} value={opt.id}>{opt.name}</option>
})}
</select>
</div>
);
};
const SelectInput = ({ label, name, placeholder, value, options, width, marginRight, onChange }) => (
<div className={styles.SelectInput} style={{ width, marginRight }}>
<label>{label}</label>
<select name={name} value={value} onChange={onChange}>
<option value=''>{placeholder}</option>
{options && options.length > 0 && options.map(option => (
<option key={option.id} value={option.id}>{option.name}</option>
))}
</select>
</div>
);

SelectInput.propTypes = {
label: PropTypes.string.isRequired,
name: PropTypes.string,
placeholder: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
options: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
name: PropTypes.string
})
),
width: PropTypes.string,
marginRight: PropTypes.string,
onChange: PropTypes.func.isRequired
}

export default SelectInput;

This file was deleted.

21 changes: 14 additions & 7 deletions app/javascript/components/TextInput/TextInput.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './TextInput.sass';

const TextInput = (props) => {
return (
<div className={styles.TextInput} style={{width: props.width, marginRight: props.marginRight}}>
<input placeholder={props.placeholder} />
</div>
);
};
const TextInput = ({ value, placeholder, width, marginRight, onChange }) => (
<div className={styles.TextInput} style={{ width, marginRight }}>
<input value={value} placeholder={placeholder} onChange={onChange} />
</div>
);

TextInput.propTypes = {
value: PropTypes.string,
placeholder: PropTypes.string,
width: PropTypes.string,
marginRight: PropTypes.string,
onChange: PropTypes.func.isRequired
}

export default TextInput;
62 changes: 37 additions & 25 deletions app/javascript/configureStore.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,43 @@
import { createStore, applyMiddleware } from 'redux';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import adoptionFiltersReducer from './reducers/adoptionFilters'

const initialState = { };
const appReducerDefaultState = {
sideNavigationVisible: false,
user: { email: null, group: null },
ngos: [],
pets: []
};

function rootReducer(state, action) {
switch (action.type) {
case "GET_NGOS_SUCCESS":
return { ngos: action.json.ngos };
case "GET_NGO_SUCCESS":
return { ngo: action.json.ngo };
case "GET_ADOPTION_SUCCESS":
return { pets: action.json.pets };
case "GET_NGO_CITIES_SUCCESS":
return { cities: action.json.cities };
default:
return state;
}
function appReducer(state = appReducerDefaultState, action) {
switch (action.type) {
case "GET_NGOS_SUCCESS":
return { ...state, ngos: action.json.ngos };
case "GET_NGO_SUCCESS":
return { ...state, ngo: action.json.ngo };
case "GET_ADOPTION_SUCCESS":
return { ...state, pets: action.json.pets };
case "GET_NGO_CITIES_SUCCESS":
return { ...state, cities: action.json.cities };
default:
return state;
}
}

export default function configureStore() {
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(
applyMiddleware(thunk)
)
);
return store;
}
const rootReducer = combineReducers({
app: appReducer,
adoptionFilters: adoptionFiltersReducer
});

export default function configureStore(user) {
const preloadedState = { app: { ...appReducerDefaultState, user } };

const store = createStore(
rootReducer,
preloadedState,
composeWithDevTools(applyMiddleware(thunk))
);

return store;
};
46 changes: 30 additions & 16 deletions app/javascript/containers/AdoptionList/AdoptionList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,28 @@ import SimpleModal from "../../components/SimpleModal/SimpleModal";
const GET_ADOPTION_REQUEST = 'GET_ADOPTION_REQUEST';
const GET_ADOPTION_SUCCESS = 'GET_ADOPTION_SUCCESS';

function fetchPetsForAdoption(userEmail) {
return dispatch => {
dispatch({type: GET_ADOPTION_REQUEST});

const params = userEmail ? "user_email=" + userEmail : null;

return fetch(`../v1/pets_for_adoption.json?` + params)
.then(response => response.json())
.then(json => dispatch(fetchPetsForAdoptionSuccess(json)))
.catch(error => console.log(error));
}
export function fetchPetsForAdoption() {
return (dispatch, getState) => {
const { userEmail, city, ngoId, sex, nameOrDescription } = getState().adoptionFilters;
dispatch({type: GET_ADOPTION_REQUEST});

let params = [
`user_email=${userEmail}`,
`city=${city}`,
`ngo_id=${ngoId}`,
`sex=${sex}`,
`name_or_description=${nameOrDescription}`
];

const urlParams = params.join('&');

return (
fetch(`../v1/pets_for_adoption.json?` + urlParams)
.then(response => response.json())
.then(json => dispatch(fetchPetsForAdoptionSuccess(json)))
.catch(error => console.log(error))
);
}
}

export function fetchPetsForAdoptionSuccess(json) {
Expand All @@ -35,8 +46,7 @@ class AdoptionList extends React.Component {
};

componentWillMount() {
const {fetchPetsForAdoption, userEmail} = this.props;
fetchPetsForAdoption(userEmail);
this.props.fetchPetsForAdoption();
}

addAdoptionInterestHandler = (userEmail, pet) => {
Expand Down Expand Up @@ -105,6 +115,9 @@ class AdoptionList extends React.Component {
</div>
}
</SimpleModal>

<AdoptionFilterBox />

<div className={styles.adoptionCards}>
{pets && this.petList(pets)}
{/* Trick to align last row of cards with flexbox */}
Expand All @@ -118,10 +131,11 @@ class AdoptionList extends React.Component {
}
}

const structuredSelector = createStructuredSelector({
pets: state => state.pets,
const mapStateToProps = createStructuredSelector({
pets: state => state.app.pets,
userEmail: state => state.app.user.email
});

const mapDispatchToProps = {fetchPetsForAdoption};

export default connect(structuredSelector, mapDispatchToProps)(AdoptionList);
export default connect(mapStateToProps, mapDispatchToProps)(AdoptionList);
Loading