├── src
├── App.css
├── index.js
├── reducers
│ ├── index.js
│ ├── techReducer.js
│ └── logReducer.js
├── components
│ ├── layout
│ │ ├── Preloader.js
│ │ ├── AddBtn.js
│ │ └── SearchBar.js
│ ├── techs
│ │ ├── TechSelectOptions.js
│ │ ├── TechItem.js
│ │ ├── TechListModal.js
│ │ └── AddTechModal.js
│ └── logs
│ │ ├── Logs.js
│ │ ├── LogItem.js
│ │ ├── AddLogModal.js
│ │ └── EditLogModal.js
├── store.js
├── actions
│ ├── types.js
│ ├── techActions.js
│ └── logActions.js
└── App.js
├── public
├── favicon.ico
├── manifest.json
└── index.html
├── .gitignore
├── README.md
├── db.json
└── package.json
/src/App.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bradtraversy/it-logger/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import logReducer from './logReducer';
3 | import techReducer from './techReducer';
4 |
5 | export default combineReducers({
6 | log: logReducer,
7 | tech: techReducer
8 | });
9 |
--------------------------------------------------------------------------------
/src/components/layout/Preloader.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Preloader = () => {
4 | return (
5 |
8 | );
9 | };
10 |
11 | export default Preloader;
12 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux';
2 | import { composeWithDevTools } from 'redux-devtools-extension';
3 | import thunk from 'redux-thunk';
4 | import rootReducer from './reducers';
5 |
6 | const initialState = {};
7 |
8 | const middleware = [thunk];
9 |
10 | const store = createStore(
11 | rootReducer,
12 | initialState,
13 | composeWithDevTools(applyMiddleware(...middleware))
14 | );
15 |
16 | export default store;
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ITLogger
2 |
3 | > React app to track IT department tasks and issues. Uses JSON-Server as a mock backend. This is part of my React course on Udemy
4 |
5 | ## Usage
6 |
7 | ### `npm install`
8 |
9 | ### `npm run dev`
10 |
11 | Runs the app with the backend JSON-Server
12 | Open [http://localhost:3000](http://localhost:3000)
13 | Server [http://localhost:5000](http://localhost:5000)
14 | db.json contains data
15 |
16 | ### `npm run build`
17 |
18 | Builds the app for production to the `build` folder.
19 |
--------------------------------------------------------------------------------
/src/actions/types.js:
--------------------------------------------------------------------------------
1 | export const GET_LOGS = 'GET_LOGS';
2 | export const ADD_LOG = 'ADD_LOG';
3 | export const DELETE_LOG = 'DELETE_LOG';
4 | export const SET_CURRENT = 'SET_CURRENT';
5 | export const CLEAR_CURRENT = 'CLEAR_CURRENT';
6 | export const UPDATE_LOG = 'UPDATE_LOG';
7 | export const CLEAR_LOGS = 'CLEAR_LOGS';
8 | export const SET_LOADING = 'SET_LOADING';
9 | export const LOGS_ERROR = 'LOGS_ERROR';
10 | export const SEARCH_LOGS = 'SEARCH_LOGS';
11 | export const GET_TECHS = 'GET_TECHS';
12 | export const ADD_TECH = 'ADD_TECH';
13 | export const DELETE_TECH = 'DELETE_TECH';
14 | export const TECHS_ERROR = 'TECHS_ERROR';
15 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 | IT Logger
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/components/layout/AddBtn.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const AddBtn = () => {
4 | return (
5 |
28 | );
29 | };
30 |
31 | export default AddBtn;
32 |
--------------------------------------------------------------------------------
/src/components/techs/TechSelectOptions.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { connect } from 'react-redux';
3 | import PropTypes from 'prop-types';
4 | import { getTechs } from '../../actions/techActions';
5 |
6 | const TechSelectOptions = ({ getTechs, tech: { techs, loading } }) => {
7 | useEffect(() => {
8 | getTechs();
9 | // eslint-disable-next-line
10 | }, []);
11 |
12 | return (
13 | !loading &&
14 | techs !== null &&
15 | techs.map(t => (
16 |
19 | ))
20 | );
21 | };
22 |
23 | TechSelectOptions.propTypes = {
24 | tech: PropTypes.object.isRequired,
25 | getTechs: PropTypes.func.isRequired
26 | };
27 |
28 | const mapStateToProps = state => ({
29 | tech: state.tech
30 | });
31 |
32 | export default connect(
33 | mapStateToProps,
34 | { getTechs }
35 | )(TechSelectOptions);
36 |
--------------------------------------------------------------------------------
/src/components/techs/TechItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import PropTypes from 'prop-types';
4 | import { deleteTech } from '../../actions/techActions';
5 | import M from 'materialize-css/dist/js/materialize.min.js';
6 |
7 | const TechItem = ({ tech: { id, firstName, lastName }, deleteTech }) => {
8 | const onDelete = () => {
9 | deleteTech(id);
10 | M.toast({ html: 'Technician deleted' });
11 | };
12 |
13 | return (
14 |
15 |
21 |
22 | );
23 | };
24 |
25 | TechItem.propTypes = {
26 | tech: PropTypes.object.isRequired,
27 | deleteTech: PropTypes.func.isRequired
28 | };
29 |
30 | export default connect(
31 | null,
32 | { deleteTech }
33 | )(TechItem);
34 |
--------------------------------------------------------------------------------
/src/components/techs/TechListModal.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { connect } from 'react-redux';
3 | import PropTypes from 'prop-types';
4 | import TechItem from './TechItem';
5 | import { getTechs } from '../../actions/techActions';
6 |
7 | const TechListModal = ({ getTechs, tech: { techs, loading } }) => {
8 | useEffect(() => {
9 | getTechs();
10 | // eslint-disable-next-line
11 | }, []);
12 |
13 | return (
14 |
15 |
16 |
Technician List
17 |
18 | {!loading &&
19 | techs !== null &&
20 | techs.map(tech => )}
21 |
22 |
23 |
24 | );
25 | };
26 |
27 | TechListModal.propTypes = {
28 | tech: PropTypes.object.isRequired,
29 | getTechs: PropTypes.func.isRequired
30 | };
31 |
32 | const mapStateToProps = state => ({
33 | tech: state.tech
34 | });
35 |
36 | export default connect(
37 | mapStateToProps,
38 | { getTechs }
39 | )(TechListModal);
40 |
--------------------------------------------------------------------------------
/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "logs": [
3 | {
4 | "message": "Changed network card in server 007",
5 | "attention": false,
6 | "tech": "Sam Smith",
7 | "date": "2019-05-31T15:46:10.179Z",
8 | "id": 1
9 | },
10 | {
11 | "id": 2,
12 | "message": "Fixed hard drive on workstation 002",
13 | "attention": false,
14 | "tech": "John Doe",
15 | "date": "2019-05-31T16:18:04.245Z"
16 | },
17 | {
18 | "message": "1122 wireless down",
19 | "attention": true,
20 | "tech": "Sara Wilson",
21 | "date": "2019-05-31T15:46:48.690Z",
22 | "id": 3
23 | },
24 | {
25 | "id": 4,
26 | "message": "Workstation 026 is up and running",
27 | "attention": false,
28 | "tech": "Sara Wilson",
29 | "date": "2019-05-31T19:57:35.544Z"
30 | }
31 | ],
32 | "techs": [
33 | {
34 | "id": 1,
35 | "firstName": "John",
36 | "lastName": "Doe"
37 | },
38 | {
39 | "id": 2,
40 | "firstName": "Sam",
41 | "lastName": "Smith"
42 | },
43 | {
44 | "id": 3,
45 | "firstName": "Sara",
46 | "lastName": "Wilson"
47 | }
48 | ]
49 | }
--------------------------------------------------------------------------------
/src/reducers/techReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | GET_TECHS,
3 | ADD_TECH,
4 | DELETE_TECH,
5 | SET_LOADING,
6 | TECHS_ERROR
7 | } from '../actions/types';
8 |
9 | const initialState = {
10 | techs: null,
11 | loading: false,
12 | error: null
13 | };
14 |
15 | export default (state = initialState, action) => {
16 | switch (action.type) {
17 | case GET_TECHS:
18 | return {
19 | ...state,
20 | techs: action.payload,
21 | loading: false
22 | };
23 | case ADD_TECH:
24 | return {
25 | ...state,
26 | techs: [...state.techs, action.payload],
27 | loading: false
28 | };
29 | case DELETE_TECH:
30 | return {
31 | ...state,
32 | techs: state.techs.filter(tech => tech.id !== action.payload),
33 | loading: false
34 | };
35 | case SET_LOADING:
36 | return {
37 | ...state,
38 | loading: true
39 | };
40 | case TECHS_ERROR:
41 | console.error(action.payload);
42 | return {
43 | ...state,
44 | error: action.payload,
45 | loading: false
46 | };
47 | default:
48 | return state;
49 | }
50 | };
51 |
--------------------------------------------------------------------------------
/src/components/logs/Logs.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { connect } from 'react-redux';
3 | import LogItem from './LogItem';
4 | import Preloader from '../layout/Preloader';
5 | import PropTypes from 'prop-types';
6 | import { getLogs } from '../../actions/logActions';
7 |
8 | const Logs = ({ log: { logs, loading }, getLogs }) => {
9 | useEffect(() => {
10 | getLogs();
11 | // eslint-disable-next-line
12 | }, []);
13 |
14 | if (loading || logs === null) {
15 | return ;
16 | }
17 |
18 | return (
19 |
20 | -
21 |
System Logs
22 |
23 | {!loading && logs.length === 0 ? (
24 | No logs to show...
25 | ) : (
26 | logs.map(log => )
27 | )}
28 |
29 | );
30 | };
31 |
32 | Logs.propTypes = {
33 | log: PropTypes.object.isRequired,
34 | getLogs: PropTypes.func.isRequired
35 | };
36 |
37 | const mapStateToProps = state => ({
38 | log: state.log
39 | });
40 |
41 | export default connect(
42 | mapStateToProps,
43 | { getLogs }
44 | )(Logs);
45 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect } from 'react';
2 | import SearchBar from './components/layout/SearchBar';
3 | import Logs from './components/logs/Logs';
4 | import AddBtn from './components/layout/AddBtn';
5 | import AddLogModal from './components/logs/AddLogModal';
6 | import EditLogModal from './components/logs/EditLogModal';
7 | import AddTechModal from './components/techs/AddTechModal';
8 | import TechListModal from './components/techs/TechListModal';
9 | import { Provider } from 'react-redux';
10 | import store from './store';
11 |
12 | import 'materialize-css/dist/css/materialize.min.css';
13 | import M from 'materialize-css/dist/js/materialize.min.js';
14 | import './App.css';
15 |
16 | const App = () => {
17 | useEffect(() => {
18 | // Init Materialize JS
19 | M.AutoInit();
20 | });
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | };
37 |
38 | export default App;
39 |
--------------------------------------------------------------------------------
/src/components/layout/SearchBar.js:
--------------------------------------------------------------------------------
1 | import React, { useRef } from 'react';
2 | import { connect } from 'react-redux';
3 | import PropTypes from 'prop-types';
4 | import { searchLogs } from '../../actions/logActions';
5 |
6 | const SearchBar = ({ searchLogs }) => {
7 | const text = useRef('');
8 |
9 | const onChange = e => {
10 | searchLogs(text.current.value);
11 | };
12 |
13 | return (
14 |
33 | );
34 | };
35 |
36 | SearchBar.propTypes = {
37 | searchLogs: PropTypes.func.isRequired
38 | };
39 |
40 | export default connect(
41 | null,
42 | { searchLogs }
43 | )(SearchBar);
44 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "it-logger",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "materialize-css": "^1.0.0",
7 | "moment": "^2.24.0",
8 | "react": "^16.8.6",
9 | "react-dom": "^16.8.6",
10 | "react-moment": "^0.9.2",
11 | "react-redux": "^7.0.3",
12 | "react-scripts": "3.0.1",
13 | "redux": "^4.0.1",
14 | "redux-devtools-extension": "^2.13.8",
15 | "redux-thunk": "^2.3.0"
16 | },
17 | "scripts": {
18 | "start": "react-scripts start",
19 | "json-server": "json-server --watch db.json --port 5000",
20 | "dev": "concurrently \"npm start\" \"npm run json-server\"",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": "react-app"
27 | },
28 | "browserslist": {
29 | "production": [
30 | ">0.2%",
31 | "not dead",
32 | "not op_mini all"
33 | ],
34 | "development": [
35 | "last 1 chrome version",
36 | "last 1 firefox version",
37 | "last 1 safari version"
38 | ]
39 | },
40 | "devDependencies": {
41 | "concurrently": "^4.1.0",
42 | "json-server": "^0.15.0"
43 | },
44 | "proxy": "http://localhost:5000"
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/logs/LogItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Moment from 'react-moment';
3 | import { connect } from 'react-redux';
4 | import PropTypes from 'prop-types';
5 | import { deleteLog, setCurrent } from '../../actions/logActions';
6 |
7 | import M from 'materialize-css/dist/js/materialize.min.js';
8 |
9 | const LogItem = ({ log, deleteLog, setCurrent }) => {
10 | const onDelete = () => {
11 | deleteLog(log.id);
12 | M.toast({ html: 'Log Deleted' });
13 | };
14 |
15 | return (
16 |
17 |
37 |
38 | );
39 | };
40 |
41 | LogItem.propTypes = {
42 | log: PropTypes.object.isRequired,
43 | deleteLog: PropTypes.func.isRequired,
44 | setCurrent: PropTypes.func.isRequired
45 | };
46 |
47 | export default connect(
48 | null,
49 | { deleteLog, setCurrent }
50 | )(LogItem);
51 |
--------------------------------------------------------------------------------
/src/actions/techActions.js:
--------------------------------------------------------------------------------
1 | import {
2 | GET_TECHS,
3 | ADD_TECH,
4 | DELETE_TECH,
5 | SET_LOADING,
6 | TECHS_ERROR
7 | } from './types';
8 |
9 | // Get techs from server
10 | export const getTechs = () => async dispatch => {
11 | try {
12 | setLoading();
13 |
14 | const res = await fetch('/techs');
15 | const data = await res.json();
16 |
17 | dispatch({
18 | type: GET_TECHS,
19 | payload: data
20 | });
21 | } catch (err) {
22 | dispatch({
23 | type: TECHS_ERROR,
24 | payload: err.response.statusText
25 | });
26 | }
27 | };
28 |
29 | // Add technician to server
30 | export const addTech = tech => async dispatch => {
31 | try {
32 | setLoading();
33 |
34 | const res = await fetch('/techs', {
35 | method: 'POST',
36 | body: JSON.stringify(tech),
37 | headers: {
38 | 'Content-Type': 'application/json'
39 | }
40 | });
41 | const data = await res.json();
42 |
43 | dispatch({
44 | type: ADD_TECH,
45 | payload: data
46 | });
47 | } catch (err) {
48 | dispatch({
49 | type: TECHS_ERROR,
50 | payload: err.response.statusText
51 | });
52 | }
53 | };
54 |
55 | export const deleteTech = id => async dispatch => {
56 | try {
57 | setLoading();
58 |
59 | await fetch(`/techs/${id}`, {
60 | method: 'DELETE'
61 | });
62 |
63 | dispatch({
64 | type: DELETE_TECH,
65 | payload: id
66 | });
67 | } catch (err) {
68 | dispatch({
69 | type: TECHS_ERROR,
70 | payload: err.response.statusText
71 | });
72 | }
73 | };
74 |
75 | // Set loading to true
76 | export const setLoading = () => {
77 | return {
78 | type: SET_LOADING
79 | };
80 | };
81 |
--------------------------------------------------------------------------------
/src/reducers/logReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | GET_LOGS,
3 | SET_LOADING,
4 | LOGS_ERROR,
5 | ADD_LOG,
6 | DELETE_LOG,
7 | UPDATE_LOG,
8 | SEARCH_LOGS,
9 | SET_CURRENT,
10 | CLEAR_CURRENT
11 | } from '../actions/types';
12 |
13 | const initialState = {
14 | logs: null,
15 | current: null,
16 | loading: false,
17 | error: null
18 | };
19 |
20 | export default (state = initialState, action) => {
21 | switch (action.type) {
22 | case GET_LOGS:
23 | return {
24 | ...state,
25 | logs: action.payload,
26 | loading: false
27 | };
28 | case ADD_LOG:
29 | return {
30 | ...state,
31 | logs: [...state.logs, action.payload],
32 | loading: false
33 | };
34 | case DELETE_LOG:
35 | return {
36 | ...state,
37 | logs: state.logs.filter(log => log.id !== action.payload),
38 | loading: false
39 | };
40 | case UPDATE_LOG:
41 | return {
42 | ...state,
43 | logs: state.logs.map(log =>
44 | log.id === action.payload.id ? action.payload : log
45 | )
46 | };
47 | case SEARCH_LOGS:
48 | return {
49 | ...state,
50 | logs: action.payload
51 | };
52 | case SET_CURRENT:
53 | return {
54 | ...state,
55 | current: action.payload
56 | };
57 | case CLEAR_CURRENT:
58 | return {
59 | ...state,
60 | current: null
61 | };
62 | case SET_LOADING:
63 | return {
64 | ...state,
65 | loading: true
66 | };
67 | case LOGS_ERROR:
68 | console.error(action.payload);
69 | return {
70 | ...state,
71 | error: action.payload
72 | };
73 | default:
74 | return state;
75 | }
76 | };
77 |
--------------------------------------------------------------------------------
/src/components/techs/AddTechModal.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { connect } from 'react-redux';
3 | import PropTypes from 'prop-types';
4 | import { addTech } from '../../actions/techActions';
5 | import M from 'materialize-css/dist/js/materialize.min.js';
6 |
7 | const AddTechModal = ({ addTech }) => {
8 | const [firstName, setFirstName] = useState('');
9 | const [lastName, setLastName] = useState('');
10 |
11 | const onSubmit = () => {
12 | if (firstName === '' || lastName === '') {
13 | M.toast({ html: 'Please enter the first and last name' });
14 | } else {
15 | addTech({
16 | firstName,
17 | lastName
18 | });
19 |
20 | M.toast({ html: `${firstName} ${lastName} was added as a tech` });
21 |
22 | // Clear Fields
23 | setFirstName('');
24 | setLastName('');
25 | }
26 | };
27 |
28 | return (
29 |
30 |
31 |
New Technician
32 |
33 |
34 | setFirstName(e.target.value)}
39 | />
40 |
43 |
44 |
45 |
46 |
47 |
48 | setLastName(e.target.value)}
53 | />
54 |
57 |
58 |
59 |
60 |
69 |
70 | );
71 | };
72 |
73 | AddTechModal.propTypes = {
74 | addTech: PropTypes.func.isRequired
75 | };
76 |
77 | export default connect(
78 | null,
79 | { addTech }
80 | )(AddTechModal);
81 |
--------------------------------------------------------------------------------
/src/components/logs/AddLogModal.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import TechSelectOptions from '../techs/TechSelectOptions';
3 | import { connect } from 'react-redux';
4 | import PropTypes from 'prop-types';
5 | import { addLog } from '../../actions/logActions';
6 | import M from 'materialize-css/dist/js/materialize.min.js';
7 |
8 | const AddLogModal = ({ addLog }) => {
9 | const [message, setMessage] = useState('');
10 | const [attention, setAttention] = useState(false);
11 | const [tech, setTech] = useState('');
12 |
13 | const onSubmit = () => {
14 | if (message === '' || tech === '') {
15 | M.toast({ html: 'Please enter a message and tech' });
16 | } else {
17 | const newLog = {
18 | message,
19 | attention,
20 | tech,
21 | date: new Date()
22 | };
23 |
24 | addLog(newLog);
25 |
26 | M.toast({ html: `Log added by ${tech}` });
27 |
28 | // Clear Fields
29 | setMessage('');
30 | setTech('');
31 | setAttention(false);
32 | }
33 | };
34 |
35 | return (
36 |
37 |
38 |
Enter System Log
39 |
40 |
41 | setMessage(e.target.value)}
46 | />
47 |
50 |
51 |
52 |
53 |
54 |
55 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
82 |
83 |
84 |
85 |
86 |
95 |
96 | );
97 | };
98 |
99 | AddLogModal.propTypes = {
100 | addLog: PropTypes.func.isRequired
101 | };
102 |
103 | const modalStyle = {
104 | width: '75%',
105 | height: '75%'
106 | };
107 |
108 | export default connect(
109 | null,
110 | { addLog }
111 | )(AddLogModal);
112 |
--------------------------------------------------------------------------------
/src/actions/logActions.js:
--------------------------------------------------------------------------------
1 | import {
2 | GET_LOGS,
3 | SET_LOADING,
4 | LOGS_ERROR,
5 | ADD_LOG,
6 | DELETE_LOG,
7 | UPDATE_LOG,
8 | SEARCH_LOGS,
9 | SET_CURRENT,
10 | CLEAR_CURRENT
11 | } from './types';
12 |
13 | // export const getLogs = () => {
14 | // return async dispatch => {
15 | // setLoading();
16 |
17 | // const res = await fetch('/logs');
18 | // const data = await res.json();
19 |
20 | // dispatch({
21 | // type: GET_LOGS,
22 | // payload: data
23 | // });
24 | // };
25 | // };
26 |
27 | // Get logs from server
28 | export const getLogs = () => async dispatch => {
29 | try {
30 | setLoading();
31 |
32 | const res = await fetch('/logs');
33 | const data = await res.json();
34 |
35 | dispatch({
36 | type: GET_LOGS,
37 | payload: data
38 | });
39 | } catch (err) {
40 | dispatch({
41 | type: LOGS_ERROR,
42 | payload: err.response.statusText
43 | });
44 | }
45 | };
46 |
47 | // Add new log
48 | export const addLog = log => async dispatch => {
49 | try {
50 | setLoading();
51 |
52 | const res = await fetch('/logs', {
53 | method: 'POST',
54 | body: JSON.stringify(log),
55 | headers: {
56 | 'Content-Type': 'application/json'
57 | }
58 | });
59 | const data = await res.json();
60 |
61 | dispatch({
62 | type: ADD_LOG,
63 | payload: data
64 | });
65 | } catch (err) {
66 | dispatch({
67 | type: LOGS_ERROR,
68 | payload: err.response.statusText
69 | });
70 | }
71 | };
72 |
73 | // Delete log from server
74 | export const deleteLog = id => async dispatch => {
75 | try {
76 | setLoading();
77 |
78 | await fetch(`/logs/${id}`, {
79 | method: 'DELETE'
80 | });
81 |
82 | dispatch({
83 | type: DELETE_LOG,
84 | payload: id
85 | });
86 | } catch (err) {
87 | dispatch({
88 | type: LOGS_ERROR,
89 | payload: err.response.statusText
90 | });
91 | }
92 | };
93 |
94 | // Update log on server
95 | export const updateLog = log => async dispatch => {
96 | try {
97 | setLoading();
98 |
99 | const res = await fetch(`/logs/${log.id}`, {
100 | method: 'PUT',
101 | body: JSON.stringify(log),
102 | headers: {
103 | 'Content-Type': 'application/json'
104 | }
105 | });
106 |
107 | const data = await res.json();
108 |
109 | dispatch({
110 | type: UPDATE_LOG,
111 | payload: data
112 | });
113 | } catch (err) {
114 | dispatch({
115 | type: LOGS_ERROR,
116 | payload: err.response.statusText
117 | });
118 | }
119 | };
120 |
121 | // Search server logs
122 | export const searchLogs = text => async dispatch => {
123 | try {
124 | setLoading();
125 |
126 | const res = await fetch(`/logs?q=${text}`);
127 | const data = await res.json();
128 |
129 | dispatch({
130 | type: SEARCH_LOGS,
131 | payload: data
132 | });
133 | } catch (err) {
134 | dispatch({
135 | type: LOGS_ERROR,
136 | payload: err.response.statusText
137 | });
138 | }
139 | };
140 |
141 | // Set current log
142 | export const setCurrent = log => {
143 | return {
144 | type: SET_CURRENT,
145 | payload: log
146 | };
147 | };
148 |
149 | // Clear current log
150 | export const clearCurrent = () => {
151 | return {
152 | type: CLEAR_CURRENT
153 | };
154 | };
155 |
156 | // Set loading to true
157 | export const setLoading = () => {
158 | return {
159 | type: SET_LOADING
160 | };
161 | };
162 |
--------------------------------------------------------------------------------
/src/components/logs/EditLogModal.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import TechSelectOptions from '../techs/TechSelectOptions';
3 | import { connect } from 'react-redux';
4 | import PropTypes from 'prop-types';
5 | import M from 'materialize-css/dist/js/materialize.min.js';
6 | import { updateLog } from '../../actions/logActions';
7 |
8 | const EditLogModal = ({ current, updateLog }) => {
9 | const [message, setMessage] = useState('');
10 | const [attention, setAttention] = useState(false);
11 | const [tech, setTech] = useState('');
12 |
13 | useEffect(() => {
14 | if (current) {
15 | setMessage(current.message);
16 | setAttention(current.attention);
17 | setTech(current.tech);
18 | }
19 | }, [current]);
20 |
21 | const onSubmit = () => {
22 | if (message === '' || tech === '') {
23 | M.toast({ html: 'Please enter a message and tech' });
24 | } else {
25 | const updLog = {
26 | id: current.id,
27 | message,
28 | attention,
29 | tech,
30 | date: new Date()
31 | };
32 |
33 | updateLog(updLog);
34 | M.toast({ html: `Log updated by ${tech}` });
35 |
36 | // Clear Fields
37 | setMessage('');
38 | setTech('');
39 | setAttention(false);
40 | }
41 | };
42 |
43 | return (
44 |
45 |
46 |
Enter System Log
47 |
48 |
49 | setMessage(e.target.value)}
54 | />
55 |
56 |
57 |
58 |
59 |
60 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
87 |
88 |
89 |
90 |
91 |
100 |
101 | );
102 | };
103 |
104 | const modalStyle = {
105 | width: '75%',
106 | height: '75%'
107 | };
108 |
109 | EditLogModal.propTypes = {
110 | current: PropTypes.object,
111 | updateLog: PropTypes.func.isRequired
112 | };
113 |
114 | const mapStateToProps = state => ({
115 | current: state.log.current
116 | });
117 |
118 | export default connect(
119 | mapStateToProps,
120 | { updateLog }
121 | )(EditLogModal);
122 |
--------------------------------------------------------------------------------