diff --git a/src/app.tsx b/src/app.tsx index 9cd8b0e..31b88e6 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -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"; @@ -27,6 +29,8 @@ export const App: React.FC = () => { + + diff --git a/src/components/layout/header.tsx b/src/components/layout/header.tsx index 57cd9fb..e17a2ec 100644 --- a/src/components/layout/header.tsx +++ b/src/components/layout/header.tsx @@ -67,8 +67,12 @@ export const Header: React.FC<{ onDark?: boolean }> = ({ onDark }) => { className="w-full h-full pr-4" /> } - - + + + + + + diff --git a/src/components/signin/index.tsx b/src/components/signin/index.tsx new file mode 100644 index 0000000..7e99982 --- /dev/null +++ b/src/components/signin/index.tsx @@ -0,0 +1,45 @@ +import React, { useState } from "react"; +import { login } from "../../users_api/UserAPI"; + +export const SignInForm: React.FC = () => { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + + 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 ( +
+
+
+ + 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" /> +
+
+ + 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" /> +
+
+ +
+
+
+ ) +} diff --git a/src/components/signup/index.tsx b/src/components/signup/index.tsx new file mode 100644 index 0000000..55b3abc --- /dev/null +++ b/src/components/signup/index.tsx @@ -0,0 +1,53 @@ +import React, { useState } from "react"; +import { createUser } from "../../users_api/UserAPI"; + +export const SignUpForm: React.FC = () => { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [password_confirmation, setPasswordConfirmation] = useState(""); + + 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 ( +
+
+
+ + 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" /> +
+
+ + 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" /> +
+
+ + 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" /> +
+
+ +
+
+
+ ) +} diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000..96a5387 --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,3 @@ +export default { + apiUrl: 'http://localhost:3000/api/v1' +} \ No newline at end of file diff --git a/src/pages/sign_in.tsx b/src/pages/sign_in.tsx new file mode 100644 index 0000000..9dc63c7 --- /dev/null +++ b/src/pages/sign_in.tsx @@ -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 = () => ( + + + + +); \ No newline at end of file diff --git a/src/pages/sign_up.tsx b/src/pages/sign_up.tsx new file mode 100644 index 0000000..ab6a89c --- /dev/null +++ b/src/pages/sign_up.tsx @@ -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 = () => ( + + + + +); \ No newline at end of file diff --git a/src/users_api/BaseAPI.ts b/src/users_api/BaseAPI.ts new file mode 100644 index 0000000..43582cf --- /dev/null +++ b/src/users_api/BaseAPI.ts @@ -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 = (response: Response): T | Promise => + response.status === 204 ? ({} as T) : response.json(); + +export const get = async (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(checkedResponse); +}; + +const requestWithBody = async ( + 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(checkedResponse); +}; + +export const post = (url: string, data: any, options?: RequestOptions) => + requestWithBody('POST', url, data, options); + +export const put = (url: string, data: any, options?: RequestOptions) => + requestWithBody('PUT', url, data, options); + +export const patch = (url: string, data: any, options?: RequestOptions) => + requestWithBody('PATCH', url, data, options); + +/** + * Makes a DELETE request. Is called destroy because delete is a JavaScript keyword. + */ +export const destroy = (url: string, data?: any, options?: RequestOptions) => + requestWithBody('DELETE', url, data, options); diff --git a/src/users_api/UserAPI.ts b/src/users_api/UserAPI.ts new file mode 100644 index 0000000..c8716bf --- /dev/null +++ b/src/users_api/UserAPI.ts @@ -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/') +} \ No newline at end of file