diff --git a/backend/data.sql b/backend/data.sql index 5ea046b..d0e01a1 100644 --- a/backend/data.sql +++ b/backend/data.sql @@ -37,7 +37,7 @@ CREATE TABLE charges ( CREATE TABLE questions ( id serial PRIMARY KEY, - user_id INTEGER NOT NULL REFERENCES users (id), + user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE, question TEXT NOT NULL, response TEXT, responder INTEGER REFERENCES users (id), diff --git a/backend/models/user.js b/backend/models/user.js index ea77c60..8193cec 100644 --- a/backend/models/user.js +++ b/backend/models/user.js @@ -90,7 +90,13 @@ class User { static async findAll() { const result = await db.query( - `SELECT id, first_name, last_name, current_company AS company, hire_date, needs, goals + `SELECT id, + first_name, + last_name, + current_company AS company, + hire_date, + needs, + goals FROM users ORDER BY id` ); @@ -101,7 +107,7 @@ class User { static async findOne(id) { const result = await db.query( - `SELECT email, is_admin, first_name, last_name, current_company, hire_date, needs, goals + `SELECT id, email, is_admin, first_name, last_name, current_company, hire_date, needs, goals FROM users WHERE id = $1`, [id] diff --git a/frontend/src/AdminPanel.js b/frontend/src/AdminPanel.js index 6525c7a..76665a3 100644 --- a/frontend/src/AdminPanel.js +++ b/frontend/src/AdminPanel.js @@ -30,15 +30,20 @@ class AdminPanel extends Component { users = await ElevateApi.getUsers(); questions = await ElevateApi.getQuestions(); } catch(err) { + console.log(err) return err; } - this.setState({ users }); - this.setState({ questions }); + this.setState({ users, questions }); } - changeView = view => { - this.setState({ view }); + // get update users after delete a user in AdminUserView + updateUserState = (users) => { + this.setState({users}) + } + + changeView = (view) => { + this.setState({ view }) } mediaQueryChanged = () => { @@ -49,8 +54,7 @@ class AdminPanel extends Component { this.setState({ sideBarOpen: !this.state.sideBarOpen }); } - getUserDetail = async evt => { - const userId = +evt.target.parentNode.firstElementChild.innerText; + getUserDetail = async (userId) => { const user = await ElevateApi.getUser(userId); this.setState({ view: 'userDetail', userDetail: user }); @@ -72,7 +76,12 @@ class AdminPanel extends Component { getUserDetail={ this.getUserDetail } view={ this.state.view } /> : null } - {this.state.view === 'userDetail' ? : null } + {this.state.view === 'userDetail' && + + }
diff --git a/frontend/src/AdminPanel.test.js b/frontend/src/AdminPanel.test.js index 32050ac..6a83d75 100644 --- a/frontend/src/AdminPanel.test.js +++ b/frontend/src/AdminPanel.test.js @@ -1,23 +1,86 @@ import React from 'react'; +import axios from 'axios'; import { shallow, mount } from 'enzyme'; import toJson from "enzyme-to-json"; import AdminPanel from './AdminPanel'; -describe('AdminPanel', function() { +jest.mock('axios'); +const users = { data: + { "users": [ + { "first_name": "Test", + "last_name": "User", + "company": "Google", + "hire_date": "2018-06-23T07:00:00.000Z", + "needs": "Talk to financial advisor about salary/equity negotiations.", + "goals": "Increase in equity." }, + { "first_name": "Admin", + "last_name": "User", + "company": "", + "hire_date": "2019-06-23T07:00:00.000Z", + "needs": "", "goals": "" } + ] + } + } +const user = { data: + {"user": + {"email":"testuser@gmail.com", + "is_admin":false, + "first_name":"Test", + "last_name":"User", + "current_company":"Google", + "hire_date":"2018-06-23T07:00:00.000Z", + "needs":"Talk to financial advisor about salary/equity negotiations.", + "goals":"Increase in equity."} + } + } + +const questions = { data: + { "questions": [ + { "first_name": "Test", + "last_name": "User", + "email": "testuser@gmail.com", + "question": "My employer didnt pay me!", + "created_date": "2019-09-01T19:28:53.468Z", + "resolved": false }, + { "first_name": "Admin", + "last_name": "User", + "email": "admin@gmail.com", + "question": "My employer wants to pay me too much!", + "created_date": "2019-09-01T19:28:53.468Z", + "resolved": false } + ] + } + } + +const id = "17" +axios.get.mockImplementation((reqUrl) => { + if (reqUrl.includes(id)) { + return user; + } + if (reqUrl.includes('users')) { + return users; + } + if (reqUrl.includes('questions')) { + return questions; + } +}); + +describe('AdminPanel', function () { + let wrapper; let users = [{ - id: 17, - email: "testadmin@test.com", - is_admin: true, - first_name: "admin", - last_name: "test", - current_company:"testcompany", - hire_date: "2018-06-23", - needs:"To test user data", - goals:"Test pass" + id: id, + email: "testadmin@test.com", + is_admin: true, + first_name: "admin", + last_name: "test", + current_company: "testcompany", + hire_date: "2018-06-23", + needs: "To test user data", + goals: "Test pass" }] let questions = [{ - user_id: 17, + user_id: id, question: "My employer didn't pay me", resolved: false, email: "user@test.com", @@ -26,15 +89,16 @@ describe('AdminPanel', function() { created_date: "2019-08-29" }] - beforeEach(() => { + beforeEach(async () => { wrapper = mount(); - wrapper.setState ({ users, questions }) + await wrapper.instance().componentDidMount() + wrapper.setState({ users, questions }) }); it('renders without crashing', function () { shallow(); }); - + it('matches snapshot', function () { const serialized = toJson(wrapper); @@ -72,12 +136,12 @@ describe('AdminPanel', function() { it('renders the users table when view state is users', function () { wrapper.find('div[id="users"]').simulate('click'); wrapper.update(); - + expect(wrapper.find('table[id="users-table"]')).toHaveLength(1); }); it('show expected user data in the table', function () { - wrapper.setState({users}); + wrapper.setState({ users }); wrapper.find('div[id="users"]').simulate('click') wrapper.update(); @@ -86,7 +150,7 @@ describe('AdminPanel', function() { const dataRow = rows.first().find('td').map(column => column.text()) expect(dataRow.length).toEqual(9); - expect(dataRow[0]).toEqual("17"); + expect(dataRow[0]).toEqual(id); expect(dataRow[1]).toEqual("testadmin@test.com"); expect(dataRow[2]).toEqual(""); expect(dataRow[3]).toEqual("admin"); @@ -100,12 +164,12 @@ describe('AdminPanel', function() { it('renders the questions table when view state is questions', function () { wrapper.find('div[id="questions"]').simulate('click'); wrapper.update(); - + expect(wrapper.find('table[id="questions-table"]')).toHaveLength(1); }); it('show expected question data in the table', function () { - wrapper.setState({questions}) + wrapper.setState({ questions }) wrapper.find('div[id="questions"]').simulate('click') wrapper.update(); @@ -114,7 +178,7 @@ describe('AdminPanel', function() { const dataRow = rows.first().find('td').map(column => column.text()); expect(dataRow.length).toEqual(7); - expect(dataRow[0]).toEqual("17"); + expect(dataRow[0]).toEqual(id); expect(dataRow[1]).toEqual("My employer didn't pay me"); expect(dataRow[2]).toEqual(""); expect(dataRow[3]).toEqual("user@test.com"); diff --git a/frontend/src/AdminTable.js b/frontend/src/AdminTable.js index 8f67b3e..1200d9c 100644 --- a/frontend/src/AdminTable.js +++ b/frontend/src/AdminTable.js @@ -8,9 +8,10 @@ const maxColumCount = mql.matches ? 5 : 12; class AdminTable extends Component { handleClick = (evt) => { - this.props.getUserDetail(evt); + const id = evt.target.parentElement.id; + + this.props.getUserDetail(id); } - createTableHeader() { const keys = Object.keys(this.props.tableObjs[0]); @@ -62,12 +63,12 @@ class AdminTable extends Component { createTableRows(keys, values) { return ( - + {values.map((value, index) => { value = this.concantinateText(value); return ( - { value } + { value } ); }).filter((value, idx) => idx < maxColumCount)} diff --git a/frontend/src/AdminUserView.js b/frontend/src/AdminUserView.js index 5971cf0..633d649 100644 --- a/frontend/src/AdminUserView.js +++ b/frontend/src/AdminUserView.js @@ -1,10 +1,29 @@ import React, { Component } from "react"; import './AdminUserView.css'; +import ElevateApi from './ElevateApi'; class AdminUserView extends Component { - render(){ - const { first_name, last_name, email, current_company, hire_date, needs, goals } = this.props.user; + handleClickDeleteUser = async () => { + await ElevateApi.deleteUser(this.props.user.id) + let users; + try { + users = await ElevateApi.getUsers(); + } catch(err) { + return err; + } + this.props.updateUserState(users) + this.props.changeView("users") + } + render(){ + const { first_name, + last_name, + email, + current_company, + hire_date, + needs, + goals } = this.props.user; +console.log('AdminUserView') return (
@@ -21,7 +40,7 @@ class AdminUserView extends Component { Hire Date: - { hire_date.slice(0, 10) } + { hire_date && hire_date.slice(0,10) } Needs: @@ -38,6 +57,12 @@ class AdminUserView extends Component {
+
) } diff --git a/frontend/src/ElevateApi.js b/frontend/src/ElevateApi.js index 4e2b902..ac06269 100644 --- a/frontend/src/ElevateApi.js +++ b/frontend/src/ElevateApi.js @@ -5,7 +5,6 @@ const BASE_URL = process.env.BASE_URL || "http://localhost:3001"; class ElevateApi { static async request(endpoint, params = {}, verb = "get") { let _token = localStorage.getItem("token"); - console.debug("API Call:", endpoint, params, verb); let q; @@ -19,6 +18,10 @@ class ElevateApi { } else if (verb === "patch") { q = axios.patch( `${BASE_URL}/${endpoint}`, { _token, ...params }); + } else if (verb === "delete") { + q = axios.delete( + `${BASE_URL}/${endpoint}`, { params: { _token, ...params } } + ) } try { @@ -56,9 +59,12 @@ class ElevateApi { let res = await this.request(`users`); // Format hire_date for each user - res.users.forEach(user => { - user.hire_date = user.hire_date.slice(0, 10); - }); + if (res.users){ + res.users.forEach(user => { + // check if the user has hire_date then format + user.hire_date = (user.hire_date && user.hire_date.slice(0, 10)); + }); + } return res.users } @@ -88,6 +94,10 @@ class ElevateApi { return res.questions } + static async deleteUser(id) { + await this.request(`users/${id}`, {}, "delete") + } + } export default ElevateApi; \ No newline at end of file diff --git a/frontend/src/LogInSignUpForm.js b/frontend/src/LogInSignUpForm.js index 70f5439..2c56162 100644 --- a/frontend/src/LogInSignUpForm.js +++ b/frontend/src/LogInSignUpForm.js @@ -37,11 +37,10 @@ class LoginSignUpForm extends Component { evt.preventDefault(); let token; - - try { if (this.state.isLogin) { - const data = { email: this.state.email, password: this.state.password }; + const data = { email: this.state.email, + password: this.state.password }; token = await ElevateApi.login(data) } else { const data = { @@ -55,7 +54,6 @@ class LoginSignUpForm extends Component { } catch (errors) { return this.setState({ errors }) } - localStorage.setItem("token", token); await this.props.getCurrentUser(); this.props.history.push("/"); diff --git a/frontend/src/Routes.js b/frontend/src/Routes.js index 0aa2c9d..bb545dd 100644 --- a/frontend/src/Routes.js +++ b/frontend/src/Routes.js @@ -19,8 +19,8 @@ class Routes extends React.Component { ()}/> - - ()}/> + + ()}/> diff --git a/frontend/src/__snapshots__/AdminUserView.test.js.snap b/frontend/src/__snapshots__/AdminUserView.test.js.snap index 2f0a635..3513f2a 100644 --- a/frontend/src/__snapshots__/AdminUserView.test.js.snap +++ b/frontend/src/__snapshots__/AdminUserView.test.js.snap @@ -85,6 +85,12 @@ exports[`AdminUserView matches snapshot 1`] = `
+
`;