Compare commits

...

5 Commits

Author SHA1 Message Date
075c80ad07 Adds readme
Some checks failed
gitea-deepak/gog_frontend/pipeline/head There was a failure building this commit
2021-04-19 12:33:30 -05:00
10b0b7544c Adds authenticated app stuff
Some checks failed
gitea-deepak/gog_frontend/pipeline/head There was a failure building this commit
2021-04-19 12:32:57 -05:00
fd4983b2dd Rearranges and adds plans search thing 2021-02-07 11:31:59 -06:00
a74d17b315 styling for authenticated app
All checks were successful
gitea-deepak/gog_frontend/pipeline/head This commit looks good
2021-02-01 16:23:07 -06:00
3aa3d41b84 Updates colour 2021-02-01 14:46:40 -06:00
30 changed files with 246 additions and 82 deletions

1
README.md Normal file
View File

@@ -0,0 +1 @@
React project for gogmagog frontend

View File

@@ -2,15 +2,14 @@ import React from "react";
import { hot } from "react-hot-loader";
import "./App.scss";
import { useAuth } from "./context/AuthContext";
import UnauthenticatedApp from "./components/UnauthenticatedApp";
import AuthenticatedApp from "./components/AuthenticatedApp";
import UnauthenticatedApp from "./screens/UnauthenticatedApp";
import AuthenticatedApp from "./screens/AuthenticatedApp";
function App() {
const { user } = useAuth();
console.log(user);
return (
<div className="app">
<h1>Frontend</h1>
<h1 className="main-title">GOG</h1>
{user ? <AuthenticatedApp /> : <UnauthenticatedApp />}
</div>
);

View File

@@ -11,3 +11,7 @@ body {
background-color: colours.$background-color;
color: colours.$text-color;
}
.main-title {
font-style: italic;
}

View File

@@ -3,4 +3,4 @@ $inverted-text-color: rgb(245, 235, 235);
$background-color: rgb(250, 245, 242);
$accent-color: rgb(70, 90, 200);
$dimmed-color: rgb(200, 200, 215);
$dimmed-color: rgb(220, 220, 245);

View File

@@ -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();
});

View File

@@ -1,17 +0,0 @@
import React from "react";
import { useAuth } from "../context/AuthContext";
function AuthenticatedApp() {
const { logout, user } = useAuth();
return (
<div>
<p>Howdy partner. Your name looks like it&apos;s {user.display_name}.</p>
<button type="button" onClick={logout}>
Logout
</button>
</div>
);
}
export default AuthenticatedApp;

View 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;

View 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;

View 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;

View File

@@ -1,17 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AuthenticatedApp Snapshot 1`] = `
<div>
<p>
Howdy partner. Your name looks like it's
Ted
.
</p>
<button
onClick={[MockFunction]}
type="button"
>
Logout
</button>
</div>
`;

View File

@@ -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;

View File

@@ -1,6 +1,7 @@
import React, { useState } from "react";
import { register, login } from "../services/auth-service";
import { getUserInfo } from "../services/user-service";
import { client } from "../services/api-client";
const localStorageKey = "__auth_token__";
const AuthContext = React.createContext();
@@ -14,6 +15,7 @@ const AuthProvider = (props) => {
return;
}
getUserInfo(token).then((user) => {
user.token = token;
setUser(user);
});
};
@@ -59,4 +61,13 @@ function useAuth() {
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 };

View 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&apos;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;

View File

@@ -0,0 +1,17 @@
@use '../common_styles/colours';
@use '../common_styles/typography';
.authenticated-app-outer {
.authenticated-app {
padding: 1em;
margin: 1em;
}
button {
background-color: colours.$accent-color;
border: 0;
color: colours.$inverted-text-color;
font: typography.$stack;
margin: 10px;
padding: 10px 15px;
}
}

View 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();
});
};

View 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",
});
};
};