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

Feature/connect user backend #81

Open
wants to merge 3 commits into
base: feature/user
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { NotFound } from "./pages/not-found";
import { Stories } from "./pages/stories";
import { Blog } from "./pages/blog";
import { Post } from "./pages/post";
import { SignUp } from './pages/sign_up';
import { SignIn } from './pages/sign_in';

import { Chapter1 } from "./pages/chapter1";
import { Chapter2 } from "./pages/chapter2";
Expand All @@ -27,6 +29,8 @@ export const App: React.FC = () => {

<Route path="/blog/:slug" component={Post} />
<Route path="/blog" component={Blog} />
<Route path="/signup" component={SignUp} />
<Route path="/signin" component={SignIn} />
<Route exact path="/" component={Stories} />
<Route component={NotFound} />
</Switch>
Expand Down
8 changes: 6 additions & 2 deletions src/components/layout/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,12 @@ export const Header: React.FC<{ onDark?: boolean }> = ({ onDark }) => {
className="w-full h-full pr-4"
/>
}
<Button onDark={onDark}>Einloggen</Button>
<Button buttonOutline={true} onDark={onDark}>Registrieren</Button>
<Link to="signin">
<Button onDark={onDark}>Einloggen</Button>
</Link>
<Link to="signup">
<Button buttonOutline={true} onDark={onDark}>Registrieren</Button>
</Link>
</div>
</div>
</div>
Expand Down
45 changes: 45 additions & 0 deletions src/components/signin/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { useState } from "react";
import { login } from "../../users_api/UserAPI";

export const SignInForm: React.FC = () => {
const [email, setEmail] = useState<string>("");
const [password, setPassword] = useState<string>("");

const handleSignIn = async () => {
let data = {
email: email,
password: password
}
let response;
try {
response = await login(data)
} catch(error) {
console.log(error)
}
console.log(response)
}

return (
<div className="center-box mt-40 w-full max-w-lg">
<form className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
<div className="mb-4">
<label className="block text-gray-700 text-sm font-bold mb-2">
E-Mail
</label>
<input value={email} onChange={ (e) => setEmail(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="username" type="text" placeholder="E-Mail" />
</div>
<div className="mb-6">
<label className="block text-gray-700 text-sm font-bold mb-2">
Passwort
</label>
<input value={password} onChange={ (e) => setPassword(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" id="password" type="password" placeholder="Passwort" />
</div>
<div className="flex items-center justify-between">
<button onClick={ () => handleSignIn() }className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" type="button">
Sign Up
</button>
</div>
</form>
</div>
)
}
53 changes: 53 additions & 0 deletions src/components/signup/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { useState } from "react";
import { createUser } from "../../users_api/UserAPI";

export const SignUpForm: React.FC = () => {
const [email, setEmail] = useState<string>("");
const [password, setPassword] = useState<string>("");
const [password_confirmation, setPasswordConfirmation] = useState<string>("");

const handleSignUp = async () => {
let data = {
email: email,
password: password,
password_confirmation: password_confirmation
}
let response;
try {
response = await createUser(data)
} catch(error) {
console.log(error)
}
console.log(response)
}

return (
<div className="center-box mt-40 w-full max-w-lg">
<form className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
<div className="mb-4">
<label className="block text-gray-700 text-sm font-bold mb-2">
E-Mail
</label>
<input value={email} onChange={ (e) => setEmail(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="username" type="text" placeholder="E-Mail" />
</div>
<div className="mb-6">
<label className="block text-gray-700 text-sm font-bold mb-2">
Passwort
</label>
<input value={password} onChange={ (e) => setPassword(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" id="password" type="password" placeholder="Passwort" />
</div>
<div className="mb-6">
<label className="block text-gray-700 text-sm font-bold mb-2">
Passwort bestätigen
</label>
<input value={password_confirmation} onChange={ (e) => setPasswordConfirmation(e.target.value)} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" id="password" type="password" placeholder="Passwort bestätigen" />
</div>
<div className="flex items-center justify-between">
<button onClick={ () => handleSignUp() }className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" type="button">
Sign Up
</button>
</div>
</form>
</div>
)
}
3 changes: 3 additions & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
apiUrl: 'http://localhost:3000/api/v1'
}
13 changes: 13 additions & 0 deletions src/pages/sign_in.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react";
import { Layout } from "../components/layout";
import { SEO } from "../components/seo";

import { SignInForm } from "../components/signin/index";


export const SignIn: React.FC = () => (
<Layout>
<SEO title="Login - Klimabox" />
<SignInForm />
</Layout>
);
13 changes: 13 additions & 0 deletions src/pages/sign_up.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react";
import { Layout } from "../components/layout";
import { SEO } from "../components/seo";

import { SignUpForm } from "../components/signup/index";


export const SignUp: React.FC = () => (
<Layout>
<SEO title="Registrierung - Klimabox" />
<SignUpForm />
</Layout>
);
142 changes: 142 additions & 0 deletions src/users_api/BaseAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import Config from '../config/index';

export interface RequestOptions {
config: { apiUrl: string };
fetchFunc: typeof window.fetch;
}

class ResponseError extends Error {
public response?: Response;
public body: any;
}

export const corsFetchOptions: {
mode: RequestMode;
credentials: RequestCredentials;
} = {
mode: 'cors',
credentials: 'include'
};

export const acceptJsonHeaders = {
Accept: 'application/json'
};

export const contentTypeJsonHeaders = {
'Content-Type': 'application/json; charset=utf-8'
};

export const contentTypeFormUrlEncodedHeaders = {
'Content-Type': 'application/x-www-form-urlencoded'
};

const checkStatus = async (response: Response) => {
if (!response.ok) {
const error = new ResponseError(
`${response.status} ${response.statusText}`
);
error.response = response;
// Try to parse the response body as JSON.
let body;
try {
body = await response.json();
} catch (e) {
// parsing the error response to json failed
// Reject the new promise
throw error;
}
// parsing the error response was succuess
// Add body property to error
error.body = body;
// Reject the new promise
throw error;
}
return response;
};

const parseJson = <T>(response: Response): T | Promise<T> =>
response.status === 204 ? ({} as T) : response.json();

export const get = async <T>(url: string, options?: RequestOptions) => {
const mergedOptions: RequestOptions = {
config: Config,
fetchFunc: fetch,
...options
};
const { config, fetchFunc } = mergedOptions;

const headers = {
...acceptJsonHeaders
};

const fetchOptions = {
...corsFetchOptions,
method: 'GET',
headers
};
const finalUrl = `${config.apiUrl}${url}`;

const response = await fetchFunc(finalUrl, fetchOptions);
const checkedResponse = await checkStatus(response);
return parseJson<T>(checkedResponse);
};

const requestWithBody = async <T>(
method: string,
url: string,
data: any,
options?: RequestOptions
) => {
const mergedOptions: RequestOptions = {
config: Config,
fetchFunc: fetch,
...options
};
const { config, fetchFunc } = mergedOptions;

// process different data types
let body: string;
let headers: { [key: string]: string };
if (typeof data === 'string') {
headers = {
...acceptJsonHeaders,
...contentTypeFormUrlEncodedHeaders
};
body = data;
} else {
headers = {
...acceptJsonHeaders,
...contentTypeJsonHeaders
};
body = JSON.stringify(data);
}

const fetchOptions = {
...corsFetchOptions,
method,
headers,
body
};

const finalUrl = `${config.apiUrl}${url}`;

// this is the browser actually doing the fetch
const response = await fetchFunc(finalUrl, fetchOptions);
const checkedResponse = await checkStatus(response);
return parseJson<T>(checkedResponse);
};

export const post = <T>(url: string, data: any, options?: RequestOptions) =>
requestWithBody<T>('POST', url, data, options);

export const put = <T>(url: string, data: any, options?: RequestOptions) =>
requestWithBody<T>('PUT', url, data, options);

export const patch = <T>(url: string, data: any, options?: RequestOptions) =>
requestWithBody<T>('PATCH', url, data, options);

/**
* Makes a DELETE request. Is called destroy because delete is a JavaScript keyword.
*/
export const destroy = <T>(url: string, data?: any, options?: RequestOptions) =>
requestWithBody<T>('DELETE', url, data, options);
29 changes: 29 additions & 0 deletions src/users_api/UserAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { destroy, get, patch, post } from './BaseAPI';

const uri = encodeURIComponent;

export const getUser = (id: number) => {
return get('/users/' + uri(id))
}

export const updateUser = (id: number, data: any) => {
return patch('/users/' + uri(id), {
user: data
})
}

export const createUser = (data: any) => {
return post('/users/', {
user: data
})
}

export const login = (data: any) => {
return post('/authenticate/', {
user: data
})
}

export const currentUser = () => {
return get('/sessions/')
}