Compare commits
2 Commits
a74d17b315
...
10b0b7544c
| Author | SHA1 | Date | |
|---|---|---|---|
|
10b0b7544c
|
|||
|
fd4983b2dd
|
@@ -2,12 +2,11 @@ import React from "react";
|
|||||||
import { hot } from "react-hot-loader";
|
import { hot } from "react-hot-loader";
|
||||||
import "./App.scss";
|
import "./App.scss";
|
||||||
import { useAuth } from "./context/AuthContext";
|
import { useAuth } from "./context/AuthContext";
|
||||||
import UnauthenticatedApp from "./components/UnauthenticatedApp";
|
import UnauthenticatedApp from "./screens/UnauthenticatedApp";
|
||||||
import AuthenticatedApp from "./components/AuthenticatedApp";
|
import AuthenticatedApp from "./screens/AuthenticatedApp";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
console.log(user);
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
<h1 className="main-title">GOG</h1>
|
<h1 className="main-title">GOG</h1>
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import renderer from "react-test-renderer";
|
|
||||||
import AuthenticatedApp from "./AuthenticatedApp";
|
|
||||||
import { AuthContext } from "../context/AuthContext";
|
|
||||||
|
|
||||||
test("AuthenticatedApp Snapshot", () => {
|
|
||||||
const appRender = renderer.create(
|
|
||||||
<AuthContext.Provider
|
|
||||||
value={{
|
|
||||||
login: jest.fn(),
|
|
||||||
register: jest.fn(),
|
|
||||||
logout: jest.fn(),
|
|
||||||
user: { display_name: "Ted" },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AuthenticatedApp />
|
|
||||||
</AuthContext.Provider>
|
|
||||||
);
|
|
||||||
|
|
||||||
const tree = appRender.toJSON();
|
|
||||||
|
|
||||||
expect(tree).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { useAuth } from "../context/AuthContext";
|
|
||||||
import "./AuthenticatedApp.scss";
|
|
||||||
|
|
||||||
function AuthenticatedApp() {
|
|
||||||
const { logout, user } = useAuth();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="authenticated-app-outer">
|
|
||||||
<button type="button" onClick={logout}>
|
|
||||||
Logout
|
|
||||||
</button>
|
|
||||||
<div className="authenticated-app">
|
|
||||||
<p>
|
|
||||||
Howdy partner. Your name looks like it's {user.display_name}.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AuthenticatedApp;
|
|
||||||
31
src/components/CurrentPlan/index.jsx
Normal file
31
src/components/CurrentPlan/index.jsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import Plan from "../Plan";
|
||||||
|
|
||||||
|
function CurrentPlan({ currentPlan, getPlanForID }) {
|
||||||
|
const [plan, setPlan] = useState();
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentPlan && currentPlan.plan_id) {
|
||||||
|
getPlanForID(currentPlan.plan_id).then((data) => {
|
||||||
|
setPlan(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [currentPlan, getPlanForID]);
|
||||||
|
|
||||||
|
const ret = currentPlan ? (
|
||||||
|
<div className="current-plan-wrapper">
|
||||||
|
<p>{JSON.stringify(currentPlan)}</p>
|
||||||
|
<Plan plan={plan} />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>No current plan</div>
|
||||||
|
);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentPlan.propTypes = {
|
||||||
|
currentPlan: PropTypes.object,
|
||||||
|
getPlanForID: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CurrentPlan;
|
||||||
26
src/components/Plan/index.jsx
Normal file
26
src/components/Plan/index.jsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import React from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
|
function Plan({ initial_plan }) {
|
||||||
|
|
||||||
|
const onChangeDescription = (e) => {
|
||||||
|
const p = {
|
||||||
|
...plan,
|
||||||
|
plan_description: e.currentTarget.value
|
||||||
|
};
|
||||||
|
savePlan(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="plan-wrapper">
|
||||||
|
<p>{plan.plan_id}: </p>
|
||||||
|
<input type="text" onChange={onChangeDescription} value={plan.plan_description}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Plan.propTypes = {
|
||||||
|
plan: PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Plan;
|
||||||
24
src/components/PlanList/index.jsx
Normal file
24
src/components/PlanList/index.jsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import React from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import Plan from "../Plan";
|
||||||
|
|
||||||
|
function PlanList({ plans, savePlan }) {
|
||||||
|
return (
|
||||||
|
<ul className="plan-list-wrapper">
|
||||||
|
{plans.map((plan) => {
|
||||||
|
return (
|
||||||
|
<li key={plan.plan_id}>
|
||||||
|
<Plan plan={plan} savePlan={savePlan}/>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlanList.propTypes = {
|
||||||
|
plans: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
savePlan: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PlanList;
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`AuthenticatedApp Snapshot 1`] = `
|
|
||||||
<div
|
|
||||||
className="authenticated-app-outer"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
onClick={[MockFunction]}
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
Logout
|
|
||||||
</button>
|
|
||||||
<div
|
|
||||||
className="authenticated-app"
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
Howdy partner. Your name looks like it's
|
|
||||||
Ted
|
|
||||||
.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { NavLink } from "react-router-dom";
|
|
||||||
|
|
||||||
function Header() {
|
|
||||||
return (
|
|
||||||
<nav>
|
|
||||||
<NavLink exact activeClassName="active" to="/">
|
|
||||||
Home
|
|
||||||
</NavLink>
|
|
||||||
<NavLink activeClassName="active" to="/users">
|
|
||||||
Users
|
|
||||||
</NavLink>
|
|
||||||
<NavLink activeClassName="active" to="/contact">
|
|
||||||
Contact
|
|
||||||
</NavLink>
|
|
||||||
</nav>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export default Header;
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { register, login } from "../services/auth-service";
|
import { register, login } from "../services/auth-service";
|
||||||
import { getUserInfo } from "../services/user-service";
|
import { getUserInfo } from "../services/user-service";
|
||||||
|
import { client } from "../services/api-client";
|
||||||
|
|
||||||
const localStorageKey = "__auth_token__";
|
const localStorageKey = "__auth_token__";
|
||||||
const AuthContext = React.createContext();
|
const AuthContext = React.createContext();
|
||||||
@@ -14,6 +15,7 @@ const AuthProvider = (props) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getUserInfo(token).then((user) => {
|
getUserInfo(token).then((user) => {
|
||||||
|
user.token = token;
|
||||||
setUser(user);
|
setUser(user);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -59,4 +61,13 @@ function useAuth() {
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { AuthProvider, AuthContext, useAuth };
|
function useClient() {
|
||||||
|
const { logout, user } = useAuth();
|
||||||
|
const token = user?.token;
|
||||||
|
return React.useCallback(
|
||||||
|
(endpoint, config) => client(endpoint, { ...config, token, logout }),
|
||||||
|
[token, logout]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { AuthProvider, AuthContext, useAuth, useClient };
|
||||||
|
|||||||
74
src/screens/AuthenticatedApp.jsx
Normal file
74
src/screens/AuthenticatedApp.jsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
|
import { useAuth, useClient } from "../context/AuthContext";
|
||||||
|
import "./AuthenticatedApp.scss";
|
||||||
|
import PlanList from "../components/PlanList";
|
||||||
|
import { getPlansFunc, savePlanFunc } from "../services/plans-service";
|
||||||
|
|
||||||
|
function AuthenticatedApp() {
|
||||||
|
const { logout, user } = useAuth();
|
||||||
|
|
||||||
|
const [plans, setPlans] = useState([]);
|
||||||
|
const [isLoading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState("HI");
|
||||||
|
const client = useClient();
|
||||||
|
// const getPlans = getPlansFunc(client);
|
||||||
|
|
||||||
|
const getPlans = useCallback(() => getPlansFunc(client)(), [client]);
|
||||||
|
const savePlan = useCallback(
|
||||||
|
(plan) => {
|
||||||
|
console.log("in the callback saveplan func");
|
||||||
|
console.log(plan);
|
||||||
|
return savePlanFunc(client)(plan)
|
||||||
|
}, [client]);
|
||||||
|
|
||||||
|
const updatePlan = (plan) => {
|
||||||
|
savePlan(plan)
|
||||||
|
.then((data) => {
|
||||||
|
console.log("returning from save plan");
|
||||||
|
console.log(data);
|
||||||
|
setError(null);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setError(error);
|
||||||
|
})
|
||||||
|
const newPlans = plans.map(currentPlan => {
|
||||||
|
if (currentPlan.plan_id === plan.plan_id) {
|
||||||
|
return plan;
|
||||||
|
}
|
||||||
|
return currentPlan;
|
||||||
|
});
|
||||||
|
setPlans(newPlans);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(true);
|
||||||
|
getPlans()
|
||||||
|
.then((data) => {
|
||||||
|
setPlans(data);
|
||||||
|
setLoading(false);
|
||||||
|
setError(null);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setError(error);
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}, [getPlans]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="authenticated-app-outer">
|
||||||
|
<button type="button" onClick={logout}>
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
<div className="authenticated-app">
|
||||||
|
<p>
|
||||||
|
Howdy partner. Your name looks like it's {user.display_name}.
|
||||||
|
</p>
|
||||||
|
<p>Error: {error || "No errors"}</p>
|
||||||
|
<p>Loading: {isLoading ? "Loading" : "Not loading"}</p>
|
||||||
|
<PlanList plans={plans} savePlan={updatePlan} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AuthenticatedApp;
|
||||||
31
src/services/api-client.jsx
Normal file
31
src/services/api-client.jsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { API_ROOT } from "./config";
|
||||||
|
|
||||||
|
export const client = (
|
||||||
|
endpoint,
|
||||||
|
{ data, token, logout, headers: customHeaders, ...customConfig } = {}
|
||||||
|
) => {
|
||||||
|
const config = {
|
||||||
|
method: data ? "POST" : "GET",
|
||||||
|
body: data ? JSON.stringify(data) : undefined,
|
||||||
|
headers: {
|
||||||
|
Authorization: token ? `Bearer ${token}` : undefined,
|
||||||
|
"Content-Type": data ? "application/json" : undefined,
|
||||||
|
Accept: "application/json",
|
||||||
|
...customHeaders,
|
||||||
|
},
|
||||||
|
...customConfig,
|
||||||
|
};
|
||||||
|
const url = `${API_ROOT}${endpoint}`;
|
||||||
|
|
||||||
|
return fetch(url, config).then(async (response) => {
|
||||||
|
if (response.status === 401) {
|
||||||
|
logout();
|
||||||
|
window.location.assign(window.location);
|
||||||
|
return Promise.reject(Error("Gotta re-auth"));
|
||||||
|
}
|
||||||
|
if (!response.ok) {
|
||||||
|
return Promise.reject(Error(response.statusText));
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
});
|
||||||
|
};
|
||||||
22
src/services/plans-service.jsx
Normal file
22
src/services/plans-service.jsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
export const getCurrentPlanFunc = (client) => {
|
||||||
|
return () => client("currentPlan");
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPlanForIDFunc = (client) => {
|
||||||
|
return (id) => {
|
||||||
|
return client(`plans/${id}`);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPlansFunc = (client) => {
|
||||||
|
return () => client("plans/");
|
||||||
|
};
|
||||||
|
|
||||||
|
export const savePlanFunc = (client) => {
|
||||||
|
return (plan) => {
|
||||||
|
return client(`plans/${plan.plan_id}`, {
|
||||||
|
data: plan,
|
||||||
|
method: "PUT",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user