diff --git a/Gemfile b/Gemfile
index acb306d..bc5482a 100644
--- a/Gemfile
+++ b/Gemfile
@@ -25,6 +25,7 @@ gem 'jbuilder', '~> 2.7'
gem 'active_model_serializers', '~> 0.10.10'
gem 'bcrypt', '~> 3.1.13'
gem 'rack-attack', '~> 6.2', '>= 6.2.2'
+gem 'rack-cors'
gem 'will_paginate', '~> 3.3'
# Use Active Storage variant
diff --git a/Gemfile.lock b/Gemfile.lock
index af92ad9..b76c2fd 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -134,6 +134,8 @@ GEM
rack (2.2.2)
rack-attack (6.2.2)
rack (>= 1.0, < 3)
+ rack-cors (1.1.1)
+ rack (>= 2.0.0)
rack-proxy (0.6.5)
rack
rack-test (1.1.0)
@@ -276,6 +278,7 @@ DEPENDENCIES
pry-rails
puma (~> 4.1)
rack-attack (~> 6.2, >= 6.2.2)
+ rack-cors
rails (~> 6.0.2, >= 6.0.2.2)
rspec-rails (~> 4.0)
rubocop
diff --git a/app/controllers/api/volunteers_controller.rb b/app/controllers/api/volunteers_controller.rb
index b58b432..19a9e12 100644
--- a/app/controllers/api/volunteers_controller.rb
+++ b/app/controllers/api/volunteers_controller.rb
@@ -17,7 +17,7 @@ def auth
volunteer = Volunteer.find_by(email: params[:auth][:email])
if volunteer&.authenticate(params[:auth][:password])
session[:volunteer_id] = volunteer.id
- render json: volunteer, status: :ok
+ render json: volunteer, status: :created
else
session[:volunteer_id] = nil
render json: { message: 'Authentication failed', code: 401 }, status: :unauthorized
diff --git a/app/javascript/client/components/Forms/Controls/Input/Input.js b/app/javascript/client/components/Forms/Controls/Input/Input.js
new file mode 100644
index 0000000..a1ee8a4
--- /dev/null
+++ b/app/javascript/client/components/Forms/Controls/Input/Input.js
@@ -0,0 +1,39 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const Input = (props) => {
+ return (
+
+ );
+};
+
+Input.propTypes = {
+ label: PropTypes.string,
+ name: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+ type: PropTypes.string,
+ placeholder: PropTypes.string,
+ customInputClass: PropTypes.string,
+ onChange: PropTypes.func.isRequired,
+};
+
+Input.defaultProps = {
+ label: '',
+ type: 'string',
+ placeholder: '',
+ customInputClass: 'input',
+};
+
+export default Input;
diff --git a/app/javascript/client/components/Forms/Controls/Input/index.js b/app/javascript/client/components/Forms/Controls/Input/index.js
new file mode 100644
index 0000000..a122bd4
--- /dev/null
+++ b/app/javascript/client/components/Forms/Controls/Input/index.js
@@ -0,0 +1,3 @@
+import Input from './Input';
+
+export default Input;
diff --git a/app/javascript/client/components/Forms/Controls/index.js b/app/javascript/client/components/Forms/Controls/index.js
new file mode 100644
index 0000000..1e902d1
--- /dev/null
+++ b/app/javascript/client/components/Forms/Controls/index.js
@@ -0,0 +1,3 @@
+import Input from './Input';
+
+export { Input };
diff --git a/app/javascript/client/components/Forms/HelpOfferForm/HelpOfferForm.js b/app/javascript/client/components/Forms/HelpOfferForm/HelpOfferForm.js
new file mode 100644
index 0000000..3d8ce92
--- /dev/null
+++ b/app/javascript/client/components/Forms/HelpOfferForm/HelpOfferForm.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const HelpOfferForm = (props) => {
+ return ;
+};
+
+HelpOfferForm.propTypes = {};
+
+export default HelpOfferForm;
diff --git a/app/javascript/client/components/Forms/HelpOfferForm/index.js b/app/javascript/client/components/Forms/HelpOfferForm/index.js
new file mode 100644
index 0000000..a870051
--- /dev/null
+++ b/app/javascript/client/components/Forms/HelpOfferForm/index.js
@@ -0,0 +1,3 @@
+import HelpOfferForm from './HelpOfferForm';
+
+export default HelpOfferForm;
diff --git a/app/javascript/client/components/Forms/VolunteerLoginForm/VolunteerLoginForm.js b/app/javascript/client/components/Forms/VolunteerLoginForm/VolunteerLoginForm.js
new file mode 100644
index 0000000..6e5f233
--- /dev/null
+++ b/app/javascript/client/components/Forms/VolunteerLoginForm/VolunteerLoginForm.js
@@ -0,0 +1,56 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import { Input } from '../Controls';
+
+const VolunteerLoginForm = (props) => {
+ const [volunteerFormData, setVolunteerFormData] = useState({
+ email: '',
+ password: '',
+ });
+
+ const handleInputChange = (e) => {
+ setVolunteerFormData({
+ ...volunteerFormData,
+ [e.target.name]: e.target.value,
+ });
+ };
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+
+ console.log(volunteerFormData);
+ };
+
+ return (
+
+ );
+};
+
+VolunteerLoginForm.propTypes = {};
+
+export default VolunteerLoginForm;
diff --git a/app/javascript/client/components/Forms/VolunteerLoginForm/index.js b/app/javascript/client/components/Forms/VolunteerLoginForm/index.js
new file mode 100644
index 0000000..ef878ae
--- /dev/null
+++ b/app/javascript/client/components/Forms/VolunteerLoginForm/index.js
@@ -0,0 +1,3 @@
+import VolunteerLoginForm from './VolunteerLoginForm';
+
+export default VolunteerLoginForm;
diff --git a/app/javascript/client/components/Forms/VolunteerRegisterForm/VolunteerRegisterForm.js b/app/javascript/client/components/Forms/VolunteerRegisterForm/VolunteerRegisterForm.js
new file mode 100644
index 0000000..4acac57
--- /dev/null
+++ b/app/javascript/client/components/Forms/VolunteerRegisterForm/VolunteerRegisterForm.js
@@ -0,0 +1,91 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import { Input } from '../Controls';
+
+const VolunteerRegisterForm = (props) => {
+ const [volunteerFormData, setVolunteerFormData] = useState({
+ fullname: '',
+ cellphone: '',
+ email: '',
+ password: '',
+ province: '',
+ county: '',
+ types_of_availability: '',
+ });
+
+ const handleInputChange = (e) => {
+ setVolunteerFormData({
+ ...volunteerFormData,
+ [e.target.name]: e.target.value,
+ });
+ };
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ props.registerNewVolunteer(volunteerFormData);
+ };
+
+ return (
+
+ );
+};
+
+VolunteerRegisterForm.propTypes = {
+ registerNewVolunteer: PropTypes.func.isRequired,
+};
+
+export default VolunteerRegisterForm;
diff --git a/app/javascript/client/components/Forms/VolunteerRegisterForm/index.js b/app/javascript/client/components/Forms/VolunteerRegisterForm/index.js
new file mode 100644
index 0000000..6273175
--- /dev/null
+++ b/app/javascript/client/components/Forms/VolunteerRegisterForm/index.js
@@ -0,0 +1,3 @@
+import VolunteerRegisterForm from './VolunteerRegisterForm';
+
+export default VolunteerRegisterForm;
diff --git a/app/javascript/client/components/Forms/index.js b/app/javascript/client/components/Forms/index.js
new file mode 100644
index 0000000..75d2e19
--- /dev/null
+++ b/app/javascript/client/components/Forms/index.js
@@ -0,0 +1,5 @@
+import HelpOfferForm from './HelpOfferForm';
+import VolunteerLoginForm from './VolunteerLoginForm';
+import VolunteerRegisterForm from './VolunteerRegisterForm';
+
+export { HelpOfferForm, VolunteerLoginForm, VolunteerRegisterForm };
diff --git a/app/javascript/client/components/HelpPage/HelpPage.js b/app/javascript/client/components/HelpPage/HelpPage.js
index 57a6539..80dcda8 100644
--- a/app/javascript/client/components/HelpPage/HelpPage.js
+++ b/app/javascript/client/components/HelpPage/HelpPage.js
@@ -1,12 +1,76 @@
-import React, { useEffect } from 'react';
+import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { useParams } from 'react-router-dom';
import PropTypes from 'prop-types';
+import {
+ VolunteerRegisterForm,
+ VolunteerLoginForm,
+ HelpOfferForm,
+} from '../Forms';
+
import { loadHelp } from '../../store/helps/actions';
+import { registerVolunteer } from '../../store/volunteers/actions';
const HelpPage = (props) => {
const { id } = useParams();
+ // TODO : Change default state to false
+ const [isHelping, setIsHelping] = useState(true);
+
+ const isAuthenticated = () => {
+ true;
+ };
+
+ const helpingForms = () => (
+
+ );
+
+ const autheticationForms = () => (
+
+ );
useEffect(() => {
props.loadHelp(id);
@@ -22,20 +86,16 @@ const HelpPage = (props) => {
{props.help.description}
-
-
+
-
- Em Desenvolvimento...
+
+ {isHelping ? (isAuthenticated ? helpingForms : autheticationForms) : null}
@@ -55,6 +115,7 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
loadHelp: (id) => dispatch(loadHelp(id)),
+ registerVolunteer: (payload) => dispatch(registerVolunteer(payload)),
};
};
diff --git a/app/javascript/client/store/index.js b/app/javascript/client/store/index.js
index 56cdbc8..e40277f 100644
--- a/app/javascript/client/store/index.js
+++ b/app/javascript/client/store/index.js
@@ -5,6 +5,7 @@ import { connectRouter, routerMiddleware } from 'connected-react-router';
import { createBrowserHistory } from 'history';
import helpsSlice from './helps/slice';
+import volunteersSlice from './volunteers/slice';
export const history = createBrowserHistory();
@@ -16,6 +17,7 @@ const rootReducer = (history) =>
combineReducers({
router: connectRouter(history),
helps: helpsSlice.reducer,
+ volunteers: volunteersSlice.reducer,
});
const store = configureStore({
diff --git a/app/javascript/client/store/volunteers/actions.js b/app/javascript/client/store/volunteers/actions.js
new file mode 100644
index 0000000..49bd72a
--- /dev/null
+++ b/app/javascript/client/store/volunteers/actions.js
@@ -0,0 +1,41 @@
+import axios from 'axios';
+import { push } from 'connected-react-router';
+
+import volunteersSlice from './slice';
+
+const { actions } = volunteersSlice;
+
+const registerVolunteer = (payload) => {
+ return (dispatch, getState) => {
+ const state = getState();
+
+ dispatch(actions.registerVolunteerInit());
+
+ axios.defaults.headers.common['X-CSRF-Token'] = document
+ .querySelector('meta[name="csrf-token"]')
+ .getAttribute('content');
+
+ axios
+ .post('/api/volunteers', {
+ volunteer: {
+ fullname: payload.fullname,
+ cellphone: payload.cellphone,
+ email: payload.email,
+ password: payload.password,
+ province: payload.province,
+ county: payload.county,
+ },
+ })
+ .then((response) => {
+ dispatch(
+ actions.registerVolunteerSuccess({ volunteer: response.data }),
+ );
+ // dispatch(push('/quero-ajudar'));
+ })
+ .catch((error) => {
+ dispatch(actions.registerVolunteerFailure(error.response.data));
+ });
+ };
+};
+
+export { registerVolunteer };
diff --git a/app/javascript/client/store/volunteers/slice.js b/app/javascript/client/store/volunteers/slice.js
new file mode 100644
index 0000000..19b8663
--- /dev/null
+++ b/app/javascript/client/store/volunteers/slice.js
@@ -0,0 +1,41 @@
+import produce from 'immer';
+import { createSlice } from '@reduxjs/toolkit';
+
+const initialState = {
+ loading: false,
+ errors: [],
+ authenticatedVolunteer: {},
+};
+
+const volunteersSlice = createSlice({
+ name: 'volunteers',
+ initialState,
+
+ reducers: {
+ registerVolunteerInit: (state) => {
+ return produce(state, (draft) => {
+ draft.loading = true;
+ draft.errors = [];
+ draft.authenticatedVolunteer = {};
+ });
+ },
+
+ registerVolunteerSuccess: (state, action) => {
+ return produce(state, (draft) => {
+ draft.loading = true;
+ draft.errors = [];
+ draft.authenticatedVolunteer = action.payload.volunteer;
+ });
+ },
+
+ registerVolunteerFailure: (state, action) => {
+ return produce(state, (draft) => {
+ draft.loading = false;
+ draft.errors = action.payload;
+ draft.authenticatedVolunteer = {};
+ });
+ },
+ },
+});
+
+export default volunteersSlice;
diff --git a/app/javascript/client/utils/setAuthToken.js b/app/javascript/client/utils/setAuthToken.js
new file mode 100644
index 0000000..5271756
--- /dev/null
+++ b/app/javascript/client/utils/setAuthToken.js
@@ -0,0 +1,11 @@
+import axios from 'axios';
+
+const setAuthToken = (token) => {
+ if (token) {
+ axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
+ } else {
+ delete axios.defaults.headers.common['Authorization'];
+ }
+};
+
+export default setAuthToken;
diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb
index f3bcce5..699a98e 100644
--- a/config/initializers/content_security_policy.rb
+++ b/config/initializers/content_security_policy.rb
@@ -1,23 +1,24 @@
# frozen_string_literal: true
+
# Be sure to restart your server when you modify this file.
# Define an application-wide content security policy
# For further information see the following documentation
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
-# Rails.application.config.content_security_policy do |policy|
-# policy.default_src :self, :https
-# policy.font_src :self, :https, :data
-# policy.img_src :self, :https, :data
-# policy.object_src :none
-# policy.script_src :self, :https
-# policy.style_src :self, :https
-# # If you are using webpack-dev-server then specify webpack-dev-server host
-# policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
+Rails.application.config.content_security_policy do |policy|
+ policy.default_src :self, :https
+ policy.font_src :self, :https, :data
+ policy.img_src :self, :https, :data
+ policy.object_src :none
+ policy.script_src :self, :https
+ policy.style_src :self, :https, :unsafe_inline
+ # If you are using webpack-dev-server then specify webpack-dev-server host
+ policy.connect_src :self, :https, 'http://localhost:3035', 'ws://localhost:3035' if Rails.env.development?
-# # Specify URI for violation reports
-# # policy.report_uri "/csp-violation-report-endpoint"
-# end
+ # Specify URI for violation reports
+ # policy.report_uri "/csp-violation-report-endpoint"
+end
# If you are using UJS then enable automatic nonce generation
# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb
new file mode 100644
index 0000000..f9506aa
--- /dev/null
+++ b/config/initializers/cors.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+Rails.application.config.middleware.insert_before 0, Rack::Cors do
+ allow do
+ origins %r{https?://(.*?)\.sos\.local$}
+
+ resource '*',
+ headers: :any,
+ methods: %i[get post put patch delete options head]
+ end
+
+ allow do
+ origins %r{https?://(.*?)\.sos\.ao$}
+
+ resource '*',
+ headers: :any,
+ methods: %i[get post put patch delete options head]
+ end
+end
diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb
new file mode 100644
index 0000000..aba98b5
--- /dev/null
+++ b/config/initializers/session_store.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+domain = Rails.env.production? ? 'sos.ao' : 'sos.local'
+
+Rails.application.config.session_store :cookie_store, key: '_sos_ao_session', domain: domain
diff --git a/spec/requests/api/volunteers_request_spec.rb b/spec/requests/api/volunteers_request_spec.rb
index 56c13c1..02119d8 100644
--- a/spec/requests/api/volunteers_request_spec.rb
+++ b/spec/requests/api/volunteers_request_spec.rb
@@ -17,7 +17,7 @@
let(:params) { { auth: { email: volunteer.email, password: 'secret$123' } } }
it { expect(response.content_type).to eq('application/json; charset=utf-8') }
- it { expect(response).to have_http_status(:ok) }
+ it { expect(response).to have_http_status(:created) }
it { expect(session[:volunteer_id]).to eq volunteer.id }